这一章在干嘛?

把第 10~12 章的三个单环 起来:位置环输出当速度环的给定,速度环输出当电流环的给定,电流环输出管占空比。这是工业运动控制的经典「串级结构」,也是直流有刷部分的毕业典礼。

13.1 三环(串级)控制原理

13.2 三组参数与四个关键机制

13.3 核心代码:三环串级一次算完

13.4 下载验证

13.1 三环(串级)控制原理

三环 = 电流环 + 速度环 + 位置环的级联。外环不直接驱动电机,而是给内环「下指标」:

原书图 13.1.1 原书图 13.1.1:三环 PID 控制流程:位置环 → 速度环 → 电流环 → PWM

为什么要把环串起来? 各环各司其职:电流环在内,响应最快,管「出多大力」;速度环居中,管「转多快」;位置环在外,管「停在哪」。外环的输出天然就是内环该有的样子,抗扰、限流、平稳定位一次搞定。

13.2 三组参数与四个关键机制

KPKIKD输出身份输出限幅积分限幅
位置环0.060.000.01目标速度±120±1000
速度环5.000.300.01目标电流≤100±200
电流环8.004.001.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 原书图 13.4.1:三环 PID 控制波形:电机平稳地一格格到位,无明显抖动

调试技巧:

想确定各环的积分限幅值,可以先用串口打印各环的累计偏差看它最大能到多少(注意别干扰 PID 数据上传)。现象上,三环系统最直观的变化是:到位过程「一格一格」很稳重,死区让电机停在目标点附近纹丝不动。