位置式 PID、增量式 PID、PI、PD控制和前馈控制简谈
一、什么是 PID
在过程控制中,PID 控制器,一直是应用最为广泛的一种自动控制器。PID 控制也一直是众多控制方法中应用最为普遍的控制算法。它不依赖复杂的数学模型,仅仅通过三个参数 (比例、积分和微分) 即可调节出效果较好控制效果。
更具象和通俗一点,对于 PID 来说,我们配置的比例、积分和微分是常量。而我们需要给 PID 控制器输入目标值和实际测量值,这样 PID 控制器就可以输出控制值。之后再采集实际测量值反复循环这个过程。

比如:你开车时,眼睛看路(传感器的捕捉的当前值),大脑对比车道(目标值),手调方向盘(PID计算控制量)。
如果车偏左了,PID会让你往右打方向盘,直到车回到车道中央。

二、位置式 PID

2.2 P 比例
比例项的物理意义是将当前的误差值乘一个系数。
如果仅使用比例控制,那么我们的 PID 公式是这样的:
= 比例系数
= 目标值和当前的值的差,也就是误差值
2.2.1 比例环仿真
在 PID Controller Simulator 平台可以仿真 PID 参数
当我们调节参数为 P = 3、I = 0、D = 0
可见实际值并不能贴合到目标值上,而且产生了一个静态误差。

虚线:目标值;粉线:实际值;绿线:PID 调节输出值
当我们调节参数为 P = 5、I = 0、D = 0
可见目标值又开始震荡了,所以只调整比例不能实现我们的控制。

虚线:目标值;粉线:实际值;绿线:PID 调节输出值
2.3 I 积分
积分项的物理意义是将一段时间内的误差累加起来。
如果仅使用积分控制,那么我们的 PID 公式是这样的:
= 积分系数
= 累加某一时间段的所有误差
2.3.1 积分环仿真
当我们调节参数为 P = 3、I = 0.03、D = 0
可见在一段时间后系统趋近于贴合目标值,但是还是有一定的震荡

虚线:目标值;粉线:实际值;绿线:PID 调节输出值
2.4 D 微分
微分项的物理意义是计算误差的微分。
如果仅使用微分控制,那么我们的 PID 公式是这样的:
= 微分系数
= 误差变化率
2.4.1 微分环仿真
当我们调节参数为 P = 2.5、I = 0.02、D = 12
震荡的稳态误差也消失了,此时系统比较符合我们的要求

虚线:目标值;粉线:实际值;绿线:PID 调节输出值
2.5 完整的 PID 公式
简单的将、积分和微分相加即构成完整的 PID 公式:
2.5 离散化
离散化(Discretization)是指将连续时间系统(如模拟信号、微分方程)转换为离散时间系统(如数字信号、差分方程)的过程。
对于计算机和微控制器 (MCU) 来说,因为只能处理离散时间的数据(即按固定时间间隔采样),并不能完整的处理连续时间系统,无法直接计算积分或微分。因此必须将连续的PID控制算法转换为离散形式。
2.6 离散化的 PID 公式
比例项不变,同时将积分项转化为累加形式并且乘时间 T,微分也简单的变成了本次误差减去上次误差除时间 T。通过离散化处理,这样计算机和 MCU 就可以计算 PID 了。
2.6 离散化公式的代码形式
下面代码中的 error、error_sum 和 derivative,分别对应离散形式的比例项,积分项和微分项。
因为我们简单的看作每次采样和计算时间间隔都是相当的,也就 T 恒等于 1。所以在 C 语言代码中直接省略 T 这个变量。
// 简易位置式PID计算(使用static变量保存状态)
float PID_Calculate(float target, float actual, float Kp, float Ki, float Kd)
{
static float error_sum = 0; // 积分项(自动保持)
static float last_error = 0; // 上次误差(自动保持)
float error = target - actual;
error_sum += error; // 累积误差(积分项)
float derivative = error - last_error; // 误差变化率(微分项)
// PID输出 = 比例 + 积分 + 微分
float output = Kp * error + Ki * error_sum + Kd * derivative;
last_error = error; // 保存本次误差供下次使用
return output;
}
三、增量式 PID
增量 PID,就是每次的比例、积分和微分求和后并不是直接输出的结果,而是上次的增量。
增量式其中的积分只累加三次,不会造成积分饱和的过冲,这样也会让响应快一些。
控制框图如下:

