2010-10-10 74 views
12

我有一個PID控制器在機器人上運行,該機器人設計用於使機器人轉向到指南針標題上。 PID校正以20Hz的速率重新計算/應用。PID控制器積分項導致極度不穩定

雖然PID控制器在PD模式下工作良好(即積分項爲零),即使最輕微的積分也會強制輸出不穩定,從而將轉向致動器推向左側或者說是極端的。

代碼:

 private static void DoPID(object o) 
    { 
     // Bring the LED up to signify frame start 
     BoardLED.Write(true); 

     // Get IMU heading 
     float currentHeading = (float)RazorIMU.Yaw; 

     // We just got the IMU heading, so we need to calculate the time from the last correction to the heading read 
     // *immediately*. The units don't so much matter, but we are converting Ticks to milliseconds 
     int deltaTime = (int)((LastCorrectionTime - DateTime.Now.Ticks)/10000); 

     // Calculate error 
     // (let's just assume CurrentHeading really is the current GPS heading, OK?) 
     float error = (TargetHeading - currentHeading); 

     LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2"); 

     // We calculated the error, but we need to make sure the error is set so that we will be correcting in the 
     // direction of least work. For example, if we are flying a heading of 2 degrees and the error is a few degrees 
     // to the left of that (IE, somewhere around 360) there will be a large error and the rover will try to turn all 
     // the way around to correct, when it could just turn to the right a few degrees. 
     // In short, we are adjusting for the fact that a compass heading wraps around in a circle instead of continuing 
     // infinity on a line 
     if (error < -180) 
      error = error + 360; 
     else if (error > 180) 
      error = error - 360; 

     // Add the error calculated in this frame to the running total 
     SteadyError = SteadyError + (error * deltaTime); 

     // We need to allow for a certain amount of tolerance. 
     // If the abs(error) is less than the set amount, we will 
     // set error to 0, effectively telling the equation that the 
     // rover is perfectly on course. 
     if (MyAbs(error) < AllowError) 
      error = 0; 

     LCD.Lines[2].Text = "Error: " + error.ToString("F2"); 

     // Calculate proportional term 
     float proportional = Kp * error; 

     // Calculate integral term 
     float integral = Ki * (SteadyError * deltaTime); 

     // Calculate derivative term 
     float derivative = Kd * ((error - PrevError)/deltaTime); 

     // Add them all together to get the correction delta 
     // Set the steering servo to the correction 
     Steering.Degree = 90 + proportional + integral + derivative; 

     // We have applied the correction, so we need to *immediately* record the 
     // absolute time for generation of deltaTime in the next frame 
     LastCorrectionTime = DateTime.Now.Ticks; 

     // At this point, the current PID frame is finished 
     // ------------------------------------------------------------ 
     // Now, we need to setup for the next PID frame and close out 

     // The "current" error is now the previous error 
     // (Remember, we are done with the current frame, so in 
     // relative terms, the previous frame IS the "current" frame) 
     PrevError = error; 

     // Done 
     BoardLED.Write(false); 
    } 

沒有人有任何想法,爲什麼這種情況正在發生或如何解決呢?

+1

120個字符長的線? 80(79)。 – 2010-10-11 14:22:03

+0

你在做什麼? PID是一個實時應用程序,但.NET上的C#不具備實時功能,大多數目標沒有FPU,所以也可以建議浮點實現。 – Clifford 2010-10-11 19:40:09

回答

8

它看起來像你正在應用你的時間基準積分三次。 錯誤已經是自上次採樣以來的累計錯誤,因此您不需要將deltaTime乘以它。所以我會將代碼更改爲以下內容。

SteadyError += error ;

SteadyError是積分或誤差的總和。

所以整體應該只是SteadyError *文

float integral = Ki * SteadyError;

編輯:

我已經通過您的代碼又理了一遍,並有我會除了解決上述幾個其他項目固定。

1)您不希望以毫秒爲單位的增量時間。在一個正常的採樣系統中,delta項是一個,但是對於20Hz的頻率,你輸入的值是50,這樣會使Ki增加這個因子,Kd減少50倍。如果您擔心抖動,則需要將增量時間轉換爲相對採樣時間。我會用公式代替。

float deltaTime = (LastCorrectionTime - DateTime.Now.Ticks)/500000.0

的500000.0是每樣本這對於20Hz的爲50ms預期蜱的數目。

2)保持積分項在一個範圍內。

if (SteadyError > MaxSteadyError) SteadyError = MaxSteadyError; 
if (SteadyError < MinSteadyError) SteadyError = MinSteadyError; 

3)更改下面的代碼,以便當錯誤在-180左右時,您不會因小的更改而出現錯誤。

if (error < -270) error += 360; 
if (error > 270) error -= 360; 

4)驗證Steering.Degree正在接收正確的分辨率和符號。5)最後喲可能只需要將deltaTime全部放在一起,並按以下方式計算微分項。

float derivative = Kd * (error - PrevError); 

隨着你所有的代碼變成了。

