Files
hexone2086 04afb3331f Initial commit: M20 Gamepad control app
山猫 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>
2026-07-17 19:24:39 +08:00

18 KiB
Raw Permalink Blame History

山猫 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 JSONXML 为 0x00
9 7 Reserved 0x00×7 预留

1.2 ASDUJSON 格式)

固定外层包裹 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. 实现要点

  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 确认。