3.1 增量式 PID 推导
这是我们的离散 PID 公式:
那么上一次输出的 PID 应该是这样的,所有 K 减 1 即可:
此时,我们把
减去
其结果应该是本次位置式 PID 输出值减去上次位置式 PID 输出的值,换句话说,也就是本次的控制量的增量(Δu),即 “这一步要比上一步多调整多少”。
结果如下:
= 本次的控制量的增量
最终输出只需要:
两者相加即可,也就是上次输出的值和本次的增量相加
3.2 增量式 PID 代码
如果没看懂公式的推导,结合代码阅读即可:
/**
* @brief 增量式PID控制器
* @param setpoint 目标设定值(如目标温度、速度等)
* @param actual 当前实际测量值(来自传感器)
* @param Kp 比例系数(决定对当前误差的反应强度)
* @param Ki 积分系数(需已乘以采样周期T,用于消除稳态误差)
* @param Kd 微分系数(需已除以采样周期T,用于抑制振荡)
* @return 计算得到的控制量输出(如PWM占空比、电机电压等)
*/
float Incremental_PID(float setpoint, float actual, float Kp, float Ki, float Kd)
{
// 静态变量声明(保持函数调用间的历史数据)
static float e_prev = 0; // 保存上一次的误差值 e(k-1)
static float e_prev2 = 0; // 保存上上次的误差值 e(k-2)
static float u_prev = 0; // 保存上一次的控制量输出 u(k-1)
// 计算当前误差 = 目标值 - 实际值
float e = setpoint - actual;
// 计算控制增量 Δu(k):
// 比例项:Kp*[e(k)-e(k-1)] —— 响应误差变化量
// 积分项:Ki*e(k) —— 消除当前误差(注意Ki已含采样周期T)
// 微分项:Kd*[e(k)-2e(k-1)+e(k-2)] —— 抑制误差波动(二阶差分)
float delta_u = Kp * (e - e_prev) // 比例项计算
+ Ki * e // 积分项计算
+ Kd * (e - 2 * e_prev + e_prev2); // 微分项计算
// 计算当前控制量 = 上一次控制量 + 本次增量
float u = u_prev + delta_u;
// 更新历史数据(为下一次计算做准备):
e_prev2 = e_prev; // 将e(k-1)存入e(k-2)(下次变为e(k-2))
e_prev = e; // 将当前误差e(k)存入e(k-1)
u_prev = u; // 保存当前控制量供下次使用
// 返回当前计算的控制量
return u;
}
四、PI 和 PD 控制
4.1 稳态误差下的两个概念
稳态误差(Steady-state Error)是自动控制系统在进入稳定状态后,期望输出与实际输出之间的固定偏差。它是衡量系统控制精度的重要指标,反映了系统长期运行时的准确性。
4.1.1 静态误差
静态误差是动态误差函数在 时的极限值。并不是实际误差函数的一部分。也就是说,当时间趋近于无穷后,实际值和目标值仍有一个固定的偏差。
比如:你希望水池内水的高度是10cm,于是你对水龙头进行了控制(把水龙头阀门打开到一定程度),经过足够长的时间,水池内的水高度不不变了,到达了9cm,于是你控制的静态就是1cm。
在 PID 控制中,I 积分项主要用于消除掉这个静态误差。
4.1.2 动态误差
动态误差是一个关于时间的函数,记录了水池内水的高度和目标高度之间的差值(误差)随时间的变化。
4.2 PD 控制器