private static void DoPID(object o) 
{ 
    // Bring the LED up to signify frame start 
    BoardLED.Write(true); 

    // Get IMU heading 
    float currentHeading = (float)RazorIMU.Yaw; 


    // Calculate error 
    // (let's just assume CurrentHeading really is the current GPS heading, OK?) 
    float error = (TargetHeading - currentHeading); 

    LCD.Lines[0].Text = "Heading: "+ currentHeading.ToString("F2"); 

    // We calculated the error, but we need to make sure the error is set 
    // so that we will be correcting in the 
    // direction of least work. For example, if we are flying a heading 
    // of 2 degrees and the error is a few degrees 
    // to the left of that (IE, somewhere around 360) there will be a 
    // large error and the rover will try to turn all 
    // the way around to correct, when it could just turn to the right 
    // a few degrees. 
    // In short, we are adjusting for the fact that a compass heading wraps 
    // around in a circle instead of continuing infinity on a line 
    if (error < -270) error += 360; 
    if (error > 270) error -= 360; 

    // Add the error calculated in this frame to the running total 
    SteadyError += error; 

    if (SteadyError > MaxSteadyError) SteadyError = MaxSteadyError; 
    if (SteadyError < MinSteadyError) SteadyError = MinSteadyError; 

    LCD.Lines[2].Text = "Error: " + error.ToString("F2"); 

    // Calculate proportional term 
    float proportional = Kp * error; 

    // Calculate integral term 
    float integral = Ki * SteadyError ; 

    // Calculate derivative term 
    float derivative = Kd * (error - PrevError) ; 

    // Add them all together to get the correction delta 
    // Set the steering servo to the correction 
    Steering.Degree = 90 + proportional + integral + derivative; 

    // At this point, the current PID frame is finished 
    // ------------------------------------------------------------ 
    // Now, we need to setup for the next PID frame and close out 

    // The "current" error is now the previous error 
    // (Remember, we are done with the current frame, so in 
    // relative terms, the previous frame IS the "current" frame) 
    PrevError = error; 

    // Done 
    BoardLED.Write(false); 
} 
+0

雖然一個錯誤,不應該像他所觀察到的那樣引起飽和。 – 2010-10-11 14:24:39

+0

我不確定爲什麼當它處於設定值的AllowError中時,會將錯誤歸零。這會引入一個死區,並且會在接近零時來回走動(因爲沒有錯誤信號來糾正錯誤)。 特別是,積分術語確實希望這個小小的錯誤能夠使事情保持在正確的軌道上。當系統開始偏離設定值並將其拉回時,它會加上小的誤差。比例項無法做到這一點。 – sbass 2010-10-12 11:56:40

+0

'if(MyAbs(error) 2010-10-12 13:05:48

1

我不知道爲什麼你的代碼不工作,但我幾乎是積極的,你無法測試它,看看爲什麼。你可能會注入一個定時器服務,所以你可以嘲笑一下,看看發生了什麼:

public interace ITimer 
{ 
    long GetCurrentTicks() 
} 

public class Timer : ITimer 
{ 
    public long GetCurrentTicks() 
    { 
     return DateTime.Now.Ticks; 
    } 
} 

public class TestTimer : ITimer 
{ 
    private bool firstCall = true; 
    private long last; 
    private int counter = 1000000000; 

    public long GetCurrentTicks() 
    { 
     if (firstCall) 
      last = counter * 10000; 
     else 
      last += 3500; //ticks; not sure what a good value is here 

     //set up for next call; 
     firstCall = !firstCall; 
     counter++; 

     return last; 
    } 
} 

然後,GetCurrentTicks()更換兩個呼叫DateTime.Now.Ticks,你可以通過代碼,看看有什麼價值樣子。

5

你正在初始化SteadyError(奇怪的名字......爲什麼不是「集成商」)?如果它在啓動時包含一些隨機值,它可能永遠不會回到接近零(1e100 + 1 == 1e100)。

您可能患有integrator windup,通常應該消失,但如果消耗的時間比完全旋轉所需的時間更長(並再次結束積分),則不會消失。如果你的系統需要,這個簡單的解決方案是對積分器施加限制,儘管有more advanced solutions(PDF,879 kB)。

Ki是否有正確的標誌?

我會強烈由於它們的任意精度,阻止使用浮點數用於PID參數。使用整數(可能是fixed point)。你將不得不進行限制檢查,但它會比使用浮動更健全。

+0

使用浮點PID算法是常態,不知道你爲什麼反對。這裏沒有任何精度類型。 – 2011-08-07 07:25:58

3

積分項已經累積了一段時間,乘以deltaTime會使它以時間平方的速率積累。事實上,由於SteadyError已經通過將誤差乘以deltaTime而錯誤計算,所以這是時間立方體!

在SteadyError中,如果您試圖補償非週期性更新,最好修復非週期性。但是,無論如何計算都是有缺陷的。您以誤差/時間單位計算,而您只需要錯誤單位。該arithmentiaclly正確的方法來彌補定時抖動,如果真的有必要將是:

SteadyError += (error * 50.0f/deltaTime); 

如果保持的DeltaTime以毫秒爲單位和名義更新速率爲20Hz。但是,如果您正嘗試檢測時序抖動,deltaTime將更好地計算爲浮點數或根本不轉換爲毫秒數;你不必要地丟棄精確度。無論哪種方式,您需要的是通過標稱時間與實際時間的比率來修改誤差值。

很好看的是PID without a PhD

+0

感謝您沒有博士學位的PID鏈接。正是我在找什麼。 – 2011-03-15 02:58:34