Files
balance-board/doc/BALANCE_BOARD_PROTOCOL.md
T
pNexus ffafbc4c6a refactor(protocol): 板面几何与 CoP 计算统一为 cm 单位,force 改 int16
compute_cop 直接输出 int16_t (cm/kg),消除打包时的浮点转换;
BOARD_HALF_WIDTH/LENGTH 从 mm 改 cm,传感器坐标表同步更新。
2026-05-12 16:23:55 +08:00

205 lines
7.1 KiB
Markdown
Raw 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.
# 平衡板协议说明
本文说明当前固件里的数据包格式和 BLE 通讯方式。
## 1. BLE 通讯方式
平衡板工作在 BLE `Peripheral` 角色,通过 Nordic UART ServiceNUS)与基站通信。
- 上行方向:平衡板 -> 基站,使用 NUS `Notify`
- 下行方向:基站 -> 平衡板,使用 NUS `Write`
- 广播内容:包含设备名和 NUS 128-bit UUID
### 1.1 NUS UUID
| 项目 | UUID | 方向 | 说明 |
|------|------|------|------|
| Service | `6E400001-B5A3-F393-E0A9-E50E24DCCA9E` | - | NUS 主服务 |
| TX Characteristic | `6E400003-B5A3-F393-E0A9-E50E24DCCA9E` | 平衡板 -> 基站 | Notify,上报 CoP 和编码器数据 |
| RX Characteristic | `6E400002-B5A3-F393-E0A9-E50E24DCCA9E` | 基站 -> 平衡板 | Write,下发阻力参数和控制命令 |
### 1.2 当前连接行为
- 连接建立后,平衡板会请求 `7.5-15 ms` 连接区间(`min=6, max=12`)。
- 所有数据通过 NUS 二进制帧发送,不走文本协议。
## 2. 数据包总览
当前协议使用双帧头 `0xAA 0x55` 作为包起始标记。所有多字节字段按 Little Endian 编码。
每帧末尾附加 1 字节 CRC-8/MAXIM 校验(poly=`0x31`, init=`0x00`, refin=true, refout=true, xorout=`0x00`)。校验范围为帧头之后、CRC 字节之前的所有字段(即跳过 `sync0`/`sync1`)。调用方式:`crc8(data + 2, len - 3, 0x31, 0x00, true)`
| 类型名 | 值 | 方向 | 长度 | 频率 | 说明 |
|--------|----|------|------|------|------|
| `PROTO_TYPE_COP` | `0x01` | 平衡板 -> 基站 | `11` | 50 Hz | 压力中心帧 |
| `PROTO_TYPE_ENCODER` | `0x02` | 平衡板 -> 基站 | `13` | TBD | 电机编码器帧(预留) |
| `PROTO_TYPE_RESISTANCE` | `0x10` | 基站 -> 平衡板 | `16` | 按需 | 阻力参数帧 |
| `PROTO_TYPE_SPOTTER` | `0x11` | 基站 -> 平衡板 | `9` | 按需 | 保护模式帧 |
| `PROTO_TYPE_HEARTBEAT` | `0x20` | 基站 -> 平衡板 | `5` | ~1 Hz | 心跳帧 |
## 3. 上行数据包
### 3.1 压力中心帧 (CoP)
CoP 帧固定 11 字节,结构如下:
| 字节偏移 | 字段 | 类型 | 值/范围 | 说明 |
|----------|------|------|---------|------|
| `0` | `sync0` | `uint8` | `0xAA` | 帧头第 1 字节 |
| `1` | `sync1` | `uint8` | `0x55` | 帧头第 2 字节 |
| `2` | `type` | `uint8` | `0x01` | CoP 包类型 |
| `3` | `flags` | `uint8` | 见下表 | 状态标志位 |
| `4-5` | `cop_x` | `int16 LE` | - | 压力中心 X 坐标 (cm) |
| `6-7` | `cop_y` | `int16 LE` | - | 压力中心 Y 坐标 (cm) |
| `8-9` | `force` | `int16 LE` | - | 总重量 (kg) |
| `10` | `crc` | `uint8` | - | CRC-8/MAXIM 校验 |
#### flags 位定义
| 位 | 名称 | 说明 |
|----|------|------|
| bit0 | `COP_FLAG_FORCE_VALID` | `1` 表示总力超过阈值,CoP 坐标有意义 |
| bit1-7 | 预留 | 填 `0` |
#### CoP 解算公式
传感器布局(俯视):
```text
S4 (FL) -------- S1 (FR)
| center |
S3 (BL) -------- S2 (BR)
```
传感器坐标(单位:cm,板面中心为原点):
| 传感器 | 位置 | X | Y |
|--------|------|---|---|
| S1 | 右前 (FR) | `+10` | `+10` |
| S2 | 右后 (BR) | `+10` | `-10` |
| S3 | 左后 (BL) | `-10` | `-10` |
| S4 | 左前 (FL) | `-10` | `+10` |
计算公式:
```text
F_total = F0 + F1 + F2 + F3
CoP_X = (F0 * x0 + F1 * x1 + F2 * x2 + F3 * x3) / F_total
CoP_Y = (F0 * y0 + F1 * y1 + F2 * y2 + F3 * y3) / F_total
```
`F_total < COP_MIN_FORCE_THRESHOLD` 时,CoP 坐标无意义,`flags.bit0 = 0``cop_x``cop_y``0`
#### 示例
下面是一个 CoP 帧示例(CoP 有效,cop_x=3 cm, cop_y=5 cm, force=75 kg):
```text
AA 55 01 01 03 00 05 00 4B 00 XX
```
含义如下:
- `AA 55`:双帧头
- `01`CoP 包
- `01`flags = `force_valid`
- `03 00``cop_x = 3` cmLE int16
- `05 00``cop_y = 5` cmLE int16
- `4B 00``force = 75` kgLE uint16
- `XX`CRC-8/MAXIM(对字节 `[2..9]` 计算)
### 3.2 电机编码器帧(预留)
编码器帧固定 13 字节,结构如下。该帧目前仅在协议中预留定义,固件暂不实现。
| 字节偏移 | 字段 | 类型 | 值/范围 | 说明 |
|----------|------|------|---------|------|
| `0` | `sync0` | `uint8` | `0xAA` | 帧头第 1 字节 |
| `1` | `sync1` | `uint8` | `0x55` | 帧头第 2 字节 |
| `2` | `type` | `uint8` | `0x02` | 编码器包类型 |
| `3` | `flags` | `uint8` | `0` | 预留 |
| `4-7` | `cable_length` | `float32 LE` | - | 拉索长度 (mm) |
| `8-11` | `velocity` | `float32 LE` | - | 拉索速度 (mm/s) |
| `12` | `crc` | `uint8` | - | CRC-8/MAXIM 校验 |
## 4. 下行数据包
### 4.1 阻力参数帧
阻力参数帧固定 16 字节,用于基站向平衡板下发电机阻力控制参数。
| 字节偏移 | 字段 | 类型 | 值/范围 | 说明 |
|----------|------|------|---------|------|
| `0` | `sync0` | `uint8` | `0xAA` | 帧头第 1 字节 |
| `1` | `sync1` | `uint8` | `0x55` | 帧头第 2 字节 |
| `2` | `type` | `uint8` | `0x10` | 阻力参数包类型 |
| `3-6` | `K` | `float32 LE` | - | 刚度 (N/m) |
| `7-10` | `B` | `float32 LE` | - | 阻尼 (Ns/m) |
| `11-14` | `Tau` | `float32 LE` | - | 时间常数 (s) |
| `15` | `crc` | `uint8` | - | CRC-8/MAXIM 校验 |
#### 示例
```text
AA 55 10 00 00 C8 42 00 00 48 41 CD CC 4C 3E XX
```
含义如下:
- `AA 55 10`:阻力参数帧头
- `00 00 C8 42``K = 100.0` N/m
- `00 00 48 41``B = 12.5` Ns/m
- `CD CC 4C 3E``Tau = 0.2` s
- `XX`CRC-8/MAXIM
### 4.2 Spotter Mode 帧
Spotter 帧固定 9 字节,用于启用或关闭保护模式。
| 字节偏移 | 字段 | 类型 | 值/范围 | 说明 |
|----------|------|------|---------|------|
| `0` | `sync0` | `uint8` | `0xAA` | 帧头第 1 字节 |
| `1` | `sync1` | `uint8` | `0x55` | 帧头第 2 字节 |
| `2` | `type` | `uint8` | `0x11` | Spotter 包类型 |
| `3-6` | `threshold` | `float32 LE` | - | 保护力阈值 (N) |
| `7` | `enable` | `uint8` | `0``1` | `0` = 关闭, `1` = 启用 |
| `8` | `crc` | `uint8` | - | CRC-8/MAXIM 校验 |
#### 示例
启用 Spotter Mode,阈值 500 N
```text
AA 55 11 00 00 FA 43 01 XX
```
### 4.3 心跳帧
心跳帧固定 5 字节,基站以约 1 Hz 频率发送,用于活性检测。
| 字节偏移 | 字段 | 类型 | 值/范围 | 说明 |
|----------|------|------|---------|------|
| `0` | `sync0` | `uint8` | `0xAA` | 帧头第 1 字节 |
| `1` | `sync1` | `uint8` | `0x55` | 帧头第 2 字节 |
| `2` | `type` | `uint8` | `0x20` | 心跳包类型 |
| `3` | `counter` | `uint8` | `0-255` | 滚动计数 |
| `4` | `crc` | `uint8` | - | CRC-8/MAXIM 校验 |
#### 示例
```text
AA 55 20 05 XX
```
含义:心跳包,计数 `5``XX` 为 CRC。
## 5. 当前实现约束
- CoP 帧固定 11 字节,可在最小 ATT MTU 23(NUS 有效负载 20 字节)下传输。
- 50 Hz CoP 上行流隐式充当平衡板到基站的活性检测,因此心跳仅为下行。
- 传感器通道到物理位置的映射:MUX `{0x01, 0x23, 0x45, 0x67}` → FL/FR/BR/BL。
- `BOARD_HALF_WIDTH_CM = 10``BOARD_HALF_LENGTH_CM = 10`(后续可能调整)。
- `ADC_TO_FORCE_SCALE` 当前为占位值 `1.0f`,待 10 kg 砝码标定后修正。
- ARM Cortex-M33 为小端架构,`float32` 在 packed struct 中天然符合 Little Endian 要求。