八路示例解释了位置算法,本节进一步观察一个十二路循迹工程如何把传感器偏差变成左右轮差速。主控统一称为 MSPM0G35XX,重点放在可以迁移的控制链,而不是某个工程文件名。
本篇目标与运行现象
工程设计目标是读取十二路数字灰度,估计赛道位置,通过方向 PID 生成左右轮差速,并由四路 PWM 驱动电机。
十二路 GPIO → 加权位置 xun → 方向 PID → 基础 PWM ± 转向量 → 四路 PWM编码器中断 → 窗口计数与低通 → 当前仅用于观测速度 PID 函数存在,但控制中断中的调用被注释。因此当前可达链路是“基础开环输出 + 方向闭环”,不是双轮速度闭环。
先认识控制链中的模块
工程使用 SysConfig 与 Keil 配置 MSPM0G35XX 外设,自研逻辑按灰度、控制、电机和编码器分层。理解这些模块的输入输出,比从目录逐个阅读文件更有效。
| 模块 | 文件 | 作用 |
|---|---|---|
| 主流程 | empty.c | 初始化、按键模式、OLED 与中断启动 |
| 灰度 | BSP/XUNJI/xunji.c | 读取十二路输入并计算位置 |
| 控制 | BSP/PID/pid.c | 速度、位置和方向控制函数 |
| 电机 | BSP/MOTER/moter.c | 方向 GPIO 与 PWM 比较值 |
| 编码器 | BSP/ENCODER/encoder.c | A 相中断计数,B 相判断方向 |
十二路位置估计
xunji_shua_xin() 读取 L6…L1、R1…R6。有效通道分别映射到 1…12,再求平均:
xun = (L6×1 + L5×2 + … + R5×11 + R6×12) / active_count几何中心为 6.5。多路同时检测时,平均值提供比单通道判断更连续的位置估计。
该算法仍以通道编号代替真实横向距离。只有当传感器近似等距时,这个坐标才近似线性;接入时需要测量真实安装间距,并实测数字比较阈值。
主流程如何启动控制
main() 调用板级初始化,初始化 OLED 和控制定时器,并把四路 PWM 清零。按键切换变量 jb,用于决定控制是否运行。
当 jb == 1 时,50 ms 定时器中断依次完成:
xunji_shua_xin() → place_PID_value(6.5f, xun) → left = 300 - steer → right = 300 + steer → Set_Pwm()300 是源码中的基础命令,不应解释为占空比百分数。它的真实比例取决于定时器周期和 PWM 配置。
方向 PID 与差速
目标位置固定为 6.5。实际位置偏离中心时,place_PID_value() 输出转向修正量。
左右轮一减一加,使总前进量近似保持,同时产生横摆力矩。若安装方向、权重顺序或电机正方向改变,差速符号也必须重新确认。
方向环在定时器中断里使用浮点运算。50 ms 只是配置意图;是否稳定达到该周期,需要用 GPIO 翻转、周期计数或逻辑分析仪测量。
电机执行链
Set_Pwm() 根据四个输入的正负选择方向 GPIO,再把绝对值写入四个定时器比较寄存器。
函数内部没有统一的 PWM 限幅,主流程原有限幅代码也被注释。方向修正过大时,基础值加减可能越过预期比较范围。
在不知道定时器计数周期、电机驱动型号和供电条件时,不能给出安全数值。首次上电必须架空车轮、限制命令并保留物理断电路径。
编码器采样链
左右编码器均在 A 相上升沿触发 GPIO 中断,随后读取 B 相判断方向。它属于单边沿正交解码,计数倍率低于四倍频,优点是中断负担较小。
SysTick 约每 10 ms 读取并清零计数,再采用:
filtered = 0.3 × new + 0.7 × old在轮径、减速比和每圈脉冲数尚未标定前,滤波结果只能称为固定窗口脉冲量,不能直接称为 RPM 或厘米每秒。
失线逻辑缺陷
没有通道有效时,xunji_shua_xin() 把 xun 恢复成 last_xun。控制中断随后尝试用 xun == 0 判断失线并停车。
由于 last_xun 初始为 6.5,失线后 xun 通常仍非零,停车分支难以触发。位置值和检测状态被错误复用为同一个变量。
正确方向是让采样函数同时返回 line_detected 和 position。失线策略再由应用层根据持续时间、最后方向和安全要求决定。
xunji_biao_zhi() 也存在语义冲突:注释称“有黑带返回 1”,代码却在任一路有效时返回 0。使用前必须统一返回值定义并增加测试。
中断实时性与调度改造
控制中断包含十二路 GPIO 读取、浮点平均、PID 和 PWM 更新。继续加入 OLED 或串口后,中断时间会继续增加。
更稳妥的结构是:中断只递增计数或设置控制任务到期标志,主循环中的非抢占式任务完成计算;同时记录每次执行时间和周期抖动。
编码器计数仍应在短中断内完成,复杂换算和滤波可转移到周期任务。这样能缩短高优先级中断路径,也便于统一观察系统负载。
建议的分层调试
- 单独显示十二路原始 GPIO,确认顺序和黑白极性。
- 手动横移黑线,确认
xun从一侧单调变化到另一侧。 - 不接电机,验证失线状态与恢复状态。
- 架空车轮,以受限命令验证每一路电机方向。
- 仅启用方向环,记录
xun、误差、转向量和限幅状态。 - 完成编码器物理标定后,再恢复双轮速度环。
接入真实小车前还要确认什么
| 检查项 | 当前工程表现 | 接入要求 |
|---|---|---|
| 外设链路 | 已配置十二路灰度、PWM、编码器和控制定时器 | 上板核对引脚、极性和实际中断周期 |
| 方向闭环 | 50 ms 路径调用 place_PID_value() | 测量周期抖动并给输出增加统一限幅 |
| 速度闭环 | 速度 PID 调用仍被注释 | 完成编码器标定后单独调通,再与方向环叠加 |
| 失线保护 | 旧位置会让停车条件难以成立 | 把 line_detected 与位置值拆成两个变量 |
| 速度单位 | 当前只有窗口脉冲计数 | 用轮径、减速比和编码器分辨率换算 |
本篇总结
- 工程实际启用十二路加权位置和方向差速控制。
- 速度 PID 文件存在,但当前调用链仍被注释。
- 电机执行缺少统一限幅,不能直接沿用源码中的基础命令。
- 失线状态必须与位置值分离,现有逻辑存在明确缺陷。
- 可靠移植应从传感器极性、位置单调性、电机方向和编码器标定逐层验证。