Hi,
Lần đầu tiên tui làm thử điều khiển PID, mới làm để demo nên chương trình chỉ việc tính các số hạng PID và điều khiển PWM theo các thông số tính được. Mạch de mo và video demo bên dưới. Tui không hiểu một lỗi xảy ra (tìm không ra nguyên nhân) khi giá trị đặt lớn gần bằng giá trị max thì PWM luôn bằng 0 cho dù sau đó giá trị setpoint được giảm nhỏ lại.
Chú thích phần video: Đường màu vàng là đường giá trị đặt, thay đổi bằng biến trở để theo dõi quá trình điều khiển. Đường màu xanh là đường biểu diễn giá trị điều khiển. Khi giá trị đặt được đưa lên cao thì giá trị điều khiển bị rớt xuống 0 và giữ nguyên giá trị 0 luôn cho dù giá trị đặt thay đổi thế nào đi chăng nữa.
Mạch test:

Video:
http://www.youtube.com/watch?v=JyIw7_3UBV0
Bác nào có kinh nghiệm trong vụ điều khiển PID xin giúp giùm.
Sau đây là code CCS chương trình test
Thân ái.
Lần đầu tiên tui làm thử điều khiển PID, mới làm để demo nên chương trình chỉ việc tính các số hạng PID và điều khiển PWM theo các thông số tính được. Mạch de mo và video demo bên dưới. Tui không hiểu một lỗi xảy ra (tìm không ra nguyên nhân) khi giá trị đặt lớn gần bằng giá trị max thì PWM luôn bằng 0 cho dù sau đó giá trị setpoint được giảm nhỏ lại.
Chú thích phần video: Đường màu vàng là đường giá trị đặt, thay đổi bằng biến trở để theo dõi quá trình điều khiển. Đường màu xanh là đường biểu diễn giá trị điều khiển. Khi giá trị đặt được đưa lên cao thì giá trị điều khiển bị rớt xuống 0 và giữ nguyên giá trị 0 luôn cho dù giá trị đặt thay đổi thế nào đi chăng nữa.
Mạch test:

Video:
http://www.youtube.com/watch?v=JyIw7_3UBV0
Bác nào có kinh nghiệm trong vụ điều khiển PID xin giúp giùm.
Sau đây là code CCS chương trình test
Code:
#include <16F886.h>
#device * = 16 ADC = 10
#fuses HS, NOPROTECT, NODEBUG, NOWDT
#use delay(clock = 20M)
#priority INT_TIMER0
// control loop constants
float Kp; // proportional gain
float Ki; // integral gain
float Kd; // differential gain
// terms
float Tp; // proportional term
float Ti; // integral term
float Td; // differential term
// circular queue vars
signed long ErrorHistory[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
int InputIdx = 0;
int GetIdx;
signed long PrevError;
signed long RecError;
signed long Error;
signed long DeDt; // change in Error over time
signed int32 ErrorSum = 0; // sum of Errors, for integral term
signed long DesiredValue;
int8 Power;
volatile long Setpoint;
float temp_float;
void main(void)
{
setup_adc(ADC_CLOCK_DIV_32);
setup_adc_ports(sAN0 | sAN1);
setup_timer_0(T0_INTERNAL | T0_DIV_1);
set_timer0(55536); // interrupt interval 2ms
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8);
set_timer1(3036); // interrupt interval 100ms
setup_timer_2(T2_DIV_BY_1, 249, 16); // PWM frequency 20KHz
setup_ccp1(CCP_PWM);
set_pwm1_duty(0);
enable_interrupts(GLOBAL);
Kp = 2;
Ki = 2.4;
Kd = 0.6;
set_adc_channel(1);
read_adc(ADC_START_ONLY);
while(!adc_done());
Setpoint = read_adc(ADC_READ_ONLY);
enable_interrupts(INT_TIMER0);
enable_interrupts(INT_TIMER1);
while(1)
{
//sleep();
}
}
#int_timer0
void PID_calculate(void)
{
int i = 0;
set_timer0(55536);
set_adc_channel(0);
read_adc(ADC_START_ONLY);
while(!adc_done());
// calculate the raw Error
Error = Setpoint - (signed long)read_adc(ADC_READ_ONLY);
// calculate the proportional term
Tp = Kp * Error;
// calculate the integral term
ErrorSum += (signed int32)Error;
temp_float = ErrorSum;
Ti = Ki * temp_float;
// use a circular queue to save a history of the last 8 samples
// this will be used to calculate the differential term
ErrorHistory[InputIdx] = Error;
InputIdx++;
InputIdx &= 0x07; // keep in 0..7 range
GetIdx = InputIdx; // Go to the oldest values in past
// calculate the average for the 4 oldest samples
for (i = 0, PrevError = 0; i < 4; i++)
{
PrevError += ErrorHistory[GetIdx];
GetIdx++;
GetIdx &= 0x07;
}
// calculate the average for the 4 most recent samples
for (i = 0, RecError = 0; i < 4; i++)
{
RecError += ErrorHistory[GetIdx];
GetIdx++;
GetIdx &= 0x07;
}
// calculate the differential term
DeDt = RecError - PrevError;
Td = Kd * DeDt;
// calculate the desired Power
DesiredValue = (signed long)(Tp + Td + Ti);
// set the correct Power
if(DesiredValue < 0)
DesiredValue = 0;
else if(DesiredValue > 1023)
DesiredValue = 1023;
temp_float = (DesiredValue*250/1023);
Power = (int)temp_float;
//Power = (int)(DesiredValue*250/1023);
set_pwm1_duty(Power); // this could be pwm duty, etc
clear_interrupt(INT_TIMER0);
}
#int_TIMER1
void update_setpoint(void)
{
set_timer1(3036);
set_adc_channel(1);
read_adc(ADC_START_ONLY);
while(!adc_done());
Setpoint = read_adc(ADC_READ_ONLY);
output_toggle(PIN_C3);
clear_interrupt(INT_TIMER1);
}

Comment