PID控制算法一勺烩 从模型到实现再到口诀

2019-07-04 09:05发布

一、PID的数学模型

在工业应用中PID及其衍生算法是应用最广泛的算法之一,是当之无愧的万能算法,如果能够熟练掌握PID算法的设计与实现过程,对于一般的研发人员来讲,应该是足够应对一般研发问题了,而难能可贵的是,在很多控制算法当中,PID控制算法又是最简单,最能体现反馈思想的控制算法,可谓经典中的经典。经典的未必是复杂的,经典的东西常常是简单的,而且是最简单的。PID算法的一般形式:

PID算法通过误差信号控制被控量,而控制器本身就是比例、积分、微分三个环节的加和。这里我们规定(在t时刻):

1.输入量为

2.输出量为

3.偏差量为

二、PID算法的数字离散化

假设采样间隔为T,则在第K个T时刻:

偏差

积分环节用加和的形式表示,即

微分环节用斜率的形式表示,即

PID算法离散化后的式子:

则可表示成为:

其中式中:

比例参数Kp:控制器的输出与输入偏差值成比例关系。系统一旦出现偏差,比例调节立即产生调节作用以减少偏差。特点:过程简单快速、比例作用大,可以加快调节,减小误差;但是使系统稳定性下降,造成不稳定,有余差。

积分参数Ki:积分环节主要是用来消除静差,所谓静差,就是系统稳定后输出值和设定值之间的差值,积分环节实际上就是偏差累计的过程,把累计的误差加到原有系统上以抵消系统造成的静差。

微分参数Kd:微分信号则反应了偏差信号的变化规律,或者说是变化趋势,根据偏差信号的变化趋势来进行超前调节,从而增加了系统的快速性。

PID的基本离散表示形式如上。目前的这种表述形式属于位置型PID,另外一种表述方式为增量式PID,由上述表达式可以轻易得到:

那么:

上式就是离散化PID的增量式表示方式,由公式可以看出,增量式的表达结果和最近三次的偏差有关,这样就大大提高了系统的稳定性。需要注意的是最终的输出结果应该为:

输出量 = U(k)+ 增量调节值

三、PID的C语言实现

1.位置式PID的C语言实现

上边已经抽象出了位置性PID和增量型PID的数学表达式,这里重点讲解C语言代码的实现过程。

第一步:定义PID变量结构体,代码如下:

struct t_pid{

float SetSpeed; //定义设定值

float ActualSpeed; //定义实际值

float err; //定义偏差值

float err_last; //定义上一个偏差值

float Kp,Ki,Kd; //定义比例、积分、微分系数

float voltage; //定义电压值(控制执行器的变量)

float integral; //定义积分值

}pid;

第二部:初始化变量,代码如下:

void PID_init(){

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;

}

统一初始化变量,尤其是Kp,Ki,Kd三个参数,调试过程当中,对于要求的控制效果,可以通过调节这三个量直接进行调节。

第三步:编写控制算法,代码如下:

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;

}

注意:这里用了最基本的算法实现形式,没有考虑死区问题,没有设定上下限,只是对公式的一种直接的实现,后面的介绍当中还会逐渐的对此改进。

到此为止,PID的基本实现部分就初步完成了。下面是测试代码:

int main(){

PID_init();

int count=0;

while(count<1000)

{

float speed=PID_realize(200.0);

printf("%f\n",speed);

count++;

}

return 0;

}

2.增量型PID的C语言实现

上一节中介绍了最简单的位置型PID的实现手段,这一节讲解增量式PID的实现方法。

#include

#include

struct t_pid{

float SetSpeed; //定义设定值

float ActualSpeed; //定义实际值

float err; //定义偏差值

float err_next; //定义上一个偏差值

float err_last; //定义最上前的偏差值

float Kp,Ki,Kd; //定义比例、积分、微分系数

}pid;

void PID_init(){

pid.SetSpeed=0.0;

pid.ActualSpeed=0.0;

pid.err=0.0;

pid.err_last=0.0;

pid.err_next=0.0;

pid.Kp=0.2;

pid.Ki=0.015;

pid.Kd=0.2;

}

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;

}

int main(){

PID_init();

int count=0;

while(count<1000)

{

float speed=PID_realize(200.0);

printf("%f\n",speed);

count++;

}

return 0;

}

3.积分分离的PID控制算法

