PID控制器积分项导致极端不稳定 [英] PID controller integral term causing extreme instability

查看:359
本文介绍了PID控制器积分项导致极端不稳定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个PID控制器运行在一个机器人上,旨在使机器人引导到罗盘前进。 PID校正以20Hz的速率重新计算/应用。



虽然PID控制器在PD模式下工作良好(IE,积分项为零)甚至最小量的积分将迫使输出不稳定,使得转向致动器被推到左或右极端。



代码:

  private static void DoPID(object o)
{
//将LED指示灯指示框架起始
BoardLED.Write(真);

//获取IMU标题
float currentHeading =(float)RazorIMU.Yaw;

//我们刚刚获得了IMU标题,所以我们需要计算从上次修正到标题读取
// *的时间* *。单位没有那么重要,但是我们将Ticks转换为毫秒
int deltaTime =(int)((LastCorrectionTime - DateTime.Now.Ticks)/ 10000);

//计算错误
//(让我们假设CurrentHeading真的是当前的GPS标题,OK?)
float error =(TargetHeading - currentHeading);

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

//我们计算出错误,但是我们需要确保错误被​​设置,以便我们将在
//方向修正最小的工作。例如,如果我们正在飞行2度的航向,并且错误在左边的这个(IE,360左右的地方)的几度
//将会有一个很大的错误,流动站将尝试转向所有
//要纠正的方式,当它只能向右转几度。
//简而言之,我们正在调整一个罗盘方向圈圈的事实,而不是在
上继续
//无穷大if(error< -180)
error = error + 360;
else if(error> 180)
error = error - 360;

//将此帧中计算的错误添加到运行总计
SteadyError = SteadyError +(error * deltaTime);

//我们需要允许一定的宽容。
//如果abs(错误)小于设置的数量,我们将
//将错误设置为0,有效地告诉方程式
// rover是完美的。
if(MyAbs(error)< AllowError)
error = 0;

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

//计算比例项
float proportional = Kp * error;

//计算积分项
float integer = Ki *(SteadyError * deltaTime);

//计算派生项
float derivative = Kd *((error - PrevError)/ deltaTime);

//将它们全部加在一起以获得校正值
//将转向伺服器设置为校正
Steering.Degree = 90 +比例+积分+导数;

//我们已经应用了更正,所以我们需要*立即*记录
//在下一个帧中生成deltaTime的绝对时间
LastCorrectionTime = DateTime.Now .Ticks;

//此时,当前的PID框架完成
// ------------------------ ------------------------------------
//现在,我们需要设置下一个PID框架并关闭

//当前错误现在是以前的错误
//(记住,我们完成了当前的框架,所以在
/ /相对项,前一帧为当前帧)
PrevError = error;

//完成
BoardLED.Write(false);有没有人知道为什么会发生这种情况,或者如何解决它?
}



<

解决方案

看起来你正在将时间应用到积分三次。
错误已经是自上一个样本以来累积的错误,所以不需要将deltaTime乘以它。所以我会把代码改成如下。



SteadyError + =错误;



SteadyError是



所以积分应该是SteadyError * Ki



float integral = Ki * SteadyError;



编辑:



再次通过你的代码,除了上面的修复之外,还有其他一些项目。



1)你不需要以毫秒为单位的增量时间。在正常采样系统中,增量项是一个,但是对于20Hz的速率来说,这个值为50,这具有通过这个因子增加Ki的效果,并且将Kd降低50倍。如果您担心抖动,则需要将增量时间转换为相对采样时间。我会使用公式。



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

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)验证转向.Degree正在收到正确的解析并签收。



5)最后你可以把deltaTime放在一起,用下面的方式计算差分项。

  float derivative = Kd *(error  -  PrevError); 

所有这些代码都变成了。

  private static void DoPID(object o)
{
//将LED指示为表示框架起始
BoardLED.Write(true);

//获取IMU标题
float currentHeading =(float)RazorIMU.Yaw;


//计算错误
//(让我们假设CurrentHeading真的是当前的GPS标题,OK?)
float error =(TargetHeading - currentHeading);

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

//我们计算出错误,但是我们需要确保错误设置为
//,以便我们将在
//方向上修正最少的工作。例如,如果我们正在飞行一个2度的标题
//,并且左边的错误是几度
//(IE,360左右),那么将有一个
//大错误,流氓将尝试将所有
//的方式转换为正确,当它可以转向右边的
//几度。简而言之,我们正在调整一个罗盘标题在圆圈中绕过
//而不是在行
上继续无限的事实if(error< -270)error + = 360;
if(error> 270)error - = 360;

//将此帧中计算的错误添加到运行总计
SteadyError + = error;

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

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

//计算比例项
float proportional = Kp * error;

//计算积分项
float integral = Ki * SteadyError;

//计算派生项
float derivative = Kd *(error - PrevError);

//将它们全部加在一起以获得校正值
//将转向伺服器设置为校正
Steering.Degree = 90 +比例+积分+导数;

//此时,当前的PID框架完成
// ------------------------ ------------------------------------
//现在,我们需要设置下一个PID框架并关闭

//当前错误现在是以前的错误
//(记住,我们完成了当前的框架,所以在
/ /相对项,前一帧为当前帧)
PrevError = error;

//完成
BoardLED.Write(false);
}


I have a PID controller running on a robot that is designed to make the robot steer onto a compass heading. The PID correction is recalculated/applied at a rate of 20Hz.

Although the PID controller works well in PD mode (IE, with the integral term zero'd out) even the slightest amount of integral will force the output unstable in such a way that the steering actuator is pushed to either the left or right extreme.

Code:

        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);
    }

Does anyone have any idea why this is happening or how to fix it?

解决方案

It looks like you are applying your time base to the integral three times. Error is already the accumulated error since the last sample so yo don't need to multiply deltaTime times it. So I would change the code to the following.

SteadyError += error ;

SteadyError is the integral or sum of error.

So the integral should just be SteadyError * Ki

float integral = Ki * SteadyError;

Edit:

I have gone through your code again and there are several other items that I would fix in addition to the above fix.

1) You don't want delta time in milliseconds. In a normal sampled system the delta term would be one but you are putting in a value like 50 for the 20Hz rate this has the effect of increasing Ki by this factor and decreasing Kd by a factor of 50 as well. If you are worried about jitter then you need to convert delta time to a relative sample time. I would use the formula instead.

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

the 500000.0 is the number of expected ticks per sample which for 20Hz is 50ms.

2) Keep the integral term within a range.

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

3) Change the following code so that when error is around -180 you do not get a step in error with a small change.

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

4) Verify Steering.Degree is receiving the correct resolution and sign.

5) Lastly yo can probably just drop deltaTime all together and calculate the differential term the following way.

float derivative = Kd * (error - PrevError);

With all of that your code becomes.

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);
}

这篇关于PID控制器积分项导致极端不稳定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