# Go1 PRO 协议规格 完全实测/抓包验证 (2026-06-20)。 ## LowCmd (主控 → MCU) **616 字节 Blowfish ECB 密文** (明文 616B, 加密后还是 616B),通过 UDP 发到 `192.168.123.10:8007`。 ### 明文 616B 布局 | 偏移 | 字节 | 字段 | 说明 | |-----|------|------|------| | 0..2 | 2 | head | `fe ef` | | 2..3 | 1 | levelFlag | `0xff` | | 3..4 | 1 | frameReserve | 0 | | 4..12 | 8 | SN | 全 0 (PRO 不填) | | 12..20 | 8 | version | 全 0 (PRO 不填) | | 20..22 | 2 | bandWidth | **`3a c0` (BE 字节序, 不是 LE!)** | | 22..562 | 540 | motorCmd[20] | 每电机 27B (mode + q + dq + tau + Kp + Kd + reserve) | | 562..566 | 4 | bms | 0 | | 566..606 | 40 | wirelessRemote | 0 | | 606..610 | 4 | reserve | 0 | | **610..612** | 2 | **固定填充** | **`00 00`** | | **612..616** | 4 | **CRC** | **`gen_crc(cmd[:612])` 裸 CRC, 不 XOR** | ### MotorCmd 单电机 27B ``` 0..1: mode (uint8) 1..5: q (float, Unitree 自定义 hex 编码, 不是标准 IEEE 754) 5..9: dq (同上) 9..11: tau (2B 自定义) 11..13: Kp (2B 自定义) 13..15: Kd (2B 自定义) 15..27: reserve (12B = 3 × 4B) ``` 各字段编解码见 `go1_pro_sdk/utils/common.py`。 ### 跟 EDU 版差异 (free-dog-sdk lowCmd) | 项 | EDU | PRO | |----|-----|-----| | 总长 | 614B | **616B** | | CRC 偏移 | 610..614 | **612..616** | | CRC 算法 | `encryptCrc(genCrc(cmd[:-6]))` (XOR 0xedcab9de) | **裸 `gen_crc(cmd[:612])`** | | bandWidth 字节序 | LE | **BE** | | SN/version | 用户提供 | 全 0 | | 是否加密 | 否 | **Blowfish ECB** | ## LowState (MCU → 主控) **858 字节 Blowfish ECB 密文** (Blowfish 8B 块, 858 取 8 对齐 = 856B 解密)。 解密后 807B 是有效 LowState,后面 49B 填充/MCU 内部。 ### 明文 807B 布局 (跟 free-dog-sdk 的 ucl/lowState.py 一致) | 偏移 | 字节 | 字段 | |-----|------|------| | 0..2 | 2 | head | | 2..3 | 1 | levelFlag | | 3..4 | 1 | frameReserve | | 4..12 | 8 | SN | | 12..20 | 8 | version | | 20..22 | 2 | bandWidth | | 22..75 | 53 | IMU (quaternion 16B + gyro 12B + accel 12B + rpy 12B + temp 1B) | | 75..715 | 640 | motorState[20] × 32B | | 715..739 | 24 | BMS | | 739..759 | 20 | footForce / footForceEst (混合) | | 759..799 | 40 | **wirelessRemote** (遥控器原始数据) | | 799..803 | 4 | reserve | | 803..807 | 4 | CRC | ## Blowfish **ECB 模式**,**LE word 字节序** (Unitree ARM64 LDP 读取顺序,不是标准 BE Blowfish)。 state (4168B) = P[18] + S0[256] + S1[256] + S2[256] + S3[256],每项 4B uint32。 ### F 函数 ```python def F(X): a = (X >> 24) & 0xff b = (X >> 16) & 0xff c = (X >> 8) & 0xff d = X & 0xff return ((((S0[a] + S1[b]) & 0xFFFFFFFF) ^ S2[c]) + S3[d]) & 0xFFFFFFFF ``` **注意 Python 运算符优先级**: `+` 比 `^` 高,必须完整括号否则结果错。 ### 16 轮 Feistel 标准 Blowfish 算法 (见 `go1_pro_sdk/codec/blowfish.py`),无定制。 ### 已知明密文对 (用于验证 state) ``` encrypt(0000000000000000) = 12aa0354236b66e3 encrypt(feefff0004020305) = 5d9874ad7eee5851 encrypt(0802010000010900) = c5ce0887ceb86f91 ``` ## 遥控器 wirelessRemote (40B) 布局 (Unitree 公开 `xRockerBtnDataStruct`): | 偏移 | 字节 | 字段 | |-----|------|------| | 0..2 | 2 | head 0x55 0xAA | | 2..4 | 2 | btn bitmap (uint16 LE) | | 4..8 | 4 | lx (float, -1..+1) | | 8..12 | 4 | rx | | 12..16 | 4 | ry | | 16..20 | 4 | L2 (0..1, 模拟扳机) | | 20..24 | 4 | ly | | 24..40 | 16 | reserved | ### 按键 bitmap | 位 | 按键 | 位 | 按键 | |----|------|----|------| | 0x0001 | R1 | 0x0100 | A | | 0x0002 | L1 | 0x0200 | B | | 0x0004 | START | 0x0400 | X | | 0x0008 | SELECT | 0x0800 | Y | | 0x0010 | R2 | 0x1000 | UP | | 0x0020 | L2 | 0x2000 | RIGHT | | 0x0040 | F1 | 0x4000 | DOWN | | 0x0080 | F2 | 0x8000 | LEFT | ## MCU 客户端切换机制 MCU 按"最近发包者的 IP:port"切换客户端。也就是说: - 我们 Mac 不需要绑特定端口,任意 OS 分配的端口都行 - 一旦我们发包,MCU 把后续 LowState 回复给我们 (Legged_sport 不再收到) - Legged_sport 没收到 state 就停止 sendto,等价于"挂起" **含义**: 必须先在 Pi 上杀 `keep_sport_alive.sh`、`Legged_sport`、`appTransit` 三个抢占者,否则会跟 MCU 反复切换导致控制失败。