从零理解 PID 控制:小球仿真到代码实现,手把手教你掌握工业级控制算法

前言
很多人应该都听说过 PID,它的运算过程简单,并能在大多情况下实现较好的控制效果,因此它是工程实践中使用最广泛的控制方法之一。
抛开公式,我将带你从案例出发,详细了解 PID 的工作原理和使用方法。
注:阅读本文不需要有过多的基础知识,只需中学物理和数学知识就能看懂(当然如果有高等数学知识和单片机知识的话理解起来会更容易)
仿真调参环境
我专门为本文搭了一个在线仿真环境,下面使用的案例都来自这个环境,读者可以搭配使用
https://pid-simulator-web.skythinker.top/
案例引入——小球位置控制
任务介绍
我们假设有一个一维的坐标轴(向右为正方向),在上面上有一个小球(可以看作质点),小球不受任何阻力,可以自由左右滑动;另外,我们还为小球规定了一个目标位置(图中的绿色标线):

现在我们有下述任务:
目标:在小球上施加一个水平方向的力(称为控制力),使小球在偏离目标位置时回到目标位置
已知条件:小球的实时坐标、目标位置坐标
看到这里的你可以停下来想一想,应该用什么样的策略来计算这个力呢?
这里大多数人应该都能想出这样的方法:
“当小球在目标左边的时候向右施力,当小球在目标右边的时候向左施力,就可以保证小球一直在目标位置上了”
思路是非常正确的,但这个策略仍不够完善。由于小球存在惯性,我们施加的力将小球拉回目标位置后小球还会具有一定的速度继续运动,并不会直接停在目标位置。
用 PID 完成任务
接下来我们来看看如果使用 PID,我们应该如何计算出这个控制力呢?
误差计算
计算 PID 的第一步就是计算误差(Error):误差=目标值-反馈值。在这个例子中,目标值是目标位置坐标,反馈值是小球实时位置坐标,那么误差就是小球当前位置与目标间的距离。
接下来的运算我们都会围绕误差进行,分为三个步骤使用误差分别算出一个分力,并将三个分力一起施加在小球上。
比例环节
第一个环节是比例环节 P(Proportion),这个环节产生的分力是

即:分力大小与误差成正比,且当小球在目标左边的时候分力向右,当小球在目标右边的时候分力向左,其中 kp 是比例系数。
比例环节的计算方法其实与上面大家通过直觉得出的方法差不多,如果只有这个分力作用的话,会产生什么效果呢?
大家可能会发现,这就与中学物理里的弹簧滑块模型是一样的,力与距离成正比,显然小球会以目标位置为中心进行左右摆动(简谐振动)(注:图中蓝色短线表示控制力):

只有比例环节时的小球运动
微分环节
那么如何让小球能够静止在目标点呢?这就要请出 PID 的另一个环节:微分环节 D(Differential)。
微分环节也会计算出一个分力,计算方法是:

也就是说,这个分力与误差的变化速度有关。假设目标位置不变,小球向右运动时误差减小,即误差变化速度为负,分力向左;反之当小球向左运动时分力向右;综合看来,微分环节产生的分力始终阻碍小球的运动。
因此如果在刚刚的基础上加入微分产生的分力,就会产生一个阻尼效果,小球会仿佛始终受到一个阻力,因此左右摆动的幅度会逐渐减小,最终收敛到目标位置上:

有比例和微分环节时的小球运动
由公式还可以看出,微分系数 Kd 可以影响这个“阻力”的大小,因此如果我们把系数调大一些,就可以让小球的运动收敛得更快一些:

调大 kd 后的小球运动
到这里,其实我们已经完成我们的目标任务了,小球可以在驱动力的作用下运动到目标位置。
积分环节
但现在,我们更希望在小球有一些外部干扰时也能实现上面的效果,比如我们在小球上加上一个水平向右的恒力,此时会发生什么呢?

恒力干扰下小球静止状态
小球在运动过程中仍然会像之前一样接近目标点,但在最终停下来时我们会发现,小球无法精确停在目标点上,而是像上图一样停在离目标点有一定距离的地方。此时控制力与干扰恒力平衡,小球静止。
稍加分析我们就能发现,此时小球静止,微分环节产生的分力为零,控制力完全由比例环节产生,且若距离更小则比例环节的输出更小,更无法平衡干扰力,因此小球无法继续向目标点接近。
此时就需要我们的第三个环节出场了:积分环节 I(Integral),它的计算方法是:

也就是说积分环节产生的分力正比于误差的积分,当误差持续存在时,这个分力会逐渐变大,试图消除误差。
加入积分作用,我们的 PID 就能完美实现在有恒力干扰的情况下对小球的控制了:

PID 作用下小球控制效果
抛开案例——更专业地理解 PID
常用术语
被控对象:需要控制的对象,案例中指小球
目标值:期望被控对象达到的状态量,案例中指目标位置的坐标
反馈值:被控对象当前时刻的状态量,案例中指小球的实时位置坐标
输出量:PID 的计算结果,案例中指控制力
误差:目标值-反馈值
稳态误差:系统稳定状态下仍存在的误差,如案例中加入干扰恒力后小球静止时仍存在的误差

