Download as pdf or txt
Download as pdf or txt
You are on page 1of 68

ĐẠI HỌC QUỐC GIA THÀNH PHỐ HỒ CHÍ MINH

TRƯỜNG ĐẠI HỌC BÁCH KHOA


KHOA ĐIỆN – ĐIỆN TỬ
-------

BÁO CÁO THÍ NGHIỆM


MÔN ĐO LƯỜNG VÀ ĐIỀU KHIỂN BẰNG MÁY TÍNH
GVHD: TRẦN HOÀNG KHÔI NGUYÊN

NHÓM 02

HUỲNH VĂN ĐẠT MSSV: 1833427

1
Mục lục

1. BÀI 1: Lập trình PLC S7-1200 - Các ứng dụng lập trình cơ bản .......................... 3
1.1 Thí Nghiệm 1: Giải thuật và Chương trình PLC các thí nghiệm 1.1 và 2.2, giải thích
hoạt động ..................................................................................................................... 3
1.2 Thí nghiệm 2: Đề xuất giải thuật tối ưu, hoàn thiện giải thuật ............................... 15
2. BÀI 2: Truyền thông nối tiếp giữa PC và PLC ..................................................... 17
2.1 Thí nghiệm 1: Làm quen với PLC S7-1200 ........................................................ 17
2.2 Thí nghiệm 2: Giao tiếp giữa PC và PLC, sử dụng ngắt nhận ký tự, truyền thông
qua PORT 0, giao thức “9600, N,8,1” ......................................................................... 20
3. BÀI 3: Giao tiếp TCP/IP ........................................................................................ 28
3.1 Thí nghiệm 1: Kết nối Modbus giữa PLC và PC ................................................. 28
3.2 Thí nghiệm 2: Đề xuất giải thuật tối ưu, hoàn thiện giải thuật ............................... 32
4. BÀI 4: Lập trình card USB giao tiếp với máy tính ............................................... 38
4.1 Thí nghiệm 1: Lập trình vi điều khiển sử dụng keil-C .......................................... 38
4.2 Thí nghiệm 2: Tạo window form app C# ............................................................ 42
4.3 Thí nghiệm 3: Điều khiển vị trí động cơ bằng bộ điều khiển PID .......................... 61

2
BÁO CÁO THÍ NGHIỆM ĐO LƯỜNG VÀ ĐIỀU KHIỂN MÁY TÍNH
BÀI THÍ NGHIỆM SỐ 1
LẬP TRÌNH PLC S7-1200- CÁC ỨNG DỤNG LẬP TRÌNH CƠ BẢN
1. Giải thuật và Chương trình PLC các thí nghiệm 1.1 và 2.2, giải thích hoạt động
1.1 Thí nghiệm 1.2:

3
Mới mở PLC, chuyển trạng thái vào cho Byte MB1 để xử lý. Khởi động trạng thái ST1.

Đọc Nút nhấn, kiểm tra trạng thái

4
Chế độ dịch phải, với tần số 1Hz

Chế độ dịch trái với tần số 0.5Hz


1.2 Thí nghiệm 2.2:
Mô phỏng đối tượng:

5
6
7
8
9
Viết chương trình phân loại sản phẩn với tín hiệu giả lập được:
Phương pháp 1: Giải quyết bài toán theo phương pháp tuần tự với bộ tín hiệu ngõ vào là
CB1_CB2_CB3; ngõ ra là N_TB_D

Giải thích: Khi đi vào chương trình, nếu có tín hiệu xung cạnh lên của cảm biến 1 thì bắt
đầu kiểm tra trạng thái của sản phẩm. Cho nó rơi vào trạng thái ngắn. Tiếp theo, nếu có
cả CB1 và CB2 cùng bị chặn thì là sản phẩm trung bình. Cuối cùng, nếu CB1 và CB3
cùng bị chặn thì sản phẩm dài. Nếu có hàng mới đi và thì bắt đầu lại chương trình.
Lập trình:

10
Khối điều kiện:

11
12
Khối trạng thái:

13
Sau khi nhận được cảm biến 3 thì bắt đầu xét trạng thái và báo đèn tín hiệu
Nhận xét: chương trình chỉ đúng khi có từng sản phẩm đi vào, kiểm tra sản phẩm này
xong mới kiểm tra sản phẩm khác.

14
2. Đề xuất giải thuật tối ưu, hoàn thiện giải thuật cho thí nghiệm 2.1:
2.1 Nhận xét chương trình mô phỏng băng tải ở trên:
Chương trình mô phỏng ở trên có thể mô phỏng được tín hiệu cảm biến cho từng
sản phẩm với kích thước khác nhau, tuy nhiên vẫn chưa chỉnh được tốc độ băng
truyền.
2.2 Phương pháp:
Sử dụng gán biến cho các thời gian so sánh ở trên để mô phỏng tốc độ của băng
truyền. Tăng thời gian Timer ứng với tốc độ chậm nhất có thể của băng tải.

3. Lập bảng so sánh ưu khuyết điển của các phương án thí nghiệm trong thí nghiệm
2.2?
Phương pháp 1 Phương pháp 2
Độ phức tạp của giải thuật Độ phức tạp thấp Độ phức tạp cao

Số lượng câu lệnh Ít hơn Nhiều hơn

Tính hệ thống của bài toán Có tính hệ thống Chưa hệ thống, mỗi khi muốn thay
đổi thì phải xét lại từng trường hợp.
Các yếu tố lỗi có thể phát sinh Nếu sản phẩm đang kiểm tra Vì kiểm tra được các trạng thái của
chưa chạy xong mà có sản phẩm điều kiện nên cho dù trong bất cứ
tiếp theo đi vào thì sẽ kiểm tra trường hợp nào thì cũng có thể phát
được sản phẩm đó. hiện được sản phẩm cho dù có
nhiều sản phẩm liên tiếp.

