最近看到了Brett Beauregard發表的有關PID的系列文章,感覺對於理解PID算法很有幫助,於是將系列文章翻譯過來!在自我提高的過程中,也希望對同道中人有所幫助。作者Brett Beauregard的原文網址:http://brettbeauregard.com/blog/2011/04/improving-the-beginner’s-pid-reset-windup/
1、問題所在
積分飽和是一個陷阱,它可能比任何其他內容對初學者有更多的要求。當 PID 認為它可以做一些它做不到的事情時,就會發生這種情況。例如,Arduino 上的 PWM 輸出接受0-255 之間的值。默認情況下,PID 不知道這一點。如果它認為300-400-500 會奏效,它將嘗試那些期望得到它所需要的東西的值。由於在現實中,該值被限制在 255,它只會繼續嘗試越來越多的數字,而不會取得任何進展。
這個問題以奇怪的滯后的形式顯現出來。上面我們可以看到,輸出以 "非常興奮" 的方式超出了外部限制。當設定值下降時,輸出必須在低於255的限制線之前逐步減少。
2、解決方案–步驟1
有幾種方法可以緩解積分飽和,但我選擇的方法如下:告訴 PID 輸出限制是什么。在下面的代碼中,您將看到現在有一個 SetOuputLimits函數。一旦達到任一限制,pid 停止求和 (積分)。它知道沒有什么可做的; 它知道自己已經無能為力。由於輸出不會積分飽和,所以當設定值下降到我們可以做一些事情的范圍內時,我們會得到立即的響應。
3、解決方案–步驟2
不過,請注意,在上面的圖表中,雖然我們擺脫了那個積分飽和滯后,但我們並沒有完成這一步。PID認為它正在發送的東西,和剛剛發送的東西間還是存在偏差。為什么?比例項和 (在較小的程度上) 微分項的存在。
盡管積分項已被安全地鉗位,但P和D仍在增加他們兩的份額,產生的結果也有可能會高於輸出限制。在我看來,這依然是不可接受的。如果用戶調用名為 "SetOutputLimits" 的函數,他們必須假定這意味着“輸出將保留在這些值之內”。因此,對於步驟2,我們將其作為一個有效的假設。除了鉗位積分項之外,我們還要鉗位輸出值,使其保持在預期的位置。
(注意: 您可能會問為什么我們需要鉗位這兩個。如果我們要控制輸出限制,為什么要單獨鉗位積分項呢?如果我們只是鉗位輸出,積分項就會不停的增長。雖然在向上的過程中,輸出看起來會很好,但我們會看到在下降的過程中,會受到積分飽和的影響。
4、代碼
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 void Compute() 9 { 10 unsigned long now = millis(); 11 int timeChange = (now - lastTime); 12 if(timeChange>=SampleTime) 13 { 14 /*Compute all the working error variables*/ 15 double error = Setpoint - Input; 16 ITerm+= (ki * error); 17 if(ITerm> outMax) ITerm= outMax; 18 else if(ITerm< outMin) ITerm= outMin; 19 double dInput = (Input - lastInput); 20 21 /*Compute PID Output*/ 22 Output = kp * error + ITerm- kd * dInput; 23 if(Output > outMax) Output = outMax; 24 else if(Output < outMin) Output = outMin; 25 26 /*Remember some variables for next time*/ 27 lastInput = Input; 28 lastTime = now; 29 } 30 } 31 32 void SetTunings(double Kp,double Ki,double Kd) 33 { 34 double SampleTimeInSec = ((double)SampleTime)/1000; 35 kp = Kp; 36 ki = Ki * SampleTimeInSec; 37 kd = Kd / SampleTimeInSec; 38 } 39 40 void SetSampleTime(int NewSampleTime) 41 { 42 if (NewSampleTime > 0) 43 { 44 double ratio = (double)NewSampleTime 45 / (double)SampleTime; 46 ki *= ratio; 47 kd /= ratio; 48 SampleTime = (unsigned long)NewSampleTime; 49 } 50 } 51 52 void SetOutputLimits(double Min,double Max) 53 { 54 if(Min > Max) return; 55 outMin = Min; 56 outMax = Max; 57 58 if(Output > outMax) Output = outMax; 59 else if(Output < outMin) Output = outMin; 60 61 if(ITerm> outMax) ITerm= outMax; 62 else if(ITerm< outMin) ITerm= outMin; 63 }
添加了一個新函數,允許用戶指定輸出限制 [52-63 行]。這些限制用於鉗位積分項 [17-18] 和輸出 [23-24]
5、最終結果
正如我們所看到的,積分飽和被消除了。此外,輸出將保留在我們希望的位置。這意味着無需對輸出進行外部鉗位。如果希望它的范圍從23到 167,您可以將它們設置為輸出限制。
歡迎關注: