什么是积分饱和
所谓积分饱和,可以这么来形容:心比天高,命比纸薄;一顿操作猛如虎,一看输出250。就这么个意思,通俗点讲就是PID控制器算的输出是一个比较大的值,奈何你的硬件只能输出一个不怎么大的值。生活中这样的控制器很常见,就如同那个拖拉机,你把油门踩进油箱里,它的速度也不会操作200码。
例如,某个硬件的输出值在0-250之间。如果你不告诉PID控制器,控制器是不知道的,它还以为它能输出500多,当计算的输出是500,真实的输出还是250,那PID控制器还会继续计算出因此它将继续尝试越来越大的数值而没有任何作用。
当目标值减小后,输出值不是从250开始下降,而是从500开始下降,这就导致输出从500降到250的这段时间,硬件系统一直是以极限输出250的值来输出的,这就导致输出调节出现一定的延时。
解决方案 – 告诉PID控制器,硬件的输出极限
这有几种方法可以缓解积分饱和,但是我选择的方法如下:告诉PID的输出极限是什么。下面的代码中可以看到有一个SetOuputLimits()函数。一旦达到了任一限制,PID便停止求和(积分)。他知道无事可做。由于输出不会饱和,因此当设定值下降到可以执行某项操作的范围时,我们会立刻得到响应。
不过请注意,在上图中,尽管我们消除了积分饱和滞后,但由于比例项和微分项的影响,即使已对积分项进行了安全钳位,比例项和微分项这两部分,依然会使输出结果高于输出限制。因此不仅要在积分项出设置输出极限值,还要给整个输出设置输出限制,这个输出限制可以使用SetOutputLimits函数。
代码的实现
/*working variables*/
unsigned long lastTime;
double Input, Output, Setpoint;
double ITerm, lastInput;
double kp, ki, kd;
int SampleTime = 1000; //1 sec
double outMin, outMax;
void Compute()
{
unsigned long now = millis();
int timeChange = (now - lastTime);
if(timeChange>=SampleTime)
{
/*Compute all the working error variables*/
double error = Setpoint - Input;
ITerm+= (ki * error);
if(ITerm> outMax) ITerm= outMax;
else if(ITerm< outMin) ITerm= outMin;
double dInput = (Input - lastInput);
/*Compute PID Output*/
Output = kp * error + ITerm- kd * dInput;
if(Output > outMax) Output = outMax;
else if(Output < outMin) Output = outMin;
/*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;
}
}
void SetOutputLimits(double Min, double Max)
{
if(Min > Max) return;
outMin = Min;
outMax = Max;
if(Output > outMax) Output = outMax;
else if(Output < outMin) Output = outMin;
if(ITerm> outMax) ITerm= outMax;
else if(ITerm< outMin) ITerm= outMin;
}
添加一个新和函数(输出限制函数)【52-63行】。用来钳制积分项和整个输出。
结果
积分饱和被消除了,输出也保持在我们想要的位置。