15
BÁO CÁO THÍ NGHIỆM ĐO LƯỜNG VÀ ĐIỀU KHIỂN MÁY TÍNH
BÀI THÍ NGHIỆM SỐ 2
TRUYỀN THÔNG NỐI TIẾP GIỮA PC VÀ PLC
TIẾN HÀNH THÍ NGHIỆM:
Thí nghiệm 1: Làm quen với PLC S7-200:
Thí nghiệm 1.1: Thực hiện điều khiển đèn giao thông:
- Yêu cầu: Thực hiện bài toán điều khiển đèn giao thông với các
thông số đèn: XA = 30s, VA = 3s, DA = 43s; XB = 40s, VB = 3s, DB =
33s
- Dùng phần mềm Step 7 Microwin thực hiện biện dịch và nạp chương
trình mẫu đã cho trong tài liệu hỗ trợ Thí nghiệm vào PLC theo cổng
COM kết nối giữa PLC với máy tính.
- Quan sát hoạt động của PLC và tín hiệu đèn giao thông, nhận xét kết
quả
Thí nghiệm 1.2: Cải tiến chương trình của thí nghiệm 1.1 hoạt động
theo các chế độ khác nhau:
Yêu cầu:

- Khi SW1 tác động: đèn hoạt động theo chu kỳ XA = 30s, VA = 3s, DA
= 43s; XB = 40s, VB = 3s, DB = 33s.
- Khi SW2 tác động: đèn hoạt động theo chu kỳ XA = 60s, VA = 5s, DA
= 63s; XB = 60s, VB = 5s, DB = 63s.
- Khi SW3 tác động: đèn VA và VB nhấp nháy theo chu kỳ 1s.

Tiến hành viết chương trình cho PLC :


Network 1

LDN T40
LPS
TON T37, VW130
A T37
TON T38, VW132
LRD
A T38

17
TON T39, VW134
LPP
A T39
TON T40, VW136

Network 2

LDN T37
= Q0.0

Network 3

LD T37
AN T38
= Q0.1

Network 4

LD T38
AN M1.2
= Q0.2

Network 5

LD T38
AN T39

= Q0.3

Network 6

LD T39

= Q0.4

Network 7

18
LDN T38
AN M1.2
= Q0.5

Network 8

LD I0.3
O M1.0
AN I0.5
AN I0.4
MOVW 300, VW130
MOVW 30, VW132
MOVW 400, VW134
MOVW 30, VW136
= M1.0

Network 9

LD I0.4
O M1.1
AN I0.5
AN I0.3
MOVW 600, VW130
MOVW 50, VW132
MOVW 600, VW134
MOVW 50, VW136
= M1.1

Network 10

LD I0.5
O M1.2
AN I0.4
AN I0.3
MOVW 0, VW130
MOVW 10, VW132
19
MOVW 0, VW134
MOVW 10, VW136
= M1.2

Thí nghiệm 2: Giao tiếp giữa PC và PLC, sử dụng ngắt nhận ký tự,
truyền thông qua PORT 0, giao thức “9600, N,8,1”
Thí nghiệm 2.1: Thực hiện Liên kết đơn giản giữa PLC và PC
- Yêu cầu: điều khiển Start/Stop động cơ từ Panel điều khiển hoặc giao diện
máy tính, hiển thị trạng thái hoạt động của động cơ lên máy tính.

 Qui định:
- Motor ON: máy tính truyền xuống PLC kí tự “A” hoặc BT1 tác động
- Motor OFF: máy tính truyền xuống PLC kí tự “B” hoặc BT2 tác động
- Khi motor ON: PLC truyền xuống máy tính kí tự “A”, hình tròn giao
diện hiển thị màu đỏ
- Khi motor OFF: PLC truyền xuống máy tính kí tự “B”, hình tròn giao
diện hiển thị màu trắng
 Viết chương trình giao diện máy tính và nạp chương trình cho PLC, quan
sát chương trình chạy đúng như yêu cầu:
- Khi nhấn SW1: ta thấy trạng thái đèn thay đổi như sau:
XA = 30s, VA = 3s, DA = 43s; XB = 40s, VB = 3s, DB = 33s.
- Khi nhấn SW2: ta thấy trạng thái đèn thay đổi như sau:
XA = 60s, VA = 5s, DA = 63s;XB = 60s, VB = 5s, DB = 63s.
- Khi nhấn SW3: ta thấy đèn VA và VB thay đổi như sau:
Sáng 0.5s rồi tắt trong 0.5s.

20
Thí nghiệm 2.2: thực hiện cải tiến bài toán điều khiển đèn
giao thông của thí nghiệm 1.1 và quan sát trên giao diện.
- Yêu cầu:
+ Thay đổi thông số của các đèn XA, VA, XB, VB
+ Hiển thị trạng thái các đèn Xanh, Vàng, Đỏ
+ Hiển thị thời gian đếm ngược dạng decimal
+ Hiển thị thời gian đếm ngược dạng led 7 SEG

Viết chương trình:


HÀM MAIN:

21
22
23
24
SM0. MOV_B
EN ENO

IN OUT VB200

Q0.0 MOV_B
EN ENO

'A' IN OUT VB201

MOV_B
EN ENO

'C' IN OUT VB201

Q0.1 MOV_B
EN ENO

'D' IN OUT VB202

MOV_B
EN ENO

'E' IN OUT VB202

Q0.2 MOV_B
EN ENO

'F' IN OUT VB203

MOV_B
EN ENO

'G' IN OUT VB203

Q0.3 MOV_B
EN ENO

25
26
HÀM INT0:

