你可能不熟悉 Control Loop 的概念,但我保证你每天都会以某种方式使用它。Control Loop 是指任何使用反馈机制来控制某个动作的系统。
当你接住在空中飞行的棒球时,你的眼睛会提供反馈信息,告知你的大脑球在哪里,以及你需要如何移动你的手才能接住它。当你想在进去之前调整淋浴的温度时,你用手感觉到水,这给你的大脑提供了反馈,并决定你是否转动旋钮使水更热或更凉。这些都是 Control Loop 的例子。
TL;DR
从代码的角度就是这样:
error = setpoint - measured
P_value = Kp * error
D_value = Kd * (error - Derivator)
Derivator = error
Integrator = Integrator + error
I_value = Integrator * Ki
output = P_value + I_value + D_value
PID如何工作
为了描述PID算法如何工作,我将使用温度控制器的简单例子。在这个例子中,我们有一个系统,包括一个电炉,一壶水,一个温度传感器,和一个控制器。
控制器可以使用温度传感器读取水的温度,它可以将燃烧器的功率水平从0调整到100%。控制器中的软件负责调整燃烧器的功率水平,使锅里的水温尽快达到一个理想值(设定点),然后无限期地保持这个温度。控制器中可以使用PID算法来解决这个问题。
Proportional 比例
PID 算法的第一个组成部分是最简单的理解,也是对控制器的性能最关键的。P代表的是比例。它意味着控制变量应与系统中的误差量成比例地调整。一个只利用P部分的PID算法可以表示为。
$$
输出=误差 \times K_p
$$
我刚刚使用了几个听起来很陌生的术语,所以让我解释一下。
- 控制变量(输出)
- 控制变量是我们要调整的控制器的输出。
- 这个例子中,控制变量是烧水器的功率。
- 过程变量(测量值)
- 过程变量是你试图控制的系统中的测量值。过程变量被用作控制器的反馈,以便它能决定如何调整控制变量。
- 这个例子中,过程变量是水传感器返回的温度值。
- 误差($设定值 - 测量值$)
- 误差是过程变量和设定温度之间的差异。
- 这个例子中,设定点是我们试图让水达到的某个温度。
- 任何时间点的误差都是当前温度和目标温度之间的差异。
$$
输出=(设定值 - 测量值) \times K_p
$$
算法的 $P$ 部分通过按比例调整输出与误差来工作。
例子
温度控制器的设定温度是 100 度。
如果目前传感器测量的温度是 60 度,$误差 = 100 - 60 =40$ 。
控制器的输出将被设置为 $40 \times K_p$,其中 $K_p$ 是比例系数。
$K_p$ 是一个常数,非负值,将在控制器的调谐过程中确定(我们将在后面详细讨论调谐)。
现在我们假设 $K_p = 10$(凭空捏造)。那么系统的输出将是 $(100-60)\times 10=400$。
你可能会想:
「400?这比燃烧器的 0 到 100% 的范围要大得多。我们怎么能把它设置为 400?」
而你是完全正确的。这被称为饱和,它经常发生在 PID 控制器中。当饱和发生时,控制变量只需要将输出设置为其最大值,即 100%。
随着测量的温度接近目标值,误差越来越小,最终系统变得不饱和。在我们的例子中,系统将在90 度时成为不饱和状态。在 95 度时,误差将是 5,系统的输出将是 $(100-95) \times 10=50%$。
使用纯比例控制器的问题是,它通常会产生一个稳态误差。随着误差越来越小,误差乘以比例系数的结果最终变得太小,无法对过程变量产生任何影响。这是因为现实世界中的系统从来没有100%的效率。而这正是算法的 $I$ 部分发挥作用的地方。
Integral 积分
$I$ 代表积分,这是一个数学术语。简单来说意味着随着时间的推移积累一些东西[1]。
在 PID 的情况下,$I$ 部分积累了随时间发生的误差。累积的误差再乘以积分系数 $K_i$,然后加到输出中。
一个只利用P和I组件的控制器算法可以用以下伪代码表示。
accumulation_of_error += error * delta_time
output = (error * Kp) + (accumulation_of_error * Ki)
控制算法的积分部分可以消除系统中的任何稳态误差,因为它随着时间的推移积累该误差并对其进行补偿,而不是只看某一时刻的误差的瞬时快照。
Derivative 导数
$D$ 代表导数,可能是最复杂的。
导数部分在控制器中使用的频率较低,但在某些应用中仍然很重要。
对于一壶水放在燃烧器上不被打扰的情况,$D$ 完全没有必要。然而,如果我们偶尔往锅里倒几块冰,使事情变得复杂,那么它就会有帮助。
导数部分负责对误差的突然变化进行补偿。 如果我们把冰块倒入我们的水壶中,温度会突然下降,误差会突然增加,算法的导数部分会启动并增加燃烧器的输出。
导数是一个数学术语,意味着 “一条曲线的斜率”。在这种情况下,该曲线是误差随时间变化的曲线。
如果误差是稳态的,那么 $D$ 部分的结果就是零。
一个完整的 PID 算法可以用下面的伪代码表示。
accumulation_of_error += error * delta_time
derivative_of_error = (error - last_error) / delta_time
last_error = error
output = (error * Kp) + (accumulation_of_error * Ki) + (derivative_of_error * Kd)
当系统已经处于或接近稳态时,导数部分大多有用。
在稳定状态下,$P$ 和 $I$ 分量都非常小,因为误差非常小。如果误差突然增加,需要一段时间后 $P$ 和 $I$ 分量才会再次开始起作用。
然而,$D$ 分量对误差的突然变化作出反应,并立即开始补偿。
由于这个原因,人们常说 $D$ 负责补偿未来的误差;它看到了误差的变化,并试图阻止它发生更大的变化。
由于导数部分对误差的变化做出反应,在测量的过程变量是有噪声的情况下,它可能会出现问题。当控制算法中使用 $D$ 部分时,这种噪声会导致输出不稳定。
PID控制器的调整
调整 PID 控制器的过程是确定 $K_p$、$K_i$ 和 $K_d$ 的理想值,以达到预期的响应。在一些系统中,你可能希望控制变量尽可能快地达到设定点,即使这意味着对设定点的超标。在其他情况下,对设定点的超调可能是不可接受的。
所有这些都可以通过仔细调整调谐系数来控制。调谐过程超出了本篇文章的范围,但请注意我的下一篇文章,我将给出一个调谐自制温度控制器的实际例子。
积分实际上是相对于其他东西的积累;它不一定是时间。例如,如果你把宽度相对于长度的积分,你会得到面积。 ↩︎