要想让智能车根据赛道不断变化灵活的行进,PID算法的采用很有意义。
控制器公式 为:
比例(P)控制
比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。
积分(I)控制
在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。
微分(D)控制
在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。
PID调试一般原则
a. 在输出不振荡时,增大比例增益P。
b. 在输出不振荡时,减小积分时间常数Ti。
c. 输出不振荡时,增大微分时间常数Td。
PID控制器的参数整定是控制系统设计的核心内容。它是根据被控过程的特性确定PID控制器的比例系数、积分时间和微分时间的大小。PID控制器参数整定的方法很多,概括起来有两大类:一是理论计算整定法。它主要是依据系统的数学模型,经过理论计算确定控制器参数。这种方法所得到的计算数据未必可以直接用,还必须通过工程实际进行调整和修改。二是工程整定方法,它主要依赖工程经验,直接在控制系统的试验中进行,且方法简单、易于掌握,在工程实际中被广泛采用。PID控制器参数的工程整定方法,主要有临界比例法、反应曲线法和衰减法。三种方法各有其特点,其共同点都是通过试验,然后按照工程经验公式对控制器参数进行整定。但无论采用哪一种方法所得到的控制器参数,都需要在实际运行中进行最后调整与完善。现在一般采用的是临界比例法。利用该方法进行PID控制器参数的整定步骤如下:
(1)首先预选择一个足够短的采样周期让系统工作﹔
(2)仅加入比例控制环节,直到系统对输入的阶跃响应出现临界振荡,记下这时的比例放大系数和临界振荡周期﹔
(3)在一定的控制度下通过公式计算得到PID控制器的参数。
PID参数的设定:是靠经验及工艺的熟悉,参考测量值跟踪与设定值曲线,从而调整PID的大小。
PID控制器参数的工程整定,各种调节系统中P.I.D参数经验数据以下可参照:
温度T: P=20~60%,T=180~600s,D=3-180s
压3-18P=30~70%,T=24~180s,
液位L: P=20~80%,T=60~300s,
流量L: P=40~100%,T=6~60s。
书上的常用口诀:
参数整定找最佳,从小到大顺序查
先是比例后积分,最后再把微分加
曲线振荡很频繁,比例度盘要放大
曲线漂浮绕大湾,比例度盘往小扳
曲线偏离回复慢,积分时间往下降
曲线波动周期长,积分时间再加长
曲线振荡频率快,先把微分降下来
动差大来波动慢。微分时间应加长
理想曲线两个波,前高后低4比1
一看二调多分析,调节质量不会低
这里介绍一种经验法。这种方法实质上是一种试凑法,它是在生产实践中总结出来的行之有效的方法,并在现场中得到了广泛的应用。
这种方法的基本程序是先根据运行经验,确定一组调节器参数,并将系统投入闭环运行,然后人为地加入阶跃扰动(如改变调节器的给定值),观察被调量或调节器输出的阶跃响应曲线。若认为控制质量不满意,则根据各整定参数对控制过程的影响改变调节器参数。这样反复试验,直到满意为止。
经验法简单可靠,但需要有一定现场运行经验,整定时易带有主观片面性。当采用PID调节器时,有多个整定参数,反复试凑的次数增多,不易得到最佳整定参数。
下面以PID调节器为例,具体说明经验法的整定步骤:
⑴让调节器参数积分系数S0=0,实际微分系数k=0,控制系统投入闭环运行,由小到大改变比例系数S1,让扰动信号作阶跃变化,观察控制过程,直到获得满意的控制过程为止。
⑵取比例系数S1为当前的值乘以0.83,由小到大增加积分系数S0,同样让扰动信号作阶跃变化,直至求得满意的控制过程。
(3)积分系数S0保持不变,改变比例系数S1,观察控制过程有无改善,如有改善则继续调整,直到满意为止。否则,将原比例系数S1增大一些,再调整积分系数S0,力求改善控制过程。如此反复试凑,直到找到满意的比例系数S1和积分系数S0为止。
⑷引入适当的实际微分系数k和实际微分时间TD,此时可适当增大比例系数S1和积分系数S0。和前述步骤相同,微分时间的整定也需反复调整,直到控制过程满意为止。
注意:仿真系统所采用的PID调节器与传统的工业 PID调节器有所不同,各个参数之间相互隔离,互不影响,因而用其观察调节规律十分方便。
PID参数是根据控制对象的惯量来确定的。大惯量如:大烘房的温度控制,一般P可在10以上,I=3-10,D=1左右。小惯量如:一个小电机带
一水泵进行压力闭环控制,一般只用PI控制。P=1-10,I=0.1-1,D=0,这些要在现场调试时进行修正的。
提供一种增量式PID供参考
△U(k)=Ae(k)-Be(k-1)+Ce(k-2)
A=Kp(1+T/Ti+Td/T)
B=Kp(1+2Td/T)
C=KpTd/T
T采样周期 Td微分时间 Ti积分时间
用上面的算法可以构造自己的PID算法。
U(K)=U(K-1)+△U(K)
对于PID算法,采样周期是相当重要的,特别是当被控制物体的运动速度较高时,若采样周期跟不上,PID算法的输出滞后严重.但若执行部件有较大滞后时PID的功效将会大打折扣,不改进执行部件的响应速度,将是PID发挥其优异控制性能最大的瓶颈.
PID算法的定义:
P:比例控制项. I:积分控制项. D:微分控制项.
设当前输出量为U,我们的期望值或是设定值为U0,则可得当前时刻误差:E=U-U0;
PID算法即是对误差量E及E的历史进行某种线性组合得到控制量的算法.
一般形式:
Up=P*E;
Ui=i*(E+E_1+E_2+...) E_n为之前的第n次误差.
Ud=i*(E-E_1)
U=Up+Ui+Ud; U为PID控制输出量.
上式中Ui的计算不太方便,长时间单方向的累加将可能出现溢出,于是将上式改为如下所示的增量形式:
Up=p*(E-E_1) 比例项增量
Ui=i*(E-2*E_1+E_2) 微分项增量
Ud=i*E 积分项增量
U=Uout_1+Up+Ui+Ud U为PID控制输出量,Uout_1为前次PID输出值
Uout=U 保存本次值
对于上面的公式或理论,便可得到相应的C语言程序:
//======================定义PID结构=========================
static float MinValue; //最大值限制
static float MaxValue; //最小值限制
static float CurrentValue; //当前采样值
static struct PID{
float Ki; //定义积分常数
float Kp; //定义比例常数
float Kd; //定义微分常数
float E_2; //存储前前次误差
float E_1; //存诸前次误差
float E; //存储本次误差
float OutPut; //本次输出量
float ValueSet; //设定值或期望值
}Control;
//===========================PID计算函数=====================
void PidWork() {
float Up,Ud,Ui;
Control.E=CurrentValue-Control.ValueSet; //得到本次误差
Up =Control.Kp*(Control.E-Control.E_1); //得到比例项
Ud=Control.Kd*(Control.E-2*Control.E_1+Control.E_2); //得到微分项
Ui=Control.Ki*Control.E; //得到积分项
Control.E_2=Control.E_1; //历史存储
Control.E_1=Control.E;
Control.OutPut+=Up+Ud+Ui; //计算增量和
if(Control.OutPut else if(Control.OutPut>MaxValue)Control.OutPut=MaxValue; } //==========================初始化速度========================= void PidInit() { MinValue=0; MaxValue=1000; CurrentValue=0; Control.Kp=-6; Control.Ki=-1.5; Control.Kd=-0.5; Control.E=0; Control.E_2=0; Control.E_1=0; Control.ValueSet=100; Control.OutPut=0; } 以上三个函数为PID的主体函数,也是万用PID函数.代码量已经相当精简了.注意上面的PID初始化函数中有Kp,Ki,Kd的符号一定要正确,否则输出量方向相反,后果不堪设想!!! #include struct _pid { int pv; /*integer that contains the process value*/ int sp; /*integer that contains the set point*/ float integral; float pgain; float igain; float dgain; int deadband; int last_error; }; struct _pid warm,*pid; int process_point, set_point,dead_band; float p_gain, i_gain, d_gain, integral_val,new_integ;; /*------------------------------------------------------------------------ pid_init DESCRIPTION This function initializes the pointers in the _pid structure to the process variable and the setpoint. *pv and *sp are integer pointers. ------------------------------------------------------------------------*/ void pid_init(struct _pid *warm, int process_point, int set_point) { struct _pid *pid; pid = warm; pid->pv = process_point; pid->sp = set_point; } /*------------------------------------------------------------------------ pid_tune DESCRIPTION Sets the proportional gain (p_gain), integral gain (i_gain), derivitive gain (d_gain), and the dead band (dead_band) of a pid control structure _pid. ------------------------------------------------------------------------*/ void pid_tune(struct _pid *pid, float p_gain, float i_gain, float d_gain, int dead_band) { pid->pgain = p_gain; pid->igain = i_gain; pid->dgain = d_gain; pid->deadband = dead_band; pid->integral= integral_val; pid->last_error=0; } /*------------------------------------------------------------------------ pid_setinteg DESCRIPTION Set a new value for the integral term of the pid equation. This is useful for setting the initial output of the pid controller at start up. ------------------------------------------------------------------------*/ void pid_setinteg(struct _pid *pid,float new_integ) { pid->integral = new_integ; pid->last_error = 0; } /*------------------------------------------------------------------------ pid_bumpless DESCRIPTION Bumpless transfer algorithim. When suddenly changing setpoints, or when restarting the PID equation after an extended pause, the derivative of the equation can cause a bump in the controller output. This function will help smooth out that bump. The process value in *pv should be the updated just before this function is used. ------------------------------------------------------------------------*/ void pid_bumpless(struct _pid *pid) { pid->last_error = (pid->sp)-(pid->pv); } /*------------------------------------------------------------------------ pid_calc DESCRIPTION Performs PID calculations for the _pid structure *a. This function uses the positional form of the pid equation, and incorporates an integral windup prevention algorithim. Rectangular integration is used, so this function must be repeated on a consistent time basis for accurate control. RETURN VALUE The new output value for the pid loop. USAGE #include "control.h"*/ float pid_calc(struct _pid *pid) { int err; float pterm, dterm, result, ferror; err = (pid->sp) - (pid->pv); if (abs(err) > pid->deadband) { ferror = (float) err; /*do integer to float conversion only once*/ pterm = pid->pgain * ferror; if (pterm > 100 || pterm < -100) { pid->integral = 0.0; } else { pid->integral += pid->igain * ferror; if (pid->integral > 100.0) { pid->integral = 100.0; } else if (pid->integral < 0.0) pid->integral = 0.0; } dterm = ((float)(err - pid->last_error)) * pid->dgain; result = pterm + pid->integral + dterm; } else result = pid->integral; pid->last_error = err; return (result); } void main(void) { float display_value; int count=0; pid = &warm; // printf("Enter the values of Process point, Set point, P gain, I gain, D gain "); // scanf("%d%d%f%f%f", &process_point, &set_point, &p_gain, &i_gain, &d_gain); process_point = 30; set_point = 40; p_gain = (float)(5.2); i_gain = (float)(0.77); d_gain = (float)(0.18); dead_band = 2; integral_val =(float)(0.01); printf("The values of Process point, Set point, P gain, I gain, D gain "); printf(" %6d %6d %4f %4f %4f ", process_point, set_point, p_gain, i_gain, d_gain); printf("Enter the values of Process point "); while(count<=20) { scanf("%d",&process_point); pid_init(&warm, process_point, set_point); pid_tune(&warm, p_gain,i_gain,d_gain,dead_band); pid_setinteg(&warm,0.0); //pid_setinteg(&warm,30.0); //Get input value for process point pid_bumpless(&warm); // how to display output display_value = pid_calc(&warm); printf("%f ", display_value); //printf(" %f%f%f%f",warm.pv,warm.sp,warm.igain,warm.dgain); count++; } }
附上一段完整代码: