# 山猫 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` 确认。