# 山猫 M20 basic_server 通信协议（UDP + JSON）

本协议基于 TCP/UDP 传输层，本项目使用 **UDP + JSON** 模式。机器人本体为服务端，上位机（Android App）为客户端。

- **协议版本**: V1.2.0
- **更新日期**: 2026-05-18
- **服务端地址**: `10.21.31.103:30000` (UDP)
- **载荷编码**: JSON（协议头 `ASDU格式` 字段填 `0x01`）

---

## 1. APDU 报文结构

```
+---------------------+------------------+
|  Header (16 bytes)  |  ASDU (JSON 文本) |
+---------------------+------------------+
```

### 1.1 Header（固定 16 字节，小端 LE）

| 偏移 | 长度 | 字段 | 值 | 说明 |
| ---: | ---: | --- | --- | --- |
| 0 | 1 | Sync0 | `0xEB` | 固定 |
| 1 | 1 | Sync1 | `0x91` | 固定 |
| 2 | 1 | Sync2 | `0xEB` | 固定 |
| 3 | 1 | Sync3 | `0x90` | 固定 |
| 4 | 2 | Length | u16 LE | ASDU 字节长度，最大 65535 |
| 6 | 2 | MsgId | u16 LE | 请求/响应配对；请求方从 0 递增，回绕 65535→0 |
| 8 | 1 | Format | `0x01` | JSON（XML 为 `0x00`） |
| 9 | 7 | Reserved | `0x00`×7 | 预留 |

### 1.2 ASDU（JSON 格式）

固定外层包裹 `PatrolDevice`，包含 4 个通用字段：

| 字段 | 类型 | 含义 |
| --- | --- | --- |
| `Type` | int | 消息类型（十进制） |
| `Command` | int | 命令码（十进制） |
| `Time` | string | 本地时区，格式 `YYYY-MM-DD HH:MM:SS` |
| `Items` | object | 参数项，视 Type/Command 而定 |

```json
{
  "PatrolDevice": {
    "Type": 100,
    "Command": 100,
    "Time": "2026-07-17 12:00:00",
    "Items": {}
  }
}
```

---

## 2. 控制类指令（上位机 → 机器人）

### 2.1 心跳指令

| Type | Command | 频率 |
| ---: | ---: | --- |
| 100 | 100 | ≥1 Hz |

`Items` 为空。服务端记录来源 IP/端口，并按需推送状态上报（见第 3 节）。

---

### 2.2 使用模式切换

