PID控制器实际上是对偏差的控制,其原理图如下:
其数学的表达如下:
u(x)=Kp(err(t)+1T.∫err(t)dt+TDderr(t)dt) u ( x ) = K p ( e r r ( t ) + 1 T . ∫ e r r ( t ) d t + T D d e r r ( t ) d t )
其中: u(x) u ( x ) 是系统的输出
我们将其离散化,可得到:
u(k)=Kperr(k)+Ki∑err(j)+Kd(err(k)−err(k−1)) u ( k ) = K p e r r ( k ) + K i ∑ e r r ( j ) + K d ( e r r ( k ) − e r r ( k − 1 ) )
其中: Kp K p 是比例系数
Ki K i 是积分系数
Kd K d 是微分系数.
先简单介绍一下这三个项的作用:
1.比例环节 Kp,作用是加快系统的响应速度,提高系统的调节精度,副作用
是会导致超调;
2.积分环节 Ki,作用是消除稳态误差,副作用是导致积分饱和现象;
3.微分环节 Kd,作用是改善系统的动态性能,副作用是延长系统的调节时间,但其抗干扰性差。
上面是位置式的PID controller,还有增量式的PID controller,其数学表达如下:
Δu(k)=Kp(err(k)−error(k−1))+Kierr(k)+Kd(err(k)−2err(k−1)+err(k−2)) Δ u ( k ) = K p ( e r r ( k ) − e r r o r ( k − 1 ) ) + K i e r r ( k ) + K d ( e r r ( k ) − 2 e r r ( k − 1 ) + e r r ( k − 2 ) )
增量式的PID controller考虑的是三个时刻的误差,其控制更稳定,但是计算输出时要注意加上增量( u(k+1)=u(k)+Δu(k) u ( k + 1 ) = u ( k ) + Δ u ( k ) )
代码如下:
#include
using namespace std;
void PID_init();
float PID_realize(float speed);
struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;
int main()
{
cout<<"System begin \n";
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
cout<<"第 "<" 次的速度为: "<return 0;
}
void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
cout<<"PID_init end \n";
}
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
pid.integral+=pid.err;
pid.voltage=pid.Kp*pid.err+pid.Ki*pid.integral+pid.Kd*(pid.err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
代码如下:
#include
using namespace std;
void PID_init();
float PID_realize(float speed);
struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_next;//定义下一个偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;
int main()
{
cout<<"System begin \n";
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
cout<<"第 "<" 次的速度为: "<return 0;
}
void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_next=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.015;
pid.Kd=0.2;
cout<<"PID_init end \n";
}
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
float
incrementSpeed=pid.Kp*(pid.err-pid.err_next)+pid.Ki*pid.err+pid.Kd*(pid.err-2*pid.err_next+pid.err_last);
pid.ActualSpeed+=incrementSpeed;
pid.err_last=pid.err_next;
pid.err_next=pid.err;
return pid.ActualSpeed;
}
在普通 PID 控制中,引入积分环节的目的,主要是为了消除静差,提高控制精度。但是在启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成 PID 运算的积分积累,导致控制量超过执行机构可能允许的最大动作范围对应极限控制量,从而引起较大的超调,甚至是震荡,这是绝对不允许的。为了克服这一问题,引入了积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。
代码如下:
#include
#include
using namespace std;
void PID_init();
float PID_realize(float speed);
struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
}pid;
bool index;
int main()
{
cout<<"System begin \n";
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
cout<<"第 "<" 次的速度为: "<return 0;
}
void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.04;
pid.Kd=0.2;
cout<<"PID_init end \n";
}
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
if(abs(pid.err)>180)
{
index=1;
}else{
index=1;
pid.integral+=pid.err;
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.
err-pid.err_last);
//算法具体实现过
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID 控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出 U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算 u(k)时,首先判断上一时刻的控制量 u(k-1)是否已经超出了极限范围: 如果 u(k−1)>umaxu(k−1)>umax u ( k − 1 ) > u m a x u ( k − 1 ) > u m a x ,则只累加负偏差; 如果 u(k−1)<umin u ( k − 1 ) < u m i n ,则只累加正偏差。从而避免控制量长时间停留在饱和区。
代码如下:
#include
using namespace std;
void PID_init();
float PID_realize(float speed);
struct _pid{
float SetSpeed;//定义设定值
float ActualSpeed;//定义实际值
float err;//定义偏差值
float err_last;//定义上一个偏差值
float Kp,Ki,Kd;//定义比例、积分、微分系数
float voltage;//定义电压值(控制执行器的变量)
float integral;//定义积分值
float umax;//执行机构可执行的最大值
float umin;//执行机构可执行的最小值
}pid;
int main()
{
cout<<"System begin \n";
PID_init();
int count=0;
while(count<1000)
{
float speed=PID_realize(200.0);
cout<<"第 "<" 次的速度为: "<return 0;
}
void PID_init(){
cout<<"PID_init begin \n";
pid.SetSpeed=0.0;
pid.ActualSpeed=0.0;
pid.err=0.0;
pid.err_last=0.0;
pid.voltage=0.0;
pid.integral=0.0;
pid.Kp=0.2;
pid.Ki=0.1;
pid.Kd=0.2;
pid.umax=400;
pid.umin=-200;
cout<<"PID_init end \n";
}
float PID_realize(float speed){
pid.SetSpeed=speed;
pid.err=pid.SetSpeed-pid.ActualSpeed;
if(pid.ActualSpeed>pid.umax)
{
//灰色底色表示抗积分饱和的实现
if(abs(pid.err)>200)
//蓝色标注为积分分离过程
{
index=0;
}else{
index=1;
if(pid.err<0)
{
pid.integral+=pid.err;
}
}
}else if(pid.ActualSpeedif(abs(pid.err)>200)
//积分分离过程
{
index=0;
}else{
index=1;
if(pid.err>0)
{
pid.integral+=pid.err;
}
}
}else{
if(abs(pid.err)>200)
//积分分离过程
{
index=0;
}else{
index=1;
pid.integral+=pid.err;
}
}
pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral+pid.Kd*(pid.
err-pid.err_last);
pid.err_last=pid.err;
pid.ActualSpeed=pid.voltage*1.0;
return pid.ActualSpeed;
}
PID控制器c语言设计
PID控制器-维基