初学者PID进阶

存在的问题

本次调整的目的是消除称为“Derivative Kick”的现象。



 

上图反应了这个问题。既然 error=Setpoint-Input, 任何 Setpoint 的变化将导致error瞬时发生变化。 这种变化的导数是无穷大(实际上,既然dt不是0,只是计算为一个非常大的数字)。这个数字被送入pid方程,输出一个非期望的峰值。幸运的是有一个简单的方法来摆脱这个。

解决方案



 
导数误差等于负导数的输入,除非当Setpoint  改变。这不是一个完美的解决方案,相对于添加(Kd *误差导数),我们减去(Kd *输入的导数)。这被称为使用“导数测量”。
 

The Code

/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double errSum, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
void Compute()
{
   unsigned long now = millis();
   int timeChange = (now - lastTime);
   if(timeChange>=SampleTime)
   {
      /*计算所有误差变量,Compute all the working error variables*/
      double error = Setpoint - Input;
      errSum += error;
      double dInput = (Input - lastInput);
 
      /*技术PID输出,Compute PID Output*/
      Output = kp * error + ki * errSum - kd * dInput;
 
      /*Remember some variables for next time*/
      lastInput = Input;
      lastTime = now;
   }
}
 
void SetTunings(double Kp, double Ki, double Kd)
{
  double SampleTimeInSec = ((double)SampleTime)/1000;
   kp = Kp;
   ki = Ki * SampleTimeInSec;
   kd = Kd / SampleTimeInSec;
}
 
void SetSampleTime(int NewSampleTime)
{
   if (NewSampleTime > 0)
   {
      double ratio  = (double)NewSampleTime
                      / (double)SampleTime;
      ki *= ratio;
      kd /= ratio;
      SampleTime = (unsigned long)NewSampleTime;
   }
}
 

这里的修改非常容易。我们替换误差导数和输入导数。记住最后输入而不是最后误差。

The Result



 

这里就是修改后的结果,注意,输入看起来仍然相同。所以我们得到同样的性能,但每次Setpoint 的变化不会输出巨大的峰值。

这或许是也或许不是一个大问题。这取决于你的程序对于输出峰值的敏感度。

猜你喜欢

转载自tedeum.iteye.com/blog/1797229