| Type | Command | 说明 |
| ---: | ---: | --- |
| 1101 | 5 | 切换机器人使用模式 |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Mode` | int | `0`=常规模式 · `1`=导航模式 · `2`=辅助模式 |

```json
{
  "PatrolDevice": {
    "Type": 1101, "Command": 5, "Time": "2026-07-17 12:00:00",
    "Items": { "Mode": 0 }
  }
}
```

- **常规模式**：支持轴指令（Type=2, Cmd=21）
- **导航模式**：支持速度指令（Type=2, Cmd=25）和导航任务

---

### 2.3 运动状态转换

| Type | Command | 说明 |
| ---: | ---: | --- |
| 2 | 22 | 机器人运动状态转换 |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `MotionParam` | int | `0`=空闲 · `1`=站立 · `2`=关节阻尼/软急停 · `3`=开机阻尼 · `4`=趴下 · `17`=RL控制 |

```json
{
  "PatrolDevice": {
    "Type": 2, "Command": 22, "Time": "2026-07-17 12:00:00",
    "Items": { "MotionParam": 0 }
  }
}
```

状态流转：`空闲 → 站立 → RL控制` 为正向流程。RL 控制状态下才能下发步态切换和运动指令。

---

### 2.4 步态切换

| Type | Command | 说明 |
| ---: | ---: | --- |
| 2 | 23 | 切换步态（需在 RL 控制下） |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `GaitParam` | int | 见下表 |

| 值 | 步态 | 适用模式 |
| ---: | --- | --- |
| `0x1001` | 基础（标准运动模式） | 常规/辅助/导航 |
| `0x1003` | 楼梯（标准运动模式） | 常规/辅助/导航 |
| `0x3002` | 平地（敏捷运动模式） | 辅助/导航 |
| `0x3003` | 楼梯（敏捷运动模式） | 辅助/导航 |

```json
{
  "PatrolDevice": {
    "Type": 2, "Command": 23, "Time": "2026-07-17 12:00:00",
    "Items": { "GaitParam": 4097 }
  }
}
```

- 敏捷运动模式速度响应更好，适合自主算法开发
- 每次起立后或切换使用模式时，步态重置为基础步态 `0x1001`
- 步态切换仅支持在 **RL 控制** 状态下执行

---

### 2.5 运动控制（轴指令 — 常规模式）

| Type | Command | 说明 |
| ---: | ---: | --- |
| 2 | 21 | 速度比例控制，仅常规模式 |

**Items** (全部 float, 范围 [-1, 1]):

| 参数 | 含义 | 说明 |
| --- | --- | --- |
| `X` | 前后方向速度比例 | 正=前进，负=后退 |
| `Y` | 左右方向速度比例 | 正=左移，负=右移 |
| `Z` | 高度方向速度比例 | 正=向上，负=向下 |
| `Roll` | 翻滚角速度比例 | |
| `Pitch` | 俯仰角速度比例 | |
| `Yaw` | 偏航角速度比例 | 正=逆时针，负=顺时针 |

```json
{
  "PatrolDevice": {
    "Type": 2, "Command": 21, "Time": "2026-07-17 12:00:00",
    "Items": { "X": 0.5, "Y": 0.0, "Z": 0.0, "Roll": 0.0, "Pitch": 0.0, "Yaw": 0.0 }
  }
}
```

- 建议发送频率 ≥20 Hz
- 常规模式的基础/楼梯步态仅 `X`, `Y`, `Yaw` 生效

---

### 2.6 运动控制（速度指令 — 导航模式）

| Type | Command | 说明 |
| ---: | ---: | --- |
| 2 | 25 | 速度绝对值控制，仅导航模式 |

**Items** (全部 float, 带物理单位):

| 参数 | 含义 | 单位 |
| --- | --- | --- |
| `X` | 前后方向速度 | m/s |
| `Y` | 左右方向速度 | m/s |
| `Z` | 高度方向速度 | m/s |
| `Roll` | 翻滚角速度 | rad/s |
| `Pitch` | 俯仰角速度 | rad/s |
| `Yaw` | 偏航角速度 | rad/s |

```json
{
  "PatrolDevice": {
    "Type": 2, "Command": 25, "Time": "2026-07-17 12:00:00",
    "Items": { "X": 0.5, "Y": 0.0, "Z": 0.0, "Roll": 0.0, "Pitch": 0.0, "Yaw": 0.0 }
  }
}
```

---

### 2.7 照明灯控制

| Type | Command | 说明 |
| ---: | ---: | --- |
| 1101 | 2 | 照明灯开关 |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Front` | int | `0`=关闭 · `1`=开启 |
| `Back` | int | `0`=关闭 · `1`=开启 |

```json
{
  "PatrolDevice": {
    "Type": 1101, "Command": 2, "Time": "2026-07-17 12:00:00",
    "Items": { "Front": 1, "Back": 0 }
  }
}
```

---

### 2.8 自主充电

| Type | Command | 说明 |
| ---: | ---: | --- |
| 2 | 24 | 自主充电控制 |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Charge` | int | `0`=结束充电 · `1`=开始充电 · `2`=清除充电状态（强制归零，谨慎使用） |

```json
{
  "PatrolDevice": {
    "Type": 2, "Command": 24, "Time": "2026-07-17 12:00:00",
    "Items": { "Charge": 1 }
  }
}
```

---

### 2.9 休眠模式设置

| Type | Command | 说明 |
| ---: | ---: | --- |
| 1101 | 6 | 设置休眠模式 |

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Sleep` | bool | `true`=进入休眠 · `false`=唤醒 |
| `Auto` | bool | `true`=启用自动休眠 · `false`=关闭 |
| `Time` | uint | 自动休眠等待时间（分钟），范围 [5, 30] |

```json
{
  "PatrolDevice": {
    "Type": 1101, "Command": 6, "Time": "2026-07-17 12:00:00",
    "Items": { "Sleep": false, "Auto": true, "Time": 5 }
  }
}
```

---

### 2.10 休眠状态查询

| Type | Command | 说明 |
| ---: | ---: | --- |
| 1101 | 7 | 查询休眠状态及设置 |

`Items` 为空。响应同 2.9 的 Items 结构（`Sleep`, `Auto`, `Time`）。

---

## 3. 状态上报（机器人 → 上位机）

UDP 下需先发一次对应查询请求触发订阅，服务端记录来源 IP/端口后持续推送。

### 3.1 基础状态上报 — Type=1002, Cmd=6

**频率**: 2 Hz

```json
{
  "PatrolDevice": {
    "Type": 1002, "Command": 6, "Time": "2026-07-17 12:00:00",
    "Items": {
      "BasicStatus": {
        "MotionState": 0,
        "Gait": 0,
        "Charge": 0,
        "HES": 0,
        "ControlUsageMode": 0,
        "Direction": 0,
        "OOA": 0,
        "PowerManagement": 0,
        "Sleep": 0,
        "Version": "STD"
      }
    }
  }
}
```

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `MotionState` | int | `0`=空闲 · `1`=站立 · `2`=软急停 · `3`=开机阻尼 · `4`=趴下 · `17`=RL控制 |
| `Gait` | int | `0x1001`=基础 · `0x1003`=楼梯 · `0x3002`=平地敏捷 · `0x3003`=楼梯敏捷 |
| `Charge` | int | `0`=空闲 · `1`=前往充电桩 · `2`=充电中 · `3`=退出充电桩 · `4`=异常 · `5`=在桩上未充电 |
| `HES` | int | `0`=未触发 · `1`=已触发（硬急停） |
| `ControlUsageMode` | int | `0`=常规 · `1`=导航 · `2`=辅助 |
| `Direction` | int | `0`=正向为前进正方向 · `1`=后向为前进正方向 |
| `OOA` | int | `0`=未启动 · `1`=空闲中 · `2`=未触发避障 · `3`=主动避障中 |
| `PowerManagement` | int | `0`=常规 · `1`=单电池模式 |
| `Sleep` | int | `0`=未休眠 · `1`=已休眠 · `2`=进入休眠中 |
| `Version` | string | `STD`=山猫M20 · `PRO`=山猫M20 Pro |

---

### 3.2 运控状态上报 — Type=1002, Cmd=4

**频率**: 10 Hz

```json
{
  "PatrolDevice": {
    "Type": 1002, "Command": 4, "Time": "2026-07-17 12:00:00",
    "Items": {
      "MotionStatus": {
        "Roll": 0.0, "Pitch": 0.0, "Yaw": 0.0,
        "OmegaZ": 0.0, "LinearX": 0.0, "LinearY": 0.0,
        "Height": 0.0, "Payload": 0.0, "RemainMile": 0.0
      },
      "MotorStatus": {
        "Joint": [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],
        "LeftFrontHipX": 0.0, "LeftFrontHipY": 0.0, "LeftFrontKnee": 0.0, "LeftFrontWheel": 0.0,
        "RightFrontHipX": 0.0, "RightFrontHipY": 0.0, "RightFrontKnee": 0.0, "RightFrontWheel": 0.0,
        "LeftBackHipX": 0.0, "LeftBackHipY": 0.0, "LeftBackKnee": 0.0, "LeftBackWheel": 0.0,
        "RightBackHipX": 0.0, "RightBackHipY": 0.0, "RightBackKnee": 0.0, "RightBackWheel": 0.0
      }
    }
  }
}
```

**MotionStatus**:

| 字段 | 单位 | 说明 |
| --- | --- | --- |
| `Roll/Pitch/Yaw` | rad | 机身姿态角 |
| `OmegaZ` | rad/s | Z 方向角速度 |
| `LinearX/LinearY` | m/s | X/Y 方向线速度 |
| `Height` | m | 机身高度 |
| `RemainMile` | km | 预计剩余续航里程 |

**MotorStatus** — 16 关节顺序：LeftFrontHipX, HipY, Knee, Wheel → RightFrontHipX, HipY, Knee, Wheel → LeftBack... → RightBack...

| 字段 | 单位 | 说明 |
| --- | --- | --- |
| `Joint` | rad / rad/s | 16 元素数组，关节角度/转速 |
| `*HipX` | rad | 侧摆关节角度 |
| `*HipY` | rad | 髋关节角度 |
| `*Knee` | rad | 膝关节角度 |
| `*Wheel` | rad/s | 足轮转速 |

---

### 3.3 设备状态上报 — Type=1002, Cmd=5

**频率**: 2 Hz。包含 BatteryList, BatteryStatus, DeviceTemperature, Led, GPS, DevEnable, CPU 七组参数。

**BatteryList** (2 块电池):
```json
"BatteryList": [
  { "Voltage": 0.0, "BatteryLevel": 100.0, "battery_temperature": 0.0, "charge": false, "serial": "xxx" },
  { "Voltage": 0.0, "BatteryLevel": 100.0, "battery_temperature": 0.0, "charge": false, "serial": "xxx" }
]
```

**BatteryStatus**:
```json
"BatteryStatus": {
  "VoltageLeft": 0.0, "VoltageRight": 0.0,
  "BatteryLevelLeft": 0.0, "BatteryLevelRight": 0.0,
  "battery_temperatureLeft": 0.0, "battery_temperatureRight": 0.0,
  "chargeLeft": false, "chargeRight": false
}
```

**DeviceTemperature** — 16 电机 + 16 驱动器温度：
```json
"DeviceTemperature": {
  "Motor": [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],
  "Driver": [0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],
  "LeftFrontHipXMotor": 0.0, "LeftFrontHipXDriver": 0.0, ...
}
```

**Led**:
```json
"Led": { "Fill": { "Front": 1, "Back": 1 } }
```

**GPS**:
```json
"GPS": {
  "Latitude": 0.0, "Longitude": 0.0, "Speed": 0.0, "Course": 0.0,
  "FixQuality": 0, "NumSatellites": 0, "Altitude": 0.0,
  "HDOP": 0.0, "VDOP": 0.0, "PDOP": 0.0, "VisibleSatellites": 0
}
```

**DevEnable**:
```json
"DevEnable": {
  "FanSpeed": 100, "LoadPower": 1, "LedHost": 1, "LedExt": 1, "FP": 1,
  "Lidar": { "Front": 1, "Back": 1 },
  "GPS": 1,
  "Video": { "Front": 1, "Back": 1 }
}
```

**CPU**:
```json
"CPU": {
  "AOS": { "Temperature": 0.0, "FrequencyInt": 0.0, "FrequencyApp": 0.0 },
  "NOS": { "Temperature": 0.0, "FrequencyInt": 0.0, "FrequencyApp": 0.0 },
  "GOS": { "Temperature": 0.0, "FrequencyInt": 0.0, "FrequencyApp": 0.0 }
}
```

---

### 3.4 异常状态上报 — Type=1002, Cmd=3

**频率**: 2 Hz，状态变更时即时上报。

