022e3424da
- 新增 PIDClear() 仅清运行时状态,保留 Kp/Ki/Kd 等配置 - 命令帧 0xA7 (3B): 自动/手动/调试三模式切换,带模式状态追踪 - 手动控制帧 0xA6 (8B): 解析 motor_speed_base(int16) + angle_velocity(float) - 调试调参帧 0xA8 (38B): 在线更新全部9个 Kp/Ki/Kd 参数 - Command 解析纳入 Task_50ms (20Hz) 定时执行 - 添加通信协议文档 docs/通信协议.md
9.3 KiB
9.3 KiB
串口通信协议
概述
- 物理层: UART2,引脚 TX=B15 / RX=B16
- 波特率: 115200 bps
- 数据位: 8,无校验,1 停止位(默认 UART 配置)
- 字节序: 多字节数值均采用小端序 (little-endian)
- 校验方式: 累加和校验(checksum = 帧内校验位之前所有字节之和,截断为 uint8_t)
- 软件 FIFO: 16 字节环形缓冲区,UART RX 中断中写入,主循环
command_task()中解析
工作模式
系统有三种工作模式,通过命令帧切换:
| 模式 | 枚举值 | 说明 | 使能的控制环 |
|---|---|---|---|
| 自动模式 | COMMAND_MODE_AUTO (0) |
视觉追踪 / 全闭环 | 电机速度环 + 角度环 + 位置环 |
| 手动模式 | COMMAND_MODE_MANUAL (1) |
手动遥控 | 电机速度环 + 角度环 |
| 调试模式 | COMMAND_MODE_DEBUG (2) |
PID 参数整定 | 无(所有控制环关闭) |
数据帧定义
1. 命令帧 0xA7(3 字节)
切换工作模式。
┌────────┬───────┬──────────┐
│ SOF │ cmd │ checksum │
│ 0xA7 │ 1 B │ 1 B │
└────────┴───────┴──────────┘
| 偏移 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 1 | SOF | uint8_t | 帧头,固定值 0xA7 |
| 1 | 1 | cmd | uint8_t | 命令码 |
| 2 | 1 | checksum | uint8_t | (0xA7 + cmd) & 0xFF |
cmd 取值:
| 值 | 功能 | 对应函数 |
|---|---|---|
0x01 |
切换到自动模式 | SwitchToAutoMode() |
0x02 |
切换到手动模式 | SwitchToManualMode() |
0x03 |
切换到调试模式 | SwitchToDebugMode() |
示例(切换手动模式): A7 02 A9
2. 手动控制帧 0xA6(8 字节)
仅在手动模式下生效,用于实时控制底盘运动。
┌────────┬────────────────────┬─────────────────────┬──────────┐
│ SOF │ motor_speed_base │ angle_velocity │ checksum │
│ 0xA6 │ int16_t (2 B) │ float (4 B) │ 1 B │
└────────┴────────────────────┴─────────────────────┴──────────┘
| 偏移 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 1 | SOF | uint8_t | 帧头,固定值 0xA6 |
| 1 | 2 | motor_speed_base | int16_t (LE) | 基础直行速度,范围 -50~+50 |
| 3 | 4 | angle_velocity | float (LE) | 角度速率,单位 °/s |
| 7 | 1 | checksum | uint8_t | (0xA6 + 前 6 字节之和) & 0xFF |
控制算法:
motor_speed_left_ref = motor_speed_base - speed_dif
motor_speed_right_ref = motor_speed_base + speed_dif
其中 speed_dif 由角度环 PID 根据 yaw_ref 与当前 imu_angle.yaw 计算得出。手动模式下 yaw_ref 以 angle_velocity 速率持续累加:
yaw_ref += angle_velocity * dt (dt = 0.02s)
3. 调试调参帧 0xA8(38 字节)
仅在调试模式下生效,一次性更新所有 PID 参数(Kp / Ki / Kd)。
┌────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┬──────────┐
│ SOF │ Kp_speed │ Ki_speed │ Kd_speed │ Kp_yaw │ Ki_yaw │ Kd_yaw │ Kp_pos │ Ki_pos │ Kd_pos │ checksum │
│ 0xA8 │ float(4) │ float(4) │ float(4) │ float(4) │ float(4) │ float(4) │ float(4) │ float(4) │ float(4) │ 1 B │
└────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
| 偏移 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 1 | SOF | uint8_t | 帧头,固定值 0xA8 |
| 1 | 4 | Kp_speed | float (LE) | 电机速度环比例系数(左右共用) |
| 5 | 4 | Ki_speed | float (LE) | 电机速度环积分系数 |
| 9 | 4 | Kd_speed | float (LE) | 电机速度环微分系数 |
| 13 | 4 | Kp_yaw | float (LE) | 角度环比例系数 |
| 17 | 4 | Ki_yaw | float (LE) | 角度环积分系数 |
| 21 | 4 | Kd_yaw | float (LE) | 角度环微分系数 |
| 25 | 4 | Kp_position | float (LE) | 位置环比例系数 |
| 29 | 4 | Ki_position | float (LE) | 位置环积分系数 |
| 33 | 4 | Kd_position | float (LE) | 位置环微分系数 |
| 37 | 1 | checksum | uint8_t | (0xA8 + 前 36 字节之和) & 0xFF |
各环默认参数:
| 控制环 | Kp | Ki | Kd | 输出限幅 | 采样周期 | 优化环节 |
|---|---|---|---|---|---|---|
| 电机速度环(左/右) | 300.0 | 150.0 | 0.0 | ±3000 (MOTOR_MAX) | 10ms | — |
| 角度环 | 1.0 | 0.0 | 0.0 | ±25 (°/s) | 20ms | 偏航角误差修正 |
| 位置环 | 0.5 | 0.0 | 0.0 | ±90 (°/s) | 20ms | — |
注意:调试模式下所有控制任务已关闭,电机停转。修改参数后需切换到自动或手动模式才能观察效果。修改的参数直接写入 PID 实例的 Kp/Ki/Kd 字段,不清除积分等运行时状态。
4. 角度速率前馈帧 0xA9(6 字节)— MCU → PC
由 MCU 经 DMA 主动发送,上报位置环计算的角度速率,供云台等设备做前馈控制。
┌────────┬─────────────────────┬──────────┐
│ SOF │ angle_velocity │ checksum │
│ 0xA9 │ float (4 B) │ 1 B │
└────────┴─────────────────────┴──────────┘
| 偏移 | 长度 | 字段 | 类型 | 说明 |
|---|---|---|---|---|
| 0 | 1 | SOF | uint8_t | 帧头,固定值 0xA9 |
| 1 | 4 | angle_velocity | float (LE) | 角度速率,单位 °/s |
| 5 | 1 | checksum | uint8_t | (0xA9 + 前 4 字节之和) & 0xFF |
发送触发条件:位置环控制任务使能时,每 20ms(50Hz)在 position_control_task() 中调用 command_send_angle_velocity()。
通信流程
┌──────────┐ ┌──────────┐
│ PC │ ──── 0xA7 cmd ────────→ │ MSPM0 │
│ (上位机) │ ←─── 0xA9 feedback ──── │ (MCU) │
│ │ ──── 0xA6 manual ─────→ │ │
│ │ ──── 0xA8 debug ──────→ │ │
└──────────┘ └──────────┘
典型操作流程:
- 上电默认:自动模式,位置环+角度环+速度环全部使能
- 切换到手动模式:发送
A7 02 A9,然后周期发送0xA6控制帧驱动底盘 - PID 调参:发送
A7 03 AA进入调试模式(电机停转),发送0xA8调参帧更新参数,再发A7 01 A8或A7 02 A9切换回运行模式观察效果
模式切换副作用
每次切换模式时,系统会执行以下操作:
| 操作 | 自动模式 | 手动模式 | 调试模式 |
|---|---|---|---|
设置 current_mode |
AUTO (0) | MANUAL (1) | DEBUG (2) |
| 清除 PID 运行时状态 | 全部 4 个 PID | 速度环+角度环 | — |
yaw_ref |
= 当前 imu_angle.yaw |
= 当前 imu_angle.yaw |
— |
angle_velocity |
归零 | 归零 | — |
motor_speed_ref |
归零 | 归零 | — |
motor_speed_base |
= 10 (AUTO_MODE_BASE_SPEED) | 归零 | — |
| 电机 PWM | 归零 | 归零 | 归零 |
| 速度环使能 | ✅ | ✅ | ❌ |
| 角度环使能 | ✅ | ✅ | ❌ |
| 位置环使能 | ✅ | ❌ | ❌ |
| 蜂鸣器 | 100ms | 200ms | 300ms |
C 语言帧构造示例
#include <stdint.h>
#include <string.h>
// --- 构造 0xA7 命令帧 ---
void build_cmd_frame(uint8_t *buf, uint8_t cmd) {
buf[0] = 0xA7;
buf[1] = cmd;
buf[2] = (uint8_t)(0xA7 + cmd);
// 发送 buf, 长度 3
}
// --- 构造 0xA6 手动控制帧 ---
void build_manual_frame(uint8_t *buf, int16_t speed, float yaw_rate) {
buf[0] = 0xA6;
memcpy(&buf[1], &speed, 2); // int16_t, 小端
memcpy(&buf[3], &yaw_rate, 4); // float, 小端
uint8_t sum = 0xA6;
for (int i = 1; i < 7; i++) sum += buf[i];
buf[7] = sum;
// 发送 buf, 长度 8
}
// --- 构造 0xA8 调试调参帧 ---
void build_debug_frame(uint8_t *buf, float kp_s, float ki_s, float kd_s,
float kp_y, float ki_y, float kd_y,
float kp_p, float ki_p, float kd_p) {
buf[0] = 0xA8;
memcpy(&buf[1], &kp_s, 4);
memcpy(&buf[5], &ki_s, 4);
memcpy(&buf[9], &kd_s, 4);
memcpy(&buf[13], &kp_y, 4);
memcpy(&buf[17], &ki_y, 4);
memcpy(&buf[21], &kd_y, 4);
memcpy(&buf[25], &kp_p, 4);
memcpy(&buf[29], &ki_p, 4);
memcpy(&buf[33], &kd_p, 4);
uint8_t sum = 0xA8;
for (int i = 1; i < 37; i++) sum += buf[i];
buf[37] = sum;
// 发送 buf, 长度 38
}