改進初學者的PID-積分飽和


  最近看到了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,您可以將它們設置為輸出限制。

歡迎關注:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM