PID算法简介

要想让智能车根据赛道不断变化灵活的行进,PID算法的采用很有意义。

控制器公式 为:

  • 比例(P)控制

比例控制是一种最简单的控制方式。其控制器的输出与输入误差信号成比例关系。当仅有比例控制时系统输出存在稳态误差(Steady-state error)。

  • 积分(I)控制

在积分控制中,控制器的输出与输入误差信号的积分成正比关系。对一个自动控制系统,如果在进入稳态后存在稳态误差,则称这个控制系统是有稳态误差的或简称有差系统(System with Steady-state Error)。为了消除稳态误差,在控制器中必须引入“积分项”。积分项对误差取决于时间的积分,随着时间的增加,积分项会增大。这样,即便误差很小,积分项也会随着时间的增加而加大,它推动控制器的输出增大使稳态误差进一步减小,直到等于零。因此,比例+积分(PI)控制器,可以使系统在进入稳态后无稳态误差。

  • 微分(D)控制

在微分控制中,控制器的输出与输入误差信号的微分(即误差的变化率)成正比关系。自动控制系统在克服误差的调节过程中可能会出现振荡甚至失稳。其原因是由于存在有较大惯性组件(环节)或有滞后(delay)组件,具有抑制误差的作用,其变化总是落后于误差的变化。解决的办法是使抑制误差的作用的变化“超前”,即在误差接近零时,抑制误差的作用就应该是零。这就是说,在控制器中仅引入“比例”项往往是不够的,比例项的作用仅是放大误差的幅值,而目前需要增加的是“微分项”,它能预测误差变化的趋势,这样,具有比例+微分的控制器,就能够提前使抑制误差的控制作用等于零,甚至为负值,从而避免了被控量的严重超调。所以对有较大惯性或滞后的被控对象,比例+微分(PD)控制器能改善系统在调节过程中的动态特性。

PID控制器的参数整定

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<MinValue)Control.OutPut=MinValue; //值域限制

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 <stdio.h>

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++;

}

}