最近看到了Brett Beauregard發表的有關PID的系列文章,感受對於理解PID算法頗有幫助,因而將系列文章翻譯過來!在自我提升的過程當中,也但願對同道中人有所幫助。做者Brett Beauregard的原文網址:http://brettbeauregard.com/blog/2011/04/improving-the-beginner's-pid-initialization/算法
1、問題所在spa
在前一節中,咱們實現了關閉和打開 PID 的功能。咱們將其關閉,但如今讓咱們來看看當咱們從新打開它時會發生什麼:翻譯
呵!PID跳回到它發送的最後一個輸出值,而後從那裏開始調整。這將致使咱們不但願出現的輸入顛簸。code
2、解決方案blog
這個很容易解決。由於咱們如今知道何時打開 (從手動到自動),咱們只需爲一個平穩的過渡作一些初始化。這意味着對2個工做變量的存儲 (積分項和最後的輸入項) 進行處理,以防止輸出跳轉。get
3、代碼it
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 void Compute() 14 { 15 if(!inAuto) return; 16 unsigned long now = millis(); 17 int timeChange = (now - lastTime); 18 if(timeChange>=SampleTime) 19 { 20 /*Compute all the working error variables*/ 21 double error = Setpoint - Input; 22 ITerm+= (ki * error); 23 if(ITerm> outMax) ITerm= outMax; 24 else if(ITerm< outMin) ITerm= outMin; 25 double dInput = (Input - lastInput); 26 27 /*Compute PID Output*/ 28 Output = kp * error + ITerm- kd * dInput; 29 if(Output> outMax) Output = outMax; 30 else if(Output < outMin) Output = outMin; 31 32 /*Remember some variables for next time*/ 33 lastInput = Input; 34 lastTime = now; 35 } 36 } 37 38 void SetTunings(double Kp,double Ki,double Kd) 39 { 40 double SampleTimeInSec = ((double)SampleTime)/1000; 41 kp = Kp; 42 ki = Ki * SampleTimeInSec; 43 kd = Kd / SampleTimeInSec; 44 } 45 46 void SetSampleTime(int NewSampleTime) 47 { 48 if (NewSampleTime > 0) 49 { 50 double ratio = (double)NewSampleTime 51 / (double)SampleTime; 52 ki *= ratio; 53 kd /= ratio; 54 SampleTime = (unsigned long)NewSampleTime; 55 } 56 } 57 58 void SetOutputLimits(double Min,double Max) 59 { 60 if(Min > Max) return; 61 outMin = Min; 62 outMax = Max; 63 64 if(Output > outMax) Output = outMax; 65 else if(Output < outMin) Output = outMin; 66 67 if(ITerm> outMax) ITerm= outMax; 68 else if(ITerm< outMin) ITerm= outMin; 69 } 70 71 void SetMode(int Mode) 72 { 73 bool newAuto = (Mode == AUTOMATIC); 74 if(newAuto && !inAuto) 75 { /*we just went from manual to auto*/ 76 Initialize(); 77 } 78 inAuto = newAuto; 79 } 80 81 void Initialize() 82 { 83 lastInput = Input; 84 ITerm = Output; 85 if(ITerm> outMax) ITerm= outMax; 86 else if(ITerm< outMin) ITerm= outMin; 87 }
咱們修改了 SetMode (...) 以檢測從手動到自動的轉換,並添加了初始化功能。它經過設置「積分項=輸出」來處理積分項,「最後輸入=輸入」以防止微分激增。比例項不依賴於過去的任何信息,所以不須要任何初始化。io
4、最終結果ast
咱們從上面的圖表中看到,正確的初始化會致使從手動到自動的無擾動切換,這正是咱們所追求的。class
5、更新:爲何不 ITerm=0?
我最近收到了不少問題,問爲何我沒有把 ITerm=0 設置爲初始化。做爲答案,我請您考慮如下方案:PID是手動的,用戶已將輸出設置爲50。一段時間後,該過程穩定到75.2 的輸入。用戶將設置點75.2 打開 PID。會發生什麼事?
我認爲,切換到自動後,輸出值應該保持在50。因爲 P 和 D 項將爲零,所以發生這種狀況的惟一方法是將 ITerm項初始化爲「輸出」值。
若是您處於須要輸出初始化爲零的狀況,則無需更改上面的代碼。在將 PID 從「手動」轉換爲「自動」以前,只需在調用例程中設置Output=0。
歡迎關注: