这一章在干嘛?

用编码器累计计数当「位置」,让电机精确转到指定角度并停住。你按一次按键,电机就多转一圈——不多不少,这就是位置闭环的魅力。

12.1 位置环控制原理

12.2 程序设计

12.3 下载验证

12.1 位置环控制原理

「位置」就是 TIM3 编码器模式的计数值 + 溢出累计值(正转加、反转减,由 CR1 的 DIR 位判断),每 5ms 读一次存入 g_motor_data.location。它与转角严格成正比:

原书图 12.1.1 原书图 12.1.1:位置环 PID 控制流程:目标位置与实际位置求偏差

编码器 44 脉冲/圈 × 减速比 30 = 1320 个计数对应输出轴一圈。按键每按一次目标 ±1320 就是 ±1 圈,限位 ±6600(最多正/反 5 圈)。

为什么 KI = 0?

本实验用 纯 P + D (KP=15.0、KI=0、KD=7.5,增量式)。定位系统怕的是「冲过头再拉回来」的抖动,积分会加剧超调,干脆不要;稳态误差靠 D 和死区兜底。

12.2 程序设计

/* TIM6 更新中断:位置闭环 */
else if (htim->Instance == TIM6) {
    int Encode_now = gtim_get_encode();
    speed_computer(Encode_now, 5);          /* 速度只用于显示 */
    g_motor_data.location = Encode_now;     /* 计数总值 = 位置反馈 */
    if (val % SMAPLSE_PID_SPEED == 0) {     /* 50ms 一次 */
        if (g_run_flag) {
            motor_pwm_temp = increment_pid_ctrl(&g_location_pid,
                             g_motor_data.location);
            /* 输出低通 0.5/0.5 后限幅 ±4200(比速度环小:定位不需要飙车) */
            g_motor_data.motor_pwm = (int32_t)((g_motor_data.motor_pwm * 0.5)
                                    + (motor_pwm_temp * 0.5));
            if (g_motor_data.motor_pwm >= 4200)
                g_motor_data.motor_pwm = 4200;
            else if (g_motor_data.motor_pwm <= -4200)
                g_motor_data.motor_pwm = -4200;
            motor_pwm_set(g_motor_data.motor_pwm);
        }
        val = 0;
    }
    val++;
}

按键逻辑:KEY0 目标 +1320(正转一圈)、KEY1 −1320、KEY2 目标清零(回初始位置)。输出可正可负,PID 自动决定正转还是反转去逼近目标。

12.3 下载验证

上位机用 debug_set_point_range(6600, -6600, 6600) 限定目标范围;HALT 命令回原点,RUN 命令目标 1320(一圈)。每 200ms 上传一次编码器总计数值。

原书图 12.4.1 原书图 12.4.1:位置环 PID 控制波形:红线(实际位置)逼近橙线(目标位置)后稳稳停住

现象解读:

按一次 KEY0,橙线跳一格(1320),红线平滑爬升到目标后 纹丝不动 ;按 KEY2 目标归零,电机自己倒回原点。如果最后停在目标附近轻微抖动,检查是否漏配死区或 KD 太小。