PD控制就是比例 + 微分控制,在系统稳定后能快速预测未来的变化并做出响应。
特点是快速性和稳定性。微分相当于一个阻力,能有效抑制系统本身产生的震荡。缺点是有静差和放大噪声,当偏差很小时 P 和 D 基本不起作用所以会有静差。噪声与系统自身调节产生的震荡是不同的,噪声一般是高频的干扰。噪声会在输出端影响系统然后反馈到输入端,由于微分控制是有一个系数的,当系数乘上噪声后,可以想象到噪声是被放大了。噪声的放大效果取决于微分系数,如果系统的微分系数很大那就要注意噪声的干扰了。
平衡小车的直立控制就需要用到 PD 控制。当角度接近机械零度时,P控制器就不工作了,控制器认为完成了任务,然而控制器忽略了一种对下一个时刻的预测,即角度在机械零度时电机还存在角速度,简单来说就是小车还有惯性。这时候就需要加入 D 控制器,预测下一时刻的偏差。
对于平衡小车,倒立摆来说,静差对于直立的影响其实并不大,也没有必要一直让平衡车保持机械中值(平衡车,倒立摆在位置不动的情况下也会自身轻微摆动保持平衡)。所以不是很依赖于积分项。视情况会使用 PD 来控制。
4.3 PI 控制器

以伺服为例,PID 的 D 分量作为微分,可以加快动态响应,就类似开车的时候提前预判马上到人行道了,提前踩刹车,防止冲到人行道上,就和抑制过冲类似。
但是在实际的离散控制系统,微分很容易放大噪声,更加建议加前馈,而不是加微分,前馈也可以加快动态响应,但是不会对噪声进行放大。伺服中,位置环,电流环,速度环都可以加前馈。在变频空调常用的单转子压缩机的抑制振荡中,也是通过给速度环加前馈的方式解决。
五、前馈控制
5.1 前馈控制简谈
通过比例 (P)、积分 (I)、微分 (D) 三个环节对系统误差进行反馈调节,适用于消除稳态误差和抑制扰动,但存在响应滞后的问题。同时在之前分析到,微分项在一些情况还会引发震荡,所以 PI + 控制能够解决我们大部分的问题。
所谓前馈控制,就是如下图所示,仅根据目标值输出一个前馈值,与 PI 相加得出输出值。也就是前馈环节根据参考信号或可测扰动(如进料流量、送风量等)生成补偿量,与 PI 输出的校正量叠加,形成总控制量。
前馈控制快速抵消主要扰动的影响,而 PI 处理剩余误差和未建模动态,两者结合可同时提升响应速度和稳态精度。

5.2 前馈控制代码解析
在这里只举例较为简单的目标微分前馈控制。
前馈补偿的核心思想是通过预测系统输入 (目标值) 的变化趋势,提前生成控制量以抵消潜在的动态误差。微分项(Δtarget = target - last_target)反映了目标值的瞬时变化率,类似于微分控制的“超前校正”特性。这种设计能够快速响应设定值的突变,减少系统因惯性导致的跟踪滞后。

核心代码如下:
// 前馈计算:补偿目标值的变化量(Δtarget = target - last_target)
float feedforward = Kff * (target - last_target);
全部代码如下:
/**
* @brief 带前馈补偿的位置式PID控制器
* @param target 目标设定值(如目标位置、温度)
* @param actual 当前实际测量值
* @param Kp 比例系数
* @param Ki 积分系数
* @param Kff 前馈增益(根据系统模型或实验调整)
* @return 控制量输出(如PWM占空比、电机电压)
*/
float Feedforward_PID(float target, float actual, float Kp, float Ki, float Kff)
{
// 静态变量声明(保存历史状态)
static float error_sum = 0; // 积分项累积
static float last_error = 0; // 上一次误差
static float last_target = 0; // 上一次目标值(用于前馈计算)
// 计算当前误差
float error = target - actual;
// PID计算
error_sum += error; // 积分项累积
float pid_output = Kp * error + Ki * error_sum;
// 前馈计算:补偿目标值的变化量(Δtarget = target - last_target)
float feedforward = Kff * (target - last_target);
// 总控制量 = PID输出 + 前馈补偿
float output = pid_output + feedforward;
// 更新历史数据
last_error = error;
last_target = target;
return output;
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)