PID 算法的原理与应用 (通俗易懂)
文章目录
- PID简介
- PID 基本概念解析
- 开环与闭环控制(理解 PID 的基础)
- 1. 开环控制
- 2. 闭环控制
- PID 算法原理与公式
- 比例项(P)
- 稳态误差
- 🌟 什么是“自发偏移力”?
- 🤔 那稳态误差是怎么产生的呢?
- 🧠 类比理解
- ✅ 总结一句话:
- 积分项(I)
- 📚 积分项的比喻
- 1. 蓄水池模型
- 2. 驾驶汽车上坡
- 🎯 积分项如何工作?
- 🌟 总结
- 微分项(D)
- 连续 PID 公式离散化
- 位置式与增量式 PID 区别
- PID 调控的实现方法
- 1. 调控周期t的确定
- 2. 定时调控的 3 种实现方案(单片机)
- 位置式PID框架
- 增量式PID框架(内部累计输出全量)
- PID算法的改进
PID简介
PID 基本概念解析
- 名称含义
PID 是比例(Proportion)、积分(Integral)、微分(Derivative) 三个英文单词的缩写,因此 PID 控制即比例、积分、微分控制。- 无需深入掌握微积分知识,仅需理解其浅显概念即可。
- 核心任务(两种等价描述)
- 作为闭环控制算法:动态改变施加到被控对象的输出值,使被控对象某一物理量的实际值快速、准确、稳定地跟踪目标值(如电机定速旋转,无论负载变化,转速始终稳定在目标值)。
- 作为基于误差调控的算法:定义误差 = 目标值 - 实际值,任务是使误差始终为 0(本质与上一描述一致,即实际值 = 目标值)。
- 应用优势
- 对被控对象模型要求低,无需建模,即使内部运作规律不明确也能调控,应用简单。
- 虽无法应对特别复杂的系统,但在多数场景效果优异,学习性价比高。
开环与闭环控制(理解 PID 的基础)
1. 开环控制
- 特点:控制器仅单向输出值给被控对象,不获取反馈,无法知晓被控对象的实际执行状态。
- 局限:只能控制物理量的大致变化趋势(如 “输出越大,速度越快”),无法精准控制具体值,也不能根据负载、环境等变化动态调整输出(例如 PWM 驱动电机时,相同占空比可能因负载变化导致转速不同)。
- 示例:家用风扇的档位调速(只需 “档位越高转越快”,无需精确转速)。
2. 闭环控制
- 特点:控制器输出值的同时,通过传感器获取被控对象的实际值反馈,形成闭合环路,可根据反馈动态修改输出值以优化控制。
- 优势:能实现高精度控制,确保实际值稳定跟踪目标值。
- 示例
- 电机控速(通过编码器反馈转速,动态调整 PWM 占空比);
- 锅炉温控(温度传感器反馈实际温度,调整燃料供给);
- 汽车自动驾驶(摄像头反馈车道位置,调整方向盘角度);
- 火箭姿态控制(姿态传感器反馈角度,调整发动机喷射方向)。
PID 算法原理与公式
PID 输出值计算公式:
Output(t)=Kp×error(t)+Ki×∫0terror(τ)dτ+Kd×d(error(t))dt\text{Output}(t) = K_p \times \text{error}(t) + K_i \times \int_0^t \text{error}(\tau)d\tau + K_d \times \frac{d(\text{error}(t))}{dt} Output(t)=Kp×error(t)+Ki×∫0terror(τ)dτ+Kd×dtd(error(t))
- 变量说明:
- error(t):误差(目标值 - 实际值,随时间变化);
- Kp:比例系数,Ki:积分系数,Kd:微分系数(需根据需求调节的核心参数);
- 三项分别对应比例项(P)、积分项(I)、微分项(D),最终输出为三项之和。
- 公式本质
- 非严格推导的物理定律,而是经验公式,类似“水多了加面,面多了加水”的生活逻辑,通过实践验证有效。
比例项(P)
核心原理与作用
- 调节逻辑:比例项输出与当前误差(error)成正比,公式为
P = KP × error
。error = 目标值 - 实际值- 当实际值低于目标值(误差为正),输出正向调控力;实际值高于目标值(误差为负),输出反向调控力,实现负反馈。
- KP 的影响
- KP 越大,调控力度越强,系统响应越快,但超调量(实际值超过目标值的幅度)也越大,甚至可能导致系统自激震荡(不稳定)。
- KP 越小,响应越慢,但超调量小。
- 关键结论:比例项仅根据当前误差实时调节,无误差时输出为 0;需通过调试确定合适的 KP 值,平衡响应速度与稳定性。
由图中可以看到:设置值为100,KP 比例参数设置为0.25, 0.5,0.75,1。查看输出图像,发现kp越小,一开始的增长速度越慢,kp越大,稳态误差越小,响应越快,但是可以会发生超调。
稳态误差
- 定义:纯比例项控制时,系统达到稳态后,实际值与目标值始终存在稳定差值(如电机控速中实际速度始终低于目标值)。
- 产生原因:
- 被控对象存在 “自发偏移力”(如电机的摩擦力)。当误差为 0 时,比例项输出为 0(无驱动力),但自发偏移力会导致实际值偏离目标值,产生新误差;直到比例项输出的调控力与自发偏移力平衡,系统稳定,此时的差值即为稳态误差。
- 例:电机无持续驱动力时会因摩擦力减速,误差增大→比例项输出增大→驱动力与摩擦力平衡→稳态误差固定。
- 特性:
- KP 越大,稳态误差越小(调控力更强,更接近平衡),但无法通过无限增大 KP 消除误差(会导致超调或震荡)。
🌟 什么是“自发偏移力”?
想象你让一个小球停在一个平面上。如果平面是水平的,小球不会自己滚动,这就是没有“自发偏移力”的情况。
但如果平面稍微倾斜了一点,即使你不推它,小球也会因为重力自己往下滑。这种“不需要外力就会动”的趋势,就叫做“自发偏移力”。
在控制系统中,比如电机,摩擦力就是一种“自发偏移力”。即使你不给它加动力,它也会因为摩擦而慢慢停下来。
🤔 那稳态误差是怎么产生的呢?
假设你想让一个电机一直以每分钟100转的速度旋转:
- 一开始,控制器根据当前速度和目标速度之间的误差来决定输出多少功率;
- 当误差为0时(也就是电机刚好转得够快),比例项(P)的输出变成0;
- 但问题是:电机有摩擦力,它会慢慢减速;
- 一旦减速了,误差又出现了,比例项开始输出一点功率来“拉回”速度;
- 最后会达到一个平衡点:
- 控制器输出的功率 = 摩擦力消耗的能量;
- 此时电机转速略低于目标值,但稳定不变;
- 这个稳定的偏差,就叫稳态误差。
🧠 类比理解
你可以把这想象成你在推一辆带刹车的车:
- 你想让它匀速前进;
- 当车速刚好合适时,你松手不推了;
- 但是车的刹车会让它慢慢停下来;
- 所以你要一直轻轻推着,才能保持匀速;
- 如果你只按误差大小来决定推多大力气(比如误差越大力气越大),那最终你会找到一个“刚刚好能维持慢速前进”的力量;
- 但这时车还是比你想要的速度慢一点——这就是稳态误差。
✅ 总结一句话:
稳态误差就像你骑自行车上坡时不踩脚踏板,自行车会慢慢停下来,哪怕你只是想让它停在原地不动。因为有阻力存在,系统无法在零误差下维持平衡,最终只能在一个“略有误差但稳定”的状态停下来。
如果你用的是纯比例控制(只有P),那就会出现这个问题;要解决它,就需要加上积分项(I),它可以把“历史上的误差”积累起来,持续调整输出,从而消除这个稳态误差。这就是为什么PID控制器中的“I”这么重要
积分项(I)
1. 核心原理与作用
- PI 控制公式:
PI = KP×error + KI×∫error(误差的积分)
,需与比例项配合使用(单独使用积分项有缺陷)。 - 积分逻辑:积分项是历史所有误差的累积(程序中通过 “误差累加变量” 实现),输出与累积误差成正比。
- 例:每次调控时,将当前误差累加到总和中,再乘以 KI,作为积分项输出。
- 核心作用:弥补纯比例项的稳态误差。
- 若系统存在稳态误差,积分项会持续累积误差,使调控力逐渐增大,直到误差为 0;此时积分项的累积输出可维持与自发偏移力(如摩擦力)的平衡,无稳态误差。
2. 优缺点与特性
- 优势:彻底消除稳态误差(只要有误差就会累积调控力)。
- 弊端
- 滞后性:积分项依赖历史误差累积,对突变响应慢(如电机需反转时,需先抵消正向累积的力,再反向累积,导致滞后)。
- 参数影响:KI 越大,误差累积越快,稳态误差消除越快,但滞后性越强,可能导致系统不稳定(如平衡控制中易震荡)。
3. 与比例项的配合(PI 控制)
- 互补逻辑
- 比例项:反应快(仅依赖当前误差),负责快速响应误差;
- 积分项:消除稳态误差,弥补比例项的缺陷。
- 实验验证(电机定速控制)
- 纯积分项(KP=0,KI=0.05):无超调,但响应慢,需逐步累积力才能接近目标值。
- PI 配合(KP=0.25,KI=0.2):比例项快速提升实际值,积分项消除稳态误差,波形平滑,跟踪目标准确(参数合适时效果最优)。
- 参数过调(KP=0.5,KI=0.5):积分项累积过快,导致超调(实际值超过目标值),甚至震荡。
📚 积分项的比喻
1. 蓄水池模型
想象你正在试图用水泵将一个空的蓄水池填满到特定的水位(目标值)。由于水泵的功率是固定的,而进水量可能受到管道阻力、水源压力变化等因素的影响,导致实际水位很难精确达到设定的水位。
- 比例控制(P):就像你根据当前水位与目标水位之间的差距来调节水泵的开启程度。如果差距大,就开得更大;差距小,就开得小一些。
- 但是,即使水位接近目标值,因为有微小的误差存在,比如少量的漏水或者进水速度稍慢,水位总是差那么一点点无法完全达到目标值。
- 积分控制(I):这就像是你在旁边放置了一个记事本,每次发现水位低于目标值时,就在上面做一个标记。随着标记数量增加(即累积了过去的误差),你会逐渐意识到需要稍微加大水泵的功率,哪怕当前的误差很小,直到最终完全填满水池。
2. 驾驶汽车上坡
假设你要开车上一个缓坡,你的目标是保持恒定的速度行驶。
- 比例控制(P):当你发现车速下降时,你就加大油门;当车速接近目标速度时,减小油门。
- 但,如果你只依赖比例控制,可能会遇到这样的情况:车速总是略低于目标速度,因为你没有考虑到之前的减速累计效应。
- 积分控制(I):这就好比你在心里记下每一次车速低于目标速度的情况,然后逐步增加油门力度,即便当前的瞬时误差已经很小,但基于之前多次的小误差,你知道需要额外加点油才能保持目标速度。
🎯 积分项如何工作?
- 累积误差:积分项通过累加所有历史误差(通常是误差的时间积分),确保即使是很小但持续存在的误差也不会被忽略。
- 修正稳态误差:由于它可以补偿那些长期存在的小偏差,因此对于消除系统中的稳态误差特别有效。
- 避免过调:需要注意的是,积分项虽然能有效地减少或消除稳态误差,但如果积分系数 KI 设置过大,可能导致系统响应过于敏感,产生超调甚至振荡现象。
🌟 总结
积分项就像是一个“累积器”,它会记住并纠正那些比例项无法解决的小且持久的误差。通过不断地积累这些误差,并据此做出调整,使得系统能够更加准确地达到并维持在目标值附近。这种机制尤其适用于需要高精度控制的应用场景中。
微分项(D)
- 阻碍误差的急剧变化
- 当误差稳定不变(斜率为 0)时,D 项输出为 0;
- 当误差向某一方向变化时,D 项输出反向作用力阻碍该变化,且变化速度越快,阻碍力越大;
- 例:误差为 100(数值大)但恒定不变时,D 项无输出;误差快速增大时,D 项输出反向力抑制其增大。
- 预测未来并提前调控
误差变化的斜率可反映未来误差的变化趋势,因此 D 项能基于趋势提前施加调控,减少滞后。 - 增加系统阻尼,防止超调
- 对惯性较大的系统(如倒立摆、平衡车、四旋翼飞行器),D 项可增加阻尼,使震荡幅度逐渐减小(类似 “阻尼震荡”),避免因 PI 调节导致的持续震荡和超调。
- 例:倒立摆控制中,无 D 项时误差会不断扩大,加入 D 项后震荡逐步衰减,系统趋于稳定。
- Kd取值的影响
- Kd 越大,系统阻尼越强,抑制震荡的效果越好;
- 但 Kd 过大会导致 “卡顿”:D 项会过度阻碍 P 项和 I 项的调控,使被控对象运动不流畅。
连续 PID 公式离散化
- 离散化公式:连续 PID 公式离散后得到位置式 PID 公式。
- 各部分离散分析:P 项离散较简单,就是将时刻误差替换为第 k 次调控时刻误差;I 项积分离散是因为积分可近似为离散矩形面积之和,所以多了调控周期 t;D 项微分离散是用两点误差差除以调控周期 t 来近似切线斜率。
- 公式简化:可将调控周期 t 合并到 ki 和 kd 中,使公式更简洁,但修改调控周期 t 时,ki 和 kd 参数需对应更改。
位置式与增量式 PID 区别
- 位置式 PID:由连续形式 PID 直接离散得到,每次计算得到全量输出值,可直接给被控对象,适合大部分场景,可单独对误差积分进行积分限幅,但信号受噪声干扰时会使执行机构大幅度变化。
- 增量式 PID:由位置式 PID 推导得到,每次计算得到输出值增量,一般不能直接给被控对象,除非对象有积分功能;内部积分的增量式 PID 可通过控制器内积分输出全量,与位置式 PID 整体功能相同;可单独对输出值增量限幅,防止执行机构大幅度变化。
对比维度 | 位置式 PID | 增量式 PID(内部无积分) |
---|---|---|
输出内容 | 全量控制值 | 控制量增量 |
所需误差 | 累计误差 + 当前 / 上次误差 | 当前 / 上次 / 上上次误差 |
限幅处理 | 需分别对积分和输出限幅 | 仅需对增量限幅 |
适用场景 | 绝大多数场景(直接驱动执行机构) | 支持增量输入的执行机构(如阀门) |
PID 调控的实现方法
1. 调控周期t的确定
- 原则:根据被控对象变化速度选择:
- 快速对象(倒立摆、四轴):t=1−50m**s;
- 中速对象(电机控速):t=20−100m**s;
- 慢速对象(锅炉加热):几百几十秒。
- 限制:受硬件(如传感器刷新频率、编码器分辨率)影响,过快无意义。
2. 定时调控的 3 种实现方案(单片机)
方案 | 实现逻辑 | 优点 | 缺点 |
---|---|---|---|
1. delay 延时 | 主循环中执行 PID 后延时t | 实现最简单 | 周期不精确,阻塞主程序 |
2. 定时器中断 | 中断中直接执行 PID 调控 | 周期精确,不阻塞主程序 | 如果在主程序和中断中都对同一硬件进行数据的读取,可能引发硬件资源访问冲突 |
3. 定时器 + 标志位 | 中断置标志位,主循环检测标志位执行 PID | 无资源冲突 | 主程序阻塞时周期不精确 |
位置式PID框架
// 变量定义
float target, actual, out; // 目标值、实际值、输出值
float kp, ki, kd; // 比例、积分、微分系数
float error0, error1; // 本次误差、上次误差
float error_int; // 误差积分(累计误差) // 主函数
void main() { 初始化定时器(定时周期t); while(1) { target = 用户设定值; // 如通过按键、串口修改目标值 }
} // 定时器中断函数(核心逻辑)
void Timer_IRQ() { // 步骤1:获取实际值 actual = 读取传感器(); // 如读取编码器、温度传感器 // 步骤2:更新误差 error1 = error0; // 上次误差 = 之前的本次误差 error0 = target - actual; // 本次误差 = 目标值 - 实际值 // 步骤3:更新积分 error_int += error0; // 累计误差累加 // 步骤4:计算位置式输出 out = kp * error0 + ki * error_int + kd * (error0 - error1); // 步骤5:输出限幅 if(out > 上限) out = 上限; if(out < 下限) out = 下限; // 步骤6:控制执行机构 执行机构输出(out); // 如设置电机PWM、加热功率
}
增量式PID框架(内部累计输出全量)
// 变量定义
float target, actual, out; // 目标值、实际值、全量输出
float kp, ki, kd;
float error0, error1, error2; // 本次误差、上次误差、上上次误差 // 主函数(同位置式)
void main() { 初始化定时器; while(1) { target = 用户设定值; }
} // 定时器中断函数
void Timer_IRQ() { // 步骤1:获取实际值 actual = 读取传感器(); // 步骤2:更新误差(传递3次误差) error2 = error1; // 上上次误差 = 之前的上次误差 error1 = error0; // 上次误差 = 之前的本次误差 error0 = target - actual; // 步骤3:计算增量 float delta_out = kp*(error0 - error1) + ki*error0 + kd*(error0 - 2*error1 + error2); // 步骤4:累加增量得到全量输出 out += delta_out; // 步骤5:输出限幅 if(out > 上限) out = 上限; if(out < 下限) out = 下限; // 步骤6:控制执行机构 执行机构输出(out);
}