根据上述流程图,我们很容易实现PID控制器:
/* PID初始化操作,需在对vPID对象的值进行修改前完成 */
/* CLASSICPID vPID,通用PID对象变量,实现数据交换与保存 */
/* float vMax,float vMin,过程变量的最大最小值(量程范围) */
void PIDParaInitialization(CLASSICPID vPID,float vMax,float vMin)
{
vPID->maximum=vMax; /*输出值上限*/
vPID->minimum=vMin; /*输出值下限*/
vPID->setpoint=vMin; /*设定值*/
vPID->kp=0.6; /*比例系数*/
vPID->ki=0.03; /*积分系数*/
vPID->kd=0.01; /*微分系数*/
vPID->lasterror=0.0; /*前一拍偏差*/
vPID->preerror=0.0; /*前两拍偏差*/
vPID->result=vMin; /*PID控制器结果*/
vPID->output=0.0; /*输出值*/
vPID->errorabsmax=(vMax-vMin)*0.8;
vPID->errorabsmin=(vMax-vMin)*0.2;
vPID->deadband=(vMax-vMin)*0.005; /*死区*/
vPID->alpha=0.2; /*不完全微分系数*/
vPID->deltadiff=0.0;
vPID->integralValue=0.0;
}
/* 通用PID控制器,采用增量型算法,具有变积分,梯形积分和抗积分饱和功能 */
/* 微分项采用不完全微分,一阶滤波,alpha值越大滤波作用越强 */
/* CLASSICPID vPID,PID对象变量,实现数据交换与保存 */
/* float pv,过程测量值,对象响应的测量数据,用于控制反馈 */
void PIDRegulator(CLASSICPID vPID,float pv)
{
float thisError;
float result;
float factor;
float increment;
float pError,dError,iError;
thisError=vPID->setpoint-processValue; //得到偏差值
result=vPID->result;
if(ABS(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=kd*(1-vPID->alpha)*dError+vPID->alpha*vPID->deltadiff;
increment=vPID->kp*pError+vPID->ki*factor*iError+vPID->deltadiff; //增量计算
}
else
{
if((abs(vPID->setpoint-vPID->minimum)deadband)&&(abs(pv-vPID->minimum)deadband))
{
result=vPID->minimum;
}
increment=0.0;
}
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=((result-vPID->minimum)/(vPID->maximum-vPID->minimum))*100.0;
}
/*变积分系数处理函数,实现一个输出0和1之间的分段线性函数 */
/* 当偏差的绝对值小于最小值时,输出为1;当偏差的绝对值大于最大值时,输出为0 */
/* 当偏差的绝对值介于最大值和最小值之间时,输出在0和1之间现行变化 */
/* float error,当前输入的偏差值 */
/* float absmax,偏差绝对值的最大值 */
/* float absmin,偏差绝对值的最小值 */
static float VariableIntegralCoefficient(float error,float absmax,floatabsmin)
{
float factor=0.0;
if(abs(error)<=absmin)
{
factor=1.0;
}
else if(abs(error)>absmax)
{
factor=0.0;
}
else
{
factor=(absmax-abs(error))/(absmax-absmin);
}
return factor;
}
(2)PWM输出
PWM输出较为简单,关于TIM配置等网上有很多,在此不再多说。根据PID控制器的输出,我们计算PWM的占空比,通过占空比来调节阀门的开度,从而控制流量大小。
*阀门控制调节*/
voidControlProcess(void)
{
uint16_t TimerPeriod = 0;
uint16_t PWMPulse = 0;
float dutyfactor=0.0;//定义占空比
vPID.setpoint=aPara.phyPara.flowSetPoint;
PIDRegulation(&vPID,aPara.phyPara.flowMeasuredValue);
dutyfactor=vPID.result/ADC1HighRange;
/*计算频率和占空比*/
TimerPeriod = PWMTimePeriod;//计算用于设置ARR寄存器的值使产生信号的频率为17.57 Khz
PWMPulse = (uint16_t) ((TimerPeriod -1)*dutyfactor);//计算CCR1寄存器的值在通道1和1N产生50%占空比,用于TIM1
HAL_TIM_PWM_Stop(&htim3,TIM_CHANNEL_1);//停止PWM
PWM_TIM_Configuration(TimerPeriod,PWMPulse);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);//启动PWM
}
3、测试结果
软件编写完成,硬件连接好后,运行并在线监控。PID控制器的调节速度还是比较快的,流量也比较稳定。如果进行细致的参数整定应该可以进一步提高控制效果。
`