不同参数下系统的阶跃响应(源:百度百科)
阶跃输入:在稳定状态下目标值发生突然变化(上图目标值在 0 时刻由 0 跃升到虚线位置)
阶跃响应:阶跃输入后被控对象的跟随状态,能够代表系统的控制性能(上图彩色线条)
响应速度:阶跃输入后被控对象再次到达目标值的速度
超调量:阶跃输入后,被控对象到达目标值后超出目标值的距离(上图各彩色线条第一个峰值与目标值的距离)
PID 计算过程

PID 信号框图
上图就是 PID 的信号框图,表示了 PID 的运行过程:
为系统指定一个目标值
PID 将目标值与被控对象当前的反馈量作差得到误差
PID 将误差值分别经过三个环节计算得到输出分量,三个分量加起来得到 PID 的输出
将 PID 的输出施加到被控对象上,使反馈量向目标值靠拢
PID 三个环节的作用
由控制小球案例我们可以总结出 PID 三个环节各自的主要作用和效应:
比例环节:起主要控制作用,使反馈量向目标值靠拢,但可能导致振荡
积分环节:消除稳态误差,但会增加超调量
微分环节:产生阻尼效果,抑制振荡和超调,但会降低响应速度
PID 中物理量的设计
我们在设计 PID 时主要关注三个量:目标值、反馈值、输出值,PID 会根据目标值和反馈值计算输出值。
需要强调的是,PID 并不知道被控对象是什么,它仅负责进行数值计算,而我们——作为控制系统的设计者,就需要为 PID 指定这三个量所对应的实际物理量,这在不同的控制系统中是不一样的。
那么如何确定实际物理量呢,我为大家总结了一个常用准则
目标值和反馈值通常为同种物理量,就是你需要控制的物理量
输出值通常是直接驱动被控对象的控制量
输出量作用在被控对象上需要经过时间积累才会产生反馈量的变化,换言之输出值通常为反馈值对于时间的低阶物理量。比如:目标值和反馈值为位置,则输出值可以为速度或加速度
对于线性关系的两个物理量(只差一个系数),可以直接替换。比如:目标和反馈值为小球位置,根据上一条准则,输出值可以为加速度。但我们无法直接控制加速度,只能控制驱动力大小,由于驱动力与加速度只差一个系数(F=ma),因此可以将输出值直接定为驱动力
接下来给出几个例子:
任务一:对小球进行速度控制
可用条件:已知小球的实时速度,并且可施加一个力来改变小球的速度
PID 目标值:需要小球达到的速度
PID 反馈值:小球的实时速度
PID 输出值:施加在小球上的力
分析:小球加速度是小球速度的低阶物理量,而施加的力正比于小球加速度
任务二:对电机转速进行控制
可用条件:已知电机的实时转速,并且可控制电机中流过的电流大小
PID 目标值:需要电机达到的转速
PID 反馈值:电机的实时转速
PID 输出值:电机中流过的电流大小
分析:电机中流过的电流大小近似正比于电机的扭矩,也就近似正比于电机角加速度的大小,是转速的低阶物理量,因此可以用电流大小作为输出值
任务三:液位高度控制
描述:容器有进水口和出水口,需要通过进水口的阀门控制容器内液位的高度
可用条件:容器内液位实时高度,可控制进水口阀门液体流速
PID 目标值:需要达到的液位高度
PID 反馈值:液位实时高度
PID 输出值:阀门液体流速
分析:阀门液体流速正比于液位高度的变化速度,是液位高度的低阶物理量
由虚到实——代码编写
程序流程
根据上面的步骤,我们其实很容易就能得出程序的执行流程了,就是在计算误差后逐个进行 PID 各环节的计算。要注意的是整个采样-计算-输出的流程需要定时执行,可以在每次流程运行完后延时一段固定的时间(一般而言计算频率不高于传感器反馈频率,但也不能太低,否则会使控制精度下降)。