```json
{
  "PatrolDevice": {
    "Type": 1002, "Command": 3, "Time": "2026-07-17 12:00:00",
    "Items": {
      "ErrorList": [
        { "errorCode": 32769, "component": 1 },
        { "errorCode": 32770, "component": 4 }
      ]
    }
  }
}
```

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `errorCode` | int | 错误码，见下文 |
| `component` | int | bit 位标识部件编号（关节/电池） |

`component` 关节位编号（自低位起）：FL HipX(0) → FL HipY(1) → FL Knee(2) → FL Wheel(3) → FR HipX(4) → ... → RB Wheel(15)。电池位编号：bit0=后侧电池，bit1=前侧电池。

常见错误码（部分）：

| errorCode | 含义 | errorCode | 含义 |
| ---: | --- | ---: | --- |
| `0x8001` | 电机温度预警 | `0x8002` | 电机温度过高保护 |
| `0x8003` | 电机温度致命截止 | `0x8007` | 关节驱动器过温 |
| `0x8008` | 驱动器欠压保护 | `0x8009` | 驱动器过压保护 |
| `0x8012` | 与关节驱动器通讯超时 | `0x8016` | 编码器无值 |
| `0x8020` | 驱动器过流保护 | `0x8102` | 低电量预警 |
| `0x8103` | 保护电量 | `0x8107` | 电池放电过温保护 |
| `0x8115` | 电池充电过温保护 | `0x8117` | 电池单体过压保护 |
| `0x8201` | CPU占用率过高预警 | `0x8202` | CPU温度过高预警 |
| `0x8211` | CPU占用率过高保护 | `0x8212` | CPU温度过高保护 |
| `0x8501` | 自主充电定位超时 | `0x8503` | 自主充电定位异常 |
| `0x8506` | 充电桩无电流 | `0x8507` | 充电停障错误 |
| `0x8510` | 退桩超时 | `0x8022` | 关节角超限 |
| `0x8027` | 机身姿态错误 | `0x8029` | 运动姿态错误 |

---

## 4. 通用响应与错误码

每一条请求都会收到同 Type/Command 的响应，Items 至少包含 ErrorCode 和 ErrorMessage：

```json
{
  "PatrolDevice": {
    "Type": 100, "Command": 100, "Time": "2026-07-17 12:00:00",
    "Items": { "ErrorCode": 0, "ErrorMessage": "Success" }
  }
}
```

| ErrorCode (hex) | dec | ErrorMessage | 含义 |
| --- | ---: | --- | --- |
| `0x0000` | 0 | Success | 命令已接收并触发（不代表业务成功） |
| `0xE001` | 57345 | Unsupported data format | 帧头 Format 非 JSON/XML |
| `0xE002` | 57346 | Data parsing failed | 缺少 PatrolDevice/Items/Type/Command |
| `0xE003` | 57347 | Unsupported protocol type | Type/Command 组合不存在 |
| `0xE004` | 57348 | Missing necessary fields | Items 缺字段 |
| `0xE005` | 57349 | Mismatched data type | 字段类型不匹配 |
| `0xE006` | 57350 | Mismatched request client | 2s 内轴指令须来自同一客户端 |
| `0xE007` | 57351 | No operation permission | 无操作权限 |
| `0xE008` | 57352 | Not allowed operation | 状态机不允许 |
| `0xE009` | 57353 | Operation failure | 操作失败，查 Cmd=3 错误详情 |
| `0xE00A` | 57354 | Unsupported function | 固件不支持（如导航需 Pro） |
| `0xE00B` | 57355 | Internal error | 内部错误，查日志 |

---

## 5. 导航任务（Pro 版本）

以下指令仅山猫 M20 Pro 支持。