27
BÁO CÁO THÍ NGHIỆM ĐO LƯỜNG VÀ ĐIỀU KHIỂN MÁY TÍNH
BÀI THÍ NGHIỆM SỐ 3
GIAO TIẾP TCP/IP
A. Bài thí nghiệm 1: kết nối Modbus giữa PLC và PC

SERVER V4.0:

Giao diện GUI:

28
Chương trình C# cho phép đọc và ghi 6 thanh ghi dữ liệu từ PLC như giao diện
trên:
- Địa chỉ PLC kết nối là 192.168.0.1, Port 502 cho kết nối Modbus.
- Nút nhấn Write điều khiển ghi dữ liệu vào xuống PLC
- Nút nhấn Read yêu cầu đọc nội dung các thanh ghi từ PLC
- Địa chỉ bắt đầu ghi và đọc được thiết lập ở nội dung Start Address.
Sau khi kết nối Modbus thì có thể gửi dữ liệu xuống PC:

29
Dữ liệu nhận được ở dưới PC:

Dữ liệu truyền từ PLC lên PC:

Dữ liệu
truyền lên
PC

30
Dữ liệu trên PC nhận được:

NHẬN XÉT:
- Truyền nhận dữ liệu xác và nhanh.
- Ít bị nhiễu.
- Tín hiệu có thể được truyền nhanh hơn trên khoảng cách lớn.
- Có thể kết nối nhiều thiết bị.

31
B. Bài thí nghiệm 2: Phát triển Thí nghiệm 1 ứng dụng điều khiển nhiệt độ
Chuẩn bị thí nghiệm:
 Đấu dây kết nối giữa lò nhiệt và Panel điều khiển như sau:
o Đầu GND giữa Panel điều khiển và Bộ công suất.
o Nối ngõ ra Q0.0 với xung RES_X của Bộ công suất.
o Nối tín hiệu Analog từ bộ transmitter vào ngõ vào Analog AI1.
 Tính toán thông số nhiệt độ:
o Transmitter cho ra điện áp từ 2V đến 10V tương ứng với nhiệt độ 00C đến
5000C.
o Giá trị chuyển đổi ADC nằm trong ô nhớ IW64 của PLC.
o Giá trị của IW64 tại 10V là 27648.
 Thiết lập thời gian hẹn giờ của lò nhiệt là 30 phút.

1. Thí nghiệm 2.1: Điều khiển ON/OFF lò nhiệt, vùng trễ 0 3 C , nhiệt độ đặt
600 C. Chu kỳ lấy mẫu 0.5s
- Sử dụng thanh ghi BUFF.data[0] điều khiển đóng ngắt lò nhiệt.
- Tín hiệu nhiệt độ IW64 được đọc về PC thông qua thanh ghi BUFF.data[6].
- Bài toán điều khiển được thực hiện từ PC.
- Vẽ đồ thị quan sát nhiệt độ đặt và nhiệt đô thực.

- Giải thuật điều khiển ON/OFF được thực hiện ở thí nghiệm 2.1:
o Khi tín hiệu trả về nhiệt độ dưới ( nhiệt độ đặt – 3) thì tín hiệu điều khiển
bằng 1.
o Khi tín hiệu trả về nhiệt độ cao hơn (nhiệt độ đặt +3) thì tín hiệu điều khiển
bằng 0.
o Khi tín hiệu trả về trong khoảng (nhiệt độ đặt -3 : nhiệt độ đặt+3) thì tín hiệu
điều khiển bằng tín hiệu điều khiển trước đó.

32
- (Các) đoạn Chương trình liên quan đến thuật toán điều khiển ON/OFF?

private void ONOFF(double setpoint, double error)


{
ek1= ek;
ek = setpoint - yk; ////////yk là tín hiệu đọc về
if (((ek - ek1) >= 0) && (ek >= error))
uk = 1;
else if (((ek - ek1) < 0) && (ek <= -error))
uk = 0;
ek1 = ek;
}

33
Kết quả thu được:

2. Thí nghiệm 2.2: Điều khiển PID lò nhiệt, thời gian lấy mẫu 0.2s; các thông số
Kp, Ki, Kd sinh viên tự chọn. Công suất ngõ ra thay đổi từ 0-100%.
- Sử dụng thanh ghi BUFF.data[0] là thông số % công suất cung cấp cho lò nhiệt.
- Sử dụng phương pháp điều rộng xung PWM với thông số độ rộng xung từ
BUFF.data[0], chu kỳ xung PWM là 1s.
- Tín hiệu nhiệt độ IW64 được đọc về PC thông qua thanh ghi BUFF.data[6].
- Bài toán điều khiển được thực hiện từ PC.
- Vẽ đồ thị quan sát nhiệt độ đặt và nhiệt đô thực.

34
- Giải thuật điều khiển PID được thực hiện ở thí nghiệm 2.2:
Từ tín hiệu trả về nhiệt độ từ cảm biến và nhiệt độ đặt ta tính ra được sai số :
ek = setpoint – yk;
- seppoint: nhiệt độ đặt
- yk: tín hiệu trả về nhiệt độ
Sau đó dùng giải thuật PID đã học tính ra udk, thì udk chính là % công suất, để
điều khiển được lò nhiệt thì ta phải chuyển udk sang % điện áp để điều rộng xung
pwm để điều khiển lò nhiệt. Chuyển tín hiệu công suất sang tín hiệu điện áp bằng
cách udk1 = √𝑢𝑑𝑘 ;
Gửi udk1 thông qua thanh ghi BUFF.data[0] xuống PLC.
Rồi từ trong PLC xử lý để xuất xung PWM điều khiển lò nhiệt.
- (Các) đoạn Chương trình liên quan đến thuật toán điều khiển ON/OFF?
private void PID(double Kp, double Ki, double Kd, double
setpoint, double T)
{
ek = setpoint - yk;
uk = uk1 + Kp * (ek - ek1) + Ki * (T / 2) * (ek +
ek1) + (Kd / T) * (ek - 2 * ek1 + ek2);
if (uk < 0)
uk = 0;
else if (uk > 1)
uk = 1;
ek2 = ek1;
ek1 = ek;
udk1 = math.sqrt(uk);
}

