3. 数字控制器:PID控制器的基本原理,位置式与增量式PID的区别
说到数字电源的环路控制,PID 绝对是绕不开的核心。很多刚入行的工程师觉得 PID 就是调三个系数,没什么技术含量。说实话,我早年也这么想过,直到在一个 48V 转 1V 的 DC-DC 项目上栽了跟头——空载纹波 20mV,一带载直接振荡,调了三天三夜才发现是积分项饱和的问题。从那以后,我再也不敢小看 PID 了。
这一节,咱们就把 PID 的基本原理和两种数字实现方式——位置式与增量式——彻底讲透。你想想看,搞懂了这些,后面调环路参数才有底气。
3.1 PID 控制器的基本原理
PID 控制器,说白了就是根据「过去、现在、未来」三个维度的误差来调整输出。
- 比例项(P):看现在的误差,误差大就猛调,误差小就微调。反应最快,但光靠它会有静差。
- 积分项(I):看过去的误差累积,专门消灭静差。但调太强容易超调,调太弱响应慢。
- 微分项(D):预测未来的误差变化趋势,提前刹车或加速。能抑制超调,但对噪声敏感。
连续域的 PID 公式长这样:
u(t) = Kp * e(t) + Ki * ∫e(τ)dτ + Kd * de(t)/dt
但在数字电源里,我们用的是离散域。把连续积分和微分换成累加和差分,就得到了数字 PID 的基础形式。
核心要点:数字 PID 的本质就是用采样周期 Ts 把连续时间切成离散点,在每个点上计算误差,然后加权求和。Ts 越小,越接近模拟 PID,但计算负担也越大。
3.2 位置式 PID
位置式 PID 是最直观的数字实现方式。它直接计算当前时刻应该输出的控制量绝对值。
离散公式如下:
u[k] = Kp * e[k] + Ki * Σe[i] + Kd * (e[k] - e[k-1])
其中:
- u[k]:第 k 个采样周期的输出控制量(比如占空比)
- e[k]:第 k 个采样周期的误差
- Σe[i]:从启动到当前的所有误差累加和
优点:
- 物理意义清晰,调试时容易理解每个项的作用
- 输出直接对应执行机构的绝对位置(比如 PWM 占空比)
缺点:
- 积分项 Σe[i] 会无限累积。一旦执行器饱和(比如占空比到 100%),误差还在累加,等误差反向时,积分项需要很长时间才能退回来——这就是我前面说的积分饱和问题。
- 输出突变时,微分项容易产生尖峰,需要做限幅或滤波。
我曾经踩过的坑:在一个 500W 的 buck 变换器上用了位置式 PID,启动时积分项从零开始累加,输出占空比直接冲到 100%,电感电流飙升,差点烧 MOSFET。后来加了积分限幅和抗饱和处理才稳住。所以用位置式 PID,积分限幅和输出限幅是必须的。
3.3 增量式 PID
增量式 PID 不直接算输出绝对值,而是算当前输出相对于上一次输出的变化量 Δu[k]。
公式推导很简单,从位置式出发:
Δu[k] = u[k] - u[k-1]
= Kp * (e[k] - e[k-1]) + Ki * e[k] + Kd * (e[k] - 2*e[k-1] + e[k-2])
实际输出由累加得到:
u[k] = u[k-1] + Δu[k]
优点:
- 没有积分累积项,从根本上避免了积分饱和问题
- 输出变化量 Δu 通常很小,执行机构动作平滑,适合伺服电机、步进电机等场景
- 从手动模式切换到自动模式时,只需要把 u[k-1] 设为当前手动值,无冲击切换
缺点:
- 输出是累加得到的,如果累加过程中出现数值误差或丢步,误差会一直带下去
- 对执行机构的积分特性有依赖(比如电机位置环),如果执行机构本身没有积分作用,增量式可能不合适
我的个人习惯:在数字电源的电压环和电流环里,我更喜欢用增量式 PID。原因很简单——电源的 PWM 占空比本身就是累加量,增量式天然匹配。而且启动时不会出现积分饱和导致的过冲。但在需要精确位置控制的场合(比如数字电源的相位调节),位置式更直观。
3.4 位置式 vs 增量式:一张表说清楚
| 对比维度 | 位置式 PID | 增量式 PID |
|---|---|---|
| 输出含义 | 控制量的绝对值 | 控制量的变化量 |
| 积分饱和风险 | 高,需额外处理 | 低,天然避免 |
| 手动/自动切换 | 需做 bump-less 处理 | 天然无冲击 |
| 计算量 | 需存储所有误差累加 | 只需存储最近 3 个误差 |
| 适用场景 | 执行机构无积分特性(如比例阀) | 执行机构有积分特性(如电机、PWM) |
| 数值稳定性 | 累加误差会持续存在 | 累加误差只影响当前步 |
3.5 两种 PID 的代码实现对比
下面是我在实际项目中用过的两种实现,你可以对比一下差异。
位置式 PID 实现:
typedef struct {
float Kp, Ki, Kd;
float integral; // 积分累加器
float prev_error; // 上一次误差
float out_max; // 输出上限
float out_min; // 输出下限
float int_max; // 积分限幅
} PositionalPID;
float PositionalPID_Calc(PositionalPID *pid, float setpoint, float feedback) {
float error = setpoint - feedback;
// 比例项
float P = pid->Kp * error;
// 积分项(带限幅)
pid->integral += error;
if (pid->integral > pid->int_max) pid->integral = pid->int_max;
if (pid->integral < -pid->int_max) pid->integral = -pid->int_max;
float I = pid->Ki * pid->integral;
// 微分项
float D = pid->Kd * (error - pid->prev_error);
pid->prev_error = error;
// 输出(带限幅)
float output = P + I + D;
if (output > pid->out_max) output = pid->out_max;
if (output < pid->out_min) output = pid->out_min;
return output;
}
增量式 PID 实现:
typedef struct {
float Kp, Ki, Kd;
float error[3]; // 环形缓冲区:e[k], e[k-1], e[k-2]
float last_output; // 上一次输出 u[k-1]
float delta_max; // 单步变化量限幅
float out_max; // 输出上限
float out_min; // 输出下限
} IncrementalPID;
float IncrementalPID_Calc(IncrementalPID *pid, float setpoint, float feedback) {
// 更新误差缓冲区
pid->error[2] = pid->error[1];
pid->error[1] = pid->error[0];
pid->error[0] = setpoint - feedback;
// 计算增量
float delta = pid->Kp * (pid->error[0] - pid->error[1])
+ pid->Ki * pid->error[0]
+ pid->Kd * (pid->error[0] - 2*pid->error[1] + pid->error[2]);
// 增量限幅
if (delta > pid->delta_max) delta = pid->delta_max;
if (delta < -pid->delta_max) delta = -pid->delta_max;
// 累加输出
pid->last_output += delta;
if (pid->last_output > pid->out_max) pid->last_output = pid->out_max;
if (pid->last_output < pid->out_min) pid->last_output = pid->out_min;
return pid->last_output;
}
注意:增量式 PID 的积分项 Ki * e[k] 其实是在 Δu 里体现的,不是直接累加误差。所以它没有传统意义上的积分饱和问题。但输出限幅还是要做,否则累加值可能溢出。
3.6 知识体系:PID 控制器核心逻辑
下面这张图帮你把位置式和增量式的区别、各自优缺点、适用场景串起来。建议保存下来,调试时对照着看。
3.7 实际项目中的选择建议
说了这么多理论,到底怎么选?我根据这些年做项目的经验,给你几个实用建议:
- 数字电源的电压环和电流环:优先用增量式。PWM 占空比本身就是累加量,增量式天然匹配,而且启动时不会因为积分饱和导致过冲。我在一个 12V 输入、1.2V 输出的 POL 电源上做过对比,增量式的启动过冲比位置式小了 60%。
- 电机的位置环或速度环:位置式更合适。因为电机的位置输出是绝对角度,增量式的累加误差会随时间漂移。我做过一个云台电机项目,用增量式 PID 位置环,半小时后位置漂了 5 度,换成位置式就稳了。
- 如果必须用位置式:一定要加积分限幅和抗饱和处理。我常用的方法是:当输出饱和时,停止积分累加,或者把积分项强制拉回到饱和边界。
- 采样周期 Ts 的选择:不管用哪种 PID,Ts 都不能太大。一般建议 Ts 小于系统时间常数的 1/10。对于开关电源,Ts 通常取开关周期的 1~2 倍。
一个小技巧:调试初期,我习惯先用增量式 PID 把环路调稳,然后再根据需求决定是否切换到位置式。增量式的参数调试更安全,不容易炸管子。等参数基本确定后,再换成位置式做精细优化。