这是一个基于 TI MSPM0G3507 LaunchPad 的 CCS 工程,用 TB6612 驱动左右两个直流减速电机,通过霍尔编码器做双轮速度闭环,并支持 OpenMV/树莓派视觉巡线、基础里程运动命令、MPU6050 姿态遥测、SPI LCD 状态显示和 PC 上位机在线调参。
当前固件默认速度环参数:
Base = 20
Kp = 4.0
Ki = 0.2
Kd = 0.4
BASE 的单位不是 PWM 百分比,而是每个 20 ms 控制周期内的编码器增量。例如 BASE 20 表示目标速度约为 20 counts / 20 ms。
- 左右轮编码器计数、测速和一阶低通滤波。
- 20 ms 双轮速度闭环,左右轮使用同一组 PID 参数。
- 速度目标斜坡、PWM 手动测试斜坡、前馈起步占空比和左右轮前馈比例校准。
- UART0 调试/调参,默认
115200 8N1,100 ms 遥测。 - OpenMV UART2 接收
LINE/LTURN视觉命令,实现自动巡线和路口原地转弯。 - 里程运动命令:速度、距离、角速度、转角。
- MPU6050 软件 I2C 姿态读取,优先 DMP,失败时回退到加速度计姿态。
- SPI LCD 初始化显示,屏幕上电后显示项目状态和右轮 PWM 引脚提示。
- LCD 二级菜单,支持文件列表、车辆状态和车辆控制。
- Python 上位机曲线显示、PID/BASE 下发和 CSV 保存。
- 树莓派/OpenCV 版本巡线脚本,可通过串口向 MCU 发送同样的视觉命令。
- 树莓派 ROS2 Jazzy 激光雷达建图:雷达发布
/scan,MCU UART2 发送ODO里程计,树莓派发布/odom和 TF,slam_toolbox发布/map。
| 周期 | 任务 |
|---|---|
| 1 ms | SysTick 系统时基 |
| 20 ms | 速度控制更新 |
| 20 ms | MPU6050 姿态输出尝试 |
| 100 ms | 小车状态遥测 |
| 500 ms | UART2 里程计 ODO 输出 |
| 500 ms | LaunchPad 状态 LED 翻转 |
| 30 s | 心跳 RUN 输出 |
| 名称 | 来源 | 说明 |
|---|---|---|
BASE |
命令/遥测 | 共享基础目标速度,遥测中按 *100 输出,单位为 counts/20ms |
LD / RD |
遥测 | 左/右轮滤波后的实际速度,按 *100 输出 |
ERR |
遥测 | LD - RD,表示左右轮速度差,不是目标速度误差 |
LO / RO |
遥测 | 左/右轮最终 PWM 输出,按 *100 输出,单位为百分比 |
OUT |
遥测 | RO - LO,左右轮输出差 |
LT / RT |
遥测 | 左/右轮当前目标速度,按 *100 输出 |
LINE / LV |
遥测 | 巡线偏差和视觉数据是否有效 |
MO / MB |
遥测 | 运动控制模式和是否忙碌 |
KP/KI/KD |
PID 参数 | 速度环 PID 参数,遥测中按 *1000 输出 |
速度输出核心逻辑在 app/app_car_control.c:
PWM = 前馈输出 + PID(目标速度 - 实际速度)
主要前馈和限幅宏:
SPEED_LOOP_START_DUTY_PERCENT
SPEED_LOOP_FEED_FORWARD_PERCENT_PER_COUNT
LEFT_SPEED_FEED_FORWARD_SCALE
RIGHT_SPEED_FEED_FORWARD_SCALE
MAX_DUTY_PERCENT如果平均速度追不上目标,优先调 SPEED_LOOP_FEED_FORWARD_PERCENT_PER_COUNT;如果某一侧轮子长期偏快或偏慢,优先微调 LEFT_SPEED_FEED_FORWARD_SCALE 或 RIGHT_SPEED_FEED_FORWARD_SCALE。
所有命令都是 ASCII 文本,以换行结束;固件会自动忽略行首空格、制表符和 >,并把小写转成大写。
PID <kp> <ki> <kd>
BASE <speed_counts_per_period>
STOP
GET
ENCZERO
TELE 0|1
QUIET 0|1
命令会自动忽略行首空格、制表符和 >,并转成大写后再解析。PID 更新左右轮共用的速度环参数;BASE 设置共享目标速度并退出手动电机/运动控制;STOP 停止电机、清除运动和手动模式;GET 输出当前状态;ENCZERO 清零左右编码器计数;TELE 1/0 打开或关闭 100 ms 遥测;QUIET 1 等价于关闭遥测。
常用调试流程:
TELE 1
BASE 20
PID 4.0 0.2 0.4
GET
STOP
PWM <left_duty_percent> <right_duty_percent> [duration_ms]
MOTOR <duty_percent> [duration_ms]
PULSE <duty_percent> <duration_ms>
PWM 分别设置左右轮开环占空比,MOTOR 给左右轮同一个开环占空比,PULSE 是短时同占空比脉冲测试。MOTOR 和 PWM 默认运行 2000 ms,最长 10000 ms;持续时间填 0 时会一直保持手动模式,直到 STOP 或新的控制命令。PULSE 最长 2000 ms。占空比会被限制在 0% 到 85%。
DRIVE <linear_mm_s> <angular_deg_s>
SPEED <linear_mm_s>
DIST <distance_mm> <linear_mm_s>
TURN <angle_deg> [angular_deg_s]
ANGLE <angle_deg> [angular_deg_s]
MSTOP
DRIVE 同时给线速度和角速度,SPEED 只给直线速度,DIST 按距离运行,TURN/ANGLE 按角度原地转向;TURN/ANGLE 不写角速度时默认 90 deg/s。收到运动命令后,固件会停止 BASE 目标并返回 OK MOTION;MSTOP 只停止运动控制模式。
运动换算参数在 app/app_motion_control.c:
- 车轮直径:
40.3 mm - 轮距:
129.0 mm - 电机编码器:
11 PPR - 减速比:
20:1 - A/B 相计数倍率:
2 - 最大线速度:
100 mm/s - 最大角速度:
120 deg/s
LINE <valid> <error_pixels>
LTURN <angle_deg>
LINE 1 error 表示视觉识别有效,error 为黑线目标点相对画面中心的横向偏差;LINE 0 0 表示暂时丢线。固件收到有效巡线数据后,会在没有显式运动命令时自动以前进基础速度巡线。
LTURN 用于路口原地转弯,当前按 90 度约 275 个编码器 count 换算,默认转弯速度 15 counts/20ms,开环转弯占空比 15%,转弯后会延迟约 1900 ms 再恢复巡线。
MPU
MPUINIT
MPUZERO
MPU 输出初始化状态、I2C 引脚状态、DMP/FIFO 调试信息和失败计数。MPUINIT 重新初始化 MPU6050 和 DMP。MPUZERO 清零回退姿态算法的参考角。
FLASHID
IMG_SAVE <name> <width> <height> <size> <crc32>
IMG_WRITE <id> <width> <height> <size> <crc32>
IMG_LIST
IMG_INFO
IMG_SHOW <id>
IMG_SLOT_SHOW <slot>
IMG_DELETE <id>
IMG_DEFRAG
图片数据通过 UART0 发送,格式为 RGB565 原始像素流。IMG_SAVE 自动分配图片 ID,名称最长 15 个非空白字符;IMG_WRITE 覆盖指定 ID,id 范围为 0..254;IMG_SHOW 从索引中按 ID 显示图片;IMG_SLOT_SHOW 兼容旧版固定槽位地址;IMG_INFO 输出容量、已用空间、剩余空间和图片数量;IMG_LIST 输出所有索引项;IMG_DEFRAG 会把有效图片向前整理,回收删除图片留下的碎片空间。推荐使用 upper_pc/image_sender.py 或上位机工具发送图片,不手工敲二进制数据。
开启 TELE 1 后,固件每 100 ms 输出一行键值对,例如:
L=1234 R=1230 LD=1987 RD=1975 ERR=12 OUT=-35 LO=2100 RO=2065 LT=2000 RT=2000 KP=4000 KI=200 KD=400 BASE=2000 LINE=0 LV=0 MM=0 DG=0 MO=0 MB=0
姿态模块会单独输出:
ATT SRC=DMP PITCH=12 ROLL=-34 YAW=9000
其中 PITCH/ROLL/YAW 按 *100 输出,单位为度。心跳行格式如下:
RUN ms=30000 L=1234 R=1230 RXDROP=0 TXDROP=0 LED=PB22
UART2 会给树莓派 ROS2 里程计节点发送低频里程计行:
ODO L=1234 R=1230 LD=0 RD=0
当前固件已打开 UART2 RX,所以同一组 UART2 也可以接收视觉寻迹脚本发来的
LINE / LTURN。如果只跑建图,至少需要 MCU PA23 -> 树莓派 RX 和 GND;
如果只跑视觉寻迹,还需要树莓派 TX -> MCU PA24。当前不建议建图节点和视觉脚本
同时抢同一个树莓派串口。
| 功能 | 引脚 | 代码/SysConfig 名称 |
|---|---|---|
| 左轮 PWM / TB6612 PWMD | PA8 | PWM_LEFT / TIMA0 CCP0 |
| 左轮方向 / TB6612 DIN1, DIN2 | PA25, PA31 | GPIO_MOTOR_A_AIN1, GPIO_MOTOR_A_AIN2 |
| 右轮 PWM / TB6612 PWMA | PB4 | PWM_RIGHT / TIMA1 CCP0 |
| 右轮方向 / TB6612 AIN1, AIN2 | PB16, PB13 | GPIO_MOTOR_B_BIN1, GPIO_MOTOR_B_BIN2 |
| TB6612 STBY | PA27 | GPIO_MOTOR_A_STBY |
| 左轮编码器 E4A, E4B | PB2, PB3 | LEFT_C0, LEFT_C1 |
| 右轮编码器 E1A, E1B | PB0, PB1 | RIGHT_C0, RIGHT_C1 |
| UART0 调试 TX, RX | PA10, PA11 | UART_DEBUG |
| OpenMV UART2 TX, RX | PA23, PA24 | UART_OPENMV |
| OpenMV P4 TX -> MCU RX | PA24 | 接 MCU UART2 RX |
| OpenMV P5 RX <- MCU TX | PA23 | 接 MCU UART2 TX |
| 树莓派 GPIO14 TX -> MCU RX | PA24 | 视觉寻迹 LINE/LTURN 命令输入 |
| 树莓派 GPIO15 RX <- MCU TX | PA23 | ROS2 里程计 ODO 输出 |
| MPU6050 SCL, SDA | PA1, PA0 | 软件 I2C,开漏上拉 |
| LCD SPI MOSI, SCLK | PB8, PB9 | SPI_LCD,16 MHz |
| LCD RES, DC, CS, BLK | PB10, PB11, PB14, PB26 | GPIO_LCD |
| W25Q64 CS, SCLK, MOSI, MISO | PA12, PA13, PA14, PA15 | 软件 SPI,GPIO_W25Q64 |
| 菜单按键 上翻, 下翻, 返回/确认 | PA16, PA17, PA21 | GPIO_MENU_KEYS,内部上拉,按下接地 |
| 状态 LED | PB22 | GPIO_STATUS_LED_PB22_LED |
左右轮命名以车尾看向车头为准,并与 main.c 的当前接线注释一致:左轮接 TB6612 的 PWMD/DIN1/DIN2,右轮接 TB6612 的 PWMA/AIN1/AIN2。代码里 AO_Control() 输出左轮 PWM(PWM_LEFT/PA8)并控制 PA25、PA31 两个方向脚,BO_Control() 输出右轮 PWM(PWM_RIGHT/PB4)并控制 PB16、PB13 两个方向脚;这里的 GPIO_MOTOR_A/B 是工程里的 GPIO 分组名,不等同于物理左右轮命名。编码器极性在 hw/hw_encoder.c 中处理,前进方向计数为正。PB9 当前用于 LCD SCLK,不再是右轮 PWM。
三个按键均为一端接 GPIO、一端接 GND,GPIO 使用内部上拉,按下为低电平:
PA16: 上翻
PA17: 下翻
PA21: 返回/确认,短按返回,长按确认
一级菜单包括 File List、Vehicle Status 和 Vehicle Control。File List 会读取 W25Q64 图片索引,显示图片 ID、名称和尺寸,长按确认可显示选中图片;Vehicle Status 显示左右编码器、运动模式、忙碌状态和当前运动目标;Vehicle Control 提供停止电机、编码器清零、BASE 0、BASE 7、遥测开关等快捷操作。
cd upper_pc
python -m pip install -r requirements.txt
python serial_tuner.py图片写入 W25Q64:
python upper_pc/image_sender.py path\to\image.png --port COMx --show脚本会把 PNG/JPG 转成 320x170 RGB565,先发送 IMG_SAVE 命令自动分配图片 ID,再通过 UART0 发送二进制像素数据。需要覆盖指定 ID 时可加 --slot 3。固件端支持 FLASHID、IMG_SAVE <name> <width> <height> <size> <crc32>、IMG_WRITE <id> <width> <height> <size> <crc32>、IMG_LIST、IMG_INFO、IMG_DELETE <id>、IMG_DEFRAG 和 IMG_SHOW <id>。
上位机主要用于连接 COM 口、下发 PID/BASE/GET、绘制 TARGET、LD、RD、ERR 曲线并保存 CSV。更详细说明见 upper_pc/README.md。
如果只想确认 MCU UART2 有没有发送里程计,可用:
python upper_pc/uart_raw_dump.py --port COMx --baudrate 115200 --seconds 10OpenMV 脚本:
openmv/xunjitest1.py
该脚本在 QVGA 画面中做黑线二值化,输出 LINE <valid> <error>;检测到直角/T 字路口并满足触发条件后,会连续发送 LTURN <angle>。
树莓派/OpenCV 脚本:
python opencv/line_follow_opencv.py --serial COMx --baudrate 115200Linux/树莓派上默认摄像头为 /dev/video0,串口为 /dev/ttyAMA0。调试时可加 --dry-run 只看识别结果,不打开串口;加 --no-show 关闭窗口显示。
树莓派上的工作区路径:
/home/heximiao/hexi/ros2/slam/lidar启动建图:
cd /home/heximiao/hexi/ros2/slam/lidar
source /opt/ros/jazzy/setup.bash
source install/setup.bash
ros2 launch lidar_py_pkg mapping.launch.py这个 launch 会同时启动激光雷达、里程计、TF、slam_toolbox 和 RViz。
真实建图时需要树莓派和雷达跟着车一起移动;只让轮子空转只能验证 /odom
和串口,不能得到可靠地图。详细说明见 ros2/lidar/README_LIDAR_ROS2.md。
gpio_toggle_output/
├── .ccsproject CCS 工程描述文件
├── .cproject Eclipse/CDT C 工程配置
├── .project Eclipse 工程元数据
├── .clangd clangd 配置
├── gpio_toggle_output.syscfg TI SysConfig 外设配置源文件
├── main.c 程序入口、任务调度和中断入口
├── README.md 当前工程说明
├── app/ 应用层逻辑
│ ├── app_car_control.c/.h 小车主控制、速度环、串口命令、遥测、巡线融合
│ ├── app_image_store.c/.h W25Q64 图片索引、写入、列表、显示和删除
│ ├── app_line_follow.c/.h 解析 LINE 数据并生成巡线转向修正
│ ├── app_menu.c/.h LCD 二级菜单、按键扫描和快捷控制
│ ├── app_motion_control.c/.h 距离、速度、转角等运动命令换算
│ ├── app_mpu6050_attitude.c/.h MPU6050 姿态初始化、读取和命令处理
│ └── app_util.h 常用缩放、限幅和绝对值工具
├── bsp/ 板级支持层
│ ├── bsp_tb6612.c/.h TB6612 电机驱动
│ └── mpu6050/ MPU6050、DMP 和软件 I2C 驱动
├── hw/ MCU 外设封装
│ ├── hw_encoder.c/.h 编码器 GPIO 中断计数
│ ├── hw_lcd.c/.h SPI LCD 初始化、绘图和字符串显示
│ ├── hw_spi.c/.h LCD SPI 写总线封装
│ ├── hw_uart.c/.h UART0 调试串口
│ ├── hw_openmv_uart.c/.h UART2 OpenMV/视觉串口
│ ├── hw_w25q64.c/.h W25Q64 软件 SPI Flash 读写擦除
│ ├── lcdfont.h LCD 中文字库
│ └── lcdfont_ascii.h LCD ASCII 字库
├── mid/
│ └── mid_pid.c/.h 通用 PID 算法
├── openmv/
│ └── xunjitest1.py OpenMV 巡线和路口识别脚本
├── opencv/
│ └── line_follow_opencv.py 树莓派/OpenCV 巡线脚本
├── ros2/
│ └── lidar/ ROS2 Jazzy 雷达、里程计和 slam_toolbox 建图工作区
├── upper_pc/
│ ├── image_sender.py 图片转 RGB565 并通过 UART 写入 W25Q64
│ ├── serial_tuner.py PC 串口调参和曲线工具
│ ├── uart_raw_dump.py 原始串口接收检查工具
│ ├── requirements.txt Python 依赖
│ └── README.md 上位机说明
├── targetConfigs/ CCS 调试/烧录目标配置
└── Debug/ CCS Debug 构建输出,可重新生成
可以忽略或重新生成的目录包括 .git/、.agents/、.settings/、.ti_appdata/、Debug/ 和 Python 的 __pycache__/。
推荐在 Code Composer Studio 中导入本工程,直接执行 Build Project,然后 Debug/Load 到 MSPM0G3507 LaunchPad。
主要构建产物:
Debug/gpio_toggle_output.out
Debug/gpio_toggle_output.hex
如果使用仓库当前的 CCS 自动生成 makefile,也可以在 Debug 目录执行:
gmake all- 平均速度低于目标:优先增大
SPEED_LOOP_FEED_FORWARD_PERCENT_PER_COUNT。 - 起步吃力:适当增大
SPEED_LOOP_START_DUTY_PERCENT。 - 速度到目标但抖动:适当降低
Kp,或小幅增加Kd。 - 长时间存在静态误差:小幅增加
Ki。 - 左右轮某一侧长期偏快:微调
LEFT_SPEED_FEED_FORWARD_SCALE或RIGHT_SPEED_FEED_FORWARD_SCALE。 - 巡线过猛:降低
LINE_TURN_KP或LINE_TURN_LIMIT_COUNTS。 - 巡线速度太快或太慢:调整
LINE_FOLLOW_BASE_COUNTS和LINE_FOLLOW_MAX_COUNTS。