Lưu ý: T là thời gian lấy mẫu, ta chọn 0.1s đối với lò nhiệt.

35
Cách thiết lập PLC hoạt động phát xung PWM, chương trình PLC nhận lệnh và
thực hiện điều khiển PWM
Thanh ghi BUFF.data[1] phát hiện là đang điều khiển PWM hay ONOFF
Với 1 là PWM còn 2 là ONOFF

36
Kết quả điều khiển được:

Bộ điều
khiển ONOFF Bộ điều khiển
để tìm PID
Kp,Ki,Kd

NHẬN XÉT:
- Bộ điều khiển PID vẫn còn dao động.
- Sai số xác lập nhỏ.
- Đáp ứng nhanh.
- Hầu như không có lọt vố.

37
BÀI THÍ NGHIỆM SỐ 4
LẬP TRÌNH CARD USB GIAO TIẾP VỚI MÁY TÍNH

I. Bài thí nghiệm 1: Lập trình vi điều khiển sử dụng Keil-C


1. Tạo project với Keil – C.
2. Add thư viện vào project.
3. Add đường dẫn tới file header và các define.
4. Viết chương trình trên vi điều khiển:
- Include các thư viện:
#include "main.h"
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include "stm32f3xx_hal.h"
#include "driver.h"
#include "usb_device.h"
#include "usbd_custom_hid_if.h"

- Khai báo các biến toàn cục:

static uint8_t DI_value;


static float AO_value[2];
static int AI_value[3];
static uint32_t C0_value;
static uint32_t DO_pwm_frequency[3];
static uint16_t Ts_ms =100;//ms
static uint8_t usb_rx_buffer[64];
static uint8_t usb_tx_buffer[17];
static volatile uint8_t usb_tx_flag = 0;
static volatile uint8_t usb_rx_flag = 0;

- Khai báo prototype của các hàm trong chương trình:


void USB_RX_Interrupt(void);
void Sample_Timer_Interrupt(void);

- Trong hà m main này, đầ u tiên ta sẽ khởi tạo các ngoại vi, cấu hình xung
clock, sau đó, chương trình sẽ vào hàm while(1). Trong hà m này, ta sẽ
liên tu ̣c kiể m tra cờ usb_tx_flag và usb_rx_flag để thực hiện việc điề u
khiể n các ngoại vi:

38
/* Reset of all peripherals, Initializes the Flash
interface and the Systick. */HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
GPIO_Init();
AI_Init();
DO_Init();
AO_Init();
Counter_Init();
MX_USB_DEVICE_Init();
Sample_Timer_Init();

- Bên trong while(1):


if(usb_tx_flag)
{
usb_tx_flag =0;
DI_value = DI_Read_All();
C0_value = Counter_Read();
AI_Read_All(AI_value);
/* DIN */
usb_tx_buffer[0] = DI_value;
/* counter */
usb_tx_buffer[1] = (uint8_t)(C0_value >> 24);
usb_tx_buffer[2] = (uint8_t)(C0_value >> 16);
usb_tx_buffer[3] = (uint8_t)(C0_value >> 8);
usb_tx_buffer[4] = (uint8_t)(C0_value);
/* AIN */
usb_tx_buffer[5] = (uint8_t)(AI_value[0] >> 24);
usb_tx_buffer[6] = (uint8_t)(AI_value[0] >> 16);
usb_tx_buffer[7] = (uint8_t)(AI_value[0] >> 8);
usb_tx_buffer[8] = (uint8_t)(AI_value[0]);
usb_tx_buffer[9] = (uint8_t)(AI_value[1] >> 24);
usb_tx_buffer[10] = (uint8_t)(AI_value[1] >> 16);
usb_tx_buffer[11] = (uint8_t)(AI_value[1] >> 8);
usb_tx_buffer[12] = (uint8_t)(AI_value[1]);
usb_tx_buffer[13] = (uint8_t)(AI_value[2] >> 24);
usb_tx_buffer[14] = (uint8_t)(AI_value[2] >> 16);
usb_tx_buffer[15] = (uint8_t)(AI_value[2] >> 8);
usb_tx_buffer[16] = (uint8_t)(AI_value[2]);
USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,usb_tx_buffer,17)
;
}

if (usb_rx_flag)

39
{
int i;
usb_rx_flag = 0;
switch (usb_rx_buffer[0])/* cmd id */
{
case 'N':/* DO, AO */
{
//DO
DO_Write_All(&usb_rx_buffer[1]);
//AO
AO_value[0] =
((float)usb_rx_buffer[9]*256+(float)usb_rx_buffer[10])/100
0;
AO_value[1] =
((float)usb_rx_buffer[11]*256+(float)usb_rx_buffer[12])/10
00;
AO_Write_All(AO_value);
//reset Counter
if (usb_rx_buffer[13] == 'R')
{
Counter_Reset();
}
break;
}
case 'F':/* pwm frequency */
{
for(i=0;i<3;i++)
{
DO_pwm_frequency[i]= (uint32_t)(usb_rx_buffer[i+1]);
}
DO_pwm_set_frequency(DO_pwm_frequency);
break;
}
case 'G':/* ADC 18 bit gain */
{
AI18_Set_Gain(usb_rx_buffer[1]);
break;
}
case 'T':/* sample time */
{
Ts_ms = ((int)usb_rx_buffer[1]<<8) +
(int)usb_rx_buffer[2];
Sample_Timer_Set_Period(Ts_ms);
}
}
}
}

40
- hàm USB_RX_Interrupt dưới hàm main:

