尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Excel仿真PID控制:从递推公式到C代码实战

Excel仿真PID控制:从递推公式到C代码实战 很多人在学习 PID 控制时第一眼看到连续公式里的积分符号和微分符号会下意识认为必须先把微积分学透才能上手。实际上PID 一旦放到离散采样系统里就只剩加减乘除。比例项是当前误差乘以系数积分项是历史误差的累加微分项是相邻两次误差之差除以采样周期。这篇文章用 Excel 或 Google Sheets 搭一个几十行数据的闭环仿真不需要编程也不需要解方程就能把 P、I、D 各自的职责和调参趋势看清楚。最后再把同一套递推逻辑改写成单片机里常见的 C 代码方便迁移到电机调速、温度控制等实际项目。1. 先理解 PID 为什么在电子表格里也能实现1.1 PID 在闭环回路里到底做什么任何闭环控制回路都有四个基本要素目标值 SV、反馈值 PV、误差 eSV-PV、控制器输出 u。控制器读取反馈值计算误差然后决定输出多大控制量去驱动被控对象让反馈值尽可能靠近目标值。PID 控制器做的就是三件事比例项只关注“当前误差有多大”误差大就输出大误差小就输出小。积分项关注“过去误差累计了多少”用来消除长期存在的稳态误差。微分项关注“误差最近变化得有多快”在误差还没变得太远之前提前刹车。电子表格里的每一行代表一个采样时刻误差、累加值、误差差值都可以用列字段保存。只要把每一行的公式写对再下拉填充PID 的递推过程就会自动跑起来。1.2 连续公式到离散公式的替换连续 PID 公式写出来是u(t) Kp * e(t) Ki * ∫e(t)dt Kd * de(t)/dt这个公式里的积分和微分在离散系统中需要改写成累加和差值积分把每个采样周期的误差乘以 dt再累加起来。微分把相邻两个采样点的误差差除以 dt。于是离散 PID 公式就变成e[n] SV[n] - PV[n] P[n] Kp * e[n] I[n] I[n-1] Ki * e[n] * dt D[n] Kd * (e[n] - e[n-1]) / dt u[n] P[n] I[n] D[n]这里的 n 是采样序号dt 是采样周期。只要上一个时刻的 PV 和 I 已知当前时刻的 PV 和 I 就能通过公式算出。这个递推过程与电子表格的行号天然对应因此完全可以在表格里实现。1.3 三个项在表格里的对应把离散公式放到表格里每个项对应一列PID 项作用表格里的计算方式P 项对当前误差立即做出反应Kp * eI 项记住历史误差消除残余偏差上一行 I 加上Ki * e * dtD 项观察误差变化趋势提前抑制超调Kd * (当前误差 - 上一行误差) / dt总输出 u实际作用到被控对象上的控制量P I D再根据执行机构限幅这三项不需要任何微积分知识只需要理解“当前值、累加值、变化量”这三个概念。很多调参问题都可以从表格数据中直接看出来。2. 在电子表格里搭建一个可下拉的 PID 仿真2.1 先选一个合适的被控对象模型要仿真闭环必须有一个“被控对象”来模拟 PV 怎样随 u 变化。这里使用一阶惯性模型近似公式是PV[n1] PV[n] (dt / T) * (K * u[n] - PV[n])解释一下参数K 是对象增益表示最终 PV 和 u 之间的比例关系。T 是时间常数表示 PV 变化的速度T 越大系统反应越慢。dt 是采样周期表示每隔多久计算一次。这个模型常用来近似电机转速、温度、液位等惯性对象。虽然真实对象可能包含非线性、滞后和噪声但用来理解 PID 的工作原理已经足够。它本身也不需要解微分方程只是把“一阶惯性”的差分近似写成一行公式。2.2 表格布局、参数区和数据列新建一个工作表第 1 行留作表头数据从第 2 行开始。参数区放在 L1:M10方便统一修改。参数区设置参数值说明dt0.5采样周期秒Kp0.8比例系数Ki0.1积分系数Kd0.5微分系数T5对象时间常数秒K1对象增益SV80目标值u_min0输出下限u_max100输出上限数据列结构列字段含义An采样序号Bt时间CSV目标值DPV当前反馈值Ee误差Fde误差变化量GP比例输出HI积分累积值ID微分输出Ju限幅后的控制器输出Knext_PV由当前输出推出的下一拍反馈值这里用 C 列放目标值D 列放当前 PVK 列放下一拍 PV。实际使用中也可以调整列的位置只要公式里引用正确即可。2.3 初始行和第 2 行公式怎么填第 2 行是初始时刻 t0。PV 初始为 0误差为 80积分从 0 开始微分项第 1 拍没有上一拍误差所以 de 初始为 0。第 2 行公式单元格公式A20B2A2*$M$2C2$M$8D20E2C2-D2F20G2$M$3*E2H2$M$4*E2*$M$2I2$M$5*F2/$M$2J2MAX($M$9, MIN($M$10, G2H2I2))K2D2($M$2/$M$6)*($M$7*J2-D2)从第 3 行开始进入递推单元格公式A3A21B3A3*$M$2C3$M$8D3K2E3C3-D3F3E3-E2G3$M$3*E3H3H2$M$4*E3*$M$2I3$M$5*F3/$M$2J3MAX($M$9, MIN($M$10, G3H3I3))K3D3($M$2/$M$6)*($M$7*J3-D3)写完后选中 A3:K3向下填充到第 102 行左右。此时每个时间步 dt0.5 秒一共仿真约 50 秒足够看到一阶系统从启动到稳定的完整过程。这里有一个需要留意的设计点H 列积分项没有做限幅J 列输出做了限幅。也就是说输出饱和时I 仍可能继续增长这正是后面要讨论的积分饱和现象。2.4 用折线图看响应选中 B 列和 D 列插入折线图。横轴是时间 t纵轴是 PV。运行后会看到 PV 从 0 开始上升逐步靠近目标值 80。图表不是必须项但它能把数字变化变成直观曲线对判断超调、振荡和收敛速度都有帮助。如果表格数据发生变化图表会自动更新因此调参时只要修改 L3:L5 的 Kp、Ki、Kd就能看到响应曲线实时变化。3. 调参实验用数据理解 P、I、D3.1 只保留比例稳态误差为什么消不掉把 Ki0Kd0只保留 Kp0.8。填写后观察 PV 的最终值。按理论计算一阶对象的稳态满足 PVKu而 uKp(SV-PV)所以PV Kp * (SV - PV) PV Kp * SV / (1 Kp)代入 Kp0.8SV80得到 PV 约等于 35.6。也就是说比例控制到位后误差仍然存在。这是因为比例输出必须保留一部分误差才能产生维持 PV 不回落所需的控制量。如果把 Kp 调大比如调到 5稳态 PV 会提高到约 66.7误差会变小但仍然不为零。而且 Kp 太大时输出很容易顶到限幅值系统也更容易出现振荡。比例项的局限性在这里体现得很清楚它没有“记忆”无法根据历史情况补足最终缺口。3.2 加入积分历史误差如何填补残差设置 Kp0.8Ki0.1Kd0。此时 H 列积分项会不断累加误差。只要 PV 仍小于 SV误差为正I 就会持续增大控制器输出 u 也随之增大PV 继续向上走。最终 PV 会到达 80误差为 0比例项为 0积分项保持在一个稳定值上以维持输出。积分项的代价是反应滞后。误差已经变成 0 时积分项里还保存着过去的大量历史信息输出不会立刻降下来因此容易出现超调。如果 Ki 取得太大比如 0.5I 增长过快输出很快达到限幅值PV 会冲过目标值然后误差变负积分项又要花很多时间回落整个过程就变成大幅振荡。在表格里可以清楚地看到 H 列和 J 列的变化输出限幅后H 列仍在变化这就是积分饱和的雏形。3.3 加入微分提前刹车和噪声代价设置 Kp0.8Ki0.1Kd0.5。D 项关注的是误差变化量。当 PV 快速靠近 SV 时误差在快速变小de 是负数D 项为负会抵消一部分 P 和 I起到提前减速的作用。加入 D 后常见的响应曲线超调量会变小收敛更平稳。微分项也有明显代价。如果测量值里有噪声相邻两拍误差可能来回跳变de 会变得很大D 项输出也会剧烈抖动。实际项目里通常不能直接把 Kd 调得很大而是先对测量值做低通滤波或者采用“微分先行”的结构只对反馈值求微分不对目标值跳变求微分。想验证噪声影响可以在某一行的 D 列 PV 值手动加入一个偏差比如从 60 突然跳到 63然后看 I 列或输出列是否出现尖峰。这个现象在表格仿真里看得非常直接。3.4 用误差列判断系统是否调好调参完成后不要只看 PV 曲线是否接近 SV还要看误差列 e 的变化趋势。常见的几种情况误差表现含义处理方向单调收敛到 0 附近系统稳定动态可能偏慢可适当增大 Kp 或 Ki 提速衰减振荡幅度不断变小参数偏激进但可以接受适当增加 Kd 或降低 Kp等幅振荡通常 Kp 或 Ki 过大降低 Kp 或 Ki检查输出限幅误差越来越发散参数严重过大或模型数值不稳定先恢复较小参数再逐步增加误差长期不为 0积分项不足增大 Ki或检查积分限幅4. 从表格仿真迁移到实际控制代码4.1 位置式 PID 的最小实现表格里的递推逻辑可以直接写成 C 代码。下面的结构体保存 PID 状态函数每次调用计算一个输出typedef struct { float Kp; float Ki; float Kd; float dt; float integral; float prev_error; float out_min; float out_max; } PID; float pid_update(PID *pid, float setpoint, float measurement) { float error setpoint - measurement; float P pid-Kp * error; pid-integral pid-Ki * error * pid-dt; float I pid-integral; float D pid-Kd * (error - pid-prev_error) / pid-dt; pid-prev_error error; float output P I D; if (output pid-out_max) { output pid-out_max; } if (output pid-out_min) { output pid-out_min; } return output; }这段代码和表格公式一一对应。实际使用前需要先初始化结构体设置好 dt、输出范围、初始积分和初始误差。4.2 固定采样时间、输出限幅和积分抗饱和真实控制器里采样周期 dt 必须固定。如果主循环里两次调用 pid_update 的时间间隔不固定PID 参数的有效性会被破坏。建议放在定时器中断或固定频率的任务里调用。另一个关键问题是积分抗饱和。表格仿真里 H 列没有限幅因此输出饱和后 I 还会继续增长。回到实际系统中如果输出已经到最大积分项还在往上累积那么反馈值一旦超过目标值积分项需要很长时间才能退回来系统就会长时间保持超调。常见解决方法之一是给积分项单独限幅if (pid-integral pid-integral_max) { pid-integral pid-integral_max; } if (pid-integral pid-integral_min) { pid-integral pid-integral_min; }另一种方法是输出饱和时暂停积分累加只有输出未饱和时才更新积分。这两种方式都可以减小积分饱和造成的影响具体选择取决于执行机构特性。4.3 微分先行和增量式 PID微分项如果直接作用在误差上目标值发生阶跃变化时误差会瞬间跳变de 很大D 项会产生一个输出尖峰。工程上通常采用“微分先行”只对反馈值做微分这样目标值变化时不会额外冲击执行机构。增量式 PID 是另一种常用形式。它计算的是输出增量而不是绝对输出Δu Kp * (e[n] - e[n-1]) Ki * e[n] * dt Kd * (e[n] - 2*e[n-1] e[n-2]) / dt u[n] u[n-1] Δu增量式适合步进电机、阀门等执行机构因为它天然输出增量误动作影响小而且不含积分累加项抗积分饱和处理更直接。电子表格仿真中以位置式为主理解清楚后增量式只是把当前拍减去上一拍的输出重新组合一遍。在实际控制器调试中建议把 SV、PV、P、I、D、u 这几个变量通过串口打印或发送到 vofa 等上位机软件实时观察曲线和电子表格里的趋势做对比。5. 常见问题排查与检查清单5.1 四类典型现象和排查路径现象常见原因检查方式处理建议PV 发散或大幅振荡Kp 过大、Ki 过大、dt 过大导致离散模型失真看误差列是否递增图表是否出现等幅振荡先大幅降低 Kp再把 Ki 降为 0恢复到可收敛状态后逐步增加稳态误差一直存在积分项未生效、积分限幅过小、执行机构饱和检查 Ki 是否为零查看 I 列是否固定输出是否长期顶到上限
返回列表