新浪博客

PLC的PID控制器代码

2015-11-23 10:05阅读:
各PLC厂家都有自己的PID功能块或者指令,但为什么还要自己编写PID控制器呢?其目的主要是为了改进PID的控制,而各厂家提供的标准PID功能块是无法修改的。 以下语言使用的是西门子的SCL语言编写,它是符合国际标准的语言,很容易转换成其他PLC语言,或者是计算机控制语言。
首先给出PLC的增量式控制代码:
(*增量式PID控制器*)
FUNCTION_BLOCK PID_INC
VAR_INPUT // Input Parameters
Manu:BOOL:=false; //确实是手动控制输出,还是由PID自动控制
Init:Bool:=false;
//参数初始化
Start:Bool:=false; //启动/停止PID或者强制手动输出
IsSmooth:=false; //确定是否平滑的启动
MVH:Real:=100; //输出上限
MVL:Real:=0; //输出下限
MVSet:Real:=0; //手动设定值
Emin:Real:=0; //死区设定
PV:Real:=0; //当前的反馈值
END_VAR
VAR_IN_OUT // input/Output Parameters
END_VAR
VAR_OUTPUT // Output Parameters
MV:Real:=0; //输出结果
END_VAR
VAR // Static Variables
ek:Real:=0; //当前偏差值
ek1:Real:=0; //前一次偏差值
ek2:Real:=0; //前两次偏差值
Kp:Real:=1; //比例系数
Ti:Real:=32767; //积分时间
Td:Real:=0; //微分时间
Ts:Real:=0; //数字PID运算的控制周期
lastMV:Real:=0; //上一次的输出值
SP:Real:=0; //设定值
SmoothFlag:Int:=0; //平滑标识
END_VAR
VAR_TEMP // Temporary Variables
END_VAR

(*增量式PID计算公式为
U(t)=Kp*{[ek(t)-ek(t-1)+Ts/Ti*ek(t)+Td/Ts*[ek(t)-2*ek(t-1)+ek(t-2)]]}+U(t-1)
在手动模式下Manu=true,Start=true时,输出手动设定值;而当Manu=true,Start=false时,
输出值维持不变;在Manu=true,Start=true时,启动PID的自动调节;
该功能块放到PLC的定时中断程序中相当于设定了固定执行周期,也可以尝试放到循环扫描程序中,
或者在循环扫描程序中用脉冲波控制Start参数
本功能块包含死区限定和输出幅值限定*)

IF Init THEN (*初始化PID参数,一般用于第一次执行PID时初始化*)
ek1:=0.0;
ek2:=0.0;
lastMV:=0.0;
SmoothFlag:=0;
MV:=0;
RETURN;
END_IF;

IF Start THEN (*启动命令*)
IF Manu THEN (*手动模式*)
MV:=MVSet;
lastMV:=MV;(*用于切换到自动时,输出的平滑*)
ek1:=0.0;
ek2:=0.0;
SmoothFlag:=0;
ELSE (*自动模式*)
ek:=SP-PV; (*计算偏差值*)
IF abs(ek) <</span> Emin THEN (*位于控制死区*)
MV:=lastMV;
ELSE IF IsSmooth & SmoothFlag<</span>4 THEN (*处于平滑过渡期*)
MV:=lastMV;
SmoothFlag:=SmoothFlag+1;
ELSE
MV:=Kp*(ek-ek1+Ts*ek/Ti+Td*(ek-ek1*2+ek2)/Ts)+lastMV;
IF MV>MVH THEN (*限制输出幅值*)
MV:=MVH;
ELSE IF MV<</span>MVL THEN
MV:=MVL;
END_IF;
lastMV:=MV; (*储存前一次的输出值*)
END_IF;
ek2=ek1; (*储存前两次的误差值*)
ek1=ek; (*储存上一次的误差值*)
END_IF;
ELSE (*停止命令*)
SmoothFlag:=0;
END_IF;
END_FUNCTION_BLOCK

