PID 控制算法 第一篇(基础篇)
- 控制算法
- 2025-11-09
- 721热度
- 0评论
PID 控制算法基础:从原理到嵌入式实现
PID 控制是自动控制领域中应用最广泛的算法,没有之一。从工业电机调速到家用空调温控,从无人机姿态稳定到 3D 打印机的温度管理,PID 的身影无处不在。本文是 PID 系列的第一篇(基础篇),将从零开始讲解 PID 的核心原理、离散化方法、代码实现和参数整定技巧。如果你已经掌握基础,可以直接阅读第二篇进阶篇,深入了解增量式 PID、抗积分饱和、串级控制等高级主题。
一、PID 是什么?
PID 是 Proportional-Integral-Derivative(比例-积分-微分)的缩写,是一种基于反馈的闭环控制算法。它的核心思想非常直观:根据目标值与实际输出值之间的误差,通过 P、I、D 三个环节协同计算出一个控制量,驱动被控对象朝目标值靠近。
1.1 典型应用场景
| 应用领域 | 被控量 | 控制量 |
|---|---|---|
| 直流电机调速 | 转速(RPM) | PWM 占空比 |
| 温度控制 | 温度(°C) | 加热器功率 |
| 压力控制 | 管道压力(kPa) | 阀门开度 |
| 伺服系统 | 位置 / 角度 | 电流指令 |
| 无人机姿态 | 俯仰 / 横滚角 | 电机转速差 |
1.2 闭环控制框图
误差 e(t) 控制量 u(t) 实际输出 y(t)
r(t) ──→ ⊕ ──→ [ PID 控制器 ] ──→ [ 执行器 + 被控对象 ] ──→──┬──→ y(t)
↑ - │
└────────────── [ 传感器(反馈) ] ◄──────────────────┘
整个控制过程形成一个闭环:传感器不断测量实际输出,PID 控制器根据误差计算控制量,执行器根据控制量作用于被控对象,循环往复直到误差趋近于零。
二、控制量 u(t) / u[k] 的物理意义
理解 PID 的关键,是搞清楚控制量 u(t) 到底代表什么。
2.1 连续 vs 离散
- 连续形式 u(t):理论分析中使用,时间 t 连续变化
- 离散形式 u\lbrack k\rbrack:嵌入式实现中使用,每隔采样周期 T 计算一次
2.2 控制量在不同场景中的物理含义
| 应用场景 | 控制量 u 的含义 | 取值范围示例 | 作用方式 |
|---|---|---|---|
| 直流电机调速 | PWM 占空比 | 0% ~ 100% | 占空比越大,电机转速越高 |
| 温度控制 | 加热功率(或 PWM) | 0 ~ 255(8-bit) | 功率越大,升温越快 |
| 压力控制 | 阀门开度 | 0° ~ 90° | 开度越大,流量越大 |
| 伺服系统 | 电流指令 | -10A ~ +10A | 电流驱动电机产生力矩 |
2.3 信号流向
目标值 r(t) 实际输出 y(t)
│ ↑
▼ │
误差 e(t) = r(t) - y(t) │
│ │
▼ │
┌────────────┐ 控制量 u(t) ┌──────────────┐ │
│ PID 控制器 │ ───────────────→ │ 执行器 + 对象 │ ──┘
└────────────┘ └──────────────┘
2.4 变量定义
| 符号 | 名称 | 含义 |
|---|---|---|
| r(t) | 目标值 / 设定值(Setpoint) | 期望的输出值 |
| y(t) | 实际输出(Process Variable) | 传感器测量到的当前值 |
| e(t) | 误差(Error) | e(t) = r(t) - y(t) |
| u(t) | 控制量(Control Output) | PID 计算得到的输出值 |
2.5 温控实例 —— 变量对应表
假设我们要将水温控制在 50°C:
| 通用符号 | 工程术语 | 本例中含义 | 当前值示例 |
|---|---|---|---|
| r(t) | SP(Setpoint) | 目标温度 | 50°C |
| y(t) | PV(Process Variable) | 当前水温 | 42°C |
| e(t) | Error | 温度偏差 | +8°C |
| u(t) | CO(Control Output) | 加热器 PWM 占空比 | 72% |
物理因果链:
PID输出 u(t)=72% → PWM占空比72% → 加热器功率↑ → 热量输入↑ → 水温上升 → y(t)→50°C
三、P、I、D 三环节详解
3.1 误差的定义
e(t) = r(t) - y(t)误差为正表示实际值低于目标值(不足),误差为负表示实际值超过目标值(超调)。
3.2 连续 PID 公式
u(t) = K_p \, e(t) + K_i \int_0^t e(\tau) \, d\tau + K_d \, \frac{de(t)}{dt}三项分别对应:比例项(P)、积分项(I)、微分项(D)。
3.3 三环节对比
| 环节 | 公式 | 作用 | 直观理解 | 缺点 |
|---|---|---|---|---|
| P(比例) | K_p \, e(t) | 按误差大小比例输出控制量 | "踩油门力度正比于偏差"——偏差大就猛踩,偏差小就轻踩 | 存在稳态误差,无法完全消除偏差 |
| I(积分) | K_i \int_0^t e(\tau) d\tau | 累积历史误差,消除稳态误差 | "记住过去所有偏差,慢慢补偿"——即使当前误差很小,只要累积不为零就持续输出 | 容易引起超调和振荡,积分饱和问题 |
| D(微分) | K_d \frac{de(t)}{dt} | 预测误差变化趋势,抑制超调 | "预判趋势,提前刹车"——误差快速减小时主动减小控制量 | 对噪声极其敏感,高频噪声被放大 |
3.4 参数增减对系统的影响
| 参数变化 | 响应速度 | 超调量 | 稳态误差 | 稳定性 |
|---|---|---|---|---|
| K_p 增大 | 加快 | 增大 | 减小(但不消除) | 降低 |
| K_p 减小 | 减慢 | 减小 | 增大 | 提高 |
| K_i 增大 | 略加快 | 明显增大 | 消除更快 | 降低 |
| K_i 减小 | 略减慢 | 减小 | 消除更慢 | 提高 |
| K_d 增大 | 略减慢 | 明显减小 | 几乎无影响 | 提高(过大则降低) |
| K_d 减小 | 略加快 | 增大 | 几乎无影响 | 降低 |
四、从连续到离散 —— 嵌入式实现基础
4.1 为什么要离散化?
在嵌入式系统中,传感器以固定周期采样,控制器以固定周期运算。我们无法处理连续时间信号,必须将连续 PID 公式转化为离散形式,让单片机能够"一步一步"地计算。
4.2 离散化基本概念
- 采样周期 T:两次计算之间的时间间隔(如 10ms)
- 第 k 次采样:时刻 t = kT 时的采样值
- 离散误差:e\lbrack k\rbrack = r\lbrack k\rbrack - y\lbrack k\rbrack
4.3 离散化近似
| 连续运算 | 离散近似方法 | 离散公式 |
|---|---|---|
| 积分 \int e \, dt | 矩形累加法 | \sum_{i=0}^{k} e\lbrack i\rbrack \cdot T |
| 微分 \frac{de}{dt} | 后向差分法 | \frac{e\lbrack k\rbrack - e\lbrack k-1\rbrack}{T} |
4.4 离散 PID 公式(位置式)
u\lbrack k\rbrack = K_p \, e\lbrack k\rbrack + K_i \sum_{i=0}^{k} e\lbrack i\rbrack \cdot T + K_d \, \frac{e\lbrack k\rbrack - e\lbrack k-1\rbrack}{T}展开来看各项的含义:
- 比例项:K_p \, e\lbrack k\rbrack —— 当前误差乘以比例系数
- 积分项:K_i \sum_{i=0}^{k} e\lbrack i\rbrack \cdot T —— 所有历史误差的累加和乘以采样周期
- 微分项:K_d \, \frac{e\lbrack k\rbrack - e\lbrack k-1\rbrack}{T} —— 当前误差与上一次误差的差值除以采样周期
4.5 两种实现形式
离散 PID 在代码层面有两种经典实现方式:
| 形式 | 特点 | 适用场景 |
|---|---|---|
| 位置式 PID | 直接输出控制量的绝对值 | 舵机位置控制、阀门开度控制 |
| 增量式 PID | 输出控制量的增量 \Delta u | 电机调速、步进电机控制 |
本篇先讲解位置式 PID 的实现,增量式 PID、抗积分饱和、微分滤波等进阶内容将在第二篇(进阶篇)中详细展开。
五、代码实现
5.1 C 语言实现(位置式 PID)
以下是一个适用于嵌入式平台的完整 PID 控制器实现:
/* ===== PID 控制器 - C 语言实现 ===== */
typedef struct {
float Kp, Ki, Kd; // PID 三个增益参数
float setpoint; // 目标值 r(t)
float integral; // 积分累积项
float prev_error; // 上一次误差(用于微分计算)
float output_max; // 输出上限(防止执行器饱和)
float output_min; // 输出下限
} PID_t;
/**
* @brief 初始化 PID 控制器
* @param pid PID 结构体指针
* @param kp/ki/kd 三个增益参数
* @param setpoint 目标值
*/
void PID_Init(PID_t *pid, float kp, float ki, float kd, float setpoint) {
pid->Kp = kp;
pid->Ki = ki;
pid->Kd = kd;
pid->setpoint = setpoint;
pid->integral = 0.0f;
pid->prev_error = 0.0f;
pid->output_max = 100.0f; // 默认输出范围 0~100
pid->output_min = 0.0f;
}
/**
* @brief PID 计算(每个采样周期调用一次)
* @param pid PID 结构体指针
* @param feedback 当前反馈值 y[k](传感器测量值)
* @param dt 采样周期 T(单位:秒)
* @return 控制量 u[k]
*/
float PID_Compute(PID_t *pid, float feedback, float dt) {
// 1. 计算误差
float error = pid->setpoint - feedback;
// 2. 积分累加(矩形近似)
pid->integral += error * dt;
// 3. 微分计算(后向差分)
float derivative = (error - pid->prev_error) / dt;
// 4. 合成 PID 输出
float output = pid->Kp * error
+ pid->Ki * pid->integral
+ pid->Kd * derivative;
// 5. 保存本次误差供下次微分使用
pid->prev_error = error;
// 6. 输出限幅(保护执行器)
if (output > pid->output_max) output = pid->output_max;
if (output < pid->output_min) output = pid->output_min;
return output;
}
1.
integral 变量在结构体中持续累加,体现了积分的"记忆"特性2.
prev_error 保存上次误差,用于差分近似微分3. 输出限幅是必须的——不限幅可能烧毁执行器或导致积分饱和
5.2 Python 实现
class PID:
"""
PID 控制器:P管现在,I管过去,D管未来
"""
def __init__(self, kp, ki, kd):
self.kp = kp
self.ki = ki
self.kd = kd
self.integral = 0.0
self.prev_error = 0.0
def compute(self, target, current, dt):
"""
计算 PID 输出
:param target: 目标值 r[k]
:param current: 当前反馈值 y[k]
:param dt: 采样周期 T(秒)
:return: 控制量 u[k]
"""
error = target - current # 误差
self.integral += error * dt # 积分累加
derivative = (error - self.prev_error) / dt # 微分差分
self.prev_error = error # 保存误差
return self.kp * error + self.ki * self.integral + self.kd * derivative
5.3 调用示例 —— 电机转速控制
# === 直流电机转速 PID 控制示例 ===
import time
pid = PID(kp=2.0, ki=0.5, kd=0.1)
target_rpm = 1000 # 目标转速
dt = 0.01 # 采样周期 10ms
for _ in range(500):
current_rpm = read_encoder() # 读取编码器测量转速
pwm_duty = pid.compute(target_rpm, current_rpm, dt)
pwm_duty = max(0, min(100, pwm_duty)) # 限幅 0~100%
set_motor_pwm(pwm_duty) # 设置 PWM 输出
time.sleep(dt)
六、实战:直流电机转速控制
6.1 控制目标
保持直流电机转速稳定在 1000 RPM,使用光电编码器测量实际转速,通过 PWM 驱动电机。
6.2 控制流程
┌─────────────────────────────────────────────────────────────────┐
│ 每隔 T=10ms 执行一次: │
│ │
│ ① 读取编码器 → y[k] = 820 RPM │
│ ② 计算误差 → e[k] = 1000 - 820 = 180 RPM │
│ ③ PID 计算 → u[k] = Kp·180 + Ki·Σe + Kd·Δe/T │
│ ④ 输出限幅 → u[k] = clamp(u[k], 0, 100) │
│ ⑤ 设置 PWM → PWM占空比 = u[k]% │
│ │
│ 等待下一个采样周期... │
└─────────────────────────────────────────────────────────────────┘
6.3 伪代码
PID_Init(&motor_pid, Kp=2.0, Ki=0.5, Kd=0.1, setpoint=1000)
while (系统运行) {
current_rpm = Encoder_ReadRPM() // 读取当前转速
pwm = PID_Compute(&motor_pid, current_rpm, 0.01) // 计算控制量
Motor_SetPWM(pwm) // 输出到电机驱动
Delay_ms(10) // 等待一个采样周期
}
6.4 响应过程分析
| 阶段 | 转速 y | 误差 e | P 项 | I 项 | D 项 | 控制量 u |
|---|---|---|---|---|---|---|
| 启动初期 | 0 RPM | +1000 | 大(猛加速) | 快速累积 | 大(误差剧变) | 接近上限 |
| 快速上升 | 600 RPM | +400 | 中等 | 继续累积 | 负值(减速趋势) | 中等偏高 |
| 接近目标 | 980 RPM | +20 | 很小 | 缓慢累积 | 负值(抑制超调) | 小幅调整 |
| 稳态运行 | 1000 RPM | ≈0 | ≈0 | 维持补偿 | ≈0 | 稳定值 |
七、参数整定方法
PID 参数整定是实际工程中最具挑战性的环节。好的参数能让系统快速、平稳地到达目标;差的参数会导致振荡、超调甚至系统失控。
7.1 常用整定方法对比
| 方法 | 原理 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 经验试凑法 | 手动调节,观察响应 | 简单直观,不需数学模型 | 依赖经验,耗时 | 简单系统、快速原型 |
| Ziegler-Nichols 法 | 基于阶跃响应特征参数 | 有公式可循,较系统化 | 需要开环阶跃响应数据 | 可获取阶跃响应的系统 |
| 临界比例法 | 先找临界振荡点,再计算参数 | 不需要数学模型 | 需让系统处于临界振荡(有风险) | 允许短暂振荡的系统 |
| MATLAB 自动调参 | 软件自动优化 | 最精确,可视化调参 | 需要系统模型或实测数据 | 复杂系统、精度要求高 |
7.2 经验试凑法 —— 快速整定步骤
第一步:调 P
将 Ki=0、Kd=0,逐渐增大 Kp。观察系统响应:
- Kp 太小 → 响应慢,误差大
- Kp 合适 → 响应较快,有轻微振荡
- Kp 太大 → 剧烈振荡,系统不稳定
选择"刚好出现轻微振荡"的 Kp 值,然后减小 20%~30% 作为最终 Kp。
第二步:调 I
保持 Kp 不变,Kd=0,从小到大增加 Ki:
- Ki 太小 → 稳态误差消除很慢
- Ki 合适 → 稳态误差在可接受时间内消除,轻微超调
- Ki 太大 → 超调严重,持续振荡
第三步:调 D
保持 Kp、Ki 不变,从小到大增加 Kd:
- Kd 合适 → 超调减小,响应更平稳
- Kd 太大 → 系统变得"迟钝"或出现高频抖动
7.3 观察与诊断
| 现象 | 可能原因 | 调整方向 |
|---|---|---|
| 响应慢,长时间达不到目标 | Kp 太小 | 增大 Kp |
| 到达目标但有持续偏差 | 缺少积分项或 Ki 太小 | 增大 Ki |
| 剧烈振荡,无法稳定 | Kp 或 Ki 过大 | 减小 Kp 和 Ki |
| 超调严重(冲过目标再回来) | Ki 过大或缺少微分 | 减小 Ki,增大 Kd |
| 高频抖动 / 输出不断跳变 | Kd 过大或传感器噪声 | 减小 Kd,增加滤波 |
| 升温/升速很快但回不来 | 积分饱和 | 添加积分限幅(进阶篇讲解) |
八、控制模式对比
实际工程中并不总是使用完整的 PID 三项,有时只用 P 或 PI 就够了。
| 控制模式 | 组成 | 优点 | 缺点 | 典型应用场景 |
|---|---|---|---|---|
| P 控制 | 仅比例项 | 结构简单,响应快 | 存在稳态误差 | 对精度要求不高的场景,如粗略调速 |
| PI 控制 | 比例 + 积分 | 消除稳态误差 | 可能超调,响应略慢 | 温度控制、流量控制等大多数工业场景 |
| PD 控制 | 比例 + 微分 | 响应快,超调小 | 无法消除稳态误差 | 快速跟踪场景,如机器人关节 |
| PID 控制 | 比例 + 积分 + 微分 | 综合性能最优 | 参数整定复杂 | 精度和动态性能都要求高的场景 |
- 如果允许一定偏差且系统简单 → 用 P 控制
- 如果必须消除稳态误差(大多数场景) → 至少用 PI 控制
- 如果超调敏感或需要快速响应 → 加上 D 项,用完整 PID
- 工业界 80% 以上的控制回路使用 PI 控制即可满足需求
九、基础问答
Q1:PID 三个参数各自的作用是什么?如何整定?
**P(比例)**:按当前误差比例输出控制量,Kp 越大响应越快但容易振荡。
**I(积分)**:累积历史误差,消除稳态偏差,Ki 越大消除越快但容易超调。
**D(微分)**:预测误差变化趋势,抑制超调和振荡,Kd 越大减振效果越强但对噪声敏感。
整定方法:经验试凑法"先 P 后 I 再 D"。先将 Ki=Kd=0 单独调 P 到轻微振荡后减小 20%~30%;再加 I 消除稳态误差;最后加 D 减小超调。也可以使用 Ziegler-Nichols 法或 MATLAB 自动调参。
Q2:PID 控制的本质是什么?u(t) 代表什么?
PID 控制的本质是**基于误差的反馈控制**——根据目标值与实际输出之间的偏差,计算应该施加多大的控制作用。
u(t) 是**控制量**,代表 PID 输出给执行器的指令。它不是目标值本身,而是实现目标的"力度"。例如在电机控制中 u(t) 是 PWM 占空比,在温控中 u(t) 是加热器功率。u(t) 通过执行器转化为物理作用,改变被控对象的状态。
Q3:离散化 PID 为什么需要固定采样周期?
离散 PID 中,积分使用累加近似(\sum e\lbrack i\rbrack \cdot T),微分使用差分近似(\frac{e\lbrack k\rbrack - e\lbrack k-1\rbrack}{T})。如果采样周期 T 不固定:
1. **积分失真**:每次累加的时间权重不同,积分值不准确
2. **微分失真**:相同的误差变化量在不同 T 下计算出不同的变化率
3. **参数失效**:Ki 和 Kd 本质上与 T 耦合,T 变化等于参数在变化
因此嵌入式实现中通常使用定时器中断确保 PID 在严格固定的周期内执行。
Q4:P 控制和 PI 控制的区别?什么时候必须加 I?
**P 控制**只有比例项,输出正比于当前误差。当系统存在外部扰动或负载时,P 控制会产生**稳态误差**——系统稳定后偏差不为零。这是因为一旦误差为零,P 项输出也为零,无法维持控制作用。
**PI 控制**增加了积分项。即使当前误差很小,只要累积误差不为零,积分项就会持续输出控制量,最终把稳态误差"积"到零。
**必须加 I 的场景**:
- 系统有恒定外部扰动(如电机带负载,需要持续力矩维持转速)
- 对稳态精度有要求(如温度必须精确控制在 ±0.5°C 以内)
- 被控对象本身不含积分环节(如大多数热系统、流量系统)
Q5:PID 有哪些局限性?什么场景下 PID 不适用?
PID 的局限性:
1. **依赖线性假设**:PID 本质是线性控制器,对强非线性系统效果差
2. **单输入单输出**:标准 PID 只处理一个误差、一个输出,多变量耦合系统需要更复杂的方案
3. **无模型预测能力**:PID 是"事后反应",不能提前预知扰动
4. **参数固定**:一组参数只在特定工作点附近最优,工况大范围变化时需要自适应或增益调度
不适用的场景:纯滞后很大的系统(如长管道温度控制)、强非线性系统(如化学反应釜)、多变量耦合系统。这些场景通常需要模型预测控制(MPC)、模糊控制或自适应控制等高级方法。
十、总结
本篇作为 PID 系列的基础篇,覆盖了从理论到实现的核心内容:
- PID 的本质:基于误差的闭环反馈控制,通过 P(比例)、I(积分)、D(微分)三项协同工作
- 控制量 u(t):PID 唯一的输出产品,是施加给执行器的"控制力度",不是目标值本身
- 三环节分工:P 管现在(按误差比例响应)、I 管过去(累积消除稳态误差)、D 管未来(预判趋势抑制超调)
- 离散化要点:积分用累加、微分用差分、采样周期必须固定
- 代码实现:结构体封装参数,每个周期调用一次计算函数,注意输出限幅
- 参数整定:先 P 后 I 再 D,根据响应现象诊断并调整
本文为 PID 控制系列第 1 篇(共 2 篇)。