• 改进初学者的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,您可以将它们设置为输出限制。

    欢迎关注:

  • 相关阅读:
    SpringMVC之@RequestMapping、ThreadLocal、DBCP、JNDI数据源管理
    安全威胁的分类
    软件可靠性的一些关注点
    LibTomMath + vs2008,安装和测试程序
    winxp下,Ice3.4.1 + VS2008_sp1,这个组合安装有点麻烦
    逗号操作符简化代码
    转,F5负载均衡算法及基本原理(Intro to Load Balancing)
    JVM大内存分析,不推荐jmap+jhat,推荐JProfiler
    选择soapUI做性能测试,这就是悲剧的开始
    关于linux LVM的好图 (Logic Volume Management,逻辑卷管理)
  • 原文地址:https://www.cnblogs.com/foxclever/p/11335172.html
Copyright © 2020-2023  润新知