最开始研究PID公式时,很多网上的资料说增量式比位置式好,如是并不想搞位置式,后来了解深入后发现,增量式在一些步进控制,也就是仅输出递增量的场合,优势明显,但当输出全量:U(k)=U(k-1)+dU(k)时,和位置式相比并没有优点:
1 全量递增式确实可以避免积分饱和,但可能出现所谓的比例/微分饱和,即比例/微分的控制量过大时,输出的限幅会将超过的量舍弃掉,累积下去最终会导致调节过慢,可使用'累积补偿'算法,但此算法由于涉及到累积,又会产生类似积分饱和的现象,相反位置式算法有抗积分饱和/积分分离等多种算法来避免饱和,并且算法很简单。
2 对于位置式,还有不完全微分/微分先行/比例先行等很多改进算法,而增量式相应的算法很复杂或者没有,譬如不完全微分,针对增量式,三篇文章就有三个不同的公式,且较复杂,个人推导了一下,感觉有问题。
下面给出了位置式的较为完整的PID算法,如有算法和公式上的问题,望指出:

(*位置式PID控制器*)
FUNCTION_BLOCK PID_PosExpT
TITLE = 'PID'
VERSION:'1'
AUTHOR:parker
NAME: PID
FAMILY: PID
VAR_INPUT // Input Parameters
Manu:BOOL:=false; //确实是手动控制输出,还是由PID自动控制
Init:Bool:=false; //参数初始化
Start:Bool:=false; //启动/停止PID(维持信号)
IsHSPID:Bool:=false; //是否使用不完全微分(=1,采用)
IsDSpeed:Bool:=false; //是否使用变速积分(=1,采用)
MVH:Real:=100; //输出上限
MVL:Real:=0; //输出下限
MVSet:Real:=0; //手动设定值
Emin:Real:=0; //死区设定
PV:Real:=0; //当前的反馈值
END_VAR
VAR_IN_OUT // input/Output Parameters
END_VAR
VAR_OUTPUT // Output Parameters
MV:Real:=0; //输出结果
END_VAR
VAR // Static Variables
ek:Bool:=0; //当前偏差值
ek1:Bool:=0; //前一次偏差值
integral:Real:=0; //积分值/偏差累计
Kp:Real:=1; //比例系数
Ti:Real:=32767; //积分时间
Td:Real:=0; //微分时间
Ts:Real:=0; //数字PID运算的控制周期(采样周期)
Tf:Real:=0; //不完全微分滤波器系数(a=Tf/(Ts+Tf))
SP:Real:=0; //设定值
IUse:Real:=100; //积分分离法中确定偏差值在所属范围内启用积分项
IVar:Real:=100; //变积分控制参数
lastUd:Real:=0; //上一次的微分项值
lastMV:Real:=0; //上一次的MV输出值
END_VAR
VAR_TEMP // Temporary Variables
pterm:Real; //比例项
dterm:Real; //微分项
IsISum:bool; //通过输出限幅确定是否累加积分项
END_VAR
(*位置式PID计算公式为
U(t)=Kp*ek(t)+(integral(t-1)+Kp*ek(t)*Ts/Ti)+Kp*Td(ek(t)-ek(t-1))/Ts
在手动模式下Manu=true,Start=true时,输出手动设定值;而当Manu=true,Start=false时,
输出值维持不变;在Manu=true,Start=true时,启动PID的自动调节;
该功能块放到PLC的定时中断程序中相当于设定了固定执行周期,也可以尝试放到循环扫描程序中,
或者在循环扫描程序中用脉冲波控制Start参数
/////////////////本功能块包含//////////////////////////////////////
死区限定/输出幅值限定/抗积分饱和/积分分离法/变速积分/不完全微分
这些功能可以彼此完美叠加使用,通过适当的参数设定,可以旁路部分功能,其中积分分离法在IUse
设定的比较大时相当于屏蔽该功能,而变速积分可通过参数IsDSpeed控制,而不完全微分可通过参数
IsHSPID进行控制,死区限定值Emin=0时,相当于屏蔽此功能,而抗积分饱和法则始终起作用,不能被屏蔽,
当然把积分Ti设定很大时,相当于屏蔽积分功能,仅使用PD控制器,此时各种积分算法无意义,
Td=0时相当于屏蔽微分功能,仅使用PI控制器
///////////////有关幅值的设定////////////////////////////
1.对于单控制对象,譬如只有一个热水调节阀,输出幅值为 0-->+模拟量输出上限
2.对于双控制对象,譬如含热水阀和冷水阀,输出幅值为 -模拟量输出上限-->+模拟量输出上限,并对
这个PID的输出进行正负的分段处理。
////////////抗积分饱和法///////////////////////////////
该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围,
如果u(k-1)>umax则只累加负偏差;如果u(k-1)
////////////积分分离法//////////////////////////////////
当偏差ek
相当于旁路积分分离功能
////////////变速积分///////////////////////////////////
变速积分法的基本思想是在偏差较大时积分慢一些,而在偏差较小时积分快一些,以尽快消除静差
如果使用了变速积分,总体上比不使用时的积分效果慢,其功能必须是在积分分离中接通了积分功能后,
才会起作用。
////////////////不完全微分///////////////////////////////
微分项公式为 Ud(k)=Kd(1-a)[e(k)-e(k-1)]+a*Ud(k-1),其中a=Tf/(Ts+Tf)
*)
IF Init THEN (*初始化PID参数,一般用于第一次执行PID时初始化*)
ek1:=0.0;
integral:=0.0;
MV:=0;
lastMV:=0.0;
lastUd:=0.0;
RETURN;
END_IF;
IF Manu THEN
IF Start THEN (*设置手动输出,并清空误差*)
ek1:=0.0;
integral:=0.0;
MV:=MVSet;
lastMV:=MV;(*记录上一次的输出值*)
lastUd:=0.0;
END_IF;
ELSE
IF Start THEN (*启动PID调节计算*)
ek:=SP-PV; (*计算偏差值*)
IF abs(ek) > Emin THEN (*位于死区外*)
(*比列项*)
pterm:=kp*ek;
(*积分项*)
IF abs(ek)<</span>IUse THEN (*积分分离法:差值在某个小区间才启用积分项*)
(*抗积分饱和:当上一次的输出达到饱和后,且偏差朝反方向变化,则可累加积分*)
IsISum:=((lastMV<</span>MVH-0.01) & (lastMV>MVL+0.01))
OR ((lastMV>=MVH-0.01) & ek<=0)
OR ((lastMV<=MVL+0.01) & ek>=0);
IF IsISum THEN (*抗积分饱和*)
IF IsDSpeed THEN (*使用变速积分*)
integral=integral+kp*Ts*ek*(1-abs(ek)/IVar)/Ti;
ELSE
integral=integral+kp*Ts*ek/Ti;
END_IF;
END_IF;
ELSE
integral=0.0;
END_IF;

(*微分项*)
IF IsHSPID THEN (*采用不完全微分*)
dterm:=(ek-ek1)*Kp*Td/(Ts+Tf)+Tf*lastUd/(Ts+Tf);
ELSE
dterm:=(ek-ek1)*Kp*Td/Ts;
END_IF;
(*输出结果*)
MV:=pterm+integral+dterm;
IF MV>MVH THEN (*限制输出幅值*)
MV:=MVH;
ELSE IF MV<</span>MVL THEN
MV:=MVL;
END_IF;
lastMV:=MV;(*记录上一次的输出值*)
lastUd:=dterm;(*储存上一次的微分项值*)
ELSE (*位于控制死区*)
MV:=lastMV;
END_IF;
ek1=ek; (*储存上一次的误差值*)
END_IF;
END_IF;

END_FUNCTION_BLOCK




我的更多文章

下载客户端阅读体验更佳

APP专享