这一章在干嘛?
把第 10~12 章的三个单环 串 起来:位置环输出当速度环的给定,速度环输出当电流环的给定,电流环输出管占空比。这是工业运动控制的经典「串级结构」,也是直流有刷部分的毕业典礼。
13.1 三环(串级)控制原理
三环 = 电流环 + 速度环 + 位置环的级联。外环不直接驱动电机,而是给内环「下指标」:
原书图 13.1.1:三环 PID 控制流程:位置环 → 速度环 → 电流环 → PWM
为什么要把环串起来? 各环各司其职:电流环在内,响应最快,管「出多大力」;速度环居中,管「转多快」;位置环在外,管「停在哪」。外环的输出天然就是内环该有的样子,抗扰、限流、平稳定位一次搞定。
13.2 三组参数与四个关键机制
| 环 | KP | KI | KD | 输出身份 | 输出限幅 | 积分限幅 |
|---|---|---|---|---|---|---|
| 位置环 | 0.06 | 0.00 | 0.01 | 目标速度 | ±120 | ±1000 |
| 速度环 | 5.00 | 0.30 | 0.01 | 目标电流 | ≤100 | ±200 |
| 电流环 | 8.00 | 4.00 | 1.00 | 占空比 | [0, 8200] | ±150 |
机制一:积分限幅——每环各有一个限幅值,防积分深度饱和:
void integral_limit(PID_TypeDef *PID, float max_limit, float min_limit)
{
if (PID->SumError >= max_limit) /* 超上限就钳住 */
PID->SumError = max_limit;
else if (PID->SumError <= min_limit)
PID->SumError = min_limit;
}
机制二:闭环死区——位置偏差绝对值 ≤20 时,偏差清零、积分清空,电机不在目标点附近「打摆子」。机制三:方向处理——电流大小无方向,由速度环输出的符号决定转向:为正则 dcmotor_dir(0) 正转,为负则取反并 dcmotor_dir(1) 反转,保证电流环给到手的恒为正。
限幅经验:
只有电流环/速度环的系统,限幅要比累计偏差最大值稍大(否则出现静态误差);最外环是位置环的系统,限幅可以更小。每次改 PID 系数后,都要复查限幅范围。
13.3 核心代码:三环串级一次算完
/* TIM6 中断里(50ms 一次),三环依序计算 */
integral_limit(&g_location_pid, 1000, -1000);
integral_limit(&g_speed_pid, 200, -200);
integral_limit(&g_current_pid, 150, -150);
/* 死区:位置偏差 |e| ≤ 20 时清零偏差与积分 */
if ((g_location_pid.Error <= 20) && (g_location_pid.Error >= -20)) {
g_location_pid.Error = 0; g_location_pid.SumError = 0;
}
/* ① 位置环:输出 = 目标速度,限幅 ±120 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid,
g_motor_data.location);
if (g_motor_data.motor_pwm >= 120) g_motor_data.motor_pwm = 120;
else if (g_motor_data.motor_pwm <= -120) g_motor_data.motor_pwm = -120;
g_speed_pid.SetPoint = g_motor_data.motor_pwm; /* 外环输出给内环 */
/* ② 速度环:输出 = 目标电流;符号决定转向 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid,
g_motor_data.speed);
if (g_motor_data.motor_pwm > 0) { dcmotor_dir(0); } /* 正转 */
else { g_motor_data.motor_pwm = -g_motor_data.motor_pwm; /* 取反 */
dcmotor_dir(1); } /* 反转 */
if (g_motor_data.motor_pwm >= 100) g_motor_data.motor_pwm = 100;
g_current_pid.SetPoint = g_motor_data.motor_pwm; /* 目标电流恒为正 */
/* ③ 电流环:输出 = 占空比,单向限幅 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid,
g_motor_data.current);
if (g_motor_data.motor_pwm >= 8200) g_motor_data.motor_pwm = 8200;
else if (g_motor_data.motor_pwm <= 0) g_motor_data.motor_pwm = 0;
dcmotor_speed(g_motor_data.motor_pwm);
13.4 下载验证
上位机同步三组 PID(TYPE_PID1/2/3 分别对应位置/速度/电流环),目标值只能设最外环 g_location_pid.SetPoint——多环系统里内环的目标是外环的输出,手动去改等于越级指挥。按键功能与位置环实验相同:KEY0 +一圈、KEY1 −一圈、KEY2 回原点。
原书图 13.4.1:三环 PID 控制波形:电机平稳地一格格到位,无明显抖动
调试技巧:
想确定各环的积分限幅值,可以先用串口打印各环的累计偏差看它最大能到多少(注意别干扰 PID 数据上传)。现象上,三环系统最直观的变化是:到位过程「一格一格」很稳重,死区让电机停在目标点附近纹丝不动。
1. 三环控制的信号流向是什么?
目标位置 → 位置环(最外环)输出作为速度环给定 → 速度环(次外环)输出作为电流环给定 → 电流环(内环)输出控制 PWM 占空比。外环的输出永远是内环的给定。
2. 三环的积分限幅各是多少?为什么速度环输出为负时要「取反 + 换向」?
位置环 ±1000、速度环 ±200、电流环 ±150。电流大小没有方向,转向由 dcmotor_dir() 单独控制;速度环输出为负说明要反转,取反后电流环给定才恒为正。
3. 闭环死区怎么设置、有什么用?
位置偏差在 −20~+20 之间时把偏差和积分清零。PID 不再响应微小偏差,电机就不会在目标位置附近来回抖动。