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