PID 控制器是工业自动化和嵌入式控制中最常见的算法,但很多初学者第一次看到公式里的积分项、微分项时,心里多少会发怵:这要不要先补一遍微积分才能调参?其实完全不用。把 PID 公式离散化之后,剩下的就是“累加”和“相减”,这东西在 Excel 里用几列公式就能跑通。本文分享一套“不用微积分的 PID”实验方法:不需要写一行代码,只需要一个空白电子表格,就能亲手搭出一个 PID 闭环控制系统,完成参数整定实验,甚至能直观看到比例、积分、微分各自对响应曲线的影响。学完之后,你可以把同一套离散化逻辑平滑迁移到 STM32、Python、MATLAB 等真实项目中。
1. 背景:为什么 PID 可以用电子表格讲清楚
1.1 PID 不是一个需要“高数”才能理解的算法
PID 是 Proportional(比例)、Integral(积分)、Differential(微分)的缩写。中文里常写成“比例-积分-微分控制器”。它的作用很简单:根据“目标值”和“当前测量值”之间的误差,计算出一个控制量,让测量值尽快、平稳地逼近目标值。
在连续数学表达式中,PID 的输出是:
[ u(t)=K_p e(t) + K_i \int e(t) dt + K_d \frac{de(t)}{dt} ]
一看到积分符号 (\int) 和导数符号 (\frac{d}{dt}),很多人的第一反应是“完了,要复习微积分”。但实际上,在数字控制器和计算机控制系统里,我们并不能直接处理连续积分和连续微分,所有公式都要离散化。
离散化之后,积分变成“误差的累加”,微分变成“误差的差值”。累加和差值,小学算术就能搞定。所以我说“Calculus-free PID”,并不是把 PID 简化成了另一个东西,而是指:在数字世界里,PID 的底层实现本来就是简单的加减乘除。
1.2 电子表格是理解 PID 的最佳“白板”
电子表格(Excel、WPS、Google Sheets 等)天然适合做离散化计算:
- 每一行可以代表一个采样周期。
- 每一列可以存放设定值、测量值、误差、比例项、积分项、微分项、输出值。
- 用填充柄向下拖拽公式,就能模拟一个连续运行的控制回路。
- 插入折线图,就能看到超调、振荡、稳态误差等经典现象。
这种方式的优势是“透明”。Python 代码写一个回调函数,你只能看到最终曲线;但在电子表格中,你能清楚地看到每一行误差如何变化、积分项如何累积、微分项如何对噪声敏感。这对于建立 PID 直觉非常有帮助。
1.3 这篇文章适合谁
- 刚开始学 PID,但被微积分公式劝退的初学者。
- 准备在 STM32、PLC、ROS 等平台上做 PID 控制,想先理解算法本质的开发者。
- 已经会调参,但想找一个快速验证工具,观察参数变化对响应影响的人。
- 需要给学生或新人做控制算法培训,需要一个可视化实验环境的人。
读完本文,你将掌握 PID 的离散化形式、电子表格模拟方法、参数整定实验,以及从电子表格迁移到真实代码的完整思路。
2. PID 控制的核心原理拆解
2.1 三个基本项:比例、积分、微分
在离散系统中,我们按固定的采样周期 (T) 定时读取测量值,每次计算一次控制输出。假设当前时刻为第 (k) 次采样,目标值为 (SV_k),测量值为 (PV_k),误差为:
[ e_k = SV_k - PV_k ]
那么三个分项分别这样算。
2.1.1 比例项:现在错多少
比例项的输出是:
[ P_k = K_p \times e_k ]
它把当前误差放大 (K_p) 倍。误差大,输出就大;误差小,输出就小。比例项是控制系统的主力,但单独使用通常会出现“稳态误差”——系统稳定后,测量值和目标值之间始终差了一截。
比如你用比例控制一个风扇转速,设定 3000 转,当负载导致转速下降时,比例项会提高输出电压,但它需要存在误差才能维持输出,所以最终转速通常会稳定在目标值附近,但不会完全等于目标值。
2.1.2 积分项:过去错多少
积分项在离散系统中是“误差的累加”:
[ I_k = I_{k-1} + K_i \times e_k \times T ]
也可以写成:
[ I_k = K_i \times T \times \sum_{j=1}^{k} e_j ]
关键点:积分项会把历史误差一直记着。只要有残差存在,积分项就会持续增大,从而推动输出逐渐逼近目标值,最终消除稳态误差。
但积分项太强也会带来问题:超调变大、系统出现低频振荡,甚至积分饱和。
2.1.3 微分项:误差变化趋势
微分项在离散系统中是“误差的差值除以采样周期”:
[ D_k = K_d \times \frac{e_k - e_{k-1}}{T} ]
它预测误差接下来是变大还是变小。如果误差正在快速减小,微分项会产生一个反向“刹车”作用,抑制超调。
但微分项有一个著名的副作用:对测量噪声非常敏感。因为噪声会让相邻两次采样误差产生抖动,微分项会把这种抖动放大,严重时系统会高频振荡。
2.2 位置式 PID 与增量式 PID
上述公式计算的是控制器输出 (u_k) 的绝对值,称为“位置式 PID”:
[ u_k = P_k + I_k + D_k ]
实际嵌入式项目中还有一种常见写法是“增量式 PID”,它只计算输出的增量:
[ \Delta u_k = u_k - u_{k-1} ]
增量式的优势是输出不会突变,适合带积分执行机构的场合,比如步进电机调速。但理解 PID 本质,先看位置式更直观。电子表格模拟也更容易。
2.3 采样周期 T 的重要性
离散 PID 公式里的 (T) 不是摆设。(T) 越小,控制越接近连续系统,但计算负担和噪声敏感度也会增加;(T) 越大,控制越粗糙,甚至可能失稳。
在电子表格中,(T) 就是一个单元格里的数值。改变它,你会立刻看到曲线变化。这也是电子表格实验的价值所在——不用写代码,改一个单元格就能体验采样周期的影响。
3. 环境准备:创建一个 PID 电子表格模拟器
3.1 软件环境
本文以最常见的 Excel 操作为例,但同样适用于 WPS 表格、Google Sheets、LibreOffice Calc。公式基本都是通用的。
版本不需要特别新,只要是近十年内的办公表格软件都可以。重点是掌握“单元格引用”“填充柄下拉”“插入折线图”这三个基础操作。
3.2 被控对象模型
要模拟闭环控制,我们需要一个“被控对象”。现实中它可能是电机、加热器、水箱液位等。本文使用一个最经典的一阶惯性系统,数学描述是:
[ T_s \frac{dPV}{dt} + PV = K_s \times u ]
其中:
- (u):控制器输出(例如控制电压)
- (PV):被控量(例如转速、温度、液位)
- (K_s):系统增益
- (T_s):系统时间常数
这个模型的好处是:只需要两个参数 (K_s) 和 (T_s),就能模拟大多数常见的惯性对象。离散化之后,下一时刻的测量值公式为:
[ PV_{new} = PV_{old} + \frac{T}{T_s} \times (K_s \times u_{old} - PV_{old}) ]
这个公式非常容易放进电子表格。它不涉及真实硬件,却保留了闭环系统的主要特征。
3.3 表格整体设计
我们准备让每一行代表一个采样时刻。列结构如下:
| 列 | 含义 | 示例公式 |
|---|---|---|
| A | 时间 (t) | (0, 0.1, 0.2...) |
| B | 设定值 (SV) | 阶跃到 100 |
| C | 测量值 (PV) | 初值为 0,后续由模型算出 |
| D | 误差 (e) | (=B2-C2) |
| E | 比例项 (P) | (=Kp*D2) |
| F | 积分项 (I) | 上一行积分 + (KieT) |
| G | 微分项 (D) | (Kd*(e - 前一时刻e)/T) |
| H | 控制器输出 (u) | (=P+I+D),并做限幅 |
参数区放在右侧单元格,例如 K 列:
- K1:(K_p)
- K2:(K_i)
- K3:(K_d)
- K4:采样周期 (T)
- K5:系统增益 (K_s)
- K6:系统时间常数 (T_s)
- K7:输出下限
- K8:输出上限
修改参数区里的数值,整个响应曲线会跟着变化,这就是“几乎在电子表格里实现 PID”的核心体验。
4. 手把手搭建 PID 电子表格模拟器
4.1 设置参数区
打开一个空白工作表,在 K1:K8 单元格输入以下初值:
| 单元格 | 参数 | 初值 |
|---|---|---|
| K1 | Kp | 1.0 |
| K2 | Ki | 0.2 |
| K3 | Kd | 0.05 |
| K4 | T | 0.1 |
| K5 | K_sys | 1.0 |
| K6 | T_sys | 1.0 |
| K7 | umin | 0 |
| K8 | umax | 100 |
这些值会在后续实验中反复修改。
4.2 输入表头
在第 1 行输入表头:
| A1 | B1 | C1 | D1 | E1 | F1 | G1 | H1 |
|---|---|---|---|---|---|---|---|
| 时间 | 设定值 | 测量值 | 误差 | 比例项 | 积分项 | 微分项 | 输出 |
4.3 输入初始状态行(第 2 行)
我们需要在第 2 行建立初始状态。此时系统刚刚上电,测量值为 0,控制器还没有开始累积积分,微分项也为 0。
在 A2 输入:
0B2 输入设定值,这里模拟一个从 0 阶跃到 100 的过程:
100C2 输入初始测量值:
0D2 输入误差公式:
=B2-C2E2 输入比例项公式:
=$K$1*D2F2 输入积分项初值:
0G2 输入微分项初值:
0H2 输入输出值,同时做限幅,防止出现负数或超过执行器范围:
=MAX($K$7,MIN($K$8,E2+F2+G2))注意这里使用了绝对引用$K$7、$K$8,目的是让公式下拉时不会改变行号。
4.4 输入第二行(第 3 行)并下拉
第 3 行开始体现真正的闭环迭代。先输入 A3 时间:
=A2+$K$4B3 设定值继续保持 100:
= B2C3 测量值需要根据上一行的输出 (u_2) 和上一行的测量值 (PV_2),用一阶惯性模型更新:
=C2 + ($K$4/$K$6)*($K$5*H2 - C2)这里再解释一下这个公式的来由:离散化后的对象模型 (PV_{new} = PV_{old} + \frac{T}{T_s}(K_s \times u - PV_{old}))。H2 是上一行输出,C2 是上一行测量值。
D3 误差公式:
=B3-C3E3 比例项:
=$K$1*D3F3 积分项,使用上一行 F2 累加:
=F2 + $K$2*D3*$K$4G3 微分项,使用当前行与上一行的误差差值:
=$K$3*(D3-D2)/$K$4H3 输出限幅:
=MAX($K$7,MIN($K$8,E3+F3+G3))选中 A3:H3,使用填充柄向下拖拽。拖到第 102 行左右,就是模拟 10 秒(100 步 x 0.1s)。
4.5 绘制响应曲线
选中 A 列、C 列、H 列,也就是时间、测量值、输出值,插入“带平滑线和数据标记的折线图”。
如果在 Excel 中不方便一次选中不连续列,可以先插入空白折线图,然后右键“选择数据”,分别添加:
- 系列名称:PV,X 轴选择 A 列数据,Y 轴选择 C 列数据。
- 系列名称:SV,X 轴选择 A 列数据,Y 轴选择 B 列数据。
- 系列名称:u,X 轴选择 A 列数据,Y 轴选择 H 列数据。
也可以直接选中 A、C、H 三列后插入折线图,Excel 会自动生成。
初始参数下,你应该能看到测量值在 1 秒左右上升到接近 100,然后出现一个较小的超调,最终稳定在 100 附近。这就是一个完整的 PID 闭环响应。
4.6 预期结果说明
在 (K_p=1.0, K_i=0.2, K_d=0.05, T=0.1) 的初始参数下,测量值会快速上升,但因为积分项存在,超调可能达到 10% 左右,然后经过几次衰减振荡后收敛。输出 u 会从 100% 开始,随后逐渐下降,稳定在维持测量值 100 所需的输出值附近。
如果你的曲线完全不动,或者出现直线上升,请先检查 C3 公式里是否引用了 H2,F3 公式是否引用了 F2,G3 公式是否引用了 D2。这是最容易出错的地方。
5. 用电子表格完成 PID 参数整定实验
有了这个“电子表格 PID 实验台”,接下来可以亲手做三个经典实验。这比直接改代码更能建立直觉。
5.1 实验一:纯比例控制,观察稳态误差
将 (K_i) 和 (K_d) 都设为 0,只保留 (K_p=1.0),刷新后下拉,观察曲线。
你会发现测量值会快速上升,然后稳定在某个值,但永远达不到 100,比如稳定在 95 左右。这个差值就是“稳态误差”。原因很简单:比例控制需要误差存在才能产生输出;误差为零时,比例项输出为零,系统无法维持。
你可以试着把 (K_p) 提高到 3、5、10,会发现稳态误差变小,但超调和振荡会变大。这验证了一个结论:单纯增加比例增益无法彻底消除稳态误差,只会让系统越来越“急躁”。
这个实验强烈建议在电子表格里做一次,因为屏幕上每一行的误差数据都清晰可见,你能亲眼看到稳态误差不是“调不好”,而是结构性的存在。
5.2 实验二:加入积分项,消除稳态误差
恢复 (K_p=1.0),设置 (K_i=0.2),(K_d=0)。
此时测量值会更快地贴近 100,最终误差趋近于零。同时你也会看到超调比纯比例控制更大,甚至出现低频振荡。
积分项的魅力在于:它“记住”了过去的误差。哪怕当前误差很小,只要不等于零,积分项就会一点点累积,推动输出继续变化,直到误差彻底归零。但积分项也像一把双刃剑,(K_i) 太大、(T) 太大,甚至启动时的初始误差就会导致积分饱和,输出长时间维持在最大值,系统剧烈超调。
你可以试着把 (K_i) 从 0.2 改到 0.5,再改到 1.0,观察超调如何一步步变大。
5.3 实验三:加入微分项,抑制超调
恢复 (K_p=1.0, K_i=0.2),设置 (K_d=0.05),然后观察曲线。
微分项会在误差快速变小时提前“刹车”,所以超调通常会小于纯比例积分控制。继续增大 (K_d) 到 0.2、0.5,你会发现超调被压制,但曲线变得“毛糙”,甚至出现高频抖动。这就是微分项在放大测量噪声。
为了更直观地体验噪声对微分项的影响,你可以在 C 列测量值公式里故意加一个小扰动,例如改成:
=C2 + ($K$4/$K$6)*($K$5*H2 - C2) + $K$9K9 单元格里填一个随机噪声值(比如=RAND()*0.5-0.25),然后观察 G 列微分项的变化。你会看到:即使测量值只抖动了一点,微分项也可能会跳得很大。
这个实验也解释了为什么真实项目里,微分项通常需要配合低通滤波使用,甚至很多系统干脆不用微分项。
5.4 参数整定的经验口诀
电子表格实验做完后,可以记住一条通用调参顺序:
- 先把 (K_i)、(K_d) 设为 0,只留 (K_p),从小到大增加,直到系统出现等幅振荡。
- 再把 (K_p) 适当回退到振荡点的 50% 左右。
- 加入 (K_i),从小到大增加,直到稳态误差被消除,但注意观察超调。
- 最后加入 (K_d),从小到大增加,用来抵消超调,但小心噪声放大。
这只是一条经验,不是万能公式。实际项目中,结合 Ziegler-Nichols 等整定方法,或者 MATLAB Response Optimizer 这类工具,可以做更精细的调优。但电子表格实验能帮你建立“参数改变后曲线会怎么变”的直觉,这种直觉在真实调试中远比死记公式更有价值。
6. 从电子表格到真实代码:Python 实现同一个 PID
如果只在电子表格里跑,你可能还会怀疑“这套逻辑能不能搬到真实程序里”。下面用 Python 实现完全相同的离散 PID 和一阶惯性系统,可以直接运行。你可以对比输出结果和电子表格生成的曲线,会发现它们几乎完全一致。这个对照过程,是把电子表格思路迁移到代码的关键一步。
6.1 完整示例代码
import numpy as np import matplotlib.pyplot as plt # PID 参数 Kp = 1.0 Ki = 0.2 Kd = 0.05 # 采样周期 T = 0.1 # 被控对象参数 K_sys = 1.0 T_sys = 1.0 # 输出限幅 umin = 0 umax = 100 # 模拟时长 t_end = 10 t = np.arange(0, t_end, T) n = len(t) # 设定值:阶跃到 100 SV = np.ones(n) * 100 # 初始化变量 PV = np.zeros(n) u = np.zeros(n) I = 0.0 e_prev = 0.0 for i in range(n): # 当前误差 e = SV[i] - PV[i] # 比例项 P = Kp * e # 积分项(使用上一次累积值,当前时刻输出中还没有加入本次误差) # 对应电子表格中 F 列:第 2 行积分为 0,第 3 行才开始累加 I = I # 保持当前 I 用于输出 # 微分项:第一次采样时微分项为 0,避免初始冲击 if i == 0: D = 0.0 else: D = Kd * (e - e_prev) / T # 控制器输出 u_raw = P + I + D u[i] = np.clip(u_raw, umin, umax) # 更新积分项,供下一次使用 I = I + Ki * e * T # 根据一阶惯性模型计算下一时刻的测量值 if i < n - 1: PV[i + 1] = PV[i] + (T / T_sys) * (K_sys * u[i] - PV[i]) # 记录当前误差,用于下一次微分项 e_prev = e # 绘图 plt.figure(figsize=(10, 5)) plt.plot(t, SV, '--', label='SV') plt.plot(t, PV, label='PV') plt.plot(t, u, label='u') plt.xlabel('Time (s)') plt.ylabel('Value') plt.legend() plt.grid(True) plt.title('Discrete PID in Python') plt.show()需要注意,代码中积分项更新位置刻意和电子表格保持一致:当前行的输出使用的是上一行累积下来的 (I),输出计算完之后才把当前误差累加进去。这样不会出现“本周期输出提前包含本周期积分变化”的问题。
运行这段代码后,你会得到与电子表格几乎一致的曲线。但由于电子表格中微分项的计算方式、浮点精度、输出限幅位置可能存在细微差异,数值不会逐位完全相同,这属于正常现象。
6.2 电子表格与代码的优势互补
电子表格的优势是“便于观察和教学”,每一列计算过程都摊开展示在眼前。代码的优势是“便于迭代和集成”,可以快速写进你的 STM32 项目、ROS 节点或自动化控制系统。
建议你同时掌握两者:先用电子表格理解 PID 内部计算过程,再用 Python 做批量参数扫描,最后迁移到目标硬件。这种路径比直接啃公式硬快得多。
7. 常见问题与排查思路
在搭建电子表格 PID 模拟器和后续控制实验过程中,容易遇到这些问题。下面整理成一张速查表。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 曲线不变化,测量值一直是 0 | C 列模型公式没有正确引用上一行 H 列 | 检查 C3 公式是否包含 H2,并确认 H2 有非零输出 |
| 输出瞬间到 100%,无法回落 | 积分项过大,或初值误差导致积分饱和 | 减小 Ki,或者对 F 列积分项做限幅 |
| 系统振荡越来越剧烈,甚至发散 | 比例项过大,采样周期 T 过大 | 减小 Kp,减小 T,查看曲线是否稳定 |
| 输出曲线出现锯齿、高频抖动 | 微分项放大噪声,或测量值存在微小突变 | 减小 Kd,或对微分项增加一阶低通滤波 |
| 稳态误差一直存在 | 积分项为 0,或 Ki 太小 | 恢复 Ki 并缓慢增大,观察稳态误差是否消失 |
| 输出长期等于 0 或 100 | 输出限幅设置太小/太大,积分饱和 | 检查 umin/umax,加入抗积分饱和逻辑 |
| 下拉公式后数值出现大量循环引用 | 公式错误引用自身单元格 | 重新检查公式中引用的行列,避免 H 列引用 H 列 |
| 图表中只显示一条直线 | 只选择了输出列,未选择时间列 | 在选择数据时,将 X 轴设置为 A 列时间 |
如果曲线发散,一个快速排查手段是把 (K_i)、(K_d) 全设为 0,只保留很小的 (K_p),确认系统能稳定。然后再逐步增加 (K_p)、(K_i)、(K_d)。这本质上就是“控制变量法”,在电子表格里操作非常方便。
8. PID 工程实战建议与安全边界
电子表格只是起点,真实系统远比这个模拟复杂。把 PID 用在实际项目中,以下建议值得认真对待。
8.1 采样时间和执行器刷新频率要匹配
采样周期 (T) 不是越短越好,也不是越长越好。它需要与传感器响应速度、执行器刷新频率、控制器计算能力匹配。电子表格里随手可以改 (T),但到嵌入式环境,定时器中断频率、通信周期都会限制最高采样率。
8.2 输出限幅和积分限幅必须一起处理
只对输出限幅,不限制积分项,很容易出现“积分饱和”。现象是:当系统长时间无法达到目标时,积分项累积到很大;一旦测量值到达目标,积分项还需要很长时间才能“吐出来”,导致严重超调。
常见处理方式是在积分项计算时做限幅,或者仅在输出未饱和时积分。具体到电子表格,可以把 F 列公式改为限幅形式:
=MAX($K$7, MIN($K$8, F2 + $K$2*D3*$K$4))这样积分项不会超过执行器能力范围,对抗积分饱和有一定帮助。
8.3 微分项必须考虑噪声抑制
真实传感器都有噪声,纯微分项会把噪声放大,因此工程上常用“不完全微分”或“微分先行”。最灵活的做法是让微分项通过一个低通滤波器,比如:
[ D_{filtered} = \alpha \times D_{raw} + (1-\alpha) \times D_{filtered_prev} ]
其中 (\alpha) 是滤波系数。电子表格只能模拟理想情况,真实项目中这一点不能省。
8.4 手动自动切换和参数保存
很多板卡和控制器支持手动/自动模式切换。在切换瞬间,如果 PID 输出和手动输出不一致,系统会产生冲击。通常需要在切换前把积分项重置为当前输出对应的值,或者实行“无扰切换”(bumpless transfer)。
此外,参数整定结果不要只看一组曲线,要保存多组参数样本,记录目标对象、工况、采样周期、Kp/Ki/Kd 和响应指标。这样后续调优才能有据可查。
8.5 生产环境安全边界
如果 PID 控制用在真实设备上,比如电机、加热器、压力阀,一定要有安全保护机制:
- 输出限幅必须根据执行器安全范围设置。
- 增加急停逻辑,例如测量值超过安全阈值时强制输出为 0 或切到安全状态。
- 修改参数应在测试环境或低功率工况下先验证,确认无异常后再接入生产系统。
- 涉及数据库、生产环境变更时,要遵守最小权限原则并做好备份。
- PID 只是控制层的一部分,不要忽视传感器故障、通信中断、执行器卡死等异常情况。
这些内容看似与“电子表格模拟 PID”无关,但实际上是从实验环境走向真实项目时最容易踩坑的地方。提前知道,能省很多现场调试时间。
9. 总结与下一步学习路线
这篇文章围绕“Calculus-free PID almost in a spreadsheet”展开,核心是让你不必纠结微积分,而是用离散化视角和电子表格工具,亲手搭出一个可视化的 PID 闭环系统。
你实际操作时,应该已经尝到了三个阶段的变化:
- 第一阶段,纯比例控制,看到稳态误差。
- 第二阶段,加入积分项,看到超调和振荡。
- 第三阶段,加入微分项,看到超调被压制,也看到噪声放大。
这种“亲眼看曲线变化”的体验,比记住任何公式都更牢固。如果你能独立完成这三个实验,并尝试修改 Kp、Ki、Kd、T 等参数,那么 PID 对你来说就不是一个黑盒了。
下一步可以继续延伸的方向包括:
- 把电子表格里的离散 PID 公式迁移到 STM32 或 Arduino,做电机转速闭环。
- 研究增量式 PID 与位置式 PID 的区别,以及各自的适用场景。
- 学习串级 PID 和双环 PID,比如四轴飞行器中常见的内环角速度、外环角度控制。
- 了解模糊 PID 等自适应控制方法,它们能在参数变化较大的工况下自动调整 PID 参数。
- 使用 MATLAB Response Optimizer 或 Simulink 做更精细的模型仿真与参数优化。
无论你最后走向哪个方向,电子表格里这几列数据,就是你理解 PID 的“最原始版本”。把这一套计算逻辑吃透,再去接触上层工具和真实硬件,会顺畅得多。现在就可以打开一个空白电子表格,把这 8 个参数和 3 行公式填进去,下拉到 100 行,再把图表画出来。只有亲手拖动一次填充柄,你才能真正感受到:“ PID 原来不过如此。”