在普通PID控制中,引入积分环节的目的,主要是为了消除静差,提高控制精度。但是在启动、结束或大幅度增减设定时,短时间内系统输出有很大的偏差,会造成PID运算的积分积累,导致控制量超过执行机构可能允许的最大动作范围对应极限控制量,从而引起较大的超调,甚至是震荡,这是绝对不允许的。

为了克服这一问题,引入了积分分离的概念,其基本思路是 当被控量与设定值偏差较大时,取消积分作用; 当被控量接近给定值时,引入积分控制,以消除静差,提高精度。其具体实现代码如下:

pid.Kp=0.2;

pid.Ki=0.04;

pid.Kd=0.2; //初始化过程

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); //算法具体实现过程

4.抗积分饱和的PID控制算法C语言实现

所谓的积分饱和现象是指如果系统存在一个方向的偏差,PID控制器的输出由于积分作用的不断累加而加大,从而导致执行机构达到极限位置,若控制器输出U(k)继续增大,执行器开度不可能再增大,此时计算机输出控制量超出了正常运行范围而进入饱和区。一旦系统出现反向偏差,u(k)逐渐从饱和区退出。进入饱和区越深则退出饱和区时间越长。在这段时间里,执行机构仍然停留在极限位置而不随偏差反向而立即做出相应的改变,这时系统就像失控一样,造成控制性能恶化,这种现象称为积分饱和现象或积分失控现象。

防止积分饱和的方法之一就是抗积分饱和法,该方法的思路是在计算u(k)时,首先判断上一时刻的控制量u(k-1)是否已经超出了极限范围: 如果u(k-1)>umax,则只累加负偏差; 如果u(k-1)

struct t_pid{

float SetSpeed; //定义设定值

float ActualSpeed; //定义实际值

float err; //定义偏差值

float err_last; //定义上一个偏差值

float Kp,Ki,Kd; //定义比例、积分、微分系数

float voltage; //定义电压值(控制执行器的变量)

float integral; //定义积分值

float umax;

float umin;

}pid;

void PID_init(){

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;

}

