前面我们发布了一系列PID控制器相关的文章,包括经典PID控制器以及参数自适应的PID控制器。这一系列PID控制器虽说实现了主要功能,也在实际使用中取得了良好效果,但还有很多的细节部分可以改进以提高性能和灵活性。所以在这篇中我们来讨论改进PID控制器以实现降低设定值阶跃带来的扰动。
1、提出问题
我们在使用PID控制器时,如果大幅度修改设定值有可能引入一个大的阶跃扰动。特别是对微分作用,这一扰动可能引起很大的振荡,这种情况我们通常称之为微分冲击。
为了应对因为微分作用对设定值变化造成的激烈响应,人们引入了微分先行的PID算法,这一算法是将基于偏差的微分修改为基于输入的微分。也就是说设定值的变化对微分不会有突变的影响,而只有作用在被控对象后微分才起作用,从而消除了设定值的变化带来的微分作用冲击。
然而有些时候,设定值的阶跃变化也会通过比例作用反映出来,这种冲击也会对系统造成振荡。当然我们也可以将其改为基于输入的比例,虽然也可以消除设定值的突变影响,但是比例和微分都是面向输入信号的对设定值的响应将会变得滞后,这也并非我们想要的。
所以我们希望能找到一种办法,既可响应设定值的变化,又不会造成大的冲击,这就是我们在此要考虑的问题。
2、分析设计
前面实际我们已经讨论了基于输入的比例和微分,这实际上改变了PID的调节方式,这里我们希望从另一个角度来考虑对设定值的响应问题。我们曾经讨论过步进式PID的控制方式就是一种比较好的处理办法。
所谓步进式PID算法,实际就是在设定值发生阶跃变化时,不直接对阶跃信号进行响应,而是在一定的时间内逐步改变设定值,直至使设定值达到目标值。这种逐步改变设定值的办法使得对象运行平稳。适用于高精度伺服系统的位置跟踪。
佷显然,这一方法并未改变PID控制器本身,而是对设定值做了前期处理。所以其结构框图与控制方程与其他的PID控制算法是一致的。
为了对设定值做必要处理,以使其不至快速变化,有多种方法。比较常用的是建立线性变化函数的办法。我们可以规定设定值从0-100%的变化时间为T,则可以确定设定值变化的斜率绝对值,或者说是步长。知道步长后,我们就可以根据步长来不断修改设定值,直到目标值。可用公式描述为:
其中SPt为设定值目标值,SPs为设定值的起始值,sl为步长,k为步长的变化系数:
我们希望能够根据我们自己的需要使用步进或者不使用步进,所以我们需要在PID对象中添加一个属性来配置其是否使用。
/*定义PID对象类型*/
typedef struct CLASSIC
{float *pPV; //测量值指针float *pSV; //设定值指针float *pMV; //输出值指针float *pKp; //比例系数指针float *pKi; //积分系数指针float *pKd; //微分系数指针uint16_t *pMA; //手自动操作指针float 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; //设定值平滑}CLASSICPID;
3、软件实现
我们讨论的对设定值的响应方式,其实就是直接给定设定值或者采用步进式给定设定值。所谓步进式其实质是将设定值的突变修改为平缓的变化,这一处理方式在控制中有大量应用。处理设定值变化过程的流程如下所示:
根据我们前面的描述和上面的流程图我们可以实现对PID控制器的修改。我们将对设定值处理的的部分单独置为函数,这样除了使用线性方式处理外我们也可以根据需要采取其他方式处理。
/*设定值平滑变化处理函数*/
static void SmoothSetpoint(CLASSICPID *vPID)
{float stepIn=(vPID->maximum-vPID->minimum)*0.1;float kFactor=0.0;if(fabs(vPID->setpoint-*vPID->pSV)<=stepIn){vPID->setpoint=*vPID->pSV;}else{if(vPID->setpoint-*vPID->pSV>0){kFactor=-1.0;}else if(vPID->setpoint-*vPID->pSV<0){kFactor=1.0;}else{kFactor=0.0;}vPID->setpoint=vPID->setpoint+kFactor*stepIn;}
}/* 通用PID控制器,采用增量型算法,具有变积分,梯形积分和抗积分饱和功能,微分项采用不完全微分,一阶滤波,alpha值越大滤波作用越强 */
void PIDRegulator(CLASSICPID *vPID)
{float thisError;float result;float factor;float increment;float pError,dError,iError;if(*vPID->pMA<1) //手动模式{vPID->output=*vPID->pMV;//设置无扰动切换vPID->result=(vPID->maximum-vPID->minimum)*vPID->output/100.0+-vPID->minimum;*vPID->pSV=*vPID->pPV;vPID->setpoint=*vPID->pSV;}else //自动模式{if(vPID->sm==SMOOTH_ENABLE){SmoothSetpoint(vPID);}else{vPID->setpoint=*vPID->pSV;}thisError=vPID->setpoint-(*vPID->pPV); //得到偏差值result=vPID->result;if (fabs(thisError)>vPID->deadband){pError=thisError-vPID->lasterror;iError=(thisError+vPID->lasterror)/2.0;dError=thisError-2*(vPID->lasterror)+vPID->preerror;//变积分系数获取factor=VariableIntegralCoefficient(thisError,vPID->errorabsmax,vPID->errorabsmin);//计算微分项增量带不完全微分vPID->deltadiff=(*vPID->pKd)*(1-vPID->alpha)*dError+vPID->alpha*vPID->deltadiff;increment=(*vPID->pKp)*pError+(*vPID->pKi)*factor*iError+vPID->deltadiff; //增量计算}else{if((fabs(vPID->setpoint-vPID->minimum)<vPID->deadband)&&(fabs((*vPID->pPV)-vPID->minimum)<vPID->deadband)){result=vPID->minimum;}increment=0.0;}//正反作用设定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)*100.0;*vPID->pMV=vPID->output;}
}
4、总结
我们引入了让设定值不大范围突变的方式,而不需要改变PID控制器的控制方式。经测试效果能够满足我们的要求。在我们这里步长固定采用量程的十分之一,事实上我们可以将其作为初始化参数予以修改,甚至我们也可以采用一定条件下变步长的方式来满足更高的控制要求。