基于 TI MSPM0G3507 的八路灰度直角弯巡线小车,使用 TB6612、编码器速度 PI 与巡线 PD 控制。
基础直线、普通弯道、左右直角弯均已完成实车验证(2026-07-15)。
- 八路灰度加权差比和巡线,移植自 STM32 car_v1.2 验证逻辑
- 转向 PD 控制,差速分配,含弯道减速和丢线惯性保持
- 直角弯状态机:一侧 ≥3 灯触发 → 内轮低速 (50mm/s)、外轮 SLOW (200mm/s) 圆弧转弯 → 中央 D4/D5 连续 2 帧捕线退出 → 15 帧防重入锁定
- 编码器速度闭环:双编码器 RISE_FALL 2× 解码 (520 脉冲/圈),EMA 速度滤波,增量式 PI 控制
- 目标加速斜率限制:PI 目标每 10ms 最多上升 50mm/s,减速立即生效
- OLED 四行实时显示:传感器状态、巡线模式、目标速度、实际速度
- 按键接口预留:四路按键 GPIO 与基础读取驱动已配置,当前尚未接入巡线主循环。
- UART 蓝牙:串口回显,预留遥控接口
| 模块 | 型号/说明 |
|---|---|
| 主控 | TI MSPM0G3507 (ARM Cortex-M0+, 80MHz, LQFP-64) |
| 电机驱动 | TB6612FNG 双 H 桥 |
| 电机 | 双直流减速电机 (带编码器, 260 线/圈, 轮径 67mm) |
| 灰度传感器 | 八路灰度 + CD4051 通道复用 (亚博智能) |
| 显示 | 0.96 寸 OLED 128×64 (I2C, 4 脚) |
| 调试探针 | XDS110 (SWD) |
| 蓝牙 | HC-05 / JDY-31 (UART) |
| 功能 | 引脚 | 外设 |
|---|---|---|
| STBY (待机) | PB24 | GPIO 输出 |
| AIN1 (左方向1) | PA13 | GPIO 输出 |
| AIN2 (左方向2) | PA12 | GPIO 输出 |
| PWMA (左 PWM) | PB4 | TIMA1 CCP0 |
| BIN1 (右方向1) | PB0 | GPIO 输出 |
| BIN2 (右方向2) | PB8 | GPIO 输出 |
| PWMB (右 PWM) | PB1 | TIMA1 CCP1 |
| 功能 | 引脚 | 中断 |
|---|---|---|
| 左 A 相 | PA15 | GPIOA RISE_FALL |
| 左 B 相 | PA16 | — |
| 右 A 相 | PA17 | GPIOA RISE_FALL |
| 右 B 相 | PB9 | — |
| 功能 | 引脚 | 说明 |
|---|---|---|
| OUT (信号) | PA14 | 输入, 内部上拉 |
| AD0 | PB18 | 通道选择 bit0 |
| AD1 | PB19 | 通道选择 bit1 |
| AD2 | PA30 | 通道选择 bit2 |
| 功能 | 引脚 | 外设 |
|---|---|---|
| OLED SDA | PA28 | I2C0 |
| OLED SCL | PA31 | I2C0 |
| UART TX | PA10 | UART0 → 蓝牙 |
| UART RX | PA11 | UART0 ← 蓝牙 |
| 按键 Kp+ | PA25 | GPIO 输入 (下拉) |
| 按键 Kp- | PA26 | GPIO 输入 (下拉) |
| 按键 Ki+ | PB23 | GPIO 输入 (下拉) |
| 按键 Ki- | PA29 | GPIO 输入 (下拉) |
| LED | PB22 | GPIO 输出 |
八路灰度读取 (CD4051, ~8ms)
│
▼
加权差比和 raw_error = sum_L - sum_R (不除以 cnt)
│
├─ 直角弯触发? (一侧≥3, 另一侧≤1)
│ ├─ YES → TURN 状态机
│ │ 左转: L=50, R=200 mm/s
│ │ 右转: L=200, R=50 mm/s
│ │ 退出: 原触发区离开 → D4/D5×2 中央捕线 → 同帧 NORMAL PD
│ │
│ └─ NO → NORMAL PD
│ error → PD (Kp×err + Kd×diff)
│ steer_out 限幅 ±200
│ 弯道减速: base = FAST × (1 - |err|/12 × 0.7)
│ 差速: L = base - steer, R = base + steer
│ 轮速地板: 低于 50 → 抬至 50
│
▼
motor_target_set(L, R)
│
▼
编码器速度 PI (10ms 节拍)
EMA 滤波 → 增量式 PI → PWM 占空比 (0~4000)
目标加速斜率: +50mm/s / 10ms
| 模块 | 算法 | 关键参数 |
|---|---|---|
| 八路灰度加权差比和 | error = Σ(Di×Wi_左) - Σ(Di×Wi_右) |
W1~W8 = 6, 4.2, 1.8, 0, 0, 1.8, 4.2, 6 |
| 巡线转向 PD | steer = Kp×error + Kd×Δerror |
Kp=20.0, Kd=10.0, STEER_MAX=200 |
| 弯道减速 | `base = FAST × (1 - | error |
| 直角弯触发 | 一侧 ≥3 灯 && 另一侧 ≤1 | 最小 3 帧, 超时 200 帧 |
| 直角弯退出 | armed → D4/D5×2 连续确认 | 防重入 15 帧 |
| 编码器速度 PI | 增量式 PI, float 累加 | Kp=0.5, Ki=0.4, 周期 10ms |
| EMA 速度滤波 | speed = 0.3×raw + 0.7×speed |
有效分辨率 ~10 mm/s |
| 目标加速斜率 | PI 目标每 10ms 最多 +50mm/s | 减速立即生效 |
| 宏 | 值 | 说明 |
|---|---|---|
TRACK_SPEED_FAST |
400 | 直道速度 mm/s |
TRACK_SPEED_SLOW |
200 | 直角弯外轮速度 mm/s |
TRACK_SPEED_MIN |
50 | 普通巡线最低基础速度 mm/s |
TRACK_SPEED_TURN_INNER |
50 | 直角弯内轮速度 mm/s |
TRACK_SPEED_WHEEL_FLOOR |
50 | 普通 PD 单轮最低速度 mm/s |
| 参数 | 值 |
|---|---|
| Kp | 20.0 |
| Kd | 10.0 |
| STEER_MAX | 200 |
| D1 | D2 | D3 | D4 | D5 | D6 | D7 | D8 |
|---|---|---|---|---|---|---|---|
| 6.0 | 4.2 | 1.8 | 0.0 | 0.0 | 1.8 | 4.2 | 6.0 |
| 参数 | 值 |
|---|---|
| 编码器有效脉冲/圈 | 520 (260线 × 2沿 RISE_FALL) |
| 轮径 | 67 mm |
| PI 周期 | 10ms (TIMG7) |
| Kp | 0.5 |
| Ki | 0.4 |
| EMA α | 0.30 |
| 目标加速限制 | +50 mm/s / 10ms |
| PWM 计数上限 | 4000 |
| PWM 频率 | 80MHz / 4000 = 20kHz |
| 参数 | 值 |
|---|---|
| 触发条件 | 一侧 ≥3, 另一侧 ≤1 |
| 最小转弯帧 | 3 |
| 超时帧数 | 200 |
| 退出确认帧 | 2 (D4/D5 连续) |
| 防重入锁定 | 15 帧 |
| 行 | 格式 | 说明 |
|---|---|---|
| Row 0 | GRAY:xxxxxxxx |
8 路传感器 (0=白 1=黑) |
| Row 1 | MODE:xxxxx |
巡线模式 (STRAIGHT/LEFT/RIGHT/TURN-LEFT/TURN-RIGHT/SEARCH) |
| Row 2 | TGT Lxxxx Rxxxx |
左右轮 PI 目标速度 (mm/s, 经加速斜坡) |
| Row 3 | SPD Lxxxx Rxxxx |
左右轮 EMA 实测速度 (mm/s) |
09_MOTOR_CAR/
├── empty.c # 主入口 main() + track() 巡线控制
├── empty.syscfg # SysConfig 外设配置源文件
├── motor.c / motor.h # 双电机驱动 (TB6612 + 编码器 + PI 闭环)
├── grayscale.c / grayscale.h # 八路灰度 CD4051 传感器驱动
├── oled.c / oled.h # OLED 128×64 I2C 显示驱动
├── oledfont.h # OLED 字库 (6×8 / 8×16)
├── key.c / key.h # 按键输入检测
├── uart.c / uart.h # UART 蓝牙串口
├── delay.c / delay.h # 毫秒/微秒延时
├── syscfg_pin_tool.py # SysConfig 引脚配置辅助脚本
├── README.md # 本文件
├── 错误日志.md # 开发踩坑全记录
├── .ccsproject / .cproject / .project # CCS Theia 工程元数据
├── .settings/ # CCS 工程设置
├── .gitignore # Git 忽略规则
└── targetConfigs/
└── MSPM0G3507.ccxml # XDS110 调试器配置
本仓库仅保留
Debug/device_linker.cmd;Debug/下其余构建产物和 SysConfig 生成文件由 CCS 构建时重新生成。
- IDE:Code Composer Studio Theia
- MSPM0 SDK:
mspm0_sdk_2.10.00.04 - 编译器:TI ARM Clang
ti-cgt-armllvm_4.0.4.LTS - SysConfig:
1.26.2 - 调试器:XDS110 (TI 官方)
- 用 CCS Theia 打开
09_MOTOR_CAR文件夹 - 右键工程 → Build Project (或
Ctrl+B) - 生成文件在
Debug/目录下
或命令行构建(PowerShell):
Set-Location "<project>\Debug"
& "<ccs>\ccs\utils\bin\gmake.exe" -B all
<project>为本仓库路径,<ccs>为 CCS 安装目录。
& "<ccs>\ccs\ccs_base\DebugServer\bin\DSLite.exe" flash `
--config="<project>\targetConfigs\MSPM0G3507.ccxml" `
--flash --verify --run --verbose --timeout=90 `
"<project>\Debug\09_MOTOR_CAR.out"
<project>为本仓库路径,<ccs>为 CCS 安装目录。--flash烧录,--verify校验,--run烧录后运行。
换烧录器后 Debug 仍弹旧探针对话框:删除 targetConfigs/MSPM0G3507.ccxml,CCS 右键工程 → Close Project → 双击打开,CCS 自动重建 ccxml。同时检查 .theia/launch.json 的 connections 字段。
motor_init() 将方向引脚设为 STOP(刹车),若 motor_set_target_speed() 仅在方向变化时才切换,首次正转可能永不被触发。修复:无条件设置方向。
GPIO 中断仅能做软件解码。用 SysConfig RISE_FALL 极性(不是 BOTH)达到 2× 解码 (520 脉冲/圈),配合 EMA 滤波 (α=0.3) 实现有效分辨率 ~10 mm/s。
4 脚 OLED 仅支持硬件 I2C。I2C 速率在 SysConfig 中设为 Fast (400kHz),不是 basicBitRate 属性。使用分块刷新 OLED_RefreshStep() 降低每帧阻塞时间。
| 目的 | ❌ 错 | ✅ 对 |
|---|---|---|
| I2C 400kHz | basicBitRate = 400 |
basicControllerStandardBusSpeed = "Fast" |
| 编码器双沿 | polarity = "BOTH" |
polarity = "RISE_FALL" |
高级定时器 TIMA 支持双 CCP(适合双电机),普通定时器 TIMG 仅单 CCP。SysConfig 中先创建 TIMG 再改 TIMA 不刷新第二个 CCP 下拉框 → 必须删除重建并直接选 TIMA。
本项目的巡线算法(加权差比和 + 转向 PD + 直角弯状态机)移植自 STM32 小车项目:
关键差异:
- STM32 使用硬件编码器 4× 解码 (1040 脉冲/圈),TI MSPM0 使用 GPIO 中断软件 2× 解码 (520 脉冲/圈)
- STM32 直接用编码器脉冲数做 PID,TI 转为 mm/s 单位
- STM32 PID 约 100ms 更新,TI PID 每 10ms 更新
- TI
motor_target_set(0, ...)触发 TB6612 主动刹车,STM32 目标 0 仅停止 PID 输出
- 蓝牙遥控指令协议设计与实现
- PID 参数在线整定(通过按键或串口)
- 速度曲线规划(梯形加减速)
- 里程计 / 航位推算
- 电池电压检测与低电量保护
- 超声波 / 红外避障
实车直角弯巡线视频(文字占位,视频文件不上传)
项目日期:2026-07 | 芯片:MSPM0G3507 | 平台:天猛星