本系列博客对下面的文章及视频有着不同程度的借鉴与理解,在这里我真诚地感谢这些乐于分享的大大们。
理解PID的原理以及控制效果:https://www.bilibili.com/video/BV1xQ4y1T7yv
理解PID的代码实现:https://www.bilibili.com/video/BV1Af4y1L7Lz
理解串级PID的原理:https://zhuanlan.zhihu.com/p/135396298
brettbeauregard的博客:http://brettbeauregard.com/blog/
PID的使用不仅仅包含PID算法本身,算法之外,依然有着足够多的问题让我们头疼。这些问题依次排序如下:
1.PID的调用频率
2.微分冲击
3.PID的开关
4.PID的反向工作
PID控制并不是随时随地都能调用的。PID的工作频率必须在合理的范围之内才能让系统正常工作,而每一个系统的PID工作频率又是不一样的,那么究竟要如何确定工作频率呢?
首先对于小车的电机来说,拥有着最大瞬时转速、编码器最快测量时间间隔等参数,而在电机测速编码器反馈电机转速的间隔内,系统是不知道这一时刻小车的速度的,也就是说在这一段时间内PID的输出控制是无效的,而一般来说,单片机内定时器可以产生的定时间隔远小于测速编码器的反馈间隔,所以如果将PID的工作频率设置得过高的话,对于单片机来说并没有什么问题,而对于实际的系统来说,就会多出很多个无效的PID控制输出,容易导致系统的工作混乱。
所以在实际中确认PID的工作频率时,需要确定好反馈机构的工作频率,PID的工作频率与其接近即可。
由于输入PID控制器的量是期望值与当前值之差,期望值的任何变化都会引起差值的瞬时变化。这种变化在时间上的导数使得它会变成一个很大的数字,从而导致PID的输出出现了尖峰,尖峰在引入D(微分项)后变得尤为明显,这种尖峰就是微分冲击:
有许多种方法可以解决这种问题,例如输出过滤等等,在这里我介绍的是一种被称为"测量导数"的方法。
从系列的第一节我们知道,PID的方程如下:
\[Output=K_{P}*e(t)+K_{I}\int e(t)dt+K_{D} \frac{d}{dt} e(t) \]我们要对付的就是D项。我们知道:
\[\frac{d}{dt} e(t)=\frac{d}{dt}target - \frac{d}{dt}feedback \]当\(target\)在时间上为一个常数,或者说在一定时间内\(target\)为一个常数的时候,该项导数为0,这个式子就不再会因为\(target\)产生尖峰了。所以在PID方程变成了下面这个样子:
\[Output=K_{P}*e(t)+K_{I}\int e(t)dt-K_{D} \frac{d}{dt} feedback \]所以在实际的编程中,\(\frac{d}{dt}feedback\)就等于当前的反馈与上一次反馈相减。
而在我们一般使用的PID方程
最后面的做差也是类似的原理。
当如此处理过后,就会有下面的效果:
有些时候,我们可能会需要关上PID控制块,让系统进行开环输出,或者说由于某种特殊的原因我们必须在某一个时刻输出某些特定的值,这个值不得不将PID的输出值覆盖掉。
单纯不调用PID控制块来解决这个问题可能会使得这一个小小的举动引发PID的剧烈反应。
想象一下:PID控制块在控制着一个机器人跑步,PID当然可以完成的很好:它可以把某个期望速度与自己当前的速度做差来进行PID运算,分毫不差就能以期望速度跑上个十万八千里。但是你突然把PID的控制权给撤了,同时让机器人静止。PID吓呆了,它明明一直在输出控制信号,但是机器人丝毫不动!于是PID可能会觉得,我得加把劲。当你在某一瞬间突然又把控制权移交给PID时,PID早就以一种不可知的方式运行了好一阵子,那一瞬间的输出对于机器人来说可能是致命的,也许它会像疯子一样瞎窜,又或许它会直接宕机。
所以我们需要一种合适的方式去开关PID控制块,一种最为简单的关闭方式就是在PID的算法函数中的最前方放置一个开关,如果不使用PID,直接return;即可。这避免了PID在认识到输出有异样之前就将其停用了。但是与之相对的问题就又出现了,当PID再次开启的时候,PID就会使用最后一次使用的数据重新开始调整,这就会导致PID初始化出现尖峰。
面对这个问题,我们还是从方程开始讲起:
我们可以看到需要过去的误差的项有I、D项,P项只与当前的误差有关,所以我们处理的对象就是I、D项。我们需要在重新调用PID前,使得:
err_last = err_now; Ki*err_Sum = Output;
第一行使得微分项为0,避免出现微分冲击;第二行的解释如下:
在PID的系统中,随着时间流逝系统在不断地运行,当系统稳定且时间足够长之后,整个系统的输出占比最大的应是积分项。比例项与微分项都完成了相应的任务,一个是调整系统达到期望的输出幅度大小,现在系统已经稳定,所以这个幅度约等于0;一个是调整系统达到期望所需的时间,系统已经稳定,所以这个时间还是约等于0。维持系统输出的就是I项中所累加的err_Sum.所以当一个系统需要重新启动的的时候,就将需要达到稳态的输出的值直接赋予给I项,这样就模拟出了一个系统已经达到稳态的结果,系统再次回到该稳态环境的时候也就不会出现大幅度的波动了。
前面我们讨论的似乎都是让PID去正向地控制某个物理量,但是还没有提及过让PID反向地控制它们————例如让你充满热水的浴缸重新冷下来。
为了达到这一个目的,我们可以将PID中的三个参数\(K_{P}\)、\(K_{I}\)、\(K_{D}\)都取相反数再做运算,但是相对来说更为简单的方法是让期望与反馈本身就可取负值,这样代入方程之后所取得的结果就能反着来啦。