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