### 5.1 下发单点导航 — Type=1003, Cmd=1

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Value` | int | 目标点编号（默认 0） |
| `MapID` | int | 地图编号（默认 0） |
| `PosX/Y/Z` | float | 目标点在地图坐标系中的位置 (m) |
| `AngleYaw` | float | 目标点朝向 (rad) |
| `PointInfo` | int | `0`=过渡点 · `1`=任务点 · `3`=充电点 |
| `Gait` | int | `0x3002`=平地敏捷 · `0x3003`=楼梯敏捷 |
| `Speed` | int | `0`=正常 · `1`=低速 · `2`=高速 |
| `Manner` | int | `0`=前进行走 · `1`=倒退行走 |
| `ObsMode` | int | `0`=开启停避障 · `1`=关闭 |
| `NavMode` | int | `0`=直线导航 · `1`=自主导航 |

### 5.2 取消导航 — Type=1004, Cmd=1

`Items` 为空。响应 `ErrorCode`: `0`=成功, `1`=失败。

### 5.3 查询导航任务状态 — Type=1007, Cmd=1

`Items` 为空。响应包含 `Value`, `Status`, `ErrorCode`。

`Status`: `0`=空闲 · `1`=退出充电桩中 · `2`=导航预处理 · `3`=导航中 · `4`=导航完成 · `5`=进入充电桩中 · `0xFF`=暂停中

### 5.4 获取地图位置 — Type=1007, Cmd=2

**响应 Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `Location` | int | `0`=定位正常 · `1`=定位丢失 |
| `PosX/Y/Z` | float | 地图坐标 (m) |
| `Roll/Pitch/Yaw` | float | 姿态角 (rad) |

### 5.5 初始化定位 — Type=2101, Cmd=1

**Items**:

| 参数 | 类型 | 说明 |
| --- | --- | --- |
| `PosX/Y/Z` | float | 重新定位坐标 (m) |
| `Yaw` | float | 绕 Z 轴姿态角 (rad) |

---

## 6. 手柄 App 接口清单

| 用途 | Type | Cmd | 方向 | 频率 |
| --- | ---: | ---: | --- | --- |
| 心跳保活 | 100 | 100 | 上位机→机器人 | ≥1 Hz |
| 使用模式切换 | 1101 | 5 | 上位机→机器人 | 事件 |
| 运动状态转换 | 2 | 22 | 上位机→机器人 | 事件 |
| 步态切换 | 2 | 23 | 上位机→机器人 | 事件 |
| 轴指令（常规模式） | 2 | 21 | 上位机→机器人 | ≥20 Hz |
| 速度指令（导航模式） | 2 | 25 | 上位机→机器人 | ≥10 Hz |
| 照明灯控制 | 1101 | 2 | 上位机→机器人 | 事件 |
| 自主充电 | 2 | 24 | 上位机→机器人 | 事件 |
| 休眠设置 | 1101 | 6 | 上位机→机器人 | 事件 |
| 订阅基础状态 | 1002 | 6 | 双向 | 2 Hz 推送 |
| 订阅运控状态 | 1002 | 4 | 双向 | 10 Hz 推送 |
| 订阅设备状态 | 1002 | 5 | 双向 | 2 Hz 推送 |
| 订阅异常状态 | 1002 | 3 | 双向 | 2 Hz / 即时 |

---

## 7. 实现要点

1. UDP socket 绑定本地随机端口（源端口即为上报回执地址）。
2. 心跳线程 1 Hz，发 `Type=100, Cmd=100`。
3. 速度/轴指令线程 ≥20 Hz（无输入时发零速度保持连接）。
4. 接收线程：解析 16 字节 header → 按 `Length` 读 JSON 字节 → 分发 handler。
5. 请求 MsgId 从 0 递增，用于匹配响应帧。
6. 首次连接时主动发 `1002/4`, `1002/5`, `1002/6`, `1002/3` 触发 UDP 订阅推送。
7. 断线判定：>3 s 无任何 UDP 包 → 标记离线并 UI 提示。
8. 轴指令 `X/Y/Yaw` 比例 [-1,1] 需映射到手柄摇杆范围。
9. 步态切换仅 RL 控制状态下有效，需先发 `MotionParam=17`。
10. 导航模式切换后需等待 `BasicStatus.ControlUsageMode` 确认。