PID 详解

一、前言:为什么大家都在用 PID?

在工业控制和嵌入式领域,只要你做自动控制,几乎绕不开一个词:PID 控制器
从简单的温度控制、电机转速控制,到无人机姿态、自动驾驶方向盘转角,PID 都大显身手。

PID 有三个特点,让它成为“通吃”方案:

  1. 原理不复杂:数学模型相对简单,工程上好理解、好上手。

  2. 通用性强:只要能测量“误差”(目标 - 实际),就基本能上 PID。

  3. 实现成本低:不需要复杂的矩阵运算。

二、什么是 PID?

PID 控制器:根据当前误差、误差的历史累积、误差变化趋势,来计算控制量。

  • P:Proportional,比例

  • I:Integral,积分

  • D:Derivative,微分

如果用一句“人话”来类比:

你在开车追前面的车:

  • 离太远了(误差大)你就多踩油门 —— 比例

  • 长时间都离太远(误差一直存在)你会更着急,多踩一点 —— 积分

  • 发现距离正在快速缩小(误差变化很快)你会提前松油门或者踩刹车 —— 微分

PID 的目标是:快速追上目标,又尽量不要来回震荡

三、三大组成部分:P、I、D 各管啥?

1. P(比例):主力军 —— 快速响应

作用:

  • 误差越大,控制量越大

  • 提高 KpK_pKp​ 可以加快系统响应速度,使输出更快接近目标

缺点:

  • 如果只有 P(纯比例控制),系统往往存在稳态误差(例如电机转速总是比设定值低一点)

  • KpK_pKp​ 太大,会导致系统振荡、甚至不稳定

2. I(积分):强迫逼近目标 —— 消除稳态误差

作用:

  • 误差长期存在,就不断积累,推动控制量一直增大

  • 可以消除稳态误差(最终能精准逼近目标),非常适合温度、液位这类慢系统

缺点:

  1. 引入超调:积累太多,一旦误差变小,控制量来不及“刹车”,会冲过头

  2. 可能导致响应变慢:积分过强,系统“粘粘糊糊”的

  3. 会出现 积分饱和 / 积分风up

    • 比如控制电机占空比,已经打满 100%,但误差还在,积分还在累,之后误差变号,会导致系统长时间反向过冲

工程上通常要做 “积分限幅 / 抗积分饱和”

  • 限制积分项的最大最小值

  • 当输出已经到极限(比如 0% 或 100% 占空比)时,暂停积分

3. D(微分):提前预判 —— 减少超调&振荡

作用:

  • 看的是误差变化的“速度”

  • 如果误差在迅速减小(说明快到目标了),D 项会提前减小控制量,起到**“刹车”**的效果

  • 可以减少超调,改善系统稳定性,使响应更“干脆”

缺点:

  1. 对噪声极其敏感:

    • 传感器信号里只要有一点抖动,微分都会放大这种高频噪声

  2. 物理系统不可能真是“连续微分”,所以实现时一般会加滤波

常见工程写法:带滤波的微分

离散实现上就是给微分信号加一个一阶低通滤波器。

四、PID 参数对系统动态性能的影响

经典控制里经常看几个指标:

  • 上升时间(Rise time)

  • 超调量(Overshoot)

  • 调节时间 / 建立时间(Settling time)

  • 稳态误差(Steady-state error)

大致影响关系可以用一个表来记:

参数增大后的典型效果
KpK_pKp​上升更快;超调变大;振荡可能变强;稳态误差减小但不一定为 0
KiK_iKi​消除稳态误差;超调变大;响应变慢;容易振荡、可能不稳定
KdK_dKd​减少超调;改善稳定性;有时让响应更快;但太大容易放大噪声

调参时的常用“经验顺序”:

  1. 先调 P:从小到大加,直到系统能跟,稍有超调/振荡

  2. 再加 I:慢慢增加,刚好消除稳态误差即可

  3. 最后补一点 D:用来压超调、减振荡、小心不要太大

五、工程实现:位置式 vs 增量式 PID

1. 位置式 PID(直接输出目标值)

typedef struct {
    float Kp;
    float Ki;
    float Kd;

    float set;     // 目标值
    float fdb;     // 反馈值
    float err;     // 当前误差
    float err_last;// 上一次误差
    float err_sum; // 误差积分
    float out;     // 输出
} PID_t;

void PID_PositionCalc(PID_t *pid)
{
    pid->err = pid->set - pid->fdb;

    pid->err_sum += pid->err;
    // 积分限幅,防止积分饱和
    if (pid->err_sum > 1000) pid->err_sum = 1000;
    if (pid->err_sum < -1000) pid->err_sum = -1000;

    float P = pid->Kp * pid->err;
    float I = pid->Ki * pid->err_sum;
    float D = pid->Kd * (pid->err - pid->err_last);

    pid->out = P + I + D;

    // 输出限幅,例如 0~100% 占空比
    if (pid->out > 100) pid->out = 100;
    if (pid->out < 0)   pid->out = 0;

    pid->err_last = pid->err;
}

2. 增量式 PID(算“这次多加多少”)

思想:不直接算输出的绝对值,而是算相对上一次要加多少:

typedef struct {
    float Kp;
    float Ki;
    float Kd;

    float set;
    float fdb;

    float err;
    float err_last;
    float err_llast; // e[k-2]

    float out;
} PID_Inc_t;

void PID_IncrementCalc(PID_Inc_t *pid)
{
    pid->err = pid->set - pid->fdb;

    float delta_u = 0;
    delta_u += pid->Kp * (pid->err - pid->err_last);
    delta_u += pid->Ki * pid->err;
    delta_u += pid->Kd * (pid->err - 2 * pid->err_last + pid->err_llast);

    pid->out += delta_u;

    // 输出限幅
    if (pid->out > 100) pid->out = 100;
    if (pid->out < 0)   pid->out = 0;

    pid->err_llast = pid->err_last;
    pid->err_last  = pid->err;
}

六、总结

PID 控制虽然只由简单的三个参数组成,但背后包含了对系统当前状态、历史误差以及未来趋势的综合考量。
对工程师来说,掌握 PID 的本质,比死记公式更重要:

  • P 决定系统“敢不敢快”;

  • I 决定系统“能不能到”;

  • D 决定系统“稳不稳当”。
    理解了这三者的作用,再结合具体对象做一些实验,就能在项目中设计出既稳定又灵敏的控制系统。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱嵌入式的乌鸦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值