Files
Chassis/docs/底盘通信协议指南.md
SkyLiCoder 022e3424da 实现完整串口指令协议:模式切换、手动控制、PID在线调参
- 新增 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
2026-06-07 16:23:10 +08:00

223 lines
9.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 串口通信协议
## 概述
- **物理层**: 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 ──────→ │ │
└──────────┘ └──────────┘
```
**典型操作流程:**
1. **上电默认**:自动模式,位置环+角度环+速度环全部使能
2. **切换到手动模式**:发送 `A7 02 A9`,然后周期发送 `0xA6` 控制帧驱动底盘
3. **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 语言帧构造示例
```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
}
```