最近看到了Brett Beauregard發表的有關PID的系列文章,感覺對於理解PID算法很有幫助,於是將系列文章翻譯過來!在自我提高的過程中,也希望對同道中人有所幫助。作者Brett Beauregard的原文網址:http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-direction/
1、問題所在
將PID連接過程分為兩組:直接作用和反向作用。到目前為止,我所展示的所有例子都是直接行動。也就是說,輸出的增加會導致輸入的增加。對於反向作用過程,情況正好相反。例如,在冰箱中,冷卻水的增加會導致溫度下降。要使初學者 PID 使用反向過程,kp、ki 和 kp 的符號都必須為負數。
這本身不是問題,但用戶必須選擇正確的符號,並確保所有參數都具有相同的符號。
2、解決方案
為了讓這個過程簡單一點,我要求 kp、ki 和 kp 都是大於等於0的。如果用戶連接到反向進程,則使用SetControllerDirection函數指定反向進程。這可以確保所有參數都具有相同的符號,並使事情操作起來更直觀。
3、代碼
1 /*working variables*/
2 unsigned long lastTime; 3 double Input,Output,Setpoint; 4 double ITerm,lastInput; 5 double kp,ki,kd; 6 int SampleTime = 1000; //1 sec
7 double outMin,outMax; 8 bool inAuto = false; 9
10 #define MANUAL 0
11 #define AUTOMATIC 1
12
13 #define DIRECT 0
14 #define REVERSE 1
15 int controllerDirection = DIRECT; 16
17 void Compute() 18 { 19 if(!inAuto) return; 20 unsigned long now = millis(); 21 int timeChange = (now - lastTime); 22 if(timeChange>=SampleTime) 23 { 24 /*Compute all the working error variables*/
25 double error = Setpoint - Input; 26 ITerm+= (ki * error); 27 if(ITerm > outMax) ITerm= outMax; 28 else if(ITerm < outMin) ITerm= outMin; 29 double dInput = (Input - lastInput); 30
31 /*Compute PID Output*/
32 Output = kp * error + ITerm- kd * dInput; 33 if(Output > outMax) Output = outMax; 34 else if(Output < outMin) Output = outMin; 35
36 /*Remember some variables for next time*/
37 lastInput = Input; 38 lastTime = now; 39 } 40 } 41
42 void SetTunings(double Kp,double Ki,double Kd) 43 { 44 if (Kp<0 || Ki<0|| Kd<0) return; 45
46 double SampleTimeInSec = ((double)SampleTime)/1000; 47 kp = Kp; 48 ki = Ki * SampleTimeInSec; 49 kd = Kd / SampleTimeInSec; 50
51 if(controllerDirection ==REVERSE) 52 { 53 kp = (0 - kp); 54 ki = (0 - ki); 55 kd = (0 - kd); 56 } 57 } 58
59 void SetSampleTime(int NewSampleTime) 60 { 61 if (NewSampleTime > 0) 62 { 63 double ratio = (double)NewSampleTime 64 / (double)SampleTime; 65 ki *= ratio; 66 kd /= ratio; 67 SampleTime = (unsigned long)NewSampleTime; 68 } 69 } 70
71 void SetOutputLimits(double Min,double Max) 72 { 73 if(Min > Max) return; 74 outMin = Min; 75 outMax = Max; 76
77 if(Output > outMax) Output = outMax; 78 else if(Output < outMin) Output = outMin; 79
80 if(ITerm > outMax) ITerm= outMax; 81 else if(ITerm < outMin) ITerm= outMin; 82 } 83
84 void SetMode(int Mode) 85 { 86 bool newAuto = (Mode == AUTOMATIC); 87 if(newAuto == !inAuto) 88 { /*we just went from manual to auto*/
89 Initialize(); 90 } 91 inAuto = newAuto; 92 } 93
94 void Initialize() 95 { 96 lastInput = Input; 97 ITerm = Output; 98 if(ITerm > outMax) ITerm= outMax; 99 else if(ITerm < outMin) ITerm= outMin; 100 } 101
102 void SetControllerDirection(int Direction) 103 { 104 controllerDirection = Direction; 105 }
4、PID 完成
差不多結束了。我們已經把“初學者的PID”變成了我目前知道的最健壯的控制器。對於那些正在尋找PID庫的詳細解釋的讀者,我希望您得到了您想要的。對於那些正在編寫自己的PID的人,我希望您能夠收集到一些想法,這些想法可以為您節省一些時間。
最后說明兩點:
- 如果這個系列中的東西看起來不對,請告訴我。我可能錯過了什么,或者可能只需要在我的解釋中更清楚。無論哪種方式,我都想知道。
- 這只是一個基本的PID。為了簡單起見,我有意省略了許多其他問題。在我的腦海中:前饋,重置平鋪,整數數學,不同的PID形式,使用速度而不是位置。如果有興趣讓我探討這些話題,請讓我知道。
歡迎關注: