說到PID算法,想必大部人並不陌生,PID算法在很多方面都有重要應用,比如電機的速度控制,恆溫槽的溫度控制,四軸飛行器的平衡控制等等,作為閉環控制系統中的一種重要算法,其優點和可實現性都成為人們的首選。下面簡單來講解一下PID算法:
首先PID算法是有比例,積分,微分三部分組成,先說下比例部分,所謂比例部分,就是呈線性關系,舉個例子,一個電熱絲加熱水,開始的時候溫度很低,離50℃很大,這時應該加大功率,離目標溫度越大,其功率應該越大,反之越小,這就是比例部分。
乍一看,既然比例部分已經可以控制溫度了為啥還需要積分和微分部分呢,難道是多此一舉么?其實不然,在實際中會出現這種情況,當加熱到50℃時,系統很難停止下來,而是會持續一段時間,這樣就會超過預設值,所以僅有比例控制並不完美,這是就需要積分部分和微分部分。積分部分就是把之前的誤差全部累加起來,這樣起始時由於誤差很大加熱功率就大,隨着接近預設值后功率開始減少,微分部分就是起始時溫度增加很快,表示此時需要很大的功率,隨着溫度接近預設值,其斜率開始減小最后為零,意味着功率也減少,當然很難為零,一般在一定的范圍內波動。
現在開始用C語言來實現PID算法:
位置式:
比例部分:
Kp:比例系數 SetValue:預設值 FactValue:當前實際值 Error_1:當前誤差
則比例部分為:
Sp = Kp*(SetValue - FactValue)
或者
Sp = Kp*Error_1
注解:Sp大小反應需要控制的量大小,比如Sp越大,功率越大。當Sp為負值時,表示要超過預設值,如果是電機,則需要反轉
積分部分:
Ki:積分系數 Error_1:當前誤差 Error_2:上一次誤差 Error_3:上上一次誤差 ........Error_n:開始時的誤差
則積分部分為:
Si = Ki*(Error_1+Error_2+Error_3+......+Error_n)
注解:因為整個是一個過程,所以上一次誤差其實就是上一次的當前誤差
微分部分:
Kd:微分系數 Error_1:當前誤差 Error_2:上一次誤差
則微分部分為:
Sd = Kd*(Error_1-Error_2)
綜上部分的PID得:
PID=Sp + Si + Sd = Kp*Error_1 + Ki*(Error_1+Error_2+Error_3+......+Error_n) + Kd*(Error_1-Error_2)
增量式:
將上述推導的PID記作時間為k時刻的PID控制量,則
PID(k) =Sp + Si + Sd = Kp*Error_1(k) + Ki*(Error_1(k)+Error_2(k-1)+Error_3(k-2)+......+Error_n(0)) + Kd*(Error_1(k)-Error_2(k-1)) 1
將上式k=k-1代入得:
PID(k-1) =Sp + Si + Sd = Kp*Error_1(k-1) + Ki*(Error_1(k-1)+Error_2(k-2)+Error_3(k-3)+......+Error_n(0)) + Kd*(Error_1(k-1)-Error_2(k-2)) 2
1-2得:
PID(k) - PID(k-1) = Kp*(Error_1(k)-Error_1(k-1)) + Ki*(Error_1(k)) + Kd*(Error_1(k)-2*Error_2(k-1)+Error_2(k-2))
將PID(k) - PID(k-1)記作detPID
detPID = Kp*(Error_1(k)-Error_1(k-1)) + Ki*(Error_1(k)) + Kd*(Error_1(k)-2*Error_2(k-1)+Error_2(k-2))
這樣就得到了增量式的PID算法,其計算的結果為增加的控制量
增量式的PID有個好處就是只與當前三個誤差量有關系,與其他無關,這樣就簡化的處理過程,而且提高了精度,下面是PID源碼:
/*文件名:PID.h*/ #ifndef _PID_H_ #define _PID_H_ extern float Kp,Ki,Kd; //系數(全局變量) extern float AclValue; //實際值 extern float SetValue; int PID(void); #endif
/*######################################################################## 文件名:PID.c 時間: 2018.9.7 備注:無 #########################################################################*/ #include "PID.h" float Kp=10,Ki=0.8,Kd=0.5; //系數 float SetValue=2000; //設定值 float AclValue=0; //實際 float Error1=0,Error2=0,Error3=0; //誤差 /* 下面為增量式PID算法 */ /********************************************************************************** 函數名:PID 返回值:輸出增量 參數:無 備注:當輸出大於0表示小於預設值,當輸出小於0表示大於預設值 ***********************************************************************************/ int PID(void) { float OutValue =0; Error3 = SetValue - AclValue; OutValue = Kp*(Error3-Error2)+Ki*(Error3)+Kd*(Error3-2*Error2+Error1); Error1=Error2; //這部分是迭代,因為上次的誤差就是上次的當前誤差 Error2=Error3; if(OutValue>3000) //這部分是規定最大輸出增量 OutValue=3000; if(OutValue<-3000) OutValue=-3000; return OutValue; }
下面給出計算機模擬代碼;
#include "stdio.h" float Kp=10,Ki=2,Kd=0.5; //系數 float SetValue=1256; //設定值 float AclValue=0; //實際 float Error1=0,Error2=0,Error3=0; //誤差 /* 下面為增量式PID算法 */ /********************************************************************************** 函數名:PID 返回值:輸出增量 參數:無 備注:當輸出大於0表示小於預設值,當輸出小於0表示大於預設值 ***********************************************************************************/ int PID(void) { float OutValue =0; Error3 = SetValue - AclValue; OutValue = Kp*(Error3-Error2)+Ki*(Error3)+Kd*(Error3-2*Error2+Error1); Error1=Error2; Error2=Error3; return OutValue; } int main(void) { unsigned int i=1000; while(i) { PID(); //特別注意這里:必須要運行,因為需要執行這一步:Error1=Error2; Error2=Error3;
printf("當前實際值為:%f \n",AclValue); AclValue += PID(); i--);
}
return 0;
}
運行結果: