- 校正舵机中位、转向符号和阿克曼后轮差速模型\n- 增加航向角速度辅助及遥控通道调试开关\n- 将速度环提升至 200Hz,并按实际 dt 计算 PI 积分\n- 将 IMU 启动校准缩短为 2 秒\n- 为 USART3 增加 DMA 发送和 MCU 采样时间戳
5.9 KiB
5.9 KiB
下位机 UART 发送协议 v2(带采样时间戳)
本文件描述 STM32 下位机通过 USART3(ROS 口,波特率 921600) 周期发送给上位机的 传感器数据帧格式。v2 在原 24 字节帧的基础上,于帧尾追加了会话 ID 与两个采样 时间戳字段,用于上位机做 IMU / 轮速 / 雷达的时间对齐。
- 对应源码:HARDWARE/usartx.c、HARDWARE/usartx.h
- 时间源实现:HARDWARE/timer.c 的
TIM7_Init()/mcu_time_us() - 设计背景与总体原则见 时间戳.md
1. 帧总览
| 项目 | 值 |
|---|---|
帧头 FRAME_HEADER |
0x7B |
帧尾 FRAME_TAIL |
0x7D |
帧长 SEND_DATA_SIZE |
44 字节(v1 为 24 字节) |
| 发送周期 | 200 Hz(data_task,USART3 非阻塞 DMA 发送) |
| 字节序 | 多字节字段一律 大端(高字节在前) |
| 校验 | 单字节 XOR,覆盖 byte 0..41 |
设计约束:byte 0..21 与 v1 完全一致,新增字段全部放在校验位之前的尾部, 使只读取旧字段的解析器仍然兼容。
2. 字节布局
| 偏移 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 1 | Frame_Header | u8 | 帧头 0x7B |
| 1 | 1 | Flag_Stop | u8 | 软件失能标志(1=停车) |
| 2 | 2 | X_speed | int16 | X 轴线速度,单位 mm/s,大端 |
| 4 | 2 | Y_speed | int16 | Y 轴线速度,单位 mm/s,大端 |
| 6 | 2 | Z_speed | int16 | Z 轴角速度,单位 mm/s(等效),大端 |
| 8 | 2 | Accel_X | int16 | 加速度计 X(ROS 坐标),大端 |
| 10 | 2 | Accel_Y | int16 | 加速度计 Y(ROS 坐标),大端 |
| 12 | 2 | Accel_Z | int16 | 加速度计 Z(ROS 坐标),大端 |
| 14 | 2 | Gyro_X | int16 | 陀螺仪 X(ROS 坐标),大端 |
| 16 | 2 | Gyro_Y | int16 | 陀螺仪 Y(ROS 坐标),大端 |
| 18 | 2 | Gyro_Z | int16 | 陀螺仪 Z(ROS 坐标),大端 |
| 20 | 2 | Power_Voltage | int16 | 电池电压×1000,大端 |
| 22 | 4 | session_id | uint32 | 本次 MCU 启动会话 ID,大端 |
| 26 | 8 | speed_sample_time_us | uint64 | 编码器采样时刻(μs),大端 |
| 34 | 8 | imu_sample_time_us | uint64 | IMU 采样时刻(μs),大端 |
| 42 | 1 | Checksum | u8 | byte 0..41 的 XOR |
| 43 | 1 | Frame_Tail | u8 | 帧尾 0x7D |
加粗行为 v2 相对 v1 的新增字段。
3. 时间源
时间戳由自由运行的 TIM7 提供,见 HARDWARE/timer.c:
- TIM7 挂在 APB1,定时器时钟 84 MHz,预分频 84 → 1 MHz 计数(1 tick = 1 μs)。
- 16 位计数器每 65.536 ms 回绕一次,更新中断把高 32 位软件计数
g_time_high加一。 mcu_time_us()返回(g_time_high << 16) | CNT,即自上电以来的单调递增微秒数, 可用位宽 48 位(约 8.9 年不回绕)。- TIM2~TIM5 被四路编码器占用、TIM8 被航模遥控输入捕获占用,故选空闲的 TIM7。
- TIM7 更新中断优先级设为 5(数值大于
configMAX_SYSCALL_INTERRUPT_PRIORITY对应的抢占门限),ISR 内只做自增,不调用任何 FreeRTOS API。
注意:
mcu_time_us()只增不减,绝不因校时而跳变。上位机负责维护T_host = a·t_mcu + b的时钟映射(见 时间戳.md 第 7、8 节)。
4. 两个时间戳分别打在哪里
v1 只有"发送时刻"隐含语义,无法表达 IMU(100 Hz)与轮速(200 Hz)是异步采集的。
v2 在各自真实采样点打点,写入全局变量,data_transition() 打包时快照:
| 时间戳 | 打点位置 | 采样率 |
|---|---|---|
speed_sample_time_us |
balance.c Get_Velocity_Form_Encoder() 读编码器锁存计数器的瞬间 |
200 Hz |
imu_sample_time_us |
MPU6050.c MPU6050_task I2C 读取前 |
100 Hz |
因此一帧内同时带两个时间戳:上位机应把加速度/角速度对齐到 imu_sample_time_us,
把 X/Y/Z 速度对齐到 speed_sample_time_us,而不是共用一个发送时刻。
IMU 打点在 I2C 突发读取之前,是在没有 DRDY(数据就绪)中断条件下能取到的、 最接近物理采样的时刻。若日后接入 MPU 的 DRDY 引脚中断,可把打点移到中断里进一步减小抖动。
5. session_id
- 定义在 usartx.c:
g_session_id。 - 当前实现为固定值
0xA5A5A5A5。 - 用途:上位机据此判断下位机是否重启过(重启后
mcu_time_us()从 0 重新计时, 时钟映射a, b必须重新拟合)。 - 待办:如需真正区分每次上电,应在启动时用熵源(如未初始化 RAM、启动 tick 等) 生成一次性随机值。当前固定值只能表明"协议版本一致",不能区分不同启动会话。
6. 校验与解析建议
- 校验位
buffer[42] = XOR(buffer[0..41]),与 v1 的计算方式相同,只是范围从 22 扩到 42。 - 上位机解析步骤:
- 同步帧头
0x7B; - 读满 44 字节;
- 校验
buffer[43] == 0x7D且XOR(buffer[0..41]) == buffer[42]; - 按上表大端解出各字段。
- 同步帧头
- 多字节字段务必按大端手动组装,不要直接
memcpy结构体(编译器填充/对齐/端序差异)。
7. 其他发送通道
| 通道 | 现状 |
|---|---|
| USART3(ROS 主口,DMA) | 已用 SEND_DATA_SIZE,自动发送完整 44 字节 |
| USART1 / USART5 | 已改用 SEND_DATA_SIZE,发送完整 44 字节 |
| CAN | 当前未被调用;CAN_SEND() 仍按 8×3=24 字节分包,不含时间戳字段。如需经 CAN 传时间戳需另行扩展分包 |
8. 上位机适配提醒
USART3 收到的帧已从 24 字节变为 44 字节。上位机(ROS 端)解析代码必须同步更新: 帧长、校验范围、以及尾部三个新字段的读取。否则会因帧长不符导致解析错位。