Professional Documents
Culture Documents
1
Bài thí nghiệm số 1
1. Thí nghiệm 1: Khảo sát hoạt động của ngõ vào và ngõ ra
Chuẩn bị thí nghiệm: Thực hiện nối dây như hình
- Đấu dây ngõ vào Zone 1 và Zone 5: BT1 – In 0.0, BT2 – In 0.1, BT2 – In 0.2; SW1
– In 0.3, SW2 – In 0.4, SW3 – In 0.5.
- Đấu dây ngõ ra Zone 1 và Zone 6: các ngõ Out 0.0 – 0.7 Zone 1 được nối lần lượt
với các DO 1-8 Zone 6
Tiến hành thí nghiệm:
a. Thí nghiệm 1.1: Làm quen PLC với bài toán điều khiển START/STOP
Yêu cầu:
- START – nút nhấn BT1
- STOP – nút nhấn BT2.
2
- Đèn báo RUN– Đèn DO 1, chớp nháy chu kỳ 1s, sử dụng xung nhịp hệ thống.
Tiến hành lập trình:
Kết quả:
- Đèn DO1 nhấp nháy chu kì 1s
3
b. Thí nghiệm 1.2: Điều khiền hoạt động với các điều kiện ngõ vào khác
nhau:
Yêu cầu: Điều khiển các đèn ngõ ra theo điều kiện cùa ngõ vào như sau:
- Default: 8 LED ngõ ra nhấp nháy chu kỳ 2s
- Nhấn nút BT1, đèn LED sang theo thứ tự từ trái sang phải, mỗi đèn sang trong thời
gian 1s
- Nhấn nút BT2, đèn LED sang theo thứ tự từ phải sang trái, mỗi đèn sang trong thời
gian 0.5s
4
5
6
Kết quả:
- Mạch chạy đúng như yêu cầu.
7
2. Bài thí nghiệm 2: Giả lập hoạt động của máy trạng thái
Khảo sát, giả lập và điều khiển vận hành hệ thống phân loại sản phẩm:
Mô tả: Hệ thống phân loại sản phẩm
- Có 3 loại sản phẩm được phân biệt theo chiều dài, bao gồm D – Sản phẩm dài; N:
Sản phẩm ngắn; TB: Sản phẩm có chiều dài trung bình.
- Chiều dài của sản phẩm được xác định bởi các cảm biến CB1, CB2 và CB3.
o Sản phẩm là N khi chỉ có 1 cảm biến phát hiện được sản phẩm.
o Sản phẩm là TB khi chỉ có 2 cảm biến phát hiện được sản phẩm.
o Sản phẩm là D khi chỉ có 3 cảm biến phát hiện được sản phẩm.
- Các ngõ ra báo chiều dài sản phẩm N, TB, D tương ứng với các ngõ ra Q0.0, Q0.1,
Q0.2. Các tín hiệu này báo trong thời gian 2s.
Tiến hành thí nghiệm:
a. Thí nghiệm 2.1: Xây dựng mô hình giả lập hoạt động của hệ thống phân
loại sản phẩm
Chuẩn bị Thí nghiệm:
Thực hiện đấu dây
Xây dựng mô phỏng tín hiệu cảm biến cho các loại sản phẩm khác nhau:
8
- Ngõ vào: Nút nhấn BT1, BT2 và BT3 tương ứng là các tín hiệu báo giả lập SP N,
TB và D.
- Ngõ ra: tín hiệu Q0.3, Q0.4 và Q0.5 tương ứng là tín hiệu giả lập của CB1, CB2,
và CB3.
- Mô tả graph tín hiệu của các cảm biến CB1, CB2 và CB3 tương ứng với các sản
phẩm.
Yêu cầu:
- Cải tiến chương trình cho phép tăng tốc, giảm tốc băng tải với ngõ vào AI0 (thời
gian thay đổi)
- Tìm khuyết điểm của chương trình trên
- Nêu phương pháp, giải thuật khác giả lập hoạt động của hệ thống.
9
10
11
12
13
14
Kết quả:
- Mạch chạy đúng theo yêu cầu đề bài, với ngõ vào là AI0
- Chương trình còn khuyết điểm đó là chưa loại bỏ được việc nhấn nút mô phòng quá
sát nhau không đúng như thực tế. Ví dụ: khi đang mô phỏng sản phẩm trung bình
vật chưa đi hết cảm biến 1 nhưng đã nhấn nút mô phỏng sản phẩm dài dẫn đến sai
thời gian đi qua các cảm biến, đây không đúng với thực tế bởi hai sản phẩm không
thể chồng lên nhau mà nối tiếp nhau.
- Hướng khắc phục : Khi đang mô phỏng sản phẩm chưa hết thời gian ở cảm biến 1
thì không cho phép nhấn nút mô phỏng sản phẩm tiếp theo.
b. Thí nghiệm 2.2: Viết chương trình phân loại sản phẩm với tín hiệu giả lập
vừa xây dựng được
Gợi ý: Sinh viên lựa chọn 1 trong các phương pháp sau để giải quyết bài toán
- 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
- Phương pháp 2: Giải quyết bài toán bằng cách đếm số lượng sản phẩm giữa các cảm
biến kết hợp trạng thái của cảm biến.
- Phương pháp 3: Giải pháp khác
15
Yêu cầu:
- Lập trình hoạt động của hệ thống theo phương án đã chọn
- Kiểm chứng hoạt động trong các trường hợp khác nhau:
o Trường hợp từng sản phẩm đi qua hệ thống phân loại thời gian cách nhau.
o Trường hợp nhiều sản phẩm đi qua hệ thống liên tiếp nhau: Ngắn – dài -
trung bình.
o Kiểm chứng trường hợp cụ thể theo yêu cầu của GVHD.
Tiến hành lập trình:
16
Kết quả:
- Chương trình chạy đúng như yêu cầu
17
Bài thí nghiệm số 2
18
19
20
21
B. 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”
4. 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.
Giải thuật:
- Thiết lập giao thức truyền từ giữa PC và PLC: ban đầu PC truyền xuống giá trị 0,
sau đó truyền lần lượt các giá trị thời gian theo thứ tự XA, VA, XB, VB; mỗi giá
trị cách nhau 10ms.
- Các giá trị thời gian XA, VA, XB, VB có đơn vị là giây, chứa trong 1 byte.
- Các timer T37, T38, T39, T30 có độ phân giải 100ms, do đó các giá trị thời gian
gởi xuống phải được nhân 10 trước khi nạp vào các timer.
- Sử dụng lệnh SEG để chuyển mã BCD sang mã LED 7 đoạn.
- Sử dụng lệnh XMT để truyền thông tin về PC.
- Format bảng tin truyền về như sau:
22
23
Chương trình C#:
24
25
26
Chương trình S7-200:
1) Main:
ORGANIZATION_BLOCK MAIN:OB1
TITLE=PROGRAM COMMENTS
BEGIN
Network 1
LD SM0.1
MOVB 16#09, SMB30
ATCH INT0, 8
ENI
Network 2
LD SM0.1
MOVB 1, VB120
MOVB 12, VB121
MOVB 1, VB122
MOVB 'B', VB123
Network 3
Network 4
LDN T40
LPS
TON T37, MW30
A T37
TON T38, VW132
LRD
27
A T38
TON T39, VW134
LPP
A T39
TON T40, VW136
Network 5
LDN T37
= Q0.0
Network 6
LD T37
AN T38
= Q0.1
Network 7
LD T38
= Q0.2
Network 8
LD T38
AN T39
= Q0.3
Network 9
LD T39
= Q0.4
Network 10
28
LDN T38
= Q0.5
Network 11
LD I0.5
MOVW 300, VW130
MOVW 30, VW132
MOVW 400, VW134
MOVW 30, VW136
Network 12
LDB= SMB2, 52
= Q1.0
Network 13
LDB> SMB2, 40
= Q1.1
Network 14
LDB> SMB2, 1
= Q1.2
Network 15
LDW= MW20, 4
= Q1.4
Network 16
LDW= MW10, 52
= Q1.5
Network 17
LDN T40
29
TON T40, 1
Network 18
LD T40
EU
+I 1, MW22
Network 19
LDW= MW30, 1
XMT VB120, 0
Network 20
LDW= MW30, 2
XMT VB122, 0
Network 21
LDW= MW30, 3
MOVW 0, MW22
2) Khối ngắt:
INTERRUPT_BLOCK INT_0:INT0
TITLE=INTERRUPT ROUTINE COMMENTS
BEGIN
Network 1
LD SM0.1
+I 1, MW8
Network 2
LDW= MW8, 1
BTI SMB2, MW10
AENO
MOVW MW10, VW130
-I +48, VW130
Network 3
LDW= MW8, 2
30
BTI SMB2, MW10
AENO
MOVW MW10, VW132
-I +48, VW132
Network 4
LDW= MW8, 3
BTI SMB2, MW10
AENO
MOVW MW10, MW20
-I +48, MW20
Network 5
LDW= MW8, 4
BTI SMB2, MW10
AENO
MOVW MW10, VW136
-I +48, VW136
Network 6
LDW= MW8, 5
MOVW 0, MW8
Network 7
LDB= SMB2, 52
MOVW 40, MW30
AENO
MOVW 40, VW132
Network 8
Network 9
Network 10
31
LDB= SMB1, 'D'
MOVB SMB2, VB100
MOVB SMB3, VB101
MOVB SMB4, VB102
MOVB SMB5, VB103
END_INTERRUPT_BLOCK
Đề xuất giải thuật khác cho thí yêu cầu bài toán ở thí nghiệm 2.2:
Ta có 4 trạng thái của led đơn, mỗi trạng thái sẽ sang trong 1 thời gian cụ thể. Do đó , ta
chỉ cần gửi 2 byte dữ liệu 1 byte chứa trạng thái và một byte đếm thời gian trạng thái đó
là có thể đồng bộ việc hiển thị giữa máy tính và PLC.
32
Bài thí nghiệm số 3
B. Bài thí nghiệm 2: Phát triển Thí nghiệm 1 ứng dụng điều khiển nhiệt độ
5. Thí nghiệm 2.1: Điều khiển ON/OFF lò nhiệt, vùng trễ 3oC, nhiệt độ đặt 60oC.
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:
- Nhiệt độ lò nhiệt đọc về < (Giá trị đặt – 3oC) => Tín hiệu điều khiển bằng 1.
- Nhiệt độ lò nhiệt đọc về > (Giá trị đặt + 3oC) => Tín hiệu điều khiển bằng 0.
- (Giá trị đặt – 3oC) < Nhiệt độ lò nhiệt đọc về < (Giá trị đặt + 3oC) => Tín hiệu điều
khiển không thay đổi (bằng tín hiệu điều khiển trước đó).
- Gửi chế độ điều khiển thông qua thanh ghi BUFF.data[1] (BUFF.data[1] = 1: chế
độ ON/OFF, BUFF.data[1] = 2: chế độ PID)
- Gửi tín hiệu điều khiển xuống PLC thông qua thanh ghi BUFF.data[0].
- PLC xuất tín hiệu bật tắt lò nhiệt.
Chương trình điều khiển ON/OFF từ máy tính:
private void Plant(double uk)
{
yk = Convert.ToUInt16(TxtRdReg1.Text);
yk2 = yk1;
yk1 = yk;
uk2 = uk1;
uk1 = uk;
}
33
private void ONOFF(double sp, double dl)
{
ek = sp - yk;
if (((ek - ek1) >= 0) && (ek >= dl))
uk = 1;
else if (((ek - ek1) < 0) && (ek <= -dl))
uk = 0;
ek1 = ek;
}
6. 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.
Giải thuật:
- Tính sai số: 𝑒𝑘 = 𝑠𝑝 – 𝑦𝑘 ;
Với: 𝑒𝑘 : sai số
𝑠𝑝: nhiệt độ đặt
𝑦𝑘 : nhiệt độ lò nhiệt trả về
34
𝐾𝐼 .𝑇
𝑢(𝑘 ) = 𝑢(𝑘 − 1) + 𝐾𝑃 (𝑒(𝑘 ) − 𝑒(𝑘 − 1)) + (𝑒(𝑘 ) + 𝑒(𝑘 − 1)) +
2
𝐾𝐷
(𝑒(𝑘 ) − 2𝑒(𝑘 − 1) + 𝑒(𝑘 − 2))
𝑇
- Scale lại tín hiệu udk (từ 0% đến 100% công suất).
- Gửi chế độ điều khiển thông qua thanh ghi BUFF.data[1] (BUFF.data[1] = 1: chế
độ ON/OFF, BUFF.data[1] = 2: chế độ PID).
- Gửi udk xuống PLC thông qua thanh ghi BUFF.data[0].
- PLC thực hiện phát xung PWM để điều khiển lò nhiệt.
Chế độ ON/OFF, udk = 1 thì bật lò nhiệt, udk = 0 thì tắt lò nhiệt
36
Chế độ PID, tính 𝑢𝑑𝑘 % ∗ 𝑇 lưu vào MW14
Chế độ PID, đưa giá trị đếm tức thời của Timer vào MW200
Chế độ PID, so sánh giá trị Timer với 𝑢𝑑𝑘 % ∗ 𝑇, nếu nhỏ hơn thì bật lò nhiệt…
37
…nếu lớn hơn thì tắt lò nhiệt
Kết quả thí nghiệm:
- Điều khiển ON/OFF:
38
Hình 2. Đáp ứng hệ thống dùng bộ điều khiển PID
Giải pháp khác có thể thực hiện cho 2 bài toán ở thí nghiệm 2.1 và 2.2:
Như vậy ở bài thí ngiệm số 3, chúng ta đã nắm bắt được cấu trúc phần cứng của hệ thống
điều khiển nhiệt độ và lập trình, kiểm chứng chất lượng điều khiển của 2 bộ ON/OFF và
PID. Tuy nhiên 2 bộ điều khiển ở 2 thí nghiệm này dù đơn giản nhưng đều mắc phải một
số nhược điểm nhất định trong khi đó, việc điều khiển chính xác nhiệt độ với độ vọt lố
nhỏ và sai số xác lập nhỏ là rất cần thiết và quyết định đến chất lượng sản phẩm cần gia
nhiệt. Thứ nhất là bộ ON/OFF: chính cái tên đã cho ta biết được bản chất của bộ điều
khiển này là chỉ có 2 trạng thái điều khiển là ON hoặc là OFF mà thôi, với các giá trị đặt
khác nhau nhiệt độ của lò được điều chỉnh để có thể dao động trong phạm vi sai số nhất
định xung quanh giá trị đặt, tuy nhiên nếu xét trong ứng dụng thực tế thì bộ điều khiển
này rõ ràng không tốt vì nhiệt độ của lò luôn dao động và không ổn định được sẽ ảnh
hưởng đến chất lượng sản phẩm, lấy ví dụ trong các lò nung gạch, nung gốm,… Thứ hai
là bộ điều khiển PID: đây là bộ điều khiển phổ biến được áp dụng rộng rãi trong công
39
nghiệp. Tuy nhiên, theo phương pháp sử dụng ở thí nghiệm 2.2 chúng ta phải tự chỉnh
định hay nois cách khác là “mò” các thông số Kp, Ki, Kd để có được đáp ứng tốt nhất.
Việc này có ý nghĩa về mặt nghiên cứu đối với sinh viên vì giúp các bạn củng cố kiến
thức, cách thức chỉnh định một bộ điều khiển PID, nhưng lại làm mất thời gian khá nhiều
vì mỗi lần lặp lại như vậy ta phải đợi cho lò nguội bớt đi để có thể chỉnh tiếp. Vì vậy
nhóm em xin được đưa ra phương pháp điều khiển cho cả 2 thí nghiệm đó là dùng “BỘ
ĐIỀU KHIỂN THÍCH NGHI PID AUTO – TUNING”. Cấu trúc của bộ điều khiển
này được thể hiện qua hình sau:
Hình 3. Sơ đồ cấu trúc bộ điều khiển PID auto_tuning. Hệ thống sử dụng bộ điều khiển
ON/OFF ở chế độ chỉnh định (D) và bộ PID ở chế độ điều khiển (C)
Hệ thống trên bao gồm 2 khối: khối điều khiển ON/OFF và khối điều khiển PID. Ban đầu
bộ điều khiển ON/OFF sẽ hoạt động để dò tìm độ lợi tới hạn và chu kì tới hạn của hệ
thống. Sau khi xác định được 2 thông số này, hệ thống sẽ tính toán các thông số Kp, Ki,
Kd và chuyển sang chế độ điều khiển PID.
Nguyên lý hoạt động của bộ điều khiển PID auto-tuning như mô tả ở Hình 3.
Ban đầu, hệ thống sử dụng bộ điều khiển ON-OFF để dò tìm độ lợi tới hạn Kc và chu kỳ
tới hạn Tc của hệ thống. Trong giai đoạn từ thời điểm A tới thời điểm B ở Hình 4, các giá
trị Kc và Tc phải được xác định. Sau thời điểm B, các thông số Kp, Ki, Kd được tính toán
theo công thức (3) – (5) và hệ thống sẽ chuyển sang bộ điều khiển PID.
1
PID( s ) K p (1 Td s ) (2)
Ti s
K P KC cos M (3)
Ti Td (4)
40
TC 4
Td tan M tan 2 M (5)
4
Hình 4. Đáp ứng nhiệt độ ngõ ra ở chế độ Hình 5. Giá trị đặt r(t)=700 ở chế độ
điều khiển ON/OFF ON/OFF và r(t)=1000 ở chế độ PID
Yếu tố quyết định đến chất lượng điều khiển của bộ PID auto-tuning là hệ thống phải
chuyển sang bộ điều khiển PID nhanh nhất có thể để thời gian quá độ nhỏ và độ vọt lố
thấp. Hơn nữa, để bộ điều khiển ON-OFF không gây ra vọt lố lớn thì ở thời điểm ban đầu
ta cài đặt r(t) bằng khoản 1/2 giá trị đặt mong muốn. Sau khi hệ thống chuyển sang bộ
điều khiển PID ta mới cài đặt r(t) bằng giá trị đặt mong muốn. Hình 5 minh họa giá trị r(t)
= 70° khi ở chế độ điều khiển ON-OFF và r(t) = 100° khi ở chế độ điều khiển PID
(100°C là giá trị đặt mong muốn). Kết quả, hệ thống không bị vọt lố trên 100° khi ở chế
độ điều khiển ON-OFF và thời gian xác định các giá trị Kc và Tc cũng nhanh hơn.
Khối tính toán thông số PID có thể được lập trình như sau:
Code MATLAB:
41
T1 = params(1); T2 = params(2); idx = params(3);
emin = params(4); emax = params(5);
if ((relay(1)~=0) && (relay(2)==0)),
idx = idx + 1;
if idx==1,
T1 = cnt;
elseif idx==2,
T2 = cnt;
else
idx = 2;
end
else
if idx==1,
if e<emin,
emin = e;
elseif e>emax
emax = e;
end
elseif idx==2,
Tc = T2 - T1;
M = abs(emax) + abs(emin);
Kc = 2*d/(pi*M/2);
% PID controller
PHIm = pi/4;
alfa = 60;
Td = ( tan(PHIm) + sqrt(4/alfa + tan(PHIm)^2) ) * Tc /
(4*pi);
Ti = alfa*Td;
Kp = Kc*cos(PHIm);
42
Ki = Kp/Ti;
Kd = Kp*Td;
PID(1) = Kc*cos(PHIm);
PID(2) = Kp/Ti;
PID(3) = Kp*Td;
select = 1;
end
end
params(1) = T1; params(2) = T2; params(3) = idx;
params(4) = emin; params(5) = emax;
Tiến hành add các thư viện cần thiết để lập trình.
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;
44
AI_Init();
DO_Init();
AO_Init();
Counter_Init();
MX_USB_DEVICE_Init();
Sample_Timer_Init();
Trong hàm “main” đầu tiên ta sẽ khởi tạo các ngoại vi và cấu hình xung clock.
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);
45
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);
}
Sau đó chương trình sẽ vào hàm “while(1)”, Kiểm tra cờ “usb_tx_flag”, nếu bằng 1 thì thì reset lại cờ
về 0 sau đó thiết lập các hàm để truyền đi các giá trị DI, AI, và giá trị của counter.
if (usb_rx_flag)
{
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])/1000;
AO_value[1] = ((float)usb_rx_buffer[11]*256+(float)usb_rx_buffer[12])/1000;
AO_Write_All(AO_value);
Kiểm tra cờ “usb_rx_flag”, nếu bằng 1 thì reset lại cờ về 0 sau đó thiết lập các hàm để nhận.
//reset Counter
if (usb_rx_buffer[13] == 'R')
{
Counter_Reset();
}
break;
}
case 'F':/* pwm frequency */
{
for(i=0;i<3;i++)
{
46
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);
}}}}}
Nếu “buffer[13]” nhận về là kí tự ‘R’ thì reset lại giá trị của counter.
Nếu nhận được kí tự ‘F’ thì thiết lập tần số PWM.
Nếu nhận được kí tự ‘G’ thì thiết lập độ lợi cho ADC 18 bit.
Nếu nhận được kí tự ‘T’ thì thiết lập thời gian lấy mẫu.
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;
}
47
{
usb_tx_flag =1;
}
#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) */
/* USER CODE END 6 */
}
#endif
48
Thí nghiệm 2: Tạo window form app C#.
49
{
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;
Int16 AO_0_value;
Int16 AO_1_value;
int ai_0_value;
int ai_1_value;
int ai_2_value;
50
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;
#endregion variable
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.
public Form1()
{
InitializeComponent();
}
51
}
else
{
e.Cancel = true;
}
}
52
F_value[1] = 2;
F1.Text = "2";
F_value[2] = 2;
F2.Text = "2";
cbEnablePID.Checked = false;
}
53
}
else
{
Usb_exit();
btnconnection.Text = "Connect";
txtproductname.Text = "";
txtvendorid.Text = "";
txtproductid.Text = "";
txtmanufacturer.Text = "";
textBox1.Text = "";
textBox2.Text = "";
textBox4.Text = "";
textBox7.Text = "";
textBox8.Text = "";
txt_AI_0.Text = "";
txt_AI_1.Text = "";
txt_Counter_0.Text = "";
54
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);
55
}
myUsbDevice = null;
UsbDevice.Exit();
}
}
#endregion USB EXIT
UsbReceiverAction = UsbReceiverActionFunction;
if ((myUsbDevice.IsOpen) && (reader != null))
{
ketthuc = this.BeginInvoke(UsbReceiverAction, e.Buffer);
}
}
56
}
57
PWM_text[0] = (Int16)double.Parse(textBox1.Text);
if (PWM_text[0] < 0 || PWM_text[0] > 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[0] = (byte)(PWM_text[0]);
}
}
}
58
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]);
}
}
}
59
else
{
DO_text[6] = (byte)(PWM_text[6]);
}
}
}
#endregion PWMtext
Hàm thiết lập duty cho các kênh PWM. Giá trị nhập vào từ 0 đến 100 tương ứng duty
cycle từ 0 đến 100. Nếu nhập số khác ngoài vùng từ 0 đến 100 sẽ báo lỗi và yêu cầu
nhập lại.
Check vào ô checkbox của PWM tương ứng và thay đổi thông số duty sẽ làm thay đổi
độ sáng đèn trên USBcard.
#region Frequency
60
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)
{
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);
}
61
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);
}
else
{
F_value[2] = (byte)(F_text[2]);
}
}
}
#endregion Frequence
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.
private void Gain1_SelectedIndexChanged(object sender, EventArgs e)
{
switch (Gain1.Text)
62
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;
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);
}
}
63
}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB Device\nDetails: " + err,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception err)
{
MessageBox.Show("Can Not Send Data To USB Device\nDetails: " + err,
"Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
64
DO_value[0] = 100;
}
else if (checkBox1.Enabled)
DO_value[0] = 0;
65
DO_value[4] = 1;
}
else if (checkBox5.Enabled)
DO_value[4] = 0;
DO_value[5] = 1;
}
else if (checkBox6.Enabled)
DO_value[5] = 0;
66
#endregion Case Check
Trong hàm ngắt của timer ta viết chương trình để cập nhật giá trị các DO (DO0 – DO7).
Ứng với checkbox được tick mà lệnh if được thỏa mãn và cập nhật giá trị cho DO tương
ứng.
Thí nghiệm 3: Điều khiển vị trí động cơ bằng bộ điều khiển PID.
Tiếp tục window form app C# ở thí nghiệm 2. Thêm các chương trình cần thiết để điều
khiển vị trí động cơ bằng bộ điều khiển PID.
private void resetC0_Click(object sender, EventArgs e)
{
reC0 = 82;//R
currentPos = 0;
}
Hàm ngắt cho sự kiện nút nhấn “reset” và “set”. Khi nhấn nút “reset” thì giá trị của ô
“txt_Counter_0” sẽ về 0 tương ứng vị trí ban đầu khi chưa đếm được xung nào. Khi
nhập giá trị vào ô “txtPIDSetpoint” và nhấn nút “set” thì vị trí cài đặt sẽ được cập nhật.
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);
67
C_value = C_value - 65536;
currentPos = C_value;
textBox9.Text = currentPos.ToString() + ',' + pidOutput.ToString();
// calculate pid
if (enablePID)
{
pidError = pidSetpoint - currentPos;
pPart = Kp * pidError;
iPart += 0.5*Ki * (pidError+ pidPreError) * Ts_text / 1000;
dPart = Kd * (pidError - pidPreError)/((double)Ts_text / 1000);
pidPreError = pidError;
if (iPart > 90)
iPart = 90;
else if (iPart < -90)
iPart = -90;
pidOutput = pPart + iPart + dPart;
if (pidOutput > 90)
pidOutput = 90;
else if (pidOutput < -90)
pidOutput = -90;
// send cmd
byte[] data_send = { 14, 78, 0, 0, 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 bytesWritten;
if (pidOutput > 0)
{
data_send[3] = (byte)pidOutput;
}
else
{
pidOutput = -pidOutput;
data_send[2] = (byte)pidOutput;
68
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);
}
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);
69
oval_DI_1.FillColor = Color.Red;
}
else
{
oval_DI_1.FillColor = Color.White;
}
//
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;
}
//
70
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
{
oval_DI_8.FillColor = Color.White;
71
}
#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();
}
Đoạn chương trình tính PID, màu sắc cho các ovalshape, hiện thị value trên textbox
AI0 - AI2.
KẾT QUẢ THÍ NGHIỆM:
72
Thay đổi giá trị AI0 (bằng cách chỉnh biến trở VR1), cho quay động cơ với
PWM = 30%
73
Thay đổi thời gian lấy mẫu lần lượt là 1000ms, 10ms
Nhận xét: Thời gian lấy mẫu tăng tốc độ thay đổi AIN chậm dần.
74
Mặt khác, tốc độ thay đổi của e có thể tính bằng đạo hàm của biến này theo thời gian. Như
vậy, khi động cơ quay từ vị trí hiện tại đến vị trí đặt, đạo hàm sai số e tăng giá trị nhưng
ngược chiều với u. Nếu sử dụng đạo hàm làm thành phần thắng thì có thể giảm overshot.
Vậy ta sẽ có bộ điều khiển như sau:
de
u KP e KD
dt
Trong đó, (de/dt) là vận tốc thay đổi của sai số e là Kd là hằng số không âm gọi là hệ số D
(Derivative gain). D giúp làm giảm dao dộng cảu đáp ứng quanh vị trí đặt.
Giả sử, thành phần D lớn hơn rất nhiều so với thành phần P hoặc bản thân thành phần P rất
nhỏ, khi đó động cơ chưa thật sự đến vị trí đặt thì đã dừng hẳn, thành phần D bằng 0. Động
cơ sẽ dừng hẳn vì thành phần P và sai số e lúc này quá nhỏ không thể thắng được ma sát
tĩnh, sai số e lúc này được gọi là sai số tĩnh steady state error. Để tránh sai số tĩnh người ta
thêm vào bộ điều khiển một thành phần có chức năng cộng dồn sai số. Khi trạng thái tĩnh
xảy ra, hai thành phần P và D mất tác dụng, thành phần cộng dần sai số sẽ làm tăng u theo
thời gian, đến một lúc nào đó u đủ mạnh để thắng ma sát tĩnh thì động cơ sẽ tiếp tục quay
dần đến vị trí đặt. Thành phần cộng dồn sai số chính là thành phần I trong bộ điều khiển
PID. Nó được tính bằng tích phân của e theo thời gian. Đến đây ta có bộ điều khiển PID
đầy đủ như sau:
de
u K P e K I e dt KD
dt
Thực tế ta rời rạc hóa công thức PID và dùng PID rời rạc để lập trình.
pidError = pidSetpoint - currentPos;
pPart = Kp * pidError;
iPart += 0.5*Ki * (pidError+ pidPreError) * Ts_text / 1000;
dPart = Kd * (pidError - pidPreError)/((double)Ts_text / 1000);
pidPreError = pidError;
Cách 2:
double pidError_2, pidError_1;
double pidOutput_1;
pidError_2 = pidError_1;
pidError_1 = pidError;
pidError = pidSetpoint - currentPos;
pidOutput_1 = pidOutput;
75
pidOutput = pidOutput_1 + Kp * (pidError - pidError_1) +
Ki * Ts_text / 2000 * (pidError + pidError_1) + Kd / Ts_text / 1000 * (pidError - 2 *
pidError_1 + pidError_2);
Dò thông số PID:
Ban đầu cho Ki và Kd bằng 0. Chỉnh Kp tăng dần đến khi đáp ứng dao động
tuần hoàn quanh giá trị mong muốn.
Đặt Ki bằng với chu kỳ dao động.
Điều chỉnh Kp lại cho phù hợp.
Nếu có vọt lố thì điều chỉnh giá trị Kd.
Kết quả bộ điều khiển PID với các Setpoint khác nhau:
76
Bổ sung thiết kế giao diện:
double Kp = 0.071, Ki
= 0.017, Kd =
Quan sát tín hiệu
0.000041;)
ngõ ra và tín hiệu
điều khiển.
77