初学者PID进阶
程序员文章站
2022-05-26 10:25:13
...
存在的问题
本次调整的目的是消除称为“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 的变化不会输出巨大的峰值。
这或许是也或许不是一个大问题。这取决于你的程序对于输出峰值的敏感度。