void USB_RX_Interrupt(void)
{
int i;
USBD_CUSTOM_HID_HandleTypeDef
*myusb=(USBD_CUSTOM_HID_HandleTypeDef
*)hUsbDeviceFS.pClassData;
//myusb->Report_buf[0]= numbers of byte data
for (i=0;i<myusb->Report_buf[0];i++)
{
usb_rx_buffer[i]=myusb->Report_buf[i+1];
}
usb_rx_flag = 1;
}

- hàm Sample_Timer_Interrupt:

void Sample_Timer_Interrupt(void)
{
usb_tx_flag =1;
}

- hàm _Error_Handler và hàm assert_failed:

void _Error_Handler(char *file, int line)


{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL
error return state */
while(1)
{
}
/* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the
file name and line number,
tex: printf("Wrong parameters value: file %s on line
%d\r\n", file, line) */

41
/* USER CODE END 6 */
}
#endif

II. Bài thí nghiệm 2: Tạo window form app C#


1. Tạo project mới.
2. Add Reference (Thư viện) cho Windown Form.
3. Tạo giao diện GUI.

42
- Khai báo các thư viện:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LibUsbDotNet;
using LibUsbDotNet.Info;
using LibUsbDotNet.Main;

- Khai báo các biến sử dụng truyền và nhận dữ liệu, các biến tính toán điều
khiển PID.

namespace HIF__
{
public partial class Form1 : Form
{
#region variable
byte DI_value;
byte[] DO_value = new byte[8];
byte[] DO_text = new byte[8];
double[] PWM_text = new double[8];
double[] F_text = new double[3];
byte[] F_value = new byte[3];
double gain1;
byte G_value;
Int16 Ts_text;
byte[] Ts_value = new byte[2];
//
double AO_0_text;
double AO_1_text;

43
Int16 AO_0_value;
Int16 AO_1_value;

byte[] AO_0 = new byte[2];


byte[] AO_1 = new byte[2];
//
double AI_0_VALUE;
double AI_1_VALUE;
double AI_2_VALUE;

int ai_0_value;
int ai_1_value;
int ai_2_value;

byte[] AI_0_value = new byte[4];


byte[] AI_1_value = new byte[4];
byte[] AI_2_value = new byte[4];
//
byte[] c_value = new byte[4];
int C_value;
//
byte reC0;
//
double pidSetpoint = 0, pidOutput = 0, pidPreError = 0;
double currentPos = 0;
double pidError = 0;
double Kp = 0.071, Ki = 0.017, Kd = 0.000041;
double pPart = 0, iPart = 0, dPart = 0;

bool enablePID = false;

#endregion variable

- Khai báo biến toàn cục cho kết nối USB:

44
public Form1()
{
InitializeComponent();
}

#region Declaring Global Variable


public static UsbDevice myUsbDevice, myUsbDevice_temp;
UsbEndpointReader reader;
UsbEndpointWriter writer;
IAsyncResult ketthuc;
#endregion Declaring Global Variable

- Đoạn chương trình đóng ứng dụng:

private void Form1_FormClosing(object sender, FormClosingEventArgs e)


{
if (MessageBox.Show("Are you want to exit ?",
"Confirmation",MessageBoxButtons.YesNo, MessageBoxIcon.Question) ==
DialogResult.Ys)

}
else
{
e.Cancel = true;
}
}

private void Form1_FormClosed(object sender,


FormClosedEventArgs e)
{
try
{
Usb_exit();
Application.Exit();
}
catch
{
}
}

45
- Config Timer và giá trị ban đầu của các Textbox.

private void Form1_Load(object sender, EventArgs e)


{
timer1.Interval = 200;
timer1.Enabled = false;
G_value = 1;
gain1 = 1;
Ts_value[0] = 0;
Ts_value[1] = 10;
Ts.Text = "10";
Ts_text = 10;
F_value[0] = 2;
F0.Text = "2";
F_value[1] = 2;
F1.Text = "2";
F_value[2] = 2;
F2.Text = "2";
cbEnablePID.Checked = false;
}

- Chương trình cho button “Connect”.

private void btnconnect_Click(object sender, EventArgs e)


{
if (btnconnect.Text == "Connect")
{
if (myUsbDevice == null)
{
UsbRegDeviceList allDevices = UsbDevice.AllDevices;

46
foreach (UsbRegistry usbRegistry in allDevices)
{
if (usbRegistry.Open(out myUsbDevice))
{
txtproductname.Text =
myUsbDevice.Info.ProductString;
txtvendorid.Text =
myUsbDevice.Info.Descriptor.VendorID.ToString();
txtproductid.Text =
myUsbDevice.Info.Descriptor.ProductID.ToString();
txtmanufacturer.Text =
myUsbDevice.Info.ManufacturerString;
USB_DATA_RECEIVER_INIT();
btnconnection.Text = "Disconnect";
timer1.Enabled = true;
}
}
}
if (myUsbDevice == null)
{
MessageBox.Show("Device Not Found !!!", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
Usb_exit();
btnconnection.Text = "Connect";
txtproductname.Text = "";
txtvendorid.Text = "";
txtproductid.Text = "";
txtmanufacturer.Text = "";
textBox1.Text = "";
textBox2.Text = "";
textBox4.Text = "";

47
textBox7.Text = "";
textBox8.Text = "";
txt_AI_0.Text = "";
txt_AI_1.Text = "";
txt_Counter_0.Text = "";
}
}

 Sau khi ấn button “connect” thì button sẽ chuyển sang “disconnect” và vi


điều khiển đã kết nối và ngược lại. Nếu chưa kết nối được thì sẽ có dòng
thông báo “DEVICE NOT FOUND” và phải kết nối lại.

- Khởi tạo nhận dữ liệu qua cổng USB.

#region USB_DATA_RECEIVER_INIT
void USB_DATA_RECEIVER_INIT()
{
IUsbDevice wholeUsbDevice = myUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
wholeUsbDevice.SetConfiguration(1);
wholeUsbDevice.ClaimInterface(0);
}
//Open usb end point reader and writer
reader =
myUsbDevice.OpenEndpointReader(ReadEndpointID.Ep01);
writer =
myUsbDevice.OpenEndpointWriter(WriteEndpointID.Ep01);

//Set Interrupt service rountie for reader complete event


reader.DataReceived += (OnRxEndPointData);
reader.DataReceivedEnabled = true;
}
#endregion USB_DATA_RECEIVER_INIT

48
- Thiết lập kết nối đóng mở.

#region USB EXIT


private void Usb_exit()
{
reader.DataReceivedEnabled = false;
reader.DataReceived -= (OnRxEndPointData);
//this.EndInvoke(ketthuc);
reader.Dispose();
writer.Dispose();
if (myUsbDevice != null)
{
if (myUsbDevice.IsOpen)
{
IUsbDevice wholeUsbDevice = myUsbDevice as IUsbDevice;
if (!ReferenceEquals(wholeUsbDevice, null))
{
wholeUsbDevice.ReleaseInterface(0);
}
myUsbDevice.Close();

}
myUsbDevice = null;
UsbDevice.Exit();
}
}
#endregion USB EXIT

- Hàm ngắt nhận.

#region USB DATA RECEIVER INTERRUPT SERVICE ROUNTIE


Action<byte[]> UsbReceiverAction;

49
private void OnRxEndPointData(object sender,
EndpointDataEventArgs e)
{

UsbReceiverAction = UsbReceiverActionFunction;
if ((myUsbDevice.IsOpen) && (reader != null))
{
ketthuc = this.BeginInvoke(UsbReceiverAction, e.Buffer);
}
}

- Hàm xử lí khi check vào ô các checkbox PWM.

#region PWMtext
private void PWM0_CheckedChanged(object sender, EventArgs e)
{
if (textBox1.Text == "")
{
DO_text[0] = 0;
}
}
private void PWM1_CheckedChanged(object sender, EventArgs e)
{
if (textBox2.Text == "")
{
DO_text[1] = 0;
}
}

private void PWM3_CheckedChanged(object sender, EventArgs e)


{
if (textBox4.Text == "")
{

50
DO_text[3] = 0;
}
}

private void PWM6_CheckedChanged(object sender, EventArgs e)


{
if (textBox7.Text == "")
{
DO_text[6] = 0;
}
}

private void PWM7_CheckedChanged(object sender, EventArgs e)


{
if (textBox8.Text == "")
{
DO_text[7] = 0;
}
}

- Hàm cho việc nhập giá trị PWM trong khoảng từ 0 – 100, nếu vượt ngoài
khoảng này sẽ báo lỗi và yêu cầu nhập lại.

private void textBox2_TextChanged(object sender, EventArgs e)


{
if (textBox2.Text == "")
{
DO_text[1] = 0;
}
else
{
PWM_text[1] = (Int16)double.Parse(textBox2.Text);
if (PWM_text[1] < 0 || PWM_text[1] > 100)

51
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 0
đến 100", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
DO_text[1] = (byte)(PWM_text[1]);
}
}
}

private void textBox4_TextChanged(object sender, EventArgs e)


{
if (textBox4.Text == "")
{
DO_text[3] = 0;
}
else
{
PWM_text[3] = (Int16)double.Parse(textBox4.Text);
if (PWM_text[3] < 0 || PWM_text[3] > 100)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 0
đến 100", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
DO_text[3] = (byte)(PWM_text[3]);
}
}
}

