我们已经设计了PID控制器,并根据实际使用的情况对器进行了诸多的改进。在这一篇中我们将讨论如何改进PID控制器超调的问题。
1、问题提出
在前面的文章中,我们曾推导过增量式PID控制器的公式,并且对其进行了离散化以适用于程序实现,具体的离散化公式如下:
以这个公式为基础,我们实现的增量式PID控制器,只需要根据偏差计算增量并调整输出就能实现自动调节了。但是我们分析一下就会发现,如果参数设置的不合适,或者偏差的数值比较大,那么计算得到的增量值就会很大,这样输出的变化就会很大,这种情况下就容易出现超调的问题。
超调一旦出现,可能需要较长时间才能让系统稳定,严重的甚至会造成振荡。这自然不是我们想要的结果,所以我们希望PID控制器具有能够防止超调能力。
2、分析设计
关于超调这个问题,我们先来分析一下什么因素能够造成超调。所谓超调我们可以理解为过度的调整反而造成了系统的不稳定。那究竟哪些情况会造成过度调整呢?一般来讲过于频繁的调节、输入信号的突然大幅度变化、输出信号的陡变以及被控对象的特性。
对于上述情况我们来逐一分析一下应对办法。首先是过于频繁的调节,很多时候我们可能觉得调节的快可以加快系统的响应。但实际上与系统本身的特性有关。如果系统不存在滞后或者滞后的特性很小,自然是没有问题的。但很多时候,系统会存在比较大的滞后特性,那么过于频繁的调节就会快速累积输出控制从而产生超调。这种情况下我们可以增大调节周期来稳定系统。
其次我们来看输入信号的突然大幅度变化的影响。我们这里所说的输入信号是指PID控制器的设定值。在一个稳定系统中,大幅度改变设定值会使偏差急剧增大,而大的偏差往往会造成控制器的输出快速累积,这种快速累积往往就会造成超调。这种情况下,我们可以控制设定值的变化速度来改善。一般让设定值线性缓慢变化,至于步进值则可根据系统的特性调整。
再来看看输出信号的陡变的影响,有些时候因为参数设置与系统的匹配问题,即使偏差变化不大输出的变化也会很大。这个时候控制变量会因此而产生较大变化,这一变化会增大偏差,进一步影响输出,如此循环累积就可能产生超调。特别是一些反应比较快的系统更容易出现这种情况。一般我们可以通过限制输出的变化来防止这种现象。
而被控对象的特性主要表现在系统的滞后特性方面,一般来说越是滞后的系统越不容易调稳,也越是容易出现超调甚至振荡。应对这一情况一般也可以采用减缓调节的方式来调整。具体方式也是前面的三种。
3、软件实现
我们已经分析了产生超调的主要原因以及应对的办法,接下来我们就来考虑怎么改进PID控制器以达到防超调的效果。针对前述提到的几种情况我们来分别设计软件上的实现方式。
对于调节相对频繁的问题,我们的PID控制器设计时就已考虑到采样周期是可调整的,所以我们只需根据不同的被控对象来适当地调整它就好了。而对于设定值的大幅度突变和调节输出的陡变两种情况,我们引入两个参数,一个参数用于开关设定值时平滑处理功能,一个参数用于设置输出增量限幅功能的开关。我们采用两个枚举来实现这一功能,具体定义如下:
/*定义设定值平滑枚举类型*/
typedef enum ClassicPIDSM{SMOOTH_DISABLE, //不启用设定值平滑SMOOTH_ENABLE //启用设定值平滑
}ClassicPIDSMType;/*定义防止输出陡变的枚举*/
typedef enum ClassicPIDPAC{PREVENT_ABRUPT_DISABLE, //不启用输出防陡变PREVENT_ABRUPT_ENABLE //启用输出防陡变
}ClassicPIDPACType;
同样的我们需要在PID对象类型中增加相应的属性。我们为PID控制器对象类型添加设定值平滑参数以及输出增量限幅参数,所以我们实现PID控制器的对象类型定义如下:
/*定义PID对象类型*/
typedef struct CLASSIC
{float *pPV; //测量值指针float *pSV; //设定值指针float *pMV; //输出值指针uint16_t *pMA; //手自动操作指针#if PID_PARAMETER_STYLE > (0)float *pKp; //比例系数指针float *pKi; //积分系数指针float *pKd; //微分系数指针
#elsefloat *pPb; //比例带float *pTi; //积分时间,单位为秒float *pTd; //微分时间,单位为秒float ts; //采样周期,单位为秒
#endiffloat setpoint; //设定值float lasterror; //前一拍偏差float preerror; //前两拍偏差float deadband; //死区float result; //PID控制器计算结果float output; //输出值0-100%float maximum; //输出值上限float minimum; //输出值下限float errorabsmax; //偏差绝对值最大值float errorabsmin; //偏差绝对值最小值float alpha; //不完全微分系数float deltadiff; //微分增量float integralValue; //积分累计量float gama; //微分先行滤波系数float lastPv; //上一拍的过程测量值float lastDeltaPv; //上一拍的过程测量值增量ClassicPIDDRType direct; //正反作用ClassicPIDSMType sm; //设定值平滑ClassicPIDCSType cas; //串级设定ClassicPIDPACType pac; //输出防陡变
}CLASSICPID;
修改了PID控制器对象的定义,我们还需要对PID控制器做必要的改进。我们添加设定值平滑操作,并通过设置参数sm来控制是否启用该操作。我们添加输出防陡变的操作,并通过配置参数pac来决定是否启用。具体实现如下:
void PIDRegulator(CLASSICPID *vPID)
{float thisError;float result;float factor;float increment;float pError,dError,iError;float kp,ki,kd;#if PID_PARAMETER_STYLE > (0)kp=*vPID->pKp;ki=*vPID->pKi;kd=*vPID->pKd;
#elseif((*vPID->pTi)<vPID->ts){*vPID->pTi=vPID->ts;}kp=100.0/(*vPID->pPb);ki=kp*(vPID->ts/(*vPID->pTi));kd=kp*((*vPID->pTd)/vPID->ts);
#endifif(*vPID->pMA<1) //手动模式{vPID->output=*vPID->pMV;//设置无扰动切换vPID->result=(vPID->maximum-vPID->minimum)*vPID->output/(float)100.0+vPID->minimum;*vPID->pSV=*vPID->pPV;vPID->setpoint=*vPID->pSV;}else //自动模式{if(vPID->sm==SMOOTH_ENABLE) //设定值平滑变化{SmoothSetpoint(vPID);}else{if(vPID->cas==CASCADE) //串级处理{vPID->setpoint=(vPID->maximum-vPID->minimum)*(*vPID->pSV)/(float)100.0+vPID->minimum;}else{vPID->setpoint=*vPID->pSV;}}thisError=vPID->setpoint-(*vPID->pPV); //得到偏差值result=vPID->result;if (fabsf(thisError)>vPID->deadband){pError=thisError-vPID->lasterror;iError=(thisError+vPID->lasterror)/(float)2.0;dError=thisError-2*(vPID->lasterror)+vPID->preerror;//变积分系数获取factor=VariableIntegralCoefficient(thisError,vPID->errorabsmax,vPID->errorabsmin);//计算微分项增量带不完全微分vPID->deltadiff=kd*(1-vPID->alpha)*dError+vPID->alpha*vPID->deltadiff;increment=kp*pError+ki*factor*iError+vPID->deltadiff; //增量计算}else{if((fabsf(vPID->setpoint-vPID->minimum)<vPID->deadband)&&(fabsf((*vPID->pPV)-vPID->minimum)<vPID->deadband)){result=vPID->minimum;}increment=0.0;}//输出变化幅度if(vPID->pac==PREVENT_ABRUPT_ENABLE){increment=fabsf(increment)>fabsf(thisError)?thisError:increment;}//正反作用设定if(vPID->direct==DIRECT){result=result+increment;}else{result=result-increment;}/*对输出限值,避免超调和积分饱和问题*/if(result>=vPID->maximum){result=vPID->maximum;}if(result<=vPID->minimum){result=vPID->minimum;} vPID->preerror=vPID->lasterror; //存放偏差用于下次运算vPID->lasterror=thisError;vPID->result=result;vPID->output=(vPID->result-vPID->minimum)/(vPID->maximum-vPID->minimum)*(float)100.0;*vPID->pMV=vPID->output;}
}
在这里我们将设定值平滑的步进值按量程比例固定了,但实际上设定值平滑的步进值应该根据采样周期来调整。并且在不同的被控系统中也应该做适当调整。而输出防陡变的阈值我们采用了偏差值。
4、总结
在这一篇中,我们分析了引起超调的几点原因并思考了各自的应对策略。依据这些,我们改进了PID控制器使其具备有防超调的基本功能。我们在温度控制和流量控制中实际使用了改进过的PID控制器,对超调的抑制作用是非常明显的。但需要指出的是如果设定值平滑的步进值设置过小或是输出增量限幅的阈值过小将会使得调节过程极其缓慢。
一般来说象温度、物位等滞后相对比较大的系统使用设定值平滑会有比较好的效果,而增加输出增量限幅则可能会调节的比较缓慢。而流量、压力等滞后相对较小的系统使用输出增量限幅会有比较好的效果,而增加设定值平滑则有可能是的调节变得缓慢。
最后我们简单说一下调节周期的问题。调节周期的选择是一个需要认真考虑的问题。即使我们采用相同的参数整定,采用相同的防超调处理,当调节周期不同时,效果可能会有较大差异,所以调节周期必须确定好。一般来说,滞后大的系统调节周期需要设置的长一点,而滞后小的系统调节周期需要设置的短一点,具体还需要根据控制要求来设置。