代码流程图
实现细节
有一个问题没有解决,积分和微分应该怎么算呢?毕竟微积分都是连续的,而我们采样得到的是离散的数据点。其实也很简单,离散状态下的积分计算其实就是把过去采样得到的所有误差加在一起,而微分计算就是把这一轮计算得到的误差与上一轮的误差相减。
最后,我们一般还会对 PID 的积分和输出进行限幅(规定上下限),积分限幅可以减小积分引起的超调,输出限幅可以保护执行机构或被控对象。
C 语言代码
最后一步——PID 参数调整
在完成控制器代码编写后,就要连接好系统进行调参了,我们需要确定最合适的 KpKiKd 使控制效果最优。
通常还是使用经验法调参,通俗而言就是“试参数”,测试多个参数选取最好的控制效果,一般的步骤如下
先将所有参数置零
将输出限幅设为执行机构能接受的最大值
增大 p 参数,使响应速度达到比较好的水平
若存在稳态误差,逐渐增加 i 参数和积分限幅,使稳态误差消失
若希望减少超调或振荡,逐渐增加 d 参数,在保证响应速度的前提下尽可能降低超调
此时大家可以使用上述的小球仿真环境体验一下各参数对系统的影响。到这里,我们就已经能够使用 PID 来控制各种对象了。
总结——使用 PID 的步骤
确定需要控制的对象,确定需要控制的物理量,确定反馈量的获取方式,确定被控对象的控制方式
检查目标值、反馈值、输出值对应物理量的关系是否符合上面说的准则
编写代码,将上述三个值的数值变量传入 PID 进行运算,并将 PID 运算结果输出到执行机构
进行参数调整
更进一步——串级PID
从单级到串级
当我们在进行小球的位置控制时,我们可能经常会发现一个问题,如果小球与目标之间的距离较远的话,小球在运动过程中的速度会很快,会导致较大的超调,而且不论怎么修改参数都很难让系统的表现更好一些。

单级 PID 控制小球效果
这时你可能会想,如果运动过程中的速度没这么快就好了,这样就不会冲过头了。没错,这就要用到串级 PID 了。
我们上面所说的算法其实就是单级 PID,目标值和反馈值经过一次 PID 计算就得到输出值并直接作为控制量,但如果目标物理量和输出物理量之间不止差了一阶的话,中间阶次的物理量我们是无法控制的。比如:目标物理量是位置,输出物理量是加速度,则小球的速度是无法控制的。
而串级 PID 就可以改善这一点。串级 PID 其实就是两个单级 PID“串”在一起组成的,它的信号框图如下:

串级 PID 信号框图
图中的外环和内环就分别是一个单级 PID,每个单级 PID 就如我们之前所说,需要获取一个目标值和一个反馈值,然后产生一个输出值。串级 PID 中两个环相“串”的方式就是将外环的输出作为内环的目标值。
串级 PID 的物理量
如果将串级 PID 看作一个整体,可以看到他有三个输入和一个输出,而此时被控对象也需要提供两个反馈量,那么它们都应该对应些什么物理量呢?
首先我们回到最开始的小球案例中,如果用串级 PID 完成同样的任务,应该这样设计:
可用条件:小球实时位置、小球实时速度、施加在小球上的控制力
目标值:小球目标位置
外环反馈:小球实时位置
内环反馈:小球实时速度
输出值:施加在小球上的控制力
此时的信号框图会变成这样:

串级 PID 控制小球信号框图
可以发现,内环与小球构成了一个恒速系统,PID 内环负责小球的速度控制;而如果把内环和小球看作一个整体被控对象,外环又与这个对象一起构成了一个位置控制系统,外环负责位置控制;总体来说,外环负责根据小球位置误差计算出小球需要达到的速度,而内环负责计算出控制力使小球达到这个目标速度,两个环协同工作,就可以完成任务了。
如果不局限于这个案例来说,串级 PID 的内环一般负责低阶物理量的调节,而外环负责高阶物理量的调节并计算出低阶物理量的目标值,比如下面这个例子:
任务:对电机进行串级角度控制
可用条件:电机实时角度、电机实时转速、可以控制电机电流大小
外环目标值:需要电机达到的角度
外环反馈值:电机的实时角度
内环反馈值:电机的实时速度
输出值:电机电流大小
分析:外环负责电机角度控制,根据电机目标角度和反馈角度计算出目标转速;内环负责转速控制,根据速度反馈和目标转速计算出电流
串级 PID 的效果
回到我们的小球控制案例,之前说使用串级之后我们就可以对速度进行控制了,如何进行控制呢?其实就是对外环 PID 的输出进行限幅,因为外环 PID 输出的是目标速度,限制外环输出就相当于限制了小球目标速度的最大值,内环也就会维持小球的速度不超过这个最大值了。

串级 PID 控制小球效果
可以看到,使用串级 PID 后小球不再像之前那样“着急”地奔着目标而去,而是以近似匀速运动到达目标点。由于位置误差很大,外环输出在大部分时间内都处于限幅的最大值,因此小球在运动中接近匀速,这个速度就是所设定的外环输出限幅。而且由于运动速度变慢了,超调也几乎消失了。这就是我们想要的“控制位置的同时还能控制速度”的效果。
串级 PID 的 C 语言代码
串级 PID 的调参
一般而言,需要先断开两环的连接,手动指定内环目标值,进行内环调参,当内环控制效果较好后再接上外环进行外环调参,具体的调参方法与单级 PID 相同。
此时大家也可以使用小球仿真环境体验一下串级控制的效果。
版权声明: 本文为 InfoQ 作者【芯动大师】的原创文章。
原文链接:【http://xie.infoq.cn/article/c842f47f89c2cadbbefe5a9b6】。文章转载请联系作者。
评论