private void textBox7_TextChanged(object sender, EventArgs e)

52
{
if (textBox7.Text == "")
{
DO_text[6] = 0;
}
else
{
PWM_text[6] = (Int16)double.Parse(textBox7.Text);
if (PWM_text[6] < 0 || PWM_text[6] > 100)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 0
đến 100", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
DO_text[6] = (byte)(PWM_text[6]);
}
}
}

private void textBox8_TextChanged(object sender, EventArgs e)


{
if (textBox8.Text == "")
{
DO_text[7] = 0;
}
else
{
PWM_text[7] = (Int16)double.Parse(textBox8.Text);
if (PWM_text[7] < 0 || PWM_text[7] > 100)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 0
đến 100", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}

53
else
{
DO_text[7] = (byte)(PWM_text[7]);
}
}
}
#endregion PWMtext

- Hàm thiết lập giá trị tần số. Giá trị nhập vào là một số 8 bit như đã khai
báo nên chỉ được nhập từ 1 đến 255.

#region Frequency
private void F0_TextChanged(object sender, EventArgs e)
{
if (F0.Text == "")
{
MessageBox.Show("Hãy nhập giá trị từ 1 đến 255", "Warning", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
else
{
F_text[0] = (Int16)double.Parse(F0.Text);
if (F_text[0] < 1 || F_text[0] > 240)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 1 đến 255", "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
F_value[0] = (byte)(F_text[0]);
}
}
}
private void F1_TextChanged(object sender, EventArgs e)

54
{
if (F1.Text == "")
{
MessageBox.Show("Hãy nhập giá trị từ 1 đến 255", "Warning", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
else
{
F_text[1] = (Int16)double.Parse(F1.Text);
if (F_text[1] < 1 || F_text[1] > 240)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 1 đến 255", "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
else
{
F_value[1] = (byte)(F_text[1]);
}
}
}
private void F2_TextChanged(object sender, EventArgs e)
{
if (F2.Text == "")
{
MessageBox.Show("Hãy nhập giá trị từ 1 đến 255", "Warning", MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
else
{
F_text[2] = (Int16)double.Parse(F2.Text);
if (F_text[2] < 1 || F_text[2] > 240)
{
MessageBox.Show("Ngoài tầm, hãy nhập giá trị từ 1 đến 255", "Warning",
MessageBoxButtons.OK, MessageBoxIcon.Warning);

55
}
else
{
F_value[2] = (byte)(F_text[2]);
}
}

}
#endregion Frequence

- Hàm sự kiện thiết lập độ lợi cho AI.

private void Gain1_SelectedIndexChanged(object sender, EventArgs e)


{
switch (Gain1.Text)
{
case "x1":
gain1 = 1;
G_value = 0x9c;
break;
case "x2":
gain1 = 2;
G_value = 0x9d;
break;
case "x4":
gain1 = 4;
G_value = 0x9e;
break;
}
byte[] data_send = { 2, 71, G_value }; //G
try
{
int bytesWritten;

56
writer.Write(data_send, 1000, out bytesWritten);

}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB
Device\nDetails: " + err, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}

- Hàm ngắt cho sự kiện xử lí nút nhấn “Config Ts”.

private void btnTs_Click(object sender, EventArgs e)


{
byte[] data_send = { 3, 84, Ts_value[0], Ts_value[1] };
try
{
int bytesWritten;
writer.Write(data_send, 1000, out bytesWritten);

}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB
Device\nDetails: " + err, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}

- Hàm ngắt cho sự kiện xử lí nút nhấn “Config Frequency”.

private void done_Click(object sender, EventArgs e)


{

57
byte[] data_send = { 4, 70, F_value[0], F_value[1],
F_value[2] }; //F
try
{
int bytesWritten;
writer.Write(data_send, 1000, out bytesWritten);

}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB
Device\nDetails: " + err, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}

- Hàm kiểm tra các check box được tích và cập nhật các giá trị cho các DO
(0 -7)
private void timer1_Tick(object sender, EventArgs e)
{
#region Case Check

if (checkBox1.Checked & checkBox1.Enabled)


{
if (PWM0.Checked)
DO_value[0] = DO_text[0];
else
DO_value[0] = 100;
}
else if (checkBox1.Enabled)
DO_value[0] = 0;

if (checkBox2.Checked & checkBox2.Enabled)


{

58
if (PWM1.Checked)
DO_value[1] = DO_text[1];
else
DO_value[1] = 100;
}
else if (checkBox2.Enabled)
DO_value[1] = 0;

if (checkBox3.Checked & checkBox3.Enabled)


{
DO_value[2] = 1;
}
else if (checkBox3.Enabled)
DO_value[2] = 0;

if (checkBox4.Checked & checkBox4.Enabled)


{
if (PWM3.Checked)
DO_value[3] = DO_text[3];
else
DO_value[3] = 100;
}
else if (checkBox4.Enabled)
DO_value[3] = 0;

if (checkBox5.Checked & checkBox5.Enabled)


{

DO_value[4] = 1;
}
else if (checkBox5.Enabled)
DO_value[4] = 0;

59
if (checkBox6.Checked & checkBox6.Enabled)
{

DO_value[5] = 1;
}
else if (checkBox6.Enabled)
DO_value[5] = 0;

if (checkBox7.Checked & checkBox7.Enabled)


{
if (PWM6.Checked)
DO_value[6] = DO_text[6];
else
DO_value[6] = 100;
}
else if (checkBox7.Enabled)
DO_value[6] = 0;

if (checkBox8.Checked & checkBox8.Enabled)


{
if (PWM7.Checked)
DO_value[7] = DO_text[7];
else
DO_value[7] = 100;
}
else if (checkBox8.Enabled)
DO_value[7] = 0;

#endregion Case Check

60
III. Thí nghiệm 3: Điều khiển vị trí động cơ bằng bộ điều khiển PID.
- ở window form bổ sung them 1 text_box để theo dõi tính hiệu điều khiển:

Tín hiệu điều


khiển Uk

- Bổ sung thêm hàm con là bộ điều khiển PID:

double ek_2, ek_1, ek, uk_1, uk, umax = 12, umin = -12, T =
0.001;
double PID_control(double setpoint, double measure)
{
Kp = 0.00009;
Ki = 0.0004;
Kd = 0.0005;
ek_2 = ek_1;
ek_1 = ek;
ek = setpoint - measure;
uk_1 = uk;
uk = uk_1 + Kp * (ek - ek_1) + Ki * T / 2 * (ek +
ek_1) + Kd / T * (ek - 2 * ek_1 + ek_2);

61
if (uk > umax) uk = umax;
if (uk < umin) uk = umin;
return (uk);
}

- Bổ sung thêm các hàm gọi bộ điều khiển PID và gửi xung PWM xuống vi
điều khiển:

private void resetC0_Click(object sender, EventArgs e)


{
reC0 = 82;//R
}

private void btnSetpoint_Click(object sender, EventArgs e)


{
pidSetpoint = Convert.ToDouble(txtPIDSetpoint.Text);
}

private void UsbReceiverActionFunction(byte[] input)


{
//di_value = String.Concat((char)input[0], (char)input[1],
(char)input[2]);
DI_value = input[0];
c_value[3] = input[1];
c_value[2] = input[2];
c_value[1] = input[3];
c_value[0] = input[4];
C_value = BitConverter.ToInt32(c_value, 0);
if (C_value > 32767)
C_value = C_value - 65536;
currentPos = C_value;
txt_Counter_0.Text = Convert.ToString(currentPos);
// calculate pid
if (enablePID)
{
// PID Control
double ukk = PID_control(pidSetpoint,currentPos);
textBox9.Text = Convert.ToString(ukk);
Chuyển đổi ngõ ra if (ukk > 0)
của bộ điều khiển PID {
DO_value[0] = Convert.ToByte((ukk / 12) * 1000);
từ điện áp sang xung DO_value[1] = 0;
pwm }
else if (ukk < 0)
{
DO_value[1] = Convert.ToByte((-ukk / 12) * 1000);
DO_value[0] = 0;

}
else
{
DO_value[0] = 0;
DO_value[1] = 0;
}
// write your code here

62
// send cmd
byte[] data_send1 = { 14, 78, DO_value[0],
DO_value[1], DO_value[2], DO_value[3], DO_value[4], DO_value[5],
DO_value[6], DO_value[7], AO_0[0], AO_0[1], AO_1[0], AO_1[1], reC0 };
try
{
int bytesWritten1;
if (pidOutput > 0)
{
data_send1[3] = (byte)pidOutput;
}
else
{
pidOutput = -pidOutput;
data_send1[2] = (byte)pidOutput;
}
writer.Write(data_send1, 1000, out bytesWritten1);

}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB
Device\nDetails: " + err, "Error", MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
reC0 = 83; //S
}

//
AI_0_value[3] = input[5];
AI_0_value[2] = input[6];
AI_0_value[1] = input[7];
AI_0_value[0] = input[8];
AI_1_value[3] = input[9];
AI_1_value[2] = input[10];
AI_1_value[1] = input[11];
AI_1_value[0] = input[12];
AI_2_value[3] = input[13];
AI_2_value[2] = input[14];
AI_2_value[1] = input[15];
AI_2_value[0] = input[16];
ai_0_value = BitConverter.ToInt32(AI_0_value, 0);
ai_1_value = BitConverter.ToInt32(AI_1_value, 0);
ai_2_value = BitConverter.ToInt32(AI_2_value, 0);

AI_0_VALUE = (Convert.ToDouble(ai_0_value) / 4095)*3.3;


AI_1_VALUE = (Convert.ToDouble(ai_1_value) / 4095)*3.3;
AI_2_VALUE = (Convert.ToDouble(ai_2_value) / (gain1 *
131072)) * 2.048;
/****************************************/
#region DI
if (Convert.ToBoolean(DI_value & 0x01))
{
oval_DI_1.FillColor = Color.Red;
}
else
{
oval_DI_1.FillColor = Color.White;
}
//

63
if (Convert.ToBoolean(DI_value >> 1 & 0x01))
{
oval_DI_2.FillColor = Color.Red;
}
else
{
oval_DI_2.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 2 & 0x01))
{
oval_DI_3.FillColor = Color.Red;
}
else
{
oval_DI_3.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 3 & 0x01))
{
oval_DI_4.FillColor = Color.Red;
}
else
{
oval_DI_4.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 4 & 0x01))
{
oval_DI_5.FillColor = Color.Red;
}
else
{
oval_DI_5.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 5 & 0x01))
{
oval_DI_6.FillColor = Color.Red;
}
else
{
oval_DI_6.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 6 & 0x01))
{
oval_DI_7.FillColor = Color.Red;
}
else
{
oval_DI_7.FillColor = Color.White;
}
//
if (Convert.ToBoolean(DI_value >> 7 & 0x01))
{
oval_DI_8.FillColor = Color.Red;
}
else
{

64
oval_DI_8.FillColor = Color.White;
}
#endregion DI

txt_AI_0.Text = AI_0_VALUE.ToString("0.000");
txt_AI_1.Text = AI_1_VALUE.ToString("0.000");
txt_AI_2.Text = AI_2_VALUE.ToString("0.000");

txt_Counter_0.Text = C_value.ToString();
}

KẾT QUẢ THÍ NGHIỆM:


1. Kết nối thiết bị:

65
2. Đặt giá trị setpoint là 100, kết quả sau khi ấn set cho chạy:

 Position Setpoint = 100;

 Đã điều khiển động cơ bám vị trí được đặt.

66
 Position Setpoint = 1000

 Position Setpoint = 20000

67
CƠ SỞ LÝ THUYẾT BỘ ĐIỀU KHIỂN PID:

Bộ điều khiển vi tích phân tỉ lệ (bộ điều khiển PID- Proportional Integral
Derivative) là một cơ chế phản hồi vòng điều khiển (bộ điều khiển) tổng quát
được sử dụng rộng rãi trong các hệ thống điều khiển công nghiệp – bộ điều
khiển PID là bộ điều khiển được sử dụng nhiều nhất trong các bộ điều khiển
phản hồi. Bộ điều khiển PID sẽ tính toán giá trị "sai số" là hiệu số giữa giá trị
đo thông số biến đổi và giá trị đặt mong muốn. Bộ điều khiển sẽ thực hiện
giảm tối đa sai số bằng cách điều chỉnh giá trị điều khiển đầu vào. Trong
trường hợp không có kiến thức cơ bản (mô hình toán học) về hệ thống điều
khiển thì bộ điều khiển PID là sẽ bộ điều khiển tốt nhất.

- SƠ ĐỒ KHỐI:

68
KHÂU TÍCH PHÂN

KHẨU ĐẠO
HÀM
- PHƯƠNG TRÌNH RỜI RẠC HÓA:

de
u = 𝐾𝑃 × 𝑒 + 𝐾𝐼 × ∫ 𝑒 × dt + 𝐾𝐷
dt

CÁC BƯỚC TÌM CÁC HỆ SỐ K:

B1: đặt giá trị Ki và KD bằng 0, tang giá trị Kp từ từ cho đến khi đạt tới
giá trị Kcr ( K giới hạn).
B2: đặt giá trị Kp = Kcr / 2
B3: tăng giá trị Ki cho tới khi sai số xác lập bằng 0. Chú ý không được
tăng quá lớn vì Ki có thể làm hệ thống dao động.
B4: tăng giá trị KD cho tới khi không còn vọt lố. Chú ý không tăng KD
quá lớn vì có thể làm hệ thống mất ổn định.

69

You might also like