
PID 控制在很多教科书里开篇就是拉普拉斯变换、传递函数、微分方程加上一堆积分项微分项直接把不少想入门电机控制或温控的工程师劝退了。实际做控制的时候大多数人面对的问题不是推导公式而是怎么把 P、I、D 三个参数调到一个能用的状态。这次我们换一个完全不用微积分的路径用一张电子表格把 PID 的完整闭环仿真跑出来参数怎么影响曲线一眼就能看明白。这篇文章的目标很明确不碰微积分用 Excel、Google Sheets 或 WPS 表格把 PID 闭环控制复现出来然后把同样的逻辑翻译成 C 语言代码落地到 STM32 或 Arduino 上。全程只需要加减乘除和下拉填充不需要 MATLAB、不需要 Simulink、不需要写程序也能先做一轮参数实验。1. 核心能力速览能力项说明项目思路用离散迭代替代积分和微分在电子表格中实现 PID 闭环仿真使用工具Excel / Google Sheets / WPS 表格均可无需额外安装软件核心优势不需要微积分基础不依赖 MATLAB 等专业仿真软件覆盖内容PID 原理、闭环仿真、参数实验、位置式 PID、增量式 PID适用人群嵌入式初学者、自动化专业学生、做电机/温控调试的工程师计算资源普通办公电脑即可100~200 行迭代瞬间完成可迁移性表格验证后的逻辑可直接翻译成 C / Python / Arduino 代码扩展方向抗积分饱和、串级 PID、模糊 PID、前馈补偿限制说明表格节点相对固定不能做超高频率实时控制真实系统还需要处理噪声、饱和和非线性2. PID 控制器的本质不用微积分也能讲清楚PID 三个字母分别对应比例、积分、微分。教科书上的标准写法是u(t) Kp * e(t) Ki * ∫e(t)dt Kd * de(t)/dt这个公式看着吓人但可以翻译成三句大白话比例项 P看现在差多少差得多输出就大。积分项 I把过去一直没消除的误差记到账上慢慢补专门消除稳态误差。微分项 D看误差是在变大还是变小如果误差在快速减小就适当刹车减少超调。工控现场常见的直流电机调速、加热棒温控、平衡车姿态控制内核都是这套逻辑。区别只在于被控对象的时间响应特性不同参数整定结果不同。真正的难点在于“积分”和“微分”这两个数学概念。连续系统里需要求积分和导数但计算机控制是离散的每隔固定时间 Ts 采一次样因此完全可以换成离散近似误差积分 ≈ 每次采样的误差乘以采样周期再累加起来误差微分 ≈ 本次误差减去上一次误差再除以采样周期这两个“约等于”只需要加法、减法和乘法。积分就是“上一行累加值 本行误差 × dt”微分就是“本行误差 - 上一行误差”没有任何微积分门槛。这就是标题里 “Calculus-free” 的含义——把连续数学问题变成一个迭代问题。3. 为什么电子表格可以跑 PID离散化是最自然的思路电子表格是一行一行组织数据的。如果把每一行看作控制算法的一次采样周期那么第 0 行记录初始状态第 1 行根据第 0 行的结果计算误差、输出第 2 行再根据第 1 行的结果继续推进每一行都是上一个时间步的延续这就天然符合离散闭环控制的迭代结构。在表格里积分累加只需要引用上一行微分只需要引用上一行和当前行而闭环反馈就是把当前测量值回传到误差计算单元格。整个闭环回路在电子表格里就是几个普通的单元格引用。被控对象也不需要复杂的传递函数。很多实际对象在一阶惯性系统建模下已经足够说明问题比如直流电机的转速响应、加热炉的温度响应都可以近似成这个模型y(k1) y(k) alpha * (u(k) - y(k)) alpha dt / T其中 y(k) 是当前测量值u(k) 是控制器输出dt 是采样周期T 是对象时间常数。alpha 越小对象响应越快达到目标值的变化越接近“缓变”。这个差分方程完全不需要求解微分方程表格里一行就能写出来。有了被控对象模型再配上 PID 控制器整个闭环系统就备齐了。电子表格里至少需要两列用于展示结果设定值和测量值。设定值可以理解为目标转速或目标温度测量值就是反馈回来的实际转速或实际温度。响应曲线可以直接用折线图画出来效果和仿真软件输出类似但实现成本低得多。4. 在电子表格中实现 PID 闭环仿真4.1 表格布局打开任意电子表格软件新建一个工作簿。假定我们把参数放在 J1、J2、J3采样周期 dt 放在 J4对象的时间常数 T 放在 J5alpha 放在 J6。项目单元格初始值KpJ15KiJ22KdJ30.5dtJ40.1TJ51alphaJ6J4/J5正文区域从 A 到 G 列表头如下列含义A步数 kB设定值 rC测量值 yD误差 e r - yE误差积分 integral integral e * dtF误差变化 delta_e e(k) - e(k-1)G控制量 u Kp * e Ki * integral Kd * delta_e / dt4.2 第一行公式A2 填入 0B2 填入设定值 100C2 填入初始测量值 0。D2、E2、F2、G2 分别如下D2 B2 - C2 E2 D2 * $J$4 F2 0 G2 $J$1 * D2 $J$2 * E2 $J$3 * F2 / $J$4注意 F2 初始填 0因为第 0 步还没有“上一次误差”微分项默认不动作。4.3 第二行及后续行的公式A3、B3、C3、D3、E3、F3、G3 分别如下A3 A2 1 B3 100 C3 C2 $J$6 * (G2 - C2) D3 B3 - C3 E3 E2 D3 * $J$4 F3 D3 - D2 G3 $J$1 * D3 $J$2 * E3 $J$3 * F3 / $J$4C3 这一步就是被控对象模型它使用上一行的控制量 G2 更新当前测量值体现了“先计算控制量再更新对象状态”的时序关系。选中 A3 到 G3向下拖拽到第 200 行左右闭环仿真就完成了。4.4 画图观察响应选中 A1 到 C200 区域插入折线图。图表中会出现两条曲线设定值 r 是一条水平直线代表目标值测量值 y 是一条上升曲线代表实际系统跟随设定值的过程如果参数设置合理y 曲线会先上升然后稳定在 100 附近。如果 Kp、Ki、Kd 设置不当曲线会出现超调、振荡甚至发散。这个过程和真实控制器调参时观察到的现象完全一致。为了避免数值问题建议给控制量 G 做一次限幅。用电子表格时可以直接写成G列限幅公式 MAX(0, MIN(200, 原始控制量公式))这样模拟的就是真实执行器输出饱和的情况。后面在测试积分饱和时这个限幅非常有用。5. 用表格做参数实验P / I / D 各自的响应特点5.1 纯 P 控制把 Ki 和 Kd 分别设置为 0只保留 Kp。此时系统会出现稳态误差。当 Kp 2 时稳态误差大约在 33% 左右把 Kp 提高到 20误差显著减小但依然存在。原因很简单P 控制必须有误差才能产生输出误差不可能等于零。这个现象在表格曲线中看得非常清楚测量值最终停在离设定值还有一段距离的地方而不是完全重合。实验步骤把 J2 改成 0J3 改成 0然后刷新曲线观察稳态值。5.2 加入积分项消除稳态误差把 Ki 从 0 慢慢调大曲线会逐渐向设定值靠拢。积分项的累加作用会把历史误差慢慢补回来稳态误差最终被消除。但 Ki 不是越大越好。Ki 过大时积分累加速度过快系统会出现明显的超调然后来回振荡。可以试试把 Ki 从 2 改成 20会看到 y 曲线冲过 100再回摆如果设置得更大可能持续振荡。这一节实验能直观理解“积分项消除稳态误差但也带来超调风险”这个工程结论。5.3 加入微分项抑制超调把 Kd 设置为 0.5观察曲线。微分项在误差变化快时给出一个反向修正相当于提前刹车可以在一定程度抑制超调。不过要注意微分项对噪声非常敏感。在实际系统里传感器信号都有噪声误差的微小抖动会被微分项放大尤其当 dt 很小时放大倍数接近 1/dt。表格里看不到噪声但在真实调试时会遇到所以工程上往往对测量值先做低通滤波再计算微分或者干脆只对测量值的变化量做微分而不是对误差做微分。5.4 观察指标通过表格曲线的形状可以直接读取几个关键指标指标曲线特征工程含义上升时间y 从 10% 升到 90% 需要多少步响应速度超调量y 超过设定值的最大幅度系统稳定性稳态误差y 最终与设定值的差值控制精度振荡次数y 在设定值附近来回穿越几次阻尼比相关这几个指标在真实控制器调试中同样是最核心的评价维度。表格里提前建立直觉后面在电机或温控现场就能少走很多弯路。6. 从表格到代码位置式 PID 与增量式 PID表格逻辑验证没问题之后下一步就是把它移植到嵌入式代码里。实际工程中最常用的两种形式是位置式 PID 和增量式 PID。6.1 位置式 PID位置式 PID 直接输出控制量适合温度控制、液位控制这类执行器可以直接设置到某个绝对位置的场景。C 语言实现如下typedef struct { float kp; float ki; float kd; float dt; float integral; float prev_error; float out_min; float out_max; } PidController; void pid_init(PidController *pid, float kp, float ki, float kd, float dt, float out_min, float out_max) { pid-kp kp; pid-ki ki; pid-kd kd; pid-dt dt; pid-integral 0.0f; pid-prev_error 0.0f; pid-out_min out_min; pid-out_max out_max; } float pid_update(PidController *pid, float setpoint, float measurement) { float error setpoint - measurement; pid-integral error * pid-dt; float derivative (error - pid-prev_error) / pid-dt; pid-prev_error error; float output pid-kp * error pid-ki * pid-integral pid-kd * derivative; if (output pid-out_max) { output pid-out_max; } else if (output pid-out_min) { output pid-out_min; } return output; }这个实现和电子表格里的公式结构完全一致。需要注意积分限幅否则系统饱和后积分会继续累加导致恢复时长时间过冲这就是经典的积分饱和现象。6.2 增量式 PID增量式 PID 输出的是控制量的增量 Δu而不是绝对控制量。它适合伺服电机、步进电机这类执行器通过“加一点”或“减一点”来调节的场景。增量式 PID 的一个重要优势是天然不容易触发积分饱和因为输出的是增量历史累计的误差不会无限堆积。typedef struct { float kp; float ki; float kd; float prev_error; float prev_prev_error; } IncrementalPid; void incremental_pid_init(IncrementalPid *pid, float kp, float ki, float kd) { pid-kp kp; pid-ki ki; pid-kd kd; pid-prev_error 0.0f; pid-prev_prev_error 0.0f; } float incremental_pid_update(IncrementalPid *pid, float setpoint, float measurement) { float error setpoint - measurement; float delta_u pid-kp * (error - pid-prev_error) pid-ki * error pid-kd * (error - 2.0f * pid-prev_error pid-prev_prev_error); pid-prev_prev_error pid-prev_error; pid-prev_error error; return delta_u; }调用端需要维护一个累计输出变量并做限幅static float output 0.0f; float delta incremental_pid_update(pid, 100.0f, current_speed); output delta; if (output 200.0f) { output 200.0f; } else if (output 0.0f) { output 0.0f; }表格里的积分累加对应位置式 PID 里的 integral 变量表格里的误差变化对应代码里的 error - prev_error。逻辑完全一致。6.3 在 STM32 或 Arduino 上验证从表格到真机建议按这个顺序走先用表格仿真确定一组初始参数。把参数写入单片机 PID 结构体。用串口把测量值和设定值实时打印出来。使用串口绘图工具或 VOFA 上位机查看波形。根据波形微调参数重点观察超调、稳态误差和振荡频率。VOFA 的波形图比普通串口助手的文本输出直观很多可以直接看出控制效果是否收敛。这也是从“表格曲线”过渡到“真机曲线”最顺手的衔接方式。7. 资源占用与性能观察7.1 电子表格模式电子表格模式下200 行迭代的公式量非常小普通办公电脑可以瞬间完成。刷新参数时只需要修改 J1、J2、J3 等单元格曲线会立刻更新。这一步的主要价值是快速做参数扫描比如把 Kp 从 2 改到 50观察曲线的趋势变化。批量参数扫描也可以在表格里做。利用电子表格的模拟运算表功能可以一次性对比多组 Kp、Ki 的组合把不同组的响应曲线叠在一张图里。这样比手动逐个改参数高效得多。7.2 嵌入式模式位置式 PID 在嵌入式上的计算开销非常小只需要 3 次乘法、几次加法和几个变量即使是主频几十 MHz 的单片机也能在微秒级完成。如果使用定点数实现连 FPU 都不需要。真正影响控制性能的不是 PID 公式本身的运算量而是控制周期是否固定。采样周期 dt 如果忽快忽慢微分项和积分项都会对应失真导致系统抖动。工程上应该用定时器中断固定控制周期而不是在主循环里用 delay 近似。7.3 表格仿真和真机的差异表格里用的是理想一阶模型没有传感器噪声、没有执行器非线性、没有通信延迟。真机调试时这些因素都会叠加在控制回路上所以表格里调好的参数搬到真机上通常还要微调。重点观察三个差异传感器噪声会放大微分项必要时对测量值滤波。执行器饱和会造成积分饱和需要做抗积分饱和处理。控制周期不固定会导致曲线异常优先保证定时器稳定触发。8. 常见问题与排查方法问题现象可能原因排查方式解决方案测量值一直无法到达设定值缺少积分项或 Ki 太小检查 Kp、Ki 设置增大 Ki确认积分项正常累加曲线大幅超调并来回振荡Ki 过大或 Kd 过小观察曲线振荡频率减小 Ki适量增加 Kd控制量长期处于饱和上限积分饱和观察 G 列是否长期接近上限对积分做限幅或改用增量式 PID微分项导致输出剧烈抖动噪声放大的微分成份检查测量值是否存在高频波动对测量值低通滤波后取微分表格出现 #DIV/0! 错误dt 为空或为 0检查 J4 单元格填入非 0 采样周期图表没有曲线数据区域未包含图表范围检查选择的数据区域重新选择 A1:C200真机曲线和表格曲线差异大对象模型不准确比较阶跃响应时间常数根据真机阶跃数据修正 T串口波形数据跳动采样周期不稳定观察串口数据间隔改到定时器中断固定周期执行系统在设定值附近小幅度抖动P 太大或传感器量化精度不足降低 Kp 观察减小 Kp或提高传感器分辨率增量式 PID 输出静止但误差不为 0增量项都为零误差稳定检查调用端是否累加输出确认 output 已正确累加并限幅9. 最佳实践与使用建议先在电子表格里把参数实验做充分再写正式代码。表格调整参数的成本几乎为零改一个单元格就能看整条响应曲线这比在真机上反复烧录固件效率高得多。调参顺序建议是先 P再加 I最后加 D。首先单独调 P把 Kp 从小到大调整记住曲线的上升速度和稳态误差。然后加 I逐步增大 Ki 直到稳态误差消失如果出现振荡就回调一点。最后加 D用微分项抵消超调。这个顺序在表格里验证一遍之后真机调试时会非常有底气。输出限幅一定要加不管表格仿真还是真机代码都要加。执行器能输出的范围是有限的控制量如果不能限幅模型里的积分项迟早出问题。表格仿真里加上限幅公式才能真实复现饱和现象。积分饱和的坑也要提前处理。最简单的方法是积分限幅也就是给 integral 变量设置一个上下界不让它无限累加。高级一点的做法是积分分离误差超过阈值时关闭积分误差进入阈值范围内才恢复积分累加。这些技巧可以先在表格里模拟看限幅效果再移植到代码中。对于真机调试无论做电机控制还是温控都建议保留完整的数据记录。串口打印设定值、测量值、控制量三个变量方便画曲线回看。只有看曲线才能定位问题只看瞬时数值很难判断系统是欠阻尼还是过阻尼。涉及具体设备时也要谨慎。PID 参数是和被控对象强相关的同一个参数在不同负载、不同环境温度下的表现可能完全不同。温度控制中环境变化会改变对象的时间常数电机控制中负载变化会改变系统的增益。表格里验证过的参数只能作为初始值不能作为最终值直接用于批量生产。工业场景下必须做充分的稳定性和鲁棒性测试。10. 总结从电子表格到真机调试这个思路最值得尝试的地方是把控制算法从“数学公式”变成了“单元格公式”。积分变成了累加微分变成了差分闭环反馈变成了单元格引用。整个闭环系统的逻辑在任何一张电子表格里都能跑得通。建议第一次尝试时先做三个动作按第 4 节的布局搭出一张 200 行的闭环仿真表。分别把 Ki、Kd 改成 0看纯 P 控制下的稳态误差。加入 Ki 后观察稳态误差如何被消除再增大 Ki 观察振荡特征。这样跑完一轮你就会对“P 消除误差速度、I 消除静态偏差、D 压超调”有一个非常直观的体感后面写代码、调真机参数才不会慌。最容易踩的坑有两个一个是积分项不带限幅导致饱和后恢复过程很长另一个是直接把表格里的参数直接搬到真机上忽略传感器噪声和对象差异。这两个坑在表格阶段就能提前预演提前处理。后续想深入的话可以在这个基础上扩展串级 PID、模糊 PID 或前馈补偿。串级 PID 适合快速对象环控制模糊 PID 适合非线性系统前馈补偿适合已知扰动工况。这些进阶内容同样可以用电子表格搭建仿真模型先把结构跑通再落到实机验证。对刚接触 PID 控制的人来说这张表格就是最低成本的入门工具。对已经在做真机调试的工程师这张表格就是最轻量的参数预演工具。建议收藏备用。