【PID系列目录】
[1、一文理解PID原理]
2、PID代码设计
本文目录
1、引出
各位朋友大家好,进入嵌入式行业已有10年多光景。从最初的一无所知,到现在自认为嵌入式入了门。一路都是自己自学,没有一个领路人带领,磕磕绊绊,走了不少弯路。头已白,发渐少,可才感觉找到嵌入式之门。但也算比较幸运,遇到一个技术管理比较严格、比较正规的公司。自己的自学加上公司的实践,总算对嵌入式有了一个系统的认识。在此,非常感谢公司这个大平台,正是因为有了这个平台,才有了今天的我。也非常感谢一起共事的小伙伴们,三人行,必有我师,我从你们那里学到了很多有用的东西。
从今天(2024年8月29日)开始,我打算以系列的方式,把我这几年学到的,常用到的东西,以笔记的形式记录下来,给入门嵌入式的朋友们点亮一盏灯,希望你们少踩一些坑,少走一些弯路。一些可以进行模块化的代码,我也会进行梳理整理,以开源的形式开放出来,提供给大家使用。
今天呢,我打算记录一下PID控制以及PID整定。PID是在工程领域使用最多的控制算法,应该没有之一。从温度,气压、流量、电机等各个可变的控制对象到小到小朋友的玩具,家用电器,精密设备,大到工程机械、钢铁熔炉各个领域都用到了PID控制。可以说PID控制无处不在。PID可是一把神器,有了这把神器,我们可以完全解决控制领域大多数最基本的问题。如果一个PID解决不了问题,那我们就用2个PID。很多人经常使用PID,但是PID这东西说简单非常简单,就一个公式,说难,也真是难以上青天,在实际使用中,经常整定不到合适的PID。所以,接下来,我会从PID的原理,PID代码的实现,PID代码工程化优化,PID自整定几个方面来记录PID的相关知识点,最终形成一个易于阅读、容易移植,便于控制的PID代码,提供给大家使用。
2、 PID概念
我们现在开始记录本系列的第一篇文章,PID原理。
所谓PID算法就是将偏差的比例(Proportion)、积分(Integral) 和 微分(Differential)通过线性组合构成控制量,用这一控制量对被控对象进行控制,这样的控制器称PID控制器。
我们来理解一下上面这段话。
2.1 首先,什么是偏差呢?
假如我们目前有一个需求,要把水烧到50±1℃。那这个需求我们就可以用PID进行控制。在这个系统中,我们的控制对象就是水温,我们的PID执行机构就是加热器,我们可以通过加热器的电流大小来控制加热器的加热快慢,即PID的输出量(output)即为电流大小。那我们要把水加热到指定的温度,我们必须还得有一个温度传感器,来获取实时水温。我们想要的水温的期望值是50±1℃,这个期望值我们叫做设定值(setpoint),温度传感器返回的实时温度值我们叫做反馈值(feedbackVal),那么所谓的偏差(error)就是反馈值偏离设定值的大小,即为error = setpoint - feedbackVal。
2.2 其次,什么是PID比例项?
什么是偏差的比例?
通常情况下,我们加热,都会采用恒定的电流加热,等水温达到设定值后,立即断电,水温低于设定值后,又立即启动加热,以此循环往复,这样进行控制(这种控制方式在工程上叫做乒乓控制)。对控制精度要求不高的场合,这样的控制方式完全够用,但如果控制精度比较高的情况下,水温要么就过了,要么达不到。这是因为大多数控制对象都有惯性,在惯性作用下,无法通过这种方式进行完美的控制。比如,我们的加热器,断电之后,它还有余热可以继续对水进行加热。比如电机,停止之后,由于惯性,它还会继续旋转。比如,关闭阀门,下达关闭指令后,它需要一定的时间才能完全关闭。
另外很多场合,我们的控制对象还受其他的因素的影响。比如,加热水,水在加热的同时,也在散热,而这个散热在夏天和冬天完全不一样,所以在冬天和夏天,将同样的水加热升温一定的温度所需要的功耗完全不一样。散热的这个因素也在影响着我们的水温。又比如,往水缸里注水,我们需要控制水缸的水位,但是同时这个水缸也在放水,那这个放水量就会影响水缸的水位。
那有没有一种方法可以在惯性以及其他因素的影响下,能够精确的控制呢?有的,方法肯定有很多,PID就是其中一种。PID的比例项是这样的。我们先求出偏差(error),根据偏差(error)的概念我们知道,如果反馈值偏离实际值很大,则偏差(error)也很大,如果反馈值距离实际值很小,则偏差(error)也很小。而我们控制一个对象,最理想的控制方式也是这样的,如果反馈值与设定值相差很大(即偏差(error)很大),我们可以输出(output)全功率,随着偏差(error)减小,我们也可以减少输出(output)功率,直到偏差(error)为0,我们的输出功率也为0。我们发现偏差的变化趋势和理想的控制方式是一样的。那我们使用偏差的概念来进行PID控制是非常完美的。
我们可以直接把偏差作为output来控制执行机构,是很理想的情况,但是一般情况下,偏差的单位和输出值的单位不一致。比如我们控制水温时,偏差的单位是摄氏度,而我们的输出值的单位是电流的单位A,这两个数往往不在一个数量级。所以我们直接将偏差作为输出值,进行控制不合适。为了能够通过偏差值对电流进行控制,我们在偏差值上乘以一个系数,这样我们就可以得到一个合理的值。我们把偏差乘以系数的这个项叫做比例项(proportion),即:
p r o p o r t i o n = K p ∗ e r r o r − − − − − − − − − − − − − − ( 1 ) proportion = Kp * error \space\space\space -------------- (1) proportion=Kp∗error −−−−−−−−−−−−−−(1)
其中:
e
r
r
o
r
=
o
u
t
p
u
t
−
s
e
t
p
o
i
n
t
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
2
)
error = output - setpoint \space\space\space -------------- (2)
error=output−setpoint −−−−−−−−−−−−−−(2)
用一个式子表示就是:
p
r
o
p
o
r
t
i
o
n
=
K
p
∗
(
o
u
t
p
u
t
−
s
e
t
p
o
i
n
t
)
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
3
)
proportion = Kp * (output - setpoint) \space\space\space -------------- (3)
proportion=Kp∗(output−setpoint) −−−−−−−−−−−−−−(3)
上式中,Kp就是我们引入的偏差所乘的比例,我们一般称之为比例常数。那一般怎么选择Kp呢,这个就需要我们在实际中不断的去试,而这个找合适的Kp的过程,我们就叫做手动PID整定。PID整定是PID控制中最难的部分,需要不断的积累经验,才能快速的找到合适的Kp值。 我这个系列不打算讲解如何去手动整定参数,我会讲解一种自整定方法,通过这种自整定方法,我们就可以得到合理的比例常数。当你看完我这个系列后,相信你对PID的理解已经足够了。你也可以不用看我这个系列,而是直接使用我后面开源的PID代码去做PID控制,PID整定,相信也一定控制的非常好。
这就是偏差的比例(proportion)。
K
p
∗
(
o
u
t
p
u
t
−
s
e
t
p
o
i
n
t
)
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
4
)
Kp * (output - setpoint) \space\space\space -------------- (4)
Kp∗(output−setpoint) −−−−−−−−−−−−−−(4)
叫做PID的比例项。
如上图所示,是在不同kp系数下,PID的不同控制效果。当给定一个很小的Kp(例如图中的Kp=0.5),则此时待PID稳定后,我们发现反馈值达不到目标值,与目标值相差甚远。我们不断增加Kp的值,发现稳定后的反馈值距离设定值越来越近。同时,随着Kp越来越大,我们发现反馈值开始振荡,振荡也越来越大。这是因为我们控制的系统存在惯性以及受其他因素的影响的原因。由于以上因素的影响,在只用比例项的控制的情况下,系统稳定后,反馈值与设定值之间总是存在一定的偏差,这个偏差我们叫做稳态误差。
1725191328368)
2.3 积分————解决稳态误差的利器
怎么解决稳态误差?
所谓稳态误差,就是反馈值和设定值之间总是存在一定的偏差。解决稳态误差的办法就是对偏差进行积分(intergration)。
如上图所示,图中的曲线是偏差值(error = feedbackVal - setpoint),则偏差的积分(intergration)就是图中阴影部分的面积。我们从图中看到,面积的大小和两个变量有关:偏差大小和时间长久,只要偏差不为0,则积分(intergration)随着时间的增加也会一直增加下去。所以偏差更多的是影响积分(intergration)的速率,偏差大,则积分(intergration)增加的快,偏差小则积分(intergration)增加的慢。所以只要偏差存在,则在时间的加持下,最终我们的输出值就会越来越大,最终会消除存在的稳态误差。
PID积分项(intergration)我们表示如下:
i
n
t
e
r
g
r
a
t
i
o
n
=
∫
0
t
d
(
e
r
r
o
r
(
t
)
)
d
t
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
5
)
intergration = \int_0^t{d(error(t))dt} \space\space\space -------------- (5)
intergration=∫0td(error(t))dt −−−−−−−−−−−−−−(5)
PID控制程序是周期性执行的,执行的周期称为采样周期(Ts)。上图中曲线为连续的实际的偏差值,但是我们通过计算机采集到的反馈值是离散的。我们一般都是每隔一个固定的时间去采集一个反馈值,并进行偏差计算,这个固定的时间间隔就是采样周期。就是上图中长方形的宽Ts了。每次PID运算时,偏差的积分就是在上一次积分的基础上,增加一个偏差(error)与采样周期(Ts)的乘积,即会增加一个图中的长方形的面积。偏差为正时,积分的增量为正,偏差为负时,积分的增量为负。
我们知道,偏差的积分的单位是时间单位和偏差单位的乘积,它的单位和比例项的单位不一样,为了单位换算一致,那我们一般需要给积分项除以一个时间系数,这个时间系数叫做积分时间常数,一般用Ti表示。引入积分时间常数后,积分项公式变为:
i n t e r g r a t i o n = 1 T i ∫ 0 t d ( e r r o r ) d t − − − − − − − − − − − − − − ( 6 ) intergration = {1\over{Ti}}\int_0^t{d(error)dt} \space\space\space -------------- (6) intergration=Ti1∫0td(error)dt −−−−−−−−−−−−−−(6)
那怎么求这个Ti的值呢,方法也是一样,需要通过整定求得,本篇文章我们不涉及整定,后面会专门讲解自整定。
引入积分项后,PID公式就变成了比例项和积分项的和。PID公式变为如下:
o
u
t
p
u
t
(
t
)
=
K
p
∗
(
e
r
r
o
r
(
t
)
+
1
T
i
∫
0
t
d
(
e
r
r
o
r
)
d
t
)
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
7
)
output(t) = Kp * (error(t) + {1\over{Ti}}\int_0^t{d(error)dt}) \space\space\space -------------- (7)
output(t)=Kp∗(error(t)+Ti1∫0td(error)dt) −−−−−−−−−−−−−−(7)
其中:
e
r
r
o
r
=
o
u
t
p
u
t
−
s
e
t
p
o
i
n
t
−
−
−
−
−
−
−
−
−
−
−
−
−
−
(
8
)
error = output - setpoint \space\space\space -------------- (8)
error=output−setpoint −−−−−−−−−−−−−−(8)
上式中,我们看到,比例常数不仅作用到比例项上,还作用到积分项上,为什么要这么做呢?它和作用在比例系数上的作用一样,在Kp和Ti共同的作用下,将输出值(output)转化为合理的执行单元(执行输出值的器件或设备)可执行的的范围内的值。
上图为纯比例作用下的PID控制展示。
2.4 微分————改善动态响应,解决超调现象
我们已经得到比例项和积分项组成的PID公式了,那这样PID公式是不是完美了呢?我们还没有讲解PID中的D,所以这个公式并不完美,它还是有缺陷的。但是在一般情况下,其实有了P和I也就够了,在P和I的作用下,一般都能达到我们想要的平衡值。
虽然只有PI作用,也可以使系统稳定,但是往往有一些副作用。比如系统响应变化过快,系统不稳定,比如在快要达到目标值阶段,往往会出现超调现象(反馈值达到目标值,并超过目标值,然后再慢慢返回来,在目标值周围振荡若干次,才逐渐平稳)。而我们加入微分项后,就可以来改善这种情况。
微分公式如下:
d i f f e r e n t i a l = d e r r o r ( t ) d t − − − − − − − − − − − − − − ( 9 ) differential = {derror(t)\over{dt}} \space\space\space -------------- (9) differential=dtderror(t) −−−−−−−−−−−−−−(9)
error(t)表示t时刻的偏差,即setpoint – feedbackVal。所以上述公式我们就可以看成是单位时间内偏差的变化,即偏差变化率。
如上图,绿色线为设定值,红色线为实时反馈值,则图中的蓝色线可表示偏差值,从图中可以看出来,随着反馈值不断的接近设定值,蓝色线越来越短,即偏差越来越小。偏差的微分对图中来说,就是右边的蓝色线减去左边的蓝色线,所以其值是负的。
那我们总结一下上述现象,就是偏差减小的过程中,微分值永远是负的,偏差增大的过程中,微分值永远是正的。偏差变化越快,此时红色的线越陡,则偏差的微分的绝对值越大。 从上述描述中,我们不难看到,微分在起一个阻力作用。上图中,偏差在减小的过程中,比例项和积分项是正的,但是微分是负的,它在阻碍偏差的减小,偏差变化越快的时候,微分的值越大,阻碍作用越大。
上图是,偏差增大的过程中,上图中比例项和微分是负的,但微分项刚好是正的。所以由此可以看出,微分项的符号和比例项与积分项的符号是反的,它始终起一个阻力的作用。
就像这个弹簧一样,不管你把它拉的多长,它最终会在阻尼作用下,停止运动,即达到平衡。
微分的单位是偏差值的单位/时间单位,所以为了能够将微分项的单位与比例项和积分项单位统一,微分项还包含一个微分时间常数Td。我们在微分项上乘以这个微分时间常数后,单位统一,则乘以微分时间常数Td以后微分项公式如下:
d i f f e r e n t i a l = T d d e r r o r ( t ) d t − − − − − − − − − − − − − − ( 10 ) differential = Td{derror(t)\over{dt}} \space\space\space -------------- (10) differential=Tddtderror(t) −−−−−−−−−−−−−−(10)
微分项对噪声特别敏感,如传感器反馈信号中有噪声或控制循环速率太低,微分项会使控制变的不稳定。噪声信号意味着反馈值突变,突变值的为微分值非常大,理论上可达到无穷大,所有其对噪声特别敏感。
上图表示微分作用。
2.5 最终的PID公式
经过上述讲解,我们已经清楚了PID公式,下面我们把最终的PID公式表示出来:
o u t p u t ( t ) = K p [ e r r o r ( t ) + 1 T i ∫ 0 t d ( e r r o r ( t ) ) d t + T d d e r r o r ( t ) d t ] − − − ( 11 ) output(t) = Kp[error(t) + {1\over{Ti}}\int_0^t{d(error(t))dt} + Td{derror(t)\over{dt}}] \space\space\space --- (11) output(t)=Kp[error(t)+Ti1∫0td(error(t))dt+Tddtderror(t)] −−−(11)
上式中,output(t) 表示t时刻的PID输出值,error(t)表示t时刻的偏差,即:t时刻的设定值setpoint与反馈值feedbackVal的差值。
e r r o r ( t ) = s e t p o i n t ( t ) − f e e d b a c k V a l ( t ) − − − − − − − − − − − − − − ( 12 ) error(t) = setpoint(t) - feedbackVal(t) \space\space\space -------------- (12) error(t)=setpoint(t)−feedbackVal(t) −−−−−−−−−−−−−−(12)
3、PID公式简化
我们观察以上公式,发现Kp作用在比例、积分、微分每一项上,如果我们需要手动整定PID的话,对于积分项和微分项来说,各有两个未知量需要求解。所以为了手动整定方便,一般需要对上式进行化简。 令:
K i = K p ∗ 1 T i , K d = K p ∗ T d − − − − − − − − − − − − − − ( 13 ) K_i = K_p*{{1}\over{T_i}} ,K_d= K_p*T_d \space\space\space -------------- (13) Ki=Kp∗Ti1,Kd=Kp∗Td −−−−−−−−−−−−−−(13)
则可得到如下公式:
o u t p u t ( t ) = K p ∗ e r r o r ( t ) + K i ∫ 0 t d ( e r r o r ( t ) ) d t + K d d e r r o r ( t ) d t − − − − ( 14 ) output(t) = Kp*error(t) + K_i\int_0^t{d(error(t))dt} + K_d{derror(t)\over{dt}} \space\space\space ---- (14) output(t)=Kp∗error(t)+Ki∫0td(error(t))dt+Kddtderror(t) −−−−(14)
上式中,比例、积分和微分项相互独立,过上式手动整定PID就比较方便。
4、PID公式离散化
我们得到的PID公式是连续的,但在计算机的处理中,无法处理连续的函数,我们对其进行离散化。
4.1、采集时间
假设,我们有一个控制系统,以固定的时间间隔通过传感器获取实时反馈值。这个时间间隔我们称之为采集时间,一般设为Ts。我们获取一次实时反馈值,计算一次偏差,然后通过偏差进行一次PID控制,所以PID的控制时间也等于采集时间。 假设k为采样序号,K = 0,1,2,3,4,5...
第一次采样,则k=1,
第二次采样,则k=2,
第三次采样,则k=3,
第四次采样,则k=4,
第五次采样,则k=3,
.
.
.
第n-1才采样,k = n-1
第n次采样,则k= n,
第n+1次采样,则k= n+1,
.
.
.
4.2、求偏差
假设,第k次采样,实时反馈值为feedbackVal(k),设置值为setpoint(k),则偏差就可以表示为:
e r r o r ( k ) = s e t p o i n t ( k ) − f e e d b a c k V a l ( k ) − − − − − − − − − − − − − − ( 15 ) error(k) = setpoint(k) - feedbackVal(k) \space\space\space -------------- (15) error(k)=setpoint(k)−feedbackVal(k) −−−−−−−−−−−−−−(15)
4.3、积分离散化
如上图中,长方形的宽为采样时间Ts,假设每个采集时刻计算处的偏差值为error(k),即为长方形的长。那么积分就是各个长方形面积的和。 用公式表示就是:
e
r
r
o
r
(
0
)
∗
T
t
+
e
r
r
o
r
(
1
)
∗
T
t
+
e
r
r
o
r
(
2
)
∗
T
t
+
e
r
r
o
r
(
3
)
∗
T
t
+
error(0)*T_t + error(1)*T_t + error(2)*T_t + error(3)*T_t +
error(0)∗Tt+error(1)∗Tt+error(2)∗Tt+error(3)∗Tt+
.
.
.
+
e
r
r
o
r
(
k
−
1
)
∗
T
t
+
e
r
r
o
r
(
k
)
∗
T
t
+
+
e
r
r
o
r
(
k
+
1
)
∗
T
t
+
.
.
.
−
−
−
(
16
)
... +error(k-1)*T_t + error(k)*T_t + +error(k+1)*T_t + ... \space\space\space --- (16)
...+error(k−1)∗Tt+error(k)∗Tt++error(k+1)∗Tt+... −−−(16)
我们对上式进行化简,则可表示为:
T
t
[
e
r
r
o
r
(
0
)
+
e
r
r
o
r
(
1
)
+
e
r
r
o
r
(
2
)
+
e
r
r
o
r
(
3
)
+
T_t [error(0) + error(1) + error(2) + error(3) +
Tt[error(0)+error(1)+error(2)+error(3)+
.
.
.
+
e
r
r
o
r
(
k
−
1
)
+
e
r
r
o
r
(
k
)
+
e
r
r
o
r
(
k
+
1
)
+
.
.
.
]
−
−
−
(
17
)
... +error(k-1) + error(k) + error(k+1)+...] \space\space\space ---(17)
...+error(k−1)+error(k)+error(k+1)+...] −−−(17)
我们继续使用求和公式来表述上述公式,则可以写为:
T t ∑ i = 0 k e r r o r ( i ) − − − − − − − − − − − − − − ( 18 ) T_t\sum_{i=0}^kerror(i) \space\space\space -------------- (18) Tti=0∑kerror(i) −−−−−−−−−−−−−−(18)
4.4、微分离散化
假设第k次求得的偏差为error(k),第k-1次求得的偏差为error(k-1),如上图中的曲线和长方形长交叉点的Y轴的值,即为各个采集点的偏差值。那么微分就相当于两个交叉点处Y轴的值的差除以采集时间Ts,就是偏差曲线的斜率。 用公式表示就是:
[ e r r o r ( k ) − e r r o r ( k − 1 ) ] / T t − − − − − − − − − − − − − − ( 19 ) [error(k) - error(k-1)]/T_t \space\space\space -------------- (19) [error(k)−error(k−1)]/Tt −−−−−−−−−−−−−−(19)
4.5、PID公式离散化
综上所述,我们把比例、积分和微分组合起来就可以得到离散化后的PID公式如下:
o
u
t
p
u
t
(
k
)
=
k
p
[
e
r
r
o
r
(
k
)
+
T
t
∑
i
=
0
k
e
r
r
o
r
(
i
)
T
i
+
T
d
[
e
r
r
o
r
(
k
)
−
e
r
r
o
r
(
k
−
1
)
]
T
t
]
−
−
−
(
20
)
output(k) = k_p [error(k) + {T_t\sum_{i=0}^kerror(i)\over{T_i}} + {Td{[error(k) - error(k-1)]}\over{T_t}}] \space\space\space --- (20)
output(k)=kp[error(k)+TiTt∑i=0kerror(i)+TtTd[error(k)−error(k−1)]] −−−(20)
上式为公式(11)的离散化表示。
o
u
t
p
u
t
(
k
)
=
K
p
∗
e
r
r
o
r
(
k
)
+
K
i
∗
T
t
∑
i
=
0
k
e
r
r
o
r
(
i
)
+
K
d
∗
[
e
r
r
o
r
(
k
)
−
e
r
r
o
r
(
k
−
1
)
]
T
t
−
−
−
(
21
)
output(k) = K_p*error(k) + Ki*{T_t\sum_{i=0}^kerror(i)} + K_d*{{[error(k) - error(k-1)]}\over{T_t}} \space\space\space ---(21)
output(k)=Kp∗error(k)+Ki∗Tti=0∑kerror(i)+Kd∗Tt[error(k)−error(k−1)] −−−(21)
上式为公式14的离散化表示。