山猫 M20 四足机器人手柄控制 Android App,使用 UDP + JSON 协议控制机器人运动。 - 协议层:16 字节小端 Header + JSON ASDU 编解码 - 网络层:UDP 客户端,心跳 1Hz,断线检测 3s - 连接状态机:空闲/连接中/已连接/断线 + 运动状态机:空闲/站立/RL 控制 - 虚拟摇杆:Compose 原生摇杆库,angle/power -> X/Y/Yaw 映射 - 视频播放:Media3 ExoPlayer RTSP,支持 AUTO/FORCE_HW/FORCE_SW_H264/FORCE_SW_H265 - 设置界面:机器人 IP/端口、RTSP 地址、编码器选择,DataStore 持久化 - 状态面板:异常列表、运控状态、设备温度、电池信息 - 控制按钮:模式/步态/照明/充电/休眠 Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
18 KiB
山猫 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 而定 |
{
"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=辅助模式 |
{
"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控制 |
{
"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 |
楼梯(敏捷运动模式) | 辅助/导航 |
{
"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 |
偏航角速度比例 | 正=逆时针,负=顺时针 |
{
"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 |
{
"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=开启 |
{
"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=清除充电状态(强制归零,谨慎使用) |
{
"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] |
{
"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
{
"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
{
"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 块电池):
"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:
"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 驱动器温度:
"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:
"Led": { "Fill": { "Front": 1, "Back": 1 } }
GPS:
"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:
"DevEnable": {
"FanSpeed": 100, "LoadPower": 1, "LedHost": 1, "LedExt": 1, "FP": 1,
"Lidar": { "Front": 1, "Back": 1 },
"GPS": 1,
"Video": { "Front": 1, "Back": 1 }
}
CPU:
"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,状态变更时即时上报。
{
"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:
{
"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. 实现要点
- UDP socket 绑定本地随机端口(源端口即为上报回执地址)。
- 心跳线程 1 Hz,发
Type=100, Cmd=100。 - 速度/轴指令线程 ≥20 Hz(无输入时发零速度保持连接)。
- 接收线程:解析 16 字节 header → 按
Length读 JSON 字节 → 分发 handler。 - 请求 MsgId 从 0 递增,用于匹配响应帧。
- 首次连接时主动发
1002/4,1002/5,1002/6,1002/3触发 UDP 订阅推送。 - 断线判定:>3 s 无任何 UDP 包 → 标记离线并 UI 提示。
- 轴指令
X/Y/Yaw比例 [-1,1] 需映射到手柄摇杆范围。 - 步态切换仅 RL 控制状态下有效,需先发
MotionParam=17。 - 导航模式切换后需等待
BasicStatus.ControlUsageMode确认。