存在的问题
本次调整的目的是消除称为“Derivative Kick”的现象。
上图反应了这个问题。既然 error=Setpoint-Input, 任何 Setpoint 的变化将导致error瞬时发生变化。 这种变化的导数是无穷大(实际上,既然dt不是0,只是计算为一个非常大的数字)。这个数字被送入pid方程,输出一个非期望的峰值。幸运的是有一个简单的方法来摆脱这个。
解决方案
![](http://dl.iteye.com/upload/attachment/0080/3731/8a2a8cee-3593-3a83-b591-7494c84a2802.png)
导数误差等于负导数的输入,除非当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 的变化不会输出巨大的峰值。
这或许是也或许不是一个大问题。这取决于你的程序对于输出峰值的敏感度。