离散化PID算法
引言
在现代控制系统工程中,数字实现已成为标准实践。从工业自动化到机器人控制,从无人机飞控到恒温系统,离散化PID控制器无处不在。本文旨在深入解析离散化PID算法的数学原理、实现要点和实际应用技巧,帮助工程师跨越连续域理论与数字实现之间的鸿沟。
一、PID控制基础:连续时间视角
1.1 理想连续PID控制器
在连续时间域中,理想的PID控制器可以用以下微分方程描述:
u(t)=Kpe(t)+Ki∫0te(τ)dτ+Kdde(t)dt u(t) = K_p e(t) + K_i \int_0^t e(\tau) d\tau + K_d \frac{de(t)}{dt} u(t)=Kpe(t)+Ki∫0te(τ)dτ+Kddtde(t)
其中:
- u(t)u(t)u(t):控制器输出(控制量)
- e(t)=r(t)−y(t)e(t) = r(t) - y(t)e(t)=r(t)−y(t):系统误差(设定值 - 实际值)
- KpK_pKp:比例增益
- KiK_iKi:积分增益
- KdK_dKd:微分增益
这个公式清晰地展示了PID控制器的三个基本作用:
- 比例项:提供即时响应,减小当前误差
- 积分项:消除稳态误差,累积历史误差
- 微分项:预测未来趋势,抑制超调
1.2 实际考虑:微分项的实现问题
在实际应用中,纯微分项会放大高频噪声,因此常采用以下形式:
u(t)=Kpe(t)+Ki∫0te(τ)dτ+Kdddt(y(t)1+τds) u(t) = K_p e(t) + K_i \int_0^t e(\tau) d\tau + K_d \frac{d}{dt} \left( \frac{y(t)}{1 + \tau_d s} \right) u(t)=Kpe(t)+Ki∫0te(τ)dτ+Kddtd(1+τdsy(t))
其中 τd\tau_dτd 是微分时间常数,sss 是拉普拉斯算子。这引出了我们讨论离散化的第一个动机:如何在数字系统中优雅地处理这类实际问题。
二、离散化的必要性:为什么需要转变?
2.1 数字控制的本质约束
现代控制系统几乎都在数字平台上实现:
- 微控制器(ARM Cortex-M, ESP32, Arduino等)
- PLC(可编程逻辑控制器)
- 工业计算机
- FPGA/DSP
这些系统本质上是离散时间系统,具有以下特征:
- 周期性采样:以固定时间间隔 TsT_sTs 采样传感器数据
- 离散时间计算:在采样时刻进行控制律计算
- 零阶保持输出:控制输出在采样间隔内保持恒定
2.2 离散化的核心挑战
离散化过程需要解决几个关键问题:
- 如何近似连续积分?→ 数值积分方法选择
- 如何近似连续微分?→ 数值微分方法选择
- 如何保持稳定性?→ 采样频率与性能权衡
- 如何处理计算延迟?→ 实时性保证
三、离散化方法:从连续到离散的数学桥梁
3.1 前向欧拉法(Forward Euler)
最简单直接的离散化方法,用前向差分近似微分:
de(t)dt≈e(k)−e(k−1)Ts \frac{de(t)}{dt} \approx \frac{e(k) - e(k-1)}{T_s} dtde(t)≈Tse(k)−e(k−1)
∫0te(τ)dτ≈Ts∑i=0ke(i) \int_0^t e(\tau) d\tau \approx T_s \sum_{i=0}^k e(i) ∫0te(τ)dτ≈Tsi=0∑ke(i)
得到的离散PID公式为:
u(k)=Kpe(k)+KiTs∑i=0ke(i)+Kde(k)−e(k−1)Ts u(k) = K_p e(k) + K_i T_s \sum_{i=0}^k e(i) + K_d \frac{e(k) - e(k-1)}{T_s} u(k)=Kpe(k)+KiTsi=0∑ke(i)+KdTse(k)−e(k−1)
优点:计算简单,易于实现
缺点:稳定性较差,需要高采样率
3.2 后向欧拉法(Backward Euler)
使用后向差分,通常更稳定:
de(t)dt≈e(k)−e(k−1)Ts \frac{de(t)}{dt} \approx \frac{e(k) - e(k-1)}{T_s} dtde(t)≈Tse(k)−e(k−1)
∫0te(τ)dτ≈Ts∑i=1ke(i) \int_0^t e(\tau) d\tau \approx T_s \sum_{i=1}^k e(i) ∫0te(τ)dτ≈Tsi=1∑ke(i)
注意积分项从 i=1i=1i=1 开始,避免当前时刻的依赖循环。
3.3 梯形法(Tustin/Bilinear变换)
更精确的离散化方法,用梯形面积近似积分:
∫0te(τ)dτ≈Ts2∑i=0k[e(i)+e(i−1)] \int_0^t e(\tau) d\tau \approx \frac{T_s}{2} \sum_{i=0}^k [e(i) + e(i-1)] ∫0te(τ)dτ≈2Tsi=0∑k[e(i)+e(i−1)]
对应的离散PID为:
u(k)=Kpe(k)+KiTs2∑i=0k[e(i)+e(i−1)]+Kde(k)−e(k−1)Ts u(k) = K_p e(k) + \frac{K_i T_s}{2} \sum_{i=0}^k [e(i) + e(i-1)] + K_d \frac{e(k) - e(k-1)}{T_s} u(k)=Kpe(k)+2KiTsi=0∑k[e(i)+e(i−1)]+KdTse(k)−e(k−1)
优点:精度高,频率响应特性好
缺点:计算量稍大
四、位置式PID算法:经典实现
4.1 算法公式
综合以上方法,最常用的位置式PID离散公式为:
u(k)=Kpe(k)+KiTs∑i=0ke(i)+Kde(k)−e(k−1)Ts u(k) = K_p e(k) + K_i T_s \sum_{i=0}^k e(i) + K_d \frac{e(k) - e(k-1)}{T_s} u(k)=Kpe(k)+KiTsi=0∑ke(i)+KdTse(k)−e(k−1)
4.2 C语言实现示例
typedef struct {
float Kp; // 比例增益
float Ki; // 积分增益
float Kd; // 微分增益
float Ts; // 采样时间(秒)
float integral; // 积分累积项
float prev_error; // 上一次误差
float out_max; // 输出上限
float out_min; // 输出下限
} PID_Controller;
float PID_Update(PID_Controller* pid, float setpoint, float measurement) {
// 计算当前误差
float error = setpoint - measurement;
// 比例项
float P_term = pid->Kp * error;
// 积分项(带抗饱和处理)
pid->integral += error * pid->Ts;
// 积分抗饱和:当输出饱和时停止积分
float I_term = pid->Ki * pid->integral;
float output_pre_sat = P_term + I_term;
if (output_pre_sat > pid->out_max) {
pid->integral -= error * pid->Ts; // 回退本次积分
I_term = pid->Ki * pid->integral;
} else if (output_pre_sat < pid->out_min) {
pid->integral -= error * pid->Ts; // 回退本次积分
I_term = pid->Ki * pid->integral;
}
// 微分项(使用测量值而非误差,减少设定值变化的冲击)
float derivative = (measurement - pid->prev_measurement) / pid->Ts;
float D_term = -pid->Kd * derivative; // 注意负号
// 计算总输出
float output = P_term + I_term + D_term;
// 输出限幅
if (output > pid->out_max) output = pid->out_max;
if (output < pid->out_min) output = pid->out_min;
// 更新状态
pid->prev_error = error;
pid->prev_measurement = measurement;
return output;
}
4.3 位置式PID的特点
优点:
- 物理意义清晰
- 适合执行机构需要绝对位置控制的场合(如阀门开度)
缺点:
- 积分项累积可能导致"积分饱和"(Windup)
- 输出与历史所有误差相关,内存占用大
- 设定值突变会引起微分项冲击
五、增量式PID算法:工业常用变体
5.1 算法推导
增量式PID通过计算控制量的增量 Δu(k)\Delta u(k)Δu(k) 来工作:
Δu(k)=u(k)−u(k−1)=Kp[e(k)−e(k−1)]+KiTse(k)+Kde(k)−2e(k−1)+e(k−2)Ts \begin{aligned} \Delta u(k) &= u(k) - u(k-1) \\ &= K_p[e(k)-e(k-1)] + K_i T_s e(k) + K_d \frac{e(k)-2e(k-1)+e(k-2)}{T_s} \end{aligned} Δu(k)=u(k)−u(k−1)=Kp[e(k)−e(k−1)]+KiTse(k)+KdTse(k)−2e(k−1)+e(k−2)
然后:
u(k)=u(k−1)+Δu(k) u(k) = u(k-1) + \Delta u(k) u(k)=u(k−1)+Δu(k)
5.2 增量式PID的特点
优点:
- 无积分饱和:本质上是PD控制器加增量积分
- 手动/自动切换无扰:输出增量小,切换平稳
- 抗干扰能力强:误差的加权差分形式对噪声有一定滤波
- 计算量小:无需保存所有历史误差
缺点:
- 需要执行机构有记忆功能(如步进电机)
- 物理意义不如位置式直观
5.3 增量式PID的C实现
typedef struct {
float Kp, Ki, Kd;
float Ts;
float prev_error[2]; // 存储前两次误差 e(k-1), e(k-2)
float prev_output; // 上一次输出值
float out_max, out_min;
} PID_Incremental;
float PID_Incremental_Update(PID_Incremental* pid, float setpoint, float measurement) {
float error = setpoint - measurement;
// 计算增量
float delta_u = pid->Kp * (error - pid->prev_error[0]) +
pid->Ki * pid->Ts * error +
pid->Kd * (error - 2*pid->prev_error[0] + pid->prev_error[1]) / pid->Ts;
// 计算新输出
float output = pid->prev_output + delta_u;
// 输出限幅
if (output > pid->out_max) output = pid->out_max;
if (output < pid->out_min) output = pid->out_min;
// 更新状态
pid->prev_error[1] = pid->prev_error[0];
pid->prev_error[0] = error;
pid->prev_output = output;
return output;
}
六、实际工程要点:超越教科书的知识
6.1 采样时间的选择经验法则
采样频率 fsf_sfs 与系统带宽 fbf_bfb 的关系:
fs=(5∼15)×fb f_s = (5 \sim 15) \times f_b fs=(5∼15)×fb
经验值:
- 温度控制:Ts=1∼10T_s = 1 \sim 10Ts=1∼10 秒
- 压力/流量控制:Ts=0.1∼1T_s = 0.1 \sim 1Ts=0.1∼1 秒
- 电机速度控制:Ts=1∼10T_s = 1 \sim 10Ts=1∼10 毫秒
- 无人机姿态控制:Ts=1∼10T_s = 1 \sim 10Ts=1∼10 毫秒(高速时可达0.25毫秒)
6.2 抗饱和处理(Anti-windup)策略
积分饱和是PID实践中最常见的问题之一,以下是几种解决方案:
6.2.1 积分分离法
// 仅在误差较小时启用积分
if (fabs(error) < threshold) {
pid->integral += error * pid->Ts;
}
6.2.2 积分限幅法
// 限制积分项的最大值
if (pid->integral > integral_max) {
pid->integral = integral_max;
} else if (pid->integral < -integral_max) {
pid->integral = -integral_max;
}
6.2.3 反向计算法(Back Calculation)
// 最有效的抗饱和方法之一
float saturated_output = output;
if (output > pid->out_max) saturated_output = pid->out_max;
if (output < pid->out_min) saturated_output = pid->out_min;
// 计算饱和误差
float saturation_error = saturated_output - output;
// 反向调整积分项
pid->integral += (pid->Kp / pid->Ki) * saturation_error * pid->Ts;
6.3 微分项的改进
6.3.1 不完全微分
为了解决微分项对噪声敏感的问题:
ud(k)=αud(k−1)+(1−α)Kde(k)−e(k−1)Ts u_d(k) = \alpha u_d(k-1) + (1-\alpha) K_d \frac{e(k)-e(k-1)}{T_s} ud(k)=αud(k−1)+(1−α)KdTse(k)−e(k−1)
其中 α=TdTd+Ts\alpha = \frac{T_d}{T_d+T_s}α=Td+TsTd,TdT_dTd 是微分滤波器时间常数。
6.3.2 微分先行
只对测量值微分,不对设定值微分,减少设定值突变冲击:
Dterm=−Kdy(k)−y(k−1)Ts D_{term} = -K_d \frac{y(k) - y(k-1)}{T_s} Dterm=−KdTsy(k)−y(k−1)
6.4 设定值加权
对比例和微分项的设定值部分进行加权:
u(k)=Kp[βr(k)−y(k)]+KiTs∑e(i)+Kd[γdr(k)dt−dy(k)dt] u(k) = K_p [\beta r(k) - y(k)] + K_i T_s \sum e(i) + K_d [\gamma \frac{dr(k)}{dt} - \frac{dy(k)}{dt}] u(k)=Kp[βr(k)−y(k)]+KiTs∑e(i)+Kd[γdtdr(k)−dtdy(k)]
其中 β,γ∈[0,1]\beta, \gamma \in [0, 1]β,γ∈[0,1] 是加权系数,用于调节设定值变化的响应速度。
七、参数整定方法:理论与实践
7.1 Ziegler-Nichols 方法(阶跃响应法)
基于开环阶跃响应的经验公式:
| 控制器类型 | KpK_pKp | TiT_iTi | TdT_dTd |
|---|---|---|---|
| P | 1/a1/a1/a | - | - |
| PI | 0.9/a0.9/a0.9/a | 3L3L3L | - |
| PID | 1.2/a1.2/a1.2/a | 2L2L2L | 0.5L0.5L0.5L |
其中:
- aaa 是阶跃响应最大斜率
- LLL 是延迟时间
7.2 临界比例度法
- 先设 Ki=0K_i = 0Ki=0, Kd=0K_d = 0Kd=0
- 增大 KpK_pKp 直到系统出现等幅振荡
- 记录临界增益 KuK_uKu 和振荡周期 TuT_uTu
- 根据下表设置参数:
| 控制器类型 | KpK_pKp | TiT_iTi | TdT_dTd |
|---|---|---|---|
| P | 0.5Ku0.5K_u0.5Ku | - | - |
| PI | 0.45Ku0.45K_u0.45Ku | 0.85Tu0.85T_u0.85Tu | - |
| PID | 0.6Ku0.6K_u0.6Ku | 0.5Tu0.5T_u0.5Tu | 0.12Tu0.12T_u0.12Tu |
7.3 试凑法经验口诀
“先比例,后积分,再微分”:
- 调KpK_pKp:从小到大,系统响应快但不振荡
- 调KiK_iKi:消除静差,但不要引起振荡
- 调KdK_dKd:抑制超调,提高稳定性
八、离散化带来的特殊问题
8.1 频率混叠(Aliasing)
当采样频率 fsf_sfs 低于信号最高频率 fmaxf_{max}fmax 的2倍时:
fs<2fmax⇒混叠发生 f_s < 2f_{max} \Rightarrow \text{混叠发生} fs<2fmax⇒混叠发生
解决方案:在采样前加抗混叠滤波器(低通滤波器)。
8.2 量化误差
数字系统的有限字长效应:
- ADC量化误差
- 计算舍入误差
- 系数量化误差
缓解方法:
- 选择合适的ADC分辨率
- 使用浮点数或高精度定点数
- 实施抖动(Dithering)技术
8.3 计算延迟的影响
实际系统存在:
- 采样延迟
- 计算延迟
- 输出延迟
总延迟 τ\tauτ 对稳定性的影响可以用以下经验公式评估:
τmax≈0.1fb \tau_{max} \approx \frac{0.1}{f_b} τmax≈fb0.1
其中 fbf_bfb 是系统带宽。
九、实际应用案例
9.1 案例:直流电机速度控制
// 电机PID控制专用结构
typedef struct {
PID_Controller speed_pid;
PID_Controller current_pid;
float current_setpoint;
float max_current;
float gear_ratio;
} Motor_Controller;
void motor_speed_control(Motor_Controller* motor, float speed_setpoint, float speed_actual) {
// 外环:速度控制
float current_target = PID_Update(&motor->speed_pid, speed_setpoint, speed_actual);
// 电流限幅保护
if (current_target > motor->max_current) current_target = motor->max_current;
if (current_target < -motor->max_current) current_target = -motor->max_current;
motor->current_setpoint = current_target;
}
9.2 案例:温度控制系统
// 温度控制专用PID,考虑热系统的大惯性
typedef struct {
PID_Controller pid;
float deadband; // 死区,避免继电器频繁动作
float hysteresis; // 迟滞,用于bang-bang控制切换
uint32_t min_on_time; // 最小加热时间(毫秒)
uint32_t min_off_time;// 最小关闭时间(毫秒)
uint32_t last_switch_time;
bool heating_on;
} Temperature_Controller;
float temperature_control(Temperature_Controller* ctrl, float target_temp, float current_temp) {
float error = target_temp - current_temp;
// 死区处理
if (fabs(error) < ctrl->deadband) {
return 0; // 不加热不制冷
}
// 计算PID输出(0-100%占空比)
float duty_cycle = PID_Update(&ctrl->pid, target_temp, current_temp);
// 保护执行机构,避免频繁开关
uint32_t current_time = get_system_time();
if (duty_cycle > 50 && !ctrl->heating_on) {
if (current_time - ctrl->last_switch_time > ctrl->min_off_time) {
ctrl->heating_on = true;
ctrl->last_switch_time = current_time;
}
} else if (duty_cycle <= 50 && ctrl->heating_on) {
if (current_time - ctrl->last_switch_time > ctrl->min_on_time) {
ctrl->heating_on = false;
ctrl->last_switch_time = current_time;
}
}
return ctrl->heating_on ? 100.0 : 0.0;
}
结论
离散化PID算法是连接连续控制理论与数字实现的关键桥梁。通过本文的探讨,我们可以看到:
- 离散化不仅是数学变换,更是工程实践的必要步骤
- 位置式和增量式各有适用场景,选择取决于执行机构特性
- 抗饱和处理是工业应用的必备技能,决定了控制的鲁棒性
- 参数整定需要理论结合经验,没有"一刀切"的方法
- 采样率选择至关重要,直接影响系统性能与稳定性
优秀的PID实现不仅需要理解数学公式,更需要考虑实际工程约束:计算资源、采样率限制、执行机构特性、安全要求等。随着嵌入式系统性能的提升和机器学习技术的发展,PID控制器也在不断演进,但其核心思想——基于误差的过去、现在和未来进行调节——将始终是自动控制的基石。
4371

被折叠的 条评论
为什么被折叠?



