讲PID的文章很多,比喻也很多,但是往往实际使用的时侯很懵,抄了一堆代码但是总是控制不好,也不知道从何着手来优化。原因是啥呢?在于你没有完成从思维到程序之间的转换。
本文换一些角度和思路,从实际使用案例层面,来讲PID控制思维的形成和程序实现,帮助刚刚从事这方面的开发者入门,帮助使用PID程序的人悟透。
这里我们假设一个温度控制的应用场景,你将使用程序来完成采集温度、PWM控制加热器功率输出、PID计算、用户界面等操作,控制一个恒温水壶。
一、假设没有PID的情况下你会怎么做?
这个问题很重要,为什么呢?因为你理清了自己的思路,才能把后面PID的控制思路和你自己的思路相结合,从而帮助你理解透PID是怎么帮助你出色的完成控制工作的。
言归正传。没有PID的话,你怎么做?
首先,你知道目标值(假设我们的目的要把水温控制在60度),知道了当前水温(假设是20度),知道了加热器的功率,比如最大1000瓦而且可调。
1.初步尝试
好的,直觉的,你的判断是需要加热升温,你打开了加热器,水温度开始升高。
那么问题就来了,这几个问题你脑袋里肯定是有了答案的。我们依次缕一缕你脑袋中的答案。
(1)加热器功率开多大?
温度还差很多,那么开最大功率好了,1000瓦。
(2)加热多久?
看水壶里有多少水咯,水多就开久一些,比如10分钟,水少就短一点,比如2分钟。
(3)多长时间观察下水温呢?
1分钟吧,你其实也不知道,时间短了浪费精力,时间长了水可能烧过温了。这个问题是你潜意识中肯定有的,但是在PID应用中很容易被入门者忽略的问题。
2.过程中的调整
一分钟过后,经过你的观察,水温从20度升高到了50度,这个时候你会怎么调整你的加热策略呢?
嗯,温度很接近了,那么肯定首先要调小加热器的功率,由1000瓦调小到300瓦;然后,你的观察时间需要减少到10秒钟观察一次,以免烧过温了。
3.过程中的再次调整
又过了10秒,经过你的观察,发现水温到了58度,怎么调整加热策略呢?
嗯,再调低加热器功率,从300瓦调到10瓦,你的观察时间也需要进一步缩短了。
烧水的事我们先讲到这里暂停一下,因为我们要引入一些PID中的思维了。
二、你烧水的过程中是如何使用P控制的?
P(proportion)控制就是比例控制,那么你的控制过程中用到了吗?答案是显而易见肯定的。那么我们来缕一下你是怎么用这个比例控制思想的。
第一次你观察到了20度水温,而目标值是60度,你做了一个减法,60-20=40。那么你怎么把这40度的温差转换成把加热器开1000瓦功率呢?人脑判断的呗!
但是我们要把这个判断过程转换成计算机程序,而程序是比较死板的,得需要你给出明确的逻辑过程来,那么我们就尝试一下。
首先我们来明确一下程序控制的输出形式PWM,这也是绝大多数PID控制环境的输出形式。
1.PWM简略了解
PWM:假定你的控制环境(如单片机)使用PWM来控制加热器,并且分辨率为8位。
简单补充下PWM知识,PWM控制是输出一定频率的方波,这个频率在控制过程中一般是不会改变的,和你被控设备能接受的频率有关。方波是有占空比的,占空比越大,方波的有效值越大。占空比为50%的话是标准方波,占空比为0%就变成了一直低电平,占空比为100%就变成了一直高电平。
分辨率并不是PWM波形的特征之一,而是程序在控制PWM占空比时所使用的一个表示占空比的变量而已。分辨率为8位,就是程序用一个字节(通常使用uint8_t,数值范围0-255)即256步来控制PWM方波占空比从0%到100%。每增加1,方波占空比增加100/256。为0时占空比为0;为255时,占空比达到100%。
下图是占空比25%、50%、75%的PWM波形。
2.Kp系数的来历
好了,知道了这些,我们就开始把40度温差转换成功率输出。你的加热器功率为1000瓦,你假定一个数字K,用温差40来乘以K得到1000,那么K应该是25。这个K就是PID控制中的P系数Kp,准确说是这个思想就是Kp的思想。
好像很简单哈?但是好像有什么不对劲。
(1)PID计算不改变量纲
首先,我们必须明确知道,PID控制的计算过程是不改变量纲的,这个很重要。通俗地讲就是你输入进去的是温度,计算出来的也是温度,PID计算结果告诉你的是,你要升温多少度,而不是加热器开多大功率。
前面我们用温差50乘以K得到1000瓦,这分明是改变了量纲的(一个是℃,一个是W),那么K就应该是带量纲的,而PID计算过程刚刚明明说了不改变量纲,K应该是一个单纯的系数而已,这不是矛盾吗?
的确,PID中说的系数就是不带量纲的,因此我们应用时,需要把PID的计算结果再转换一次,成为我们控制设备的输出量,就是PWM的占空比。
(2)控制过程中的量纲转换
把PID计算的结果转换成PWM的占空比这个事情在PID的应用中是必须要自己去完成的,也没有一定的规范或者要求怎么做,所以有很多种方法。下面讲的方法仅供参考。
第一种方法:直接把PID输出作为PWM占空比。接前面,我们的控制量是一个表示PWM占空比的数字,范围在0-255,255表示加热器满功率1000瓦运行。我们可以考虑把PID计算的结果直接作为PWM的输入。但是,PID的计算结果是有可能超过255的,这个时候就会产生溢出的问题。为了解决这个问题,我们可以把PID的计算结果做一个限幅,使其在输出超过255时还是保持为255。
第二种方法:把PID的输出按比例折算成PWM占空比。比如PWM输出为16位,就按比例把16位的数字折算成8位数字,作为PWM占空比。比较少用,控制器响应速度慢。
(3)Kp产生
前面讲了PWM,又讲量纲转换,其实都是为了搞透对PID计算输出与控制量之间关系的理解,其实和Kp关系不大。
终于,我们要讲下Kp了。前面讲到,我们观察到温差是40℃,我们把这个40输入给PID计算程序,希望它给我一个什么输出呢?PID的计算中的P部分,其实就是差值乘以一个系数Kp。
考虑到我们得PWM占空比范围是0-255,那么这个系数设置为多少呢?
我们结合下图考虑三种情况:
A.很小的Kp值。比如这个场景里我们选Kp=0.5。水温0度的情况下要烧水到100度,计算的输出O=(100-0)*Kp=50,直接输出到PWM占空比,加热器都不能到满功率运行,显然Kp=0.5是偏小了的。图中蓝色线就是Kp=0.5偏小。
B.很大的Kp值。比如这个场景里我们选Kp=50。如果达到PWM占空比255时,温差为255/50≈5℃。意思就是你如果要把温度稳定在60度,在60-5℃=55的时候,加热器都会是满功率运行。用你的脚趾头想一想都晓得,这个肯定不行,这一把火可能直接把水给烧开了!图中淡蓝色线就是Kp=10偏大的控制效果,控制理论中叫做“过冲”严重。甚至,再过大的P值会导致震荡。
C.合理的Kp值。那么合理的P值选择,应该是使得控制曲线有一些许过冲,但是又能很快的回到目标值附近并稳定下来。
3.纯P控制存在的问题
(1)稳态误差(需要引入I)
这里我们假设Kp值的选择已经比较合理了,控制器的输出也能比较好的把温度控制在目标值60℃周围。
但是,你会发现,稳定的温度并不是60℃,而是在其周围有一定误差,为什么呢?
这不得不让我们来仔细审视纯P比例控制的计算过程和控制逻辑了。
为了严谨,这里我们要把PID控制周期考虑进来,比如PID计算和控制的周期是1秒,就是说你每1秒观察一次温度,并完成PID计算和输出动作。
P控制在当前值越接近目标值,也就是目标值与当前值之差越小的情况下,其输出量会越来越小。比如在当前值为58℃时,Kp=5的情况下,P输出为(60-58)*5=10。
然而,水壶在向四周空气中散热呀!在这1秒内,加热器持续以10的占空比在加热所产生的热量如果和水壶向四周散热的热量相等的话呢?对,水壶温度就不会上升了........
这就是纯P控制所带来的稳态误差。在这个误差值下,PID计算得出的控制器输出量与系统扰动造成的误差量相等,所达到的稳态值。
这个就是你达不到控制目标60℃的原因了。
要注意这里有个逻辑问题,关于PID周期的。你能达到稳态的前提是PID控制周期够小。如果过大,比如极端的你60分钟计算控制一次,想想结果就很离谱。。。。等你去看的时候,水壶都烧成灰了。所以控制周期很重要!
(2)超调严重(需要引入D)
纯P控制的场景下,系统是很容易存在超调的。就像下图这样:
为啥呢?我们回到烧水的场景中。假设到了55℃时,合理的Kp=5情况下,计算输出(60-55)*Kp=25给PWM。但是这个25的输出量给加热器后,1秒后水温可能就升到65去了。
你可能要说了,这时因为Kp给大了。的确,你可以调小Kp来减轻超调情况。但是,减小Kp也意味着,系统的整体响应变慢了,不能实现快速的升温。试想一下你得控制对象是平衡车、无人机之类需要快速响应的对象,过小的Kp显然是不能满足实际需求的。
那么,我们就需要一种方法,通过对纯Kp的输出结果进行反向作用,来达到既能误差大时快速响应,又能接近目标值时稳态逼近。这是后面要讲的D部分。
三、I 控制的引入
P啰嗦了这么久,终于到 I 这步了。在P阶段啰嗦那么多,也是需要把一些相关知识讲清楚,否则后面的 I 和D阶段就乱了。
我们看到了纯P控制会带来稳态误差,达不到我们得控制目标值,所以得想一种方法,如果长期达不到控制目标值,就增加控制器的输出,让其逼近目标值。
这就是 I 的引入了。
首先要明确一个事情,PID理论里P、I、D三项是可以分别计算的,可以有各自的计算周期。然而我们在小型应用中,通常三者是同一个周期。因为不同的周期会增加很多计算量。
I 就是数学的积分思想,把误差一步一步累积起来驱动控制器,使控制器的输出往目标值逼近。是不是很简单?其实就是这么简单。它在公式里是红圈部分。
那你可能有疑问了:
1.如果系统的起始位置偏离比较远,等到目标值附近,误差累积就会已经比较到大了,这个时候 I 的作用会不会使得系统出现超调?
会的。所以你得Ki系数应该是小的。
2.误差会随着运行累积一直变大吗?
不会的,因为超过目标值误差会反号,代数和把累积误差又抵消一部分。
3.I 的作用与系统的起始位置有关吗?
当然有的,这个问题和1其实是一样的。起始位置偏离目标值越远,误差累积越大,I 的作用就会越明显了。但是实际上系统的起始位置是不确定的,所以通常在使用时,如果误差在比较大的时候是没必要引入 I 控制的,过大的 I 作用会导致在目标附近震荡久久不能平息,这种情况可以把先Ki设置为0。当前值比较接近目标值得时候,才开始加入 I 的控制作用,那么 I 的作用就比较平稳了。
四、D控制的引入
前面我们已经讲到了纯P控制存在的超调问题和响应速度慢的问题,需要打个补丁,实现既能快速响应,又能减少超调,这就是D控制的引入。
回到烧水场景。假设起始时水温20度,离目标差60-20=40度;1分钟后水温40度,离目标差60-40=20度;2分钟后水温50度,离目标差60-50=10度。
你发现了一个规律:0分钟到1分钟这个过程误差差值为40-20=20度;1分钟到2分钟这个过程误差差值20-10=10度。误差差值的减少意味着,我们正在消除掉误差。
我们可以把这个变化过程也引入到控制器的输出,来增强输出的阻尼作用。这就是D。
实现的过程就和你的思维一样:设置一个Kd,乘以误差的差值,叠加到控制器输出上去。就像下面公式中这样。
下面用一个表格来看看实际效果,假设目标为60,Kp=10,Kd=2:
当前值 | 目标值-当前值: E(k) | 误差变化趋势: E(k)-E(k-1) | 纯P控制的输出: Kp*E(k) | P+D控制的输出: Kp*E(k)+Kd*(E(k)-E(k-1)) |
20 | 40 | 40 | 400 | 400 |
40 | 20 | 20 | 200 | 240 |
50 | 10 | 10 | 100 | 120 |
55 | 5 | 5 | 50 | 60 |
57 | 3 | 2 | 30 | 34 |
59 | 1 | 2 | 10 | 14 |
除开第一行(因为第一次算,所以此时没有E(k-1),所以无法计算D部分)之外,我们发现达到了我们得预期效果:
(1)离目标值越远,D所产生的附加激励越强,使我们能快速逼近目标;
(2)离目标值越近,D所产生的附加激励越弱,使我们能在目标值附近稳定。
五、容易遇到的问题
1.周期问题
这也是很多人在实际应用PID时比较迷惑,网上很多讲PID控制中被忽略,但是却异常重要的问题。前面我们讲烧水的时候,都举了几个极端的场景,如果你观察控制的间隔时间过长,必然会导致控制效果变差。
有的人使用时把别人的Kp、Ki、Kd参数都抄过去,结果控制效果一塌糊涂,很大原因是忽略了周期的问题。
回想一下PID公式,I 和D部分每一次的计算都是和前面周期的计算结果是关联的。其中I是累积量,运行10秒内计算10次和计算20次,误差的累积量肯定是差距很大的。 D是变化趋势,控制周期长意味着发现误差变化趋势的能力就变弱了。
所以,PID的控制周期对控制效果起着相当大的影响。同一场景下,不同的控制周期,所使用三个参数肯定是不一样的。
当然控制周期也不是越小越好,一方面是CPU负荷的增加,另一方面周期过短让 I 的作用会被放大使得Ki参数调试困难。
2.控制器响应的滞后问题
实际应用中有很多系统对控制器输出的响应是滞后的。比如烧水场景中,加热器500瓦功率烧5秒,所产生的热量减去热耗散,水的温度理论应该升高5℃。
但是5秒后你得传感器读数只升高了2℃,这时你把加热器停掉。水温不是马上就停止上升了,而是在继续升高,可能过了20秒后,传感器温度才达到最高点然后开始下降。
这就是滞后效应。
滞后效应是由很多因素造成的。烧水的场景中:加热棒对水的热传递过程需要时间、水的热对流需要时间、传感器本省升温需要时间、温度传感器的输出也需要响应时间。这些都是造成滞后效应的原因。
特别要提的是“温度传感器的输出也需要响应时间”所造成的滞后效应。我们常用的K型热电偶,有温度曲线,比如500℃时应该输出0.1V电压。但是实际上你突然把热电偶从室温加热到500摄氏度(直接放火上烤),热电偶的输出马上就变到0.1V了吗?并不是,热电偶从0V到0.1V输出的过程可能是需要10几秒的。具体参数每个厂家的热电偶是不一样的,但是通常都需要10秒左右。这个是热控制PID场景中最多遇到的热滞后问题。
关于热电偶的响应时间问题,你可以百度一下。
还有一个就是温度传到传感器的时间。如果你得传感器位置不合理,比如安装在加热板的边缘,那么滞后现象会更加严重。你应该考虑把它安装在加热板中央。
严重滞后系统的PID控制会造成超调、震荡等问题,参数很不好调,甚至调不成功,控制效果很差。这种情况你可能需要考虑针对大滞后系统的PID算法,百度一下“史密斯PID控制算法”你会了解到更多关于大滞后系统PID怎么控制的思路。
全文完,水平有限,有不当之处欢迎指出。