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

资讯详情

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

增量式PID与位置式PID:从原理推导到单片机代码落地

增量式PID与位置式PID:从原理推导到单片机代码落地 简介这份PDF文档面向自动控制、嵌入式开发及单片机课程的学习者系统讲解增量式PID与位置式PID两种数字控制算法的原理与实现差异。内容从模拟PID控制器的理想化方程出发说明比例、积分、微分三个环节的独立调节作用再过渡到计算机采样控制下的离散化处理重点分析位置式算法需累加全部历史误差、占用内存大且计算效率低的问题以及增量式算法仅计算控制量变化量Δu(k)、只依赖最近几次采样值的优势并给出由Kp、Ti、Td推导离散参数q0、q1、q2的递推公式。资源包为1个PDF文件约336KB篇幅精炼、公式推导完整适合作为课程笔记或工程调试时的速查参考。目前已有1097人学习下载读者可借此理清两种算法的适用边界为步进电机控制等场景选型提供依据。1. 从模拟到数字为什么增量式PID在步进电机控制里更常见很多人第一次写PID代码直接照抄教科书上的位置式公式结果在步进电机或舵机项目里发现一旦传感器抖动或者中途需要切换手动/自动模式执行机构就会猛地一抖。问题不在参数而在算法结构本身。这份《增量式PID和位置式PID.pdf》把两种数字实现放在一起讲核心就一句话位置式输出的是控制量的绝对值 u(k)增量式输出的是控制量的变化量 Δu(k)。前者需要累加历史误差后者只跟最近三次采样有关。如果你在做单片机闭环调速、温度控制或者小车方向环搞清楚这两者的差分方程推导和适用边界比盲目调 Kp、Ki、Kd 有用得多。下面按「模拟方程离散化 → 位置式递推 → 增量式推导 → 参数换算 → 代码落地」的顺序拆一遍。2. 模拟PID离散化与位置式算法的差分方程推导2.1 模拟PID理想方程与三个参数的物理含义文档给出的理想化方程为 u(t) Kp·[e(t) (1/Ti)∫e(t)dt Td·de(t)/dt]其中 e(t) 是输入与反馈之差u(t) 是控制输出。Kp 决定当前误差的放大倍数Ti 决定历史误差累积的快慢Td 决定对误差变化趋势的抑制强度。原文特别强调“互不影响”改变 Kp 只影响比例作用不会直接改变积分或微分项的系数结构。这一点在整定参数时很关键——你可以先把 Ti 设得很大、Td 设为零单独调 Kp 看响应再逐步加入积分消除静差最后加微分抑制超调。2.2 用一阶差分和矩形积分把积分微分项离散化计算机是采样控制只能拿到 e(k)、e(k-1) 这些离散点。常见做法是微分项用一阶后向差分 de(t)/dt ≈ [e(k) - e(k-1)] / T积分项用矩形累加 ∫e(t)dt ≈ T·Σe(j)。代入后得到位置式的非递推形式u(k) Kp·e(k) Ki·Σe(j) Kd·[e(k) - e(k-1)]其中 Ki Kp·T/TiKd Kp·Td/T。这个公式直接算的话k 越大累加项越长内存和时间都吃不消。文档指出这正是要化成递推公式的原因。2.3 位置式递推公式只保留最近三次误差把 k 次和 k-1 次的输出相减累加项被消掉得到位置式递推形式u(k) u(k-1) q0·e(k) q1·e(k-1) q2·e(k-2)其中 q0 Kp(1 T/Ti Td/T)q1 -Kp(1 2Td/T)q2 Kp·Td/T。这样每次只需要 e(k)、e(k-1)、e(k-2) 和上一次输出 u(k-1)计算量固定。注意 u(k) 仍然是绝对控制量执行机构拿到的是“应该到哪个位置”所以叫位置式。对比项位置式PID增量式PID输出含义控制量绝对值 u(k)控制量增量 Δu(k)需要存储u(k-1) 及最近误差最近三次误差累加运算递推后无显式累加无累加切换冲击较大较小典型场景阀门、温度精确控制步进电机、舵机调速提示位置式递推公式里的 q0、q1、q2 是由 Kp、Ti、Td 和采样周期 T 共同决定的改 T 必须重新算这三个系数否则等效参数就变了。3. 增量式PID的推导与q0/q1/q2参数换算3.1 从位置式做差得到增量式差分方程增量式不输出 u(k)而是输出 Δu(k) u(k) - u(k-1)。对位置式递推公式两边做差直接得到Δu(k) Kp·[e(k) - e(k-1)] Ki·e(k) Kd·[e(k) - 2e(k-1) e(k-2)]整理成 q0、q1、q2 形式Δu(k) q0·e(k) q1·e(k-1) q2·e(k-2)其中 q0 Kp(1 T/Ti Td/T)q1 -Kp(1 2Td/T)q2 Kp·Td/T。注意这里的 q0、q1、q2 和位置式递推里的系数完全一致只是位置式还要加上 u(k-1)。文档里式(2-6)、(2-7)讲的就是这个换算关系已知模拟参数 Kp、Ti、Td在采样时间很短时可以直接算出离散系数。3.2 梯形积分逼近的变体与系数差异文档还给了梯形积分逼近的版本积分项用 [e(k) e(k-1)]·T/2 代替矩形累加。这时增量式系数会变成q0 Kp(1 T/(2Ti) Td/T) q1 -Kp(1 2Td/T - T/(2Ti)) q2 Kp·Td/T差别只在积分项的 T/(2Ti) 符号和系数上。实际工程里矩形积分够用梯形积分在采样周期较大时精度更好。我一般先在 MATLAB 或 Python 里用矩形版验证响应再决定要不要换梯形。3.3 参数换算的代码实现与验证下面这段 Python 用来从模拟参数算离散系数并模拟两种算法的输出差异# 从模拟PID参数换算增量式离散系数 def calc_coeff(Kp, Ti, Td, T): # 矩形积分版本 q0 Kp * (1 T/Ti Td/T) q1 -Kp * (1 2*Td/T) q2 Kp * Td/T return q0, q1, q2 # 增量式PID单步计算 def incremental_pid(q0, q1, q2, e_k, e_k1, e_k2): # 返回控制量增量需要外部累加到执行机构 delta_u q0*e_k q1*e_k1 q2*e_k2 return delta_u # 位置式PID单步计算递推形式 def positional_pid(u_prev, q0, q1, q2, e_k, e_k1, e_k2): # 直接返回绝对控制量 u_k u_prev q0*e_k q1*e_k1 q2*e_k2 return u_k # 示例Kp2.0, Ti0.5s, Td0.1s, 采样周期T0.01s q0, q1, q2 calc_coeff(2.0, 0.5, 0.1, 0.01) print(fq0{q0:.4f}, q1{q1:.4f}, q2{q2:.4f})逻辑说明calc_coeff把连续域参数映射到离散域incremental_pid只算增量调用方需要自己维护u delta_upositional_pid直接给出绝对值。参数 T 越小q0 越接近 Kp离散化误差越小。如果 T 大于 Ti 的十分之一建议改用梯形积分或重新整定。注意增量式算法里如果执行机构是步进电机Δu(k) 直接对应步数如果是 PWM 调速Δu(k) 对应占空比增量需要做限幅否则积分饱和时增量会累积到超出寄存器范围。4. 单片机上的代码落地与抗积分饱和处理4.1 位置式与增量式的C语言实现骨架在 STM32 或 51 上通常把两种算法写成独立函数用结构体保存状态。位置式需要保存u_prev增量式只需要保存最近三次误差typedef struct { float Kp, Ti, Td, T; float q0, q1, q2; float e_k, e_k1, e_k2; // 最近三次误差 float u_prev; // 位置式用上次输出 float u_max, u_min; // 输出限幅 } PID_t; // 增量式计算返回Δu float PID_Incremental(PID_t *pid, float setpoint, float feedback) { pid-e_k setpoint - feedback; float delta_u pid-q0 * pid-e_k pid-q1 * pid-e_k1 pid-q2 * pid-e_k2; // 更新误差历史 pid-e_k2 pid-e_k1; pid-e_k1 pid-e_k; return delta_u; } // 位置式计算返回绝对u float PID_Positional(PID_t *pid, float setpoint, float feedback) { pid-e_k setpoint - feedback; float u pid-u_prev pid-q0 * pid-e_k pid-q1 * pid-e_k1 pid-q2 * pid-e_k2; // 输出限幅同时做抗积分饱和 if (u pid-u_max) u pid-u_max; if (u pid-u_min) u pid-u_min; pid-u_prev u; pid-e_k2 pid-e_k1; pid-e_k1 pid-e_k; return u; }逻辑说明增量式函数返回 Δu调用方累加到电机步数或 PWM 寄存器位置式函数内部维护u_prev并做限幅。参数u_max、u_min根据执行机构量程设定比如 PWM 占空比 0~1000。限幅放在更新u_prev之前能避免积分项继续累积导致退饱和滞后。4.2 积分饱和与输出限幅的工程处理位置式最大的坑是积分饱和当设定值突变或反馈断开时误差长期存在累加项会涨到很大即使误差反向也要很久才能拉回来。常见做法是“遇限削弱积分”——一旦输出达到限幅值就停止累加或只累加反向误差。增量式天然没有显式累加但 Δu 连续同号也会让执行机构走到极限所以同样要对 Δu 做限幅或者对累加后的总量做限幅。问题现象位置式排查点增量式排查点响应超调大减小 Kp 或增大 Ti减小 q0 或增大 q1 绝对值静差消不掉检查积分项是否被限幅截断检查 Δu 是否长期为零切换时冲击切换前把 u_prev 同步为当前输出切换前清零 e_k1、e_k2高频抖动增大 Td 或加低通滤波增大 Td 或减小 q24.3 采样周期T的选择与参数整定顺序采样周期 T 直接进入 q0、q1、q2 的计算。T 太大离散化误差大微分项噪声放大T 太小CPU 负担重且量化误差占比高。常见经验是 T 取系统时间常数的 1/10 到 1/20电机调速里常用 1ms~10ms。整定顺序建议先令 Ti∞、Td0只调 Kp 到临界振荡然后减小 Ti 消除静差最后加 Td 抑制超调。每次改完参数用串口把设定值、反馈值、输出值打出来看曲线比盲调快得多。5. 用Python仿真对比两种算法的动态响应5.1 搭建一阶惯性对象仿真环境拿一个一阶惯性环节 G(s) 1/(Ts1) 当被控对象离散化后用差分迭代。下面代码同时跑位置式和增量式画阶跃响应import numpy as np import matplotlib.pyplot as plt # 一阶对象离散化y[k] a*y[k-1] b*u[k-1] T_obj 0.5 # 对象时间常数 T 0.01 # 采样周期 a T_obj / (T_obj T) b T / (T_obj T) def simulate(mode, Kp, Ti, Td, steps2000): q0 Kp * (1 T/Ti Td/T) q1 -Kp * (1 2*Td/T) q2 Kp * Td/T e_k e_k1 e_k2 0.0 u 0.0 y 0.0 ys, us [], [] for _ in range(steps): setpoint 1.0 e_k setpoint - y if mode incremental: delta_u q0*e_k q1*e_k1 q2*e_k2 u delta_u else: u u q0*e_k q1*e_k1 q2*e_k2 u max(0, min(1.5, u)) # 限幅 y a*y b*u e_k2, e_k1 e_k1, e_k ys.append(y); us.append(u) return ys, us ys_inc, us_inc simulate(incremental, 2.0, 0.5, 0.05) ys_pos, us_pos simulate(positional, 2.0, 0.5, 0.05) plt.plot(ys_inc, labelincremental) plt.plot(ys_pos, --, labelpositional) plt.legend(); plt.xlabel(sample); plt.ylabel(output) plt.show()逻辑说明simulate里两种模式共用同一组 q0、q1、q2唯一区别是增量式累加 Δu位置式直接算 u。限幅设 0~1.5观察饱和后的退饱和速度。跑出来会发现参数相同时两者稳态一致但增量式在设定值突变时冲击更小位置式在限幅后恢复更慢。5.2 观察切换冲击与抗干扰差异把设定值在第 1000 步从 1.0 跳到 0.5再在第 1500 步加一个反馈扰动对比两条曲线。增量式因为只输出变化量切换瞬间 Δu 不会突变位置式如果切换前 u_prev 没同步会直接跳变。这也是文档里说“增量式误差动作影响小、切换冲击小”的实际含义。仿真时把u_prev在切换点强制同步为当前输出位置式也能做到无扰切换但需要额外代码。5.3 从仿真曲线反推参数调整方向如果曲线上升太慢增大 Kp 或减小 Ti如果超调大增大 Td 或减小 Kp如果稳态有静差检查积分项是否被限幅截断。仿真里可以快速扫参数比如 Kp 从 1 到 5、Ti 从 0.1 到 1.0 做网格找超调小于 10% 且调节时间最短的组合。实际单片机里再用串口把数据传到 vofa 或类似上位机看实时曲线仿真和实测对照调参效率会高很多。本文还有配套的精品资源点击获取
返回列表