float PID_realize(float speed){

int index;

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.ActualSpeed

if(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;

}

5.梯形积分的PID控制算法

先看一下梯形算法的积分环节公式

作为PID控制律的积分项,其作用是消除余差,为了尽量减小余差,应提高积分项运算精度,为此可以将矩形积分改为梯形积分,具体实现的语句为:

pid.voltage=pid.Kp*pid.err+index*pid.Ki*pid.integral/2+pid.Kd*(pid.err-pid.err_last); //梯形积分

6.变积分的PID控制算法

变积分PID可以看成是积分分离的PID算法的更一般的形式。在普通的PID控制算法中,由于积分系数是常数,所以在整个控制过程中,积分增量是不变的。但是,系统对于积分项的要求是,系统偏差大时,积分作用应该减弱甚至是全无,而在偏差小时,则应该加强。积分系数取大了会产生超调,甚至积分饱和,取小了又不能短时间内消除静差。因此,根据系统的偏差大小改变积分速度是有必要的。

变积分PID的基本思想是设法改变积分项的累加速度,使其与偏差大小相对应:偏差越大,积分越慢; 偏差越小,积分越快。

这里给积分系数前加上一个比例值index:

当abs(err)<180时,index=1;

当180

当abs(err)>200时,index=0;

最终的比例环节的比例系数值为ki*index;

具体PID实现代码如下:

pid.Kp=0.4;

pid.Ki=0.2; //增加了积分系数

pid.Kd=0.2;

float PID_realize(float speed){

float index;

pid.SetSpeed=speed;

pid.err=pid.SetSpeed-pid.ActualSpeed;

if(abs(pid.err)>200) //变积分过程

{

index=0.0;

}else if(abs(pid.err)<180){

index=1.0;

pid.integral+=pid.err;

}else{

index=(200-abs(pid.err))/20;

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;

}

7.专家PID和模糊PID

从前面的讲解中不难看出,PID的控制思想非常简单,其主要问题点和难点在于比例、积分、微分环节上的参数整定过程,对于执行器控制模型确定或者控制模型简单的系统而言,参数的整定可以通过计算获得,对于一般精度要求不是很高的执行器系统,可以采用拼凑的方法进行实验型的整定。

然而,在实际的控制系统中,线性系统毕竟是少数,大部分的系统属于非线性系统,或者说是系统模型不确定的系统,如果控制精度要求较高的话,那么对于参数的整定过程是有难度的。专家PID和模糊PID就是为满足这方面的需求而设计的。专家算法和模糊算法都归属于智能算法的范畴,智能算法最大的优点就是在控制模型未知的情况下,可以对模型进行控制。这里需要注意的是,专家PID也好,模糊PID也罢,绝对不是专家系统或模糊算法与PID控制算法的简单加和,他是专家系统或者模糊算法在PID控制器参数整定上的应用。也就是说,智能算法是辅助PID进行参数整定的手段。

专家系统、模糊算法,需要参数整定就一定要有整定的依据,也就是说什么情况下整定什么值是要有依据的,这个依据是一些逻辑的组合,只要找出其中的逻辑组合关系来,这些依据就再明显不过了。下面先说一下专家PID的C语言实现。正如前面所说,需要找到一些依据,还得从PID系数本身说起。

(1).比例系数Kp的作用是加快系统的响应速度,提高系统的调节精度。Kp越大,系统的响应速度越快,系统的调节精度越高,但是容易产生超调,甚至会使系统不稳定。Kp取值过小,则会降低调节精度,使响应速度缓慢,从而延长调节时间,是系统静态、动态特性变差;

(2).积分作用系数Ki的作用是消除系统的稳态误差。Ki越大,系统的静态误差消除的越快,但是

Ki过大,在响应过程的初期会产生积分饱和的现象,从而引起响应过程的较大超调。若Ki过小,将使系统静态误差难以消除,影响系统的调节精度;

(3).微分系数Kd的作用是改善系统的动态特性,其作用主要是在响应过程中抑制偏差向任何方向的变化,对偏差变化进行提前预报。但是Kd过大,会使响应过程提前制动,从而延长调节时间,而且会降低系统的抗干扰性。

反应系统性能的两个参数是系统误差e和误差变化律ec,这点还是好理解的:

首先我们规定一个误差的极限值,假设为Mmax;规定一个误差的比较大的值,假设为Mmid;规定一个误差的较小值,假设为Mmin;

当abs(e)>Mmax时,说明误差的绝对值已经很大了,不论误差变化趋势如何,都应该考虑控制器的输入应按最大(或最小)输出,以达到迅速调整误差的效果,使误差绝对值以最大的速度减小。此时,相当于实施开环控制。

当e*ec>0时,说明误差在朝向误差绝对值增大的方向变化,此时,如果abs(e)>Mmid,说明误差也较大,可考虑由控制器实施较强的控制作用,以达到扭转误差绝对值向减小的方向变化,并迅速减小误差的绝对值。此时如果abs(e)

当e*err<0且e*err(k-1)>0或者e=0时,说明误差的绝对值向减小的方向变化,或者已经达到平衡状态,此时保持控制器输出不变即可。

当e*err<0且e*err(k-1)<0时,说明误差处于极限状态。如果此时误差的绝对值较大,大于Mmin,可以考虑实施较强控制作用。如果此时误差绝对值较小,可以考虑实施较弱控制作用。

当abs(e)

上面的逻辑判断过程,实际上就是对于控制系统的一个专家判断过程。

实际上模糊算法属于智能算法,智能算法也可以叫非模型算法,智能算法包含了专家系统、模糊算法、遗传算法、神经网络算法等。其实这其中的任何一种算法都可以跟PID去做结合,而选择的关键在于,处理的实时性能不能得到满足。当我们处理器的速度足够快速时,我们可以选择更为复杂的、精度更加高的算法。但是,控制器的处理速度限制了我们算法的选择。当然,成本是限制处理器速度最根本的原因。模糊PID适应一般的控制系统是没有问题。

模糊算法其实并不模糊。模糊算法其实也是逐次求精的过程。这里举个例子说明。我们设计一个倒立摆系统,假如摆针偏差<5°,我们说它的偏差比较“小”;摆针偏差在5°和10°之间,我们说它的偏差处于“中”的状态;当摆针偏差>10°的时候,我们说它的偏差有点儿“大”了。对于“小”、“中”、“大”这样的词汇来讲,他们是精确的表述,可问题是如果摆针偏差是3°呢,那么这是一种什么样的状态呢。我们可以用“很小”来表述它。如果是7°呢,可以说它是“中”偏“小”。那么如果到了80°呢,它的偏差可以说“非常大”。而我们调节的过程实际上就是让系统的偏差由非常“大”逐渐向非常“小”过度的过程。当然,我们系统这个调节过程是快速稳定的。通过上面的说明,可以认识到,其实对于每一种状态都可以划分到大、中、小三个状态当中去,只不过他们隶属的程度不太一样,比如6°隶属于小的程度可能是0.3,隶属于中的程度是0.7,隶属于大的程度是0。这里实际上是有一个问题的,就是这个隶属的程度怎么确定?这就要求我们去设计一个隶属函数。详细内容可以查阅相关的资料,这里没有办法那么详细的说明了。那么,知道了隶属度的问题,就可以根据目前隶属的程度来控制电机以多大的速度和方向转动了,当然,最终的控制量肯定要落实在控制电压上。这点可以很容易的想想,我们控制的目的就是让倒立摆从隶属“大”的程度为1的状态,调节到隶属“小”的程度为1的状态。当隶属大多一些的时候,我们就加快调节的速度,当隶属小多一些的时候,我们就减慢调节的速度,进行微调。可问题是,大、中、小的状态是汉字,怎么用数字表示,进而用程序代码表示呢?其实我们可以给大、中、小三个状态设定三个数字来表示,比如大表示用3表示,中用2表示,小用1表示。那么我们完全可以用1*0.3+2*0.7+3*0.0=1.7来表示它,当然这个公式也不一定是这样的,这个公式的设计是系统模糊化和精确化的一个过程,读者也可参见相关文献理解。但就1.7这个数字而言,可以说明,目前6°的角度偏差处于小和中之间,但是更偏向于中。我们就可以根据这个数字来调节电机的转动速度和时间了。当然,这个数字与电机转速的对应关系,也需要根据实际情况进行设计和调节。

前面一个例子已经基本上说明了模糊算法的基本原理了。可是实际上,一个系统的限制因素常常不是一个。上面的例子中,只有偏差角度成为了系统调节的参考因素。而实际系统中,比如PID系统,我们需要调节的是比例、积分、微分三个环节,那么这三个环节的作用就需要我们认清,也就是说,我们需要根据超调量、调节时间、震荡情况等信息来考虑对这三个环节调节的比重,输入量和输出量都不是单一的,可是其中必然有某种内在的逻辑联系。所以这种逻辑联系就成为我们设计工作的重点了。

四、PID算法参数整定方法

1.临界比例度法

(1)将调节器的积分时间

置于最大

,微分时间置零

,比例度δ适当,平衡操作一段时间,把系统投入自动运行。

(2)将比例度δ逐渐减小,得到等幅振荡过程,记下临界比例度

和临界振荡周期

值。

(3)根据和值,采用经验公式,计算出调节器各个参数,即δ的值。

(4)按“先P后I最后D”的操作程序将调节器整定参数调到计算值上。若还不够满意,可再作进一步调整。

临界振荡整定计算公式

2.衰减曲线法

在纯比例作用下,由大到小调整比例度以得到具有衰减比(4:1)的过渡过程,记下此时的比例度及振荡周期,根据经验公式,求出相应的积分时间和微分时间。



衰减曲线法控制器参数计算表

Note:(1).反应较快的控制系统,要认定4:1衰减曲线和读出Ts比较困难,此时,可用记录来回摆动两次就达到稳定作为4:1衰减过程。 (2).在生产过程中,负荷变化会影响过程特性。当负荷变化较大时,必须重新整定调节器参数值。 (3).若认为4:1衰减太慢,宜应用10:1衰减过程。对于10:1衰减曲线法整定调节器参数的步骤与上述完全相同,仅仅采用计算公式有些不同。

3.经验法

根据经验先将控制器参数放在某些数值上,直接在闭合的控制系统中通过改变给定值以施加干扰,看输出曲线的形状,以δ、、,对控制过程的规律为指导,调整相应的参数进行凑试,直到合适为止。

长期的生产实践中总结出来的参数表

4.经验口诀

参数整定找最佳,从小到大顺序查。

先是比例后积分,最后再把微分加。

曲线振荡很频繁,比例度盘要放大。

曲线漂浮绕大湾,比例度盘往小扳。

曲线偏离回复慢,积分时间往下降。

曲线波动周期长,积分时间再加长。

曲线振荡频率快,先把微分降下来。

动差大来波动慢。微分时间应加长。

理想曲线两个波,前高后低4比1。

一看二调多分析,调节质量不会低。

文章来源: https://www.toutiao.com/group/6707505176594350604/