1. 什么是 PID 控制
1.1 自动控制的核心问题:设定值 vs 反馈值
自动控制解决一个根本问题:让某个物理量(温度、速度、位置、压力……)维持在期望值。
两个关键概念:
- 设定值(Setpoint,SV):你期望系统达到的目标值,记作 。
- 反馈值(Process Variable,PV):传感器实测到的当前值,记作 。
两者之差即是误差:
控制器的工作就是根据 计算出控制量(Manipulated Variable,MV) ,驱动执行机构(加热器、电机、阀门……)让 趋近 。
┌─────────┐ r(t) ┌──────────┐ u(t) ┌─────────┐ y(t)设定 ──→│ 比较器 │──→ e(t) ──→│ PID控制器 │────→│ 执行器 │──→│ 被控对象 │──→ 输出 └─────────┘ └──────────┘ └─────────┘ └─────────┘ ↑ │ └─────────────────── 传感器反馈 ─────────────────────────┘1.2 PID 的直觉理解
洗澡调水温
你站在淋浴下,手拧混水阀。水太冷 —— 左手往热水方向拧。太烫 —— 往冷水方向拧。拧动的幅度取决于”当前温度和期望温度的差距”。差距大,拧猛一点(比例 P)。
但光这样不行:调到 38°C 后,冷水管水压波动,温度掉到 36°C。2°C 的偏差太小,你拧一点,温度回到 37.8°C,差 0.2°C 就不再动了 —— 这就是稳态误差。
你想”已经偏冷好一会儿了”,于是再多拧一点来补偿这持续存在的偏差 —— 这就是积分 I 在起作用:累积过去的误差,把残余偏差消灭。
水突然变烫,你预判温度还在往上冲,提前往冷水方向拧一下防止烫伤 —— 这就是微分 D:根据误差变化速率提前抑制,防止过冲。
| 场景 | 对应项 | 直觉 |
|---|---|---|
| 偏差越大,拧得越猛 | P(比例) | 看现在 |
| 偏差持续存在,越拧越多 | I(积分) | 看过去 |
| 温度快速上升,提前回调 | D(微分) | 看未来 |
定速巡航
设定 100 km/h。当前 80 km/h,误差 20。ECU 加大油门(P)。到 95 km/h 后误差变小,油门也变小,但最终可能稳定在 98 km/h,差 2 km/h 永远消不掉(稳态误差)。积分项感知到”有 2 km/h 偏差一直存在”,逐渐累积额外的油门补偿,最终推到 100 km/h。前方上坡,车速即将下降,微分项感知到误差变化速率,提前加油防止掉速。
1.3 适用系统与局限性
PID 擅长的场景:
- 线性时不变(LTI)系统:系统的动态特性不随时间变化,且输出与输入成比例叠加。
- 低阶系统(一阶、二阶):温度、液位、压力、流量、速度等大多数工业过程可以用一阶惯性 + 纯滞后(FOPDT)描述,PID 处理得很好。
- 有明确反馈信号:传感器能提供可靠、足够快的测量值。
PID 面临的挑战:
| 系统特性 | 问题 | 应对思路 |
|---|---|---|
| 大时滞(纯滞后时间长) | 控制作用要等很久才看到效果,P 和 D 会过度反应导致振荡 | Smith 预估器、串级控制 |
| 高阶系统(>3 阶) | 相位滞后大,PID 增益不能太高,响应慢 | 降阶建模、串级 PID |
| 强非线性 | 增益在不同工作点差异巨大 | 增益调度(Gain Scheduling)、模糊 PID、MPC |
| 强耦合多变量 | 一个输入影响多个输出 | 解耦控制、MPC |
| 对噪声极度敏感 | D 项放大高频噪声 | 不完全微分、微分先行、舍弃 D 只用 PI |
经验法则:工业控制中 90%+ 的闭环回路仍然使用 PID 或其变体。当你遇到一个控制问题时,先尝试 PI(多数场景),不够再加 D。
2. PID 的数学原理
2.1 标准数学形式
PID 控制器的连续时间表达式:
或等效的平行形式(工程中更常用):
其中:
| 符号 | 名称 | 单位 |
|---|---|---|
| 比例增益 | 无量纲(或控制量/误差量量纲) | |
| 积分增益 | ||
| 微分增益 | ||
| 积分时间常数 | ||
| 微分时间常数 |
传递函数形式:
频域视角能更清晰地看到:P 是全频段等比例放大,I 在低频段(稳态)贡献巨大增益,D 在高频段放大噪声。
2.2 三项各自的作用
比例项(P):即时反应
- 物理意义:误差越大,控制量越大。像一根弹簧,拉力与伸长量成正比。
- 单独使用的问题:对纯比例控制,系统稳定后必须有 维持控制量,这意味着 ——稳态误差。
- 增大:响应加快、稳态误差减小,但过大会引起振荡甚至不稳定。
为什么纯 P 控制存在稳态误差? 以加热器为例,环境在散热。系统稳定时,加热功率必须等于散热功率。纯比例控制下,要维持非零的加热功率,误差 也必然非零。只有积分项能”记住”历史偏差,持续修正。
积分项(I):消除稳态误差
- 物理意义:只要误差存在,积分项就不断累积,直到误差归零。像水龙头慢慢拧:偏差一直存在,积分就一直在加力,直到偏差消失。
- 副作用:积分累积有滞后性。当误差穿越零时,积分值可能已经累积过头,造成超调和振荡。
- 增大:稳态误差消除更快,但超调增大,振荡倾向增强。
微分项(D):预判未来
- 物理意义:根据误差变化的斜率提前干预。误差快速增大时,D 施加反向力抑制变化;误差快速减小时,D 施加同向力防止回调过头。
- 典型作用:增加系统阻尼,减小超调,缩短调节时间。
- 致命弱点:对高频噪声敏感。白噪声经过微分后幅度随频率线性增长,可能放大到淹没有用信号的程度。
三项总结:
| P | I | D | |
|---|---|---|---|
| 作用时间 | 现在 | 过去 | 未来 |
| 对稳态误差 | 减小但不消除 | 消除 | 无直接影响 |
| 对稳定性 | ↑ → 稳定性↓ | ↑ → 稳定性↓ | ↑ → 稳定性↑(适度) |
| 对响应速度 | 加快 | 可能减慢(取饱和方向) | 影响小 |
| 对噪声 | 不敏感 | 不敏感 | 敏感 |
| 对超调 | 增大 | 增大 | 减小 |
3. 离散化与计算机实现
3.1 位置式 PID 与增量式 PID
计算机只能处理离散采样值。设采样周期为 ,第 次采样时的误差为 。
位置式 PID
直接对连续公式离散化:
积分用矩形法近似:
微分用后向差分近似:
代入得:
特点:输出的是控制量的绝对值 。每次计算需要累积所有历史误差。如果有积分饱和或手动切换, 可能跳变。
增量式 PID
对 求一阶差分 :
最终输出:
增量式在工程上更常用的原因:
- 天然抗饱和:只输出增量,不累积历史。可对 限幅,实现简单的抗积分饱和。
- 无扰切换:手动切自动时, 从当前值平滑过渡,消除冲击(bumpless transfer)。
- 算量更小:只需保存最近三次误差 ,无需累加全部历史。
- 安全性好:控制器故障时输出保持最后值,不会跳变到危险状态。
注意:位置式适用于需要直接计算绝对控制量的场景(如某些阀门需要绝对开度)。增量式适用于步进电机、数字增量输出等场景。实际工程中增量式占绝大多数。
3.2 采样周期的选择
香农定理要求采样频率 (信号最高频率的两倍)。实际控制中这个标准太宽松 —— 为了可靠跟踪动态过程,工程经验要求:
其中 是闭环系统的带宽频率(近似为闭环阶跃响应的主要频率成分)。
经验法则对照表:
| 被控对象类型 | 典型时间常数 | 推荐采样周期 |
|---|---|---|
| 流量 | 1 ~ 3 s | 0.1 ~ 0.5 s |
| 液位 | 5 ~ 30 s | 0.5 ~ 3 s |
| 压力 | 1 ~ 10 s | 0.2 ~ 1 s |
| 温度 | 60 ~ 600 s | 5 ~ 60 s |
| 电机电流环 | ~1 ms | 50 ~ 200 μs |
| 电机速度环 | ~10 ms | 1 ~ 5 ms |
| 电机位置环 | ~100 ms | 10 ~ 50 ms |
实用判断依据:
- 采样期内系统动态变化不超过期望精度的 1/5 ~ 1/10。
- 采样周期应远小于系统的主导时间常数()。
- 如果传感器噪声大,采样周期可以适当拉长来获得天然滤波效果。
- 如果采样太快(远快于系统动态), 会趋近于噪声,D 项严重恶化。
3.3 伪代码实现
C 语言增量式 PID
typedef struct { float Kp, Ki, Kd; // PID 增益 float T; // 采样周期(秒) float integral_limit; // 积分限幅 float output_limit; // 输出限幅 float prev_error; // e[k-1] float prev_prev_error; // e[k-2] float integral; // 积分累积(位置式用) float output; // 当前输出 u[k]} PIDController;
void PID_Init(PIDController *pid, float Kp, float Ki, float Kd, float T) { pid->Kp = Kp; pid->Ki = Ki; pid->Kd = Kd; pid->T = T; pid->integral_limit = 100.0f; pid->output_limit = 100.0f; pid->prev_error = 0.0f; pid->prev_prev_error = 0.0f; pid->integral = 0.0f; pid->output = 0.0f;}
float PID_UpdateIncremental(PIDController *pid, float setpoint, float measurement) { float error = setpoint - measurement;
// 增量式 PID 公式 float p_term = pid->Kp * (error - pid->prev_error); float i_term = pid->Ki * pid->T * error; float d_term = (pid->Kd / pid->T) * (error - 2.0f * pid->prev_error + pid->prev_prev_error);
float delta_u = p_term + i_term + d_term;
// 输出限幅(简单抗饱和) float new_output = pid->output + delta_u; if (new_output > pid->output_limit) { delta_u = pid->output_limit - pid->output; } else if (new_output < -pid->output_limit) { delta_u = -pid->output_limit - pid->output; }
pid->output += delta_u; pid->prev_prev_error = pid->prev_error; pid->prev_error = error;
return pid->output;}Python 增量式 PID
from dataclasses import dataclass
@dataclassclass PIDConfig: Kp: float = 1.0 Ki: float = 0.0 Kd: float = 0.0 T: float = 0.01 # 采样周期 10ms out_max: float = 100.0 out_min: float = -100.0
class IncrementalPID: def __init__(self, cfg: PIDConfig): self.cfg = cfg self.prev_error = 0.0 self.prev_prev_error = 0.0 self.output = 0.0
def update(self, setpoint: float, measurement: float) -> float: error = setpoint - measurement
p_delta = self.cfg.Kp * (error - self.prev_error) i_delta = self.cfg.Ki * self.cfg.T * error d_delta = (self.cfg.Kd / self.cfg.T) * ( error - 2.0 * self.prev_error + self.prev_prev_error )
delta_u = p_delta + i_delta + d_delta new_output = self.output + delta_u
# 输出限幅 new_output = max(self.cfg.out_min, min(self.cfg.out_max, new_output)) delta_u = new_output - self.output
self.output = new_output self.prev_prev_error = self.prev_error self.prev_error = error
return self.output4. 参数整定
4.1 经验试凑法(先 P 后 I 再 D)
这是最实用、最通用的人工整定方法,不需要系统模型。
步骤:
- 置零 I 和 D(),从较小的 开始增大。
- 每次增大 后施加阶跃信号,观察响应。 增大到出现临界振荡(不衰减的等幅振荡),记下此时的 为 ,振荡周期为 。
- 将 降回 的 60% ~ 70% 作为工作点。
- 加入 I:从较小的 开始,逐步增大,直到稳态误差在可接受时间内消除。注意超调会增大。
- 加入 D(如需要):从较小的 开始,逐步增大,抑制 I 带来的超调和振荡。如果噪声明显恶化,减小 或直接弃用 D。
调参口诀(定性指导):
| 现象 | 可能的参数问题 | 调整方向 |
|---|---|---|
| 响应太慢 | 太小 | 增大 |
| 超调量大 | 太大 或 太大 | 减小 / |
| 振荡不衰减 | 太大 或 太大 | 减小 / |
| 稳态误差长时间不消失 | 太小 | 增大 |
| 围绕设定值等幅振荡 | 太大 | 减小 |
| 噪声大、执行器抖动 | 太大 | 减小 |
| 超调后长时间不收敛(缓慢衰减) | 缺少 D | 适当加入 |
4.2 Ziegler-Nichols 整定法
方法一:临界比例度法(闭环)
- 。
- 增大 直到输出出现等幅振荡(临界稳定),记录此时的比例增益 和振荡周期 。
- 按下表换算:
| 控制器类型 | |||
|---|---|---|---|
| P | — | — | |
| PI | — | ||
| PID |
其中 ,。
注意:Z-N 整定的 PID 参数通常超调偏大(~25%),追求快速响应。工业应用中往往需要在此基础上适当调小 。
方法二:响应曲线法(开环)
- 断开闭环,手动施加一个阶跃输入 。
- 记录输出响应曲线,做切线找到拐点,测得:
- 等效滞后时间
- 等效时间常数
- 静态增益
- 按下表换算:
| 控制器类型 | |||
|---|---|---|---|
| P | — | — | |
| PI | — | ||
| PID |
4.3 常见整定问题速查
| 问题 | 根因 | 解决 |
|---|---|---|
| 响应慢,无明显超调 | 和 都偏小 | 增大 ,再调 |
| 超调后衰减很慢(1~2 个周期后仍大幅波动) | 太大 | 减小 ,或适当增大 |
| 输出高频抖动 | 太大 或 传感器噪声大 | 减小 ,或给传感器加低通滤波 |
| 启动时超调巨大 | 积分饱和(见 5.1) | 加抗饱和措施 |
| 设定值改变后跟踪很慢 | 太小 | 增大 |
| 负载扰动后恢复很慢 | 太小 | 增大 |
| 多个周期等幅振荡 | 系统临界稳定 | 减小 至少 20% |
5. 工程优化与抗积分饱和
5.1 积分饱和的产生与危害
什么是积分饱和?
当执行器达到物理极限(阀门全开/全关、电机最大转速、加热器最大功率)时,误差 仍然存在。积分项继续累积,导致 远超物理限制。当误差符号反转时,积分值需要很长时间才能”退回到”有效范围,期间控制器输出仍保持饱和 —— 表现为严重的超调和长时间振荡。
典型场景:加热器从室温加热到 80°C。设定值为 80°C,当前 25°C,误差 55°C。加热器全功率运行(饱和)。PID 输出可能在积分累积下达到 150%(实际物理极限 100%)。当温度到达 80°C 时,积分项仍需很长时间才能降到 100% 以下,导致严重超调。
5.2 常用抗饱和方法
积分分离(Integral Separation)
当误差大于阈值时,关闭积分项;误差进入阈值范围内才开启积分。
#define INTEGRAL_SEPARATION_THRESHOLD 20.0f
float error = setpoint - measurement;float i_term = 0.0f;
if (fabsf(error) < INTEGRAL_SEPARATION_THRESHOLD) { pid->integral += error * pid->T; i_term = pid->Ki * pid->integral;}优点:简单有效,避免大偏差时积分累积。 缺点:阈值设定依赖经验,阈值附近可能有轻微跳变。
遇限削弱积分法(Clamping / 条件积分)
当输出已饱和且误差与控制量同向时,停止积分累积。这是工程中使用最广泛的抗饱和方法。
float PID_UpdateClamping(PIDController *pid, float setpoint, float measurement) { float error = setpoint - measurement;
// 条件积分:只有当输出未饱和 或 误差方向使输出退出饱和时才积分 bool output_high_saturated = (pid->output >= pid->output_limit) && (error > 0); bool output_low_saturated = (pid->output <= -pid->output_limit) && (error < 0);
if (!output_high_saturated && !output_low_saturated) { pid->integral += error * pid->T; }
float p_term = pid->Kp * error; float i_term = pid->Ki * pid->integral; float d_term = (pid->Kd / pid->T) * (error - pid->prev_error);
float u = p_term + i_term + d_term; // 输出限幅 if (u > pid->output_limit) u = pid->output_limit; if (u < -pid->output_limit) u = -pid->output_limit;
pid->output = u; pid->prev_error = error; return u;}原理:只有在”输出还能跟随计算值”的时候才积分。如果输出已饱和且误差还在把输出推向更饱和的方向,积分停掉。
变速积分(Variable-Speed Integration)
积分速度随误差大小变化:误差大时积分慢,误差小时积分快。
float error_abs = fabsf(error);float speed;
if (error_abs > 50.0f) { speed = 0.0f; // 误差大,不积分} else if (error_abs > 20.0f) { speed = 0.3f; // 中等误差,慢速积分} else if (error_abs > 5.0f) { speed = 0.7f; // 较小误差,接近正常积分} else { speed = 1.0f; // 小误差,全速积分}
pid->integral += speed * error * pid->T;5.3 微分项的改进
不完全微分(加入低通滤波器)
标准微分项 是一个理想微分器,对高频噪声极其敏感。实际工程中使用不完全微分:
其中 , 通常在 3 ~ 20 之间。这是一阶低通滤波器 + 微分的组合。
时域实现(后向欧拉离散化):
// alpha = T / (T + tau_f) —— 滤波系数,典型值 0.05 ~ 0.3float alpha = pid->T / (pid->T + tau_f);float raw_derivative = (error - pid->prev_error) / pid->T;pid->filtered_derivative = (1.0f - alpha) * pid->filtered_derivative + alpha * raw_derivative;float d_term = pid->Kd * pid->filtered_derivative;效果:滤除高频噪声成分,微分输出更平滑,执行器寿命大大延长。
微分先行(Derivative on Measurement)
标准 PID 对误差微分:。当设定值 发生阶跃变化时, 瞬间跳变,微分项产生巨大尖峰(称为微分冲击)。
微分先行对反馈值 微分而非误差:
因为 ,当设定值恒定时 ,两者等价。但当设定值跳变时,微分先行避免了冲击。
// 微分先行:对测量值微分float d_term = -(pid->Kd / pid->T) * (measurement - pid->prev_measurement);一句话选择指南:如果设定值频繁阶跃变化 —— 使用微分先行。如果跟踪斜坡/正弦设定值 —— 标准微分也可以。
6. 性能评估与调优
6.1 时域性能指标
对阶跃响应进行分析,四个核心指标:
y(t) ↑ │ ┌──── 超调量 ────┐ │ ╱│ ╲ │ ╱ │ ╲___________ 稳态值 │ ╱ │ │ │╱ │ │ │ │ │──┼────┼──────────────────┼──────────→ t │ │← 上升时间 →│ │ │ │←── 调节时间 ──────→│ │ │ (进入±2%误差带) │| 指标 | 定义 | 期望 |
|---|---|---|
| 上升时间 | 响应从 10% 上升到 90% 稳态值的时间 | 小 |
| 超调量 | 峰值与稳态值之差,占稳态值的百分比 | 小(< 10%) |
| 调节时间 | 响应进入 ±2%(或 ±5%)误差带且不再超出的时间 | 小 |
| 稳态误差 | 无穷时间后的残余误差 | 0 |
参数对指标的影响方向:
| 增大参数 | 上升时间 | 超调量 | 调节时间 | 稳态误差 |
|---|---|---|---|---|
| ↓ 减小 | ↑ 增大 | 小幅变化 | ↓ 减小 | |
| ↓ 小幅减小 | ↑ 增大 | ↑ 增大 | 消除 | |
| 小幅变化 | ↓ 减小 | ↓ 减小 | 无影响 |
工程权衡:不存在同时让所有指标最优的参数。快速响应和低超调是矛盾的。通常先确保稳定性,再在超调可接受的前提下尽量减小上升时间和调节时间。
6.2 参数自整定简介
继电反馈自整定(Relay Feedback Autotuning)
1984 年由 Åström 和 Hägglund 提出。用继电环节(Bang-Bang 控制)替代 PID 控制器,使系统产生小幅等幅振荡,从中辨识临界增益 和临界周期 ,再用 Ziegler-Nichols 表换算 PID 参数。
这是目前工业界最常用的自适应整定方式,被 Siemens、ABB、Honeywell 等厂商广泛采用。
基于模型的自整定
通过在线系统辨识(如递推最小二乘法 RLS)实时估计系统的 FOPDT 模型参数,然后用 IMC(Internal Model Control)等方法计算 PID 参数。需要一定计算能力,但在 DCS 和高端 PLC 中已普及。
6.3 与其他控制算法的比较
| PID | 模糊控制 (Fuzzy) | MPC | |
|---|---|---|---|
| 依赖模型 | 不需要 | 不需要 | 需要模型 |
| 处理非线性 | 差(需增益调度) | 好 | 好 |
| 处理约束 | 困难(仅输出限幅) | 无天然约束处理 | 天然处理约束 |
| 处理多变量耦合 | 困难 | 困难 | 天然解耦 |
| 计算复杂度 | 极低 | 低 | 高 |
| 可解释性 | 好 | 好 | 中 |
| 参数整定 | 成熟(Z-N 等) | 依赖经验规则 | 需要建模技能 |
| 适用场景 | 绝大多数工业回路 | 非线性强、难建模 | 多变量、有约束、慢过程 |
选型建议:
- 90% 的场合:PID(或 PI)足矣。
- 强非线性且难以建模:模糊 PID / 模糊控制。
- 多变量、有约束、慢过程(化工、石化):MPC。
- 对实时性要求极高且系统模型已知:考虑前馈 + PID。
7. 实战案例
7.1 温度控制系统(FOPDT 模型整定)
场景:电阻加热炉,目标温度 200°C,环境温度 25°C,加热功率 0~100% PWM。
系统特性:温度控制典型的一阶惯性 + 纯滞后(FOPDT)系统:
建模(开环阶跃响应法):
- 施加 50% 加热功率,记录温度上升曲线。
- 测得: °C/%, s, s。
整定(响应曲线法):
- s →
- s →
实现要点:
- 采样周期 s()。
- 必须使用抗积分饱和(加热器只能 0~100%)。
- 微分项使用不完全微分抑制传感器噪声。
- 加热器初始冷态时误差巨大(175°C),强烈推荐积分分离。
Python 仿真代码(见附录 8.1)。
7.2 电机速度 / 位置控制(串级 PID)
结构:电流环(内环)→ 速度环(中环)→ 位置环(外环)
位置设定 ──→ [位置PID] ──→ [速度PID] ──→ [电流PID] ──→ [PWM / 逆变器] ──→ [电机] ↑ ↑ ↑ 位置反馈(编码器) 速度反馈(编码器差分/测速发电机) 电流反馈(霍尔/shunt)| 环路 | 带宽(典型) | 周期 | 作用 |
|---|---|---|---|
| 电流环 | 1 ~ 2 kHz | 50 ~ 100 μs | 控制绕组电流,提供快速力矩响应 |
| 速度环 | 100 ~ 300 Hz | 1 ~ 5 ms | 控制转速,抑制负载扰动 |
| 位置环 | 10 ~ 50 Hz | 10 ~ 50 ms | 控制角度/位置,跟踪轨迹 |
整定顺序(由内而外):
- 先调电流环( 大、 中、 小或零),确保电流响应快且不振荡。
- 电流环整好后视为一阶环节,再调速度环。速度环通常使用 PI( 会放大编码器量化噪声)。
- 位置环最外层,通常只用 P(或 P + 小 I)。
FOC(磁场定向控制)中 PID 的特殊性:
FOC 将三相电流变换到旋转 dq 坐标系下,(励磁)和 (转矩)分别由两个独立的 PID 控制。此时被控对象近似为纯阻感负载,可以整定得很快。
7.3 四轴飞行器姿态控制(双闭环 PID)
结构:角度环(外环)→ 角速度环(内环)
目标角度 ──→ [角度PID] ──→ [角速度PID] ──→ [混控器] ──→ [电机/电调] ──→ [机体] (roll/pitch/yaw) ↑ ↑ 姿态解算(IMU + 互补/卡尔曼滤波) ← 陀螺仪 + 加速度计 + 磁力计| 环路 | 频率 | 传感器 | 作用 |
|---|---|---|---|
| 角速度环(内环) | 1 ~ 8 kHz | 陀螺仪 | 抑制扰动,提供阻尼,响应极快 |
| 角度环(外环) | 200 ~ 500 Hz | 加速度计 + 陀螺仪融合 | 跟踪目标姿态角 |
为什么无人机必须用双闭环(串级)?
单靠角度环控制响应太慢,风扰或电机转速突变时无法及时补偿。角速度环直接感知机体瞬时角速度变化,在角度明显偏离之前就产生修正力矩,是无人机抗风性和稳定的关键。
典型参数参考(穿越机 5 寸,Betaflight 经验值):
| 轴 | 角度环 P | 角度环 I | 角速度环 P | 角速度环 I | 角速度环 D |
|---|---|---|---|---|---|
| Roll | 40 | 40 | 45 | 45 | 28 |
| Pitch | 42 | 42 | 48 | 48 | 30 |
| Yaw | 40 | 40 | 60 | 50 | 0 |
整定过程:
- 先调角速度环: 和 置零,增大 到出现微振(高频抖动),退回 70%。
- 加入 (消除角速度稳态误差),(抑制过冲和噪声)。
- 角速度环整好后,调角度环:只用 P( 很小或零,避免姿态恢复时超调引起振荡)。
- 实际飞行测试:快速拨动摇杆,观察姿态跟踪有无过冲和振荡。用黑盒日志(Blackbox Log)分析频响。
D 项的低通滤波至关重要:陀螺仪噪声经过 D 项放大后会直接耦合到电机输出,表现为电机发热、抖动,甚至烧毁。Betaflight 默认使用两阶动态低通滤波器 + 动态 notch 滤波处理陀螺仪信号。
8. 附录
8.1 完整实现代码
C 语言抗饱和增量式 PID(完整版)
#include <math.h>#include <stdbool.h>
typedef struct { // 参数 float Kp, Ki, Kd; // PID 增益 float T; // 采样周期 (s) float tau_f; // 不完全微分滤波时间常数 (s)
// 限幅 float out_max, out_min; float integ_limit;
// 状态 float prev_error; // e[k-1] float prev_prev_error; // e[k-2] float prev_measurement; // y[k-1] (微分先行用) float integral; // 积分累积 float filtered_derivative; // 不完全微分滤波值 float output; // 当前输出 u[k]
// 功能开关 bool use_clamping; // 遇限削弱积分 bool use_deriv_on_meas; // 微分先行 bool use_filtered_deriv; // 不完全微分 bool use_integ_separation; // 积分分离 float integ_sep_threshold; // 积分分离阈值} PIDController;
void PID_Init(PIDController *p, float Kp, float Ki, float Kd, float T) { p->Kp = Kp; p->Ki = Ki; p->Kd = Kd; p->T = T; p->tau_f = Kd / (Kp * 10.0f); // N = 10 p->out_max = 100.0f; p->out_min = -100.0f; p->integ_limit = 100.0f; p->prev_error = 0.0f; p->prev_prev_error = 0.0f; p->prev_measurement = 0.0f; p->integral = 0.0f; p->filtered_derivative = 0.0f; p->output = 0.0f; p->use_clamping = true; p->use_deriv_on_meas = false; p->use_filtered_deriv = false; p->use_integ_separation = true; p->integ_sep_threshold = 30.0f;}
float PID_Update(PIDController *p, float setpoint, float measurement) { float error = setpoint - measurement;
// ---- 比例项 ---- float p_term = p->Kp * error;
// ---- 积分项(带抗饱和) ---- bool sat_high = (p->output >= p->out_max) && (error > 0); bool sat_low = (p->output <= p->out_min) && (error < 0); bool integrate = true;
if (p->use_clamping && (sat_high || sat_low)) { integrate = false; } if (p->use_integ_separation && fabsf(error) > p->integ_sep_threshold) { integrate = false; }
if (integrate) { p->integral += error * p->T; // 积分限幅 if (p->integral > p->integ_limit) p->integral = p->integ_limit; if (p->integral < -p->integ_limit) p->integral = -p->integ_limit; } float i_term = p->Ki * p->integral;
// ---- 微分项 ---- float d_term = 0.0f; float derivative_input;
if (p->use_deriv_on_meas) { // 微分先行:对测量值微分 derivative_input = -(measurement - p->prev_measurement) / p->T; } else { // 标准:对误差微分 derivative_input = (error - p->prev_error) / p->T; }
if (p->use_filtered_deriv) { // 不完全微分(一阶低通滤波) float alpha = p->T / (p->T + p->tau_f); p->filtered_derivative = (1.0f - alpha) * p->filtered_derivative + alpha * derivative_input; d_term = p->Kd * p->filtered_derivative; } else { d_term = p->Kd * derivative_input; }
// ---- 合成 ---- float u = p_term + i_term + d_term;
// 输出限幅 if (u > p->out_max) u = p->out_max; if (u < p->out_min) u = p->out_min;
p->output = u; p->prev_prev_error = p->prev_error; p->prev_error = error; p->prev_measurement = measurement;
return u;}Python 温度控制系统仿真
import numpy as npimport matplotlib.pyplot as pltfrom dataclasses import dataclass, field
@dataclassclass FOPDTPlant: """一阶惯性 + 纯滞后被控对象""" gain: float # K tau: float # 时间常数 (s) delay: float # 纯滞后 (s) y: float = 25.0 # 当前输出 (初始环境温度) _delay_buffer: list = field(default_factory=list)
def __post_init__(self): self._delay_buffer = [self.y] * max(1, int(self.delay / 0.1))
def update(self, u: float, dt: float) -> float: # 一阶惯性:tau * dy/dt + y = K * u dy = (self.gain * u - self.y) / self.tau * dt self.y += dy self._delay_buffer.append(self.y) if len(self._delay_buffer) > max(1, int(self.delay / dt)): self._delay_buffer.pop(0) return self._delay_buffer[0]
class PID: def __init__(self, Kp, Ki, Kd, T, out_max=100, out_min=0): self.Kp, self.Ki, self.Kd, self.T = Kp, Ki, Kd, T self.out_max, self.out_min = out_max, out_min self.prev_error = 0.0 self.prev_prev_error = 0.0 self.integral = 0.0 self.output = 0.0
def update(self, sp, pv): error = sp - pv
# 增量式 p_delta = self.Kp * (error - self.prev_error) i_delta = self.Ki * self.T * error d_delta = (self.Kd / self.T) * ( error - 2 * self.prev_error + self.prev_prev_error ) delta = p_delta + i_delta + d_delta
new_out = self.output + delta
# 抗饱和:遇限削弱积分 clamped = max(self.out_min, min(self.out_max, new_out)) if clamped != new_out: self.integral -= (new_out - clamped) * 0.1 # 反算抗饱和
self.output = clamped self.prev_prev_error = self.prev_error self.prev_error = error return self.output
# 仿真参数K, tau, L = 3.5, 180.0, 30.0plant = FOPDTPlant(K, tau, L, y=25.0)
Kp, Ki, Kd = 2.06, 0.034, 30.9T = 3.0pid = PID(Kp, Ki, Kd, T, out_max=100, out_min=0)
setpoint = 200.0t_total = 1200 # 20 分钟t = np.arange(0, t_total, T)y_hist, u_hist, sp_hist = [], [], []
for _ in t: pv = plant.update(pid.output, T) pid.update(setpoint, pv) y_hist.append(pv) u_hist.append(pid.output) sp_hist.append(setpoint)
# 绘图fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 6), sharex=True)ax1.plot(t, sp_hist, 'k--', label='Setpoint')ax1.plot(t, y_hist, 'b-', label='PV (Temperature)')ax1.set_ylabel('Temperature (°C)')ax1.legend(); ax1.grid(True)
ax2.plot(t, u_hist, 'r-', label='Control Output')ax2.set_ylabel('Output (%)')ax2.set_xlabel('Time (s)')ax2.legend(); ax2.grid(True)plt.tight_layout()plt.show()8.2 在线仿真工具推荐
| 工具 | 类型 | 特点 |
|---|---|---|
| PID Tuner (MATLAB/Simulink) | 商业 | 最强大的 PID 整定工具,自动线性化 + 参数优化 |
| Python Control Systems Library | 开源 | control.pid() + step_response() 快速原型验证 |
| Scipy signal | 开源 | 低层控制工具箱,配合 NumPy 使用 |
| KiCad + ngspice | 开源 | 模拟电路 PID 的 SPICE 仿真 |
| WebPID | 在线 | 无需安装的在线 PID 仿真,适合教学演示 |
| Betaflight Configurator | 开源 | 无人机 PID 整定专用,含黑盒分析 |
8.3 推荐阅读经典论文与书籍
书籍:
- Åström, K. J. & Hägglund, T. (2006). Advanced PID Control. ISA. — PID 领域的”圣经”,涵盖结构、整定、实现、自适应。
- Åström, K. J. & Murray, R. M. (2020). Feedback Systems: An Introduction for Scientists and Engineers. Princeton. — 从零开始的反馈控制入门。
- 刘金琨 (2016). 先进 PID 控制 MATLAB 仿真(第 4 版). 电子工业出版社. — 中文领域最好的 PID 工程参考书,大量 MATLAB 仿真实例。
论文:
- Ziegler, J. G. & Nichols, N. B. (1942). “Optimum Settings for Automatic Controllers.” Transactions of the ASME, 64, 759-768. — Z-N 整定的原始论文。
- Åström, K. J. & Hägglund, T. (1984). “Automatic Tuning of Simple Regulators with Specifications on Phase and Amplitude Margins.” Automatica, 20(5), 645-651. — 继电反馈自整定的开创性工作。
- Rivera, D. E., Morari, M. & Skogestad, S. (1986). “Internal Model Control. 4. PID Controller Design.” Industrial & Engineering Chemistry Process Design and Development, 25(1), 252-265. — IMC-PID 设计方法。
- Skogestad, S. (2003). “Simple Analytic Rules for Model Reduction and PID Controller Tuning.” Journal of Process Control, 13(4), 291-309. — SIMC 整定规则,目前工业界最实用的基于模型的整定方法。
从纸上公式到实际系统:PID 算法是控制工程的基石。掌握它不仅仅是背下三条公式,而是理解每一项如何影响闭环性能,知道什么场景用什么改进,以及能根据实际响应曲线判断参数调整方向。希望这份指南能帮助你从”会用 PID”走向”理解 PID”再走向”驾驭 PID”。