Files
go1_pro_sdk/docs/OFFICIAL_API_COMPATIBILITY.md
2026-07-30 15:25:48 +08:00

7.7 KiB
Raw Blame History

Unitree 官方 SDK 接口兼容说明

对比基准Unitree unitree_legged_sdk go1 分支README 版本 v3.8.6,提交 4539a6c10dfbc9781cea6fcb7d51bc6ddc6f71e12023-07-11

结论

本项目和官方 SDK 使用相近的低层对象模型,但此前不是调用兼容:本项目入口是 MCUClient.send()/recv_state(),官方 Python wrapper 入口是 robot_interface.UDP.SetSend()/Send()/Recv()/GetRecv()

当前项目已提供 robot_interface 兼容模块。官方低层 Python 示例可保留原来的 导入和主要调用流程,同时底层会使用 Go1 PRO 所需的 Blowfish 加密和 616 字节 私有 LowCmd 协议。

反过来,要让本项目编写的程序直接在官方 SDK 上复现,应用必须导入 robot_interface,并限制在官方 wrapper 真正导出的公共子集内。应用不应直接导入 go1_pro_sdk

接口矩阵

官方接口 本项目原接口 当前兼容状态
robot_interface.LowCmd/LowState 同名类型 已对齐命令和常用状态字段名
MotorCmd/MotorState/IMU 同名类型 常用字段已对齐
BmsState BMS 名称已对齐PRO 状态区较短,见下文
BmsCmd, Cartesian, UDPState 原先缺失 已补齐
UDP(LOWLEVEL, ...) MCUClient(...) 已适配标准四参数构造
InitCmdData 原先缺失 已实现官方 stop sentinel 初始化
Recv/GetRecv/SetSend/Send recv_latest/send 已适配
Safety.PositionLimit position_limit 已适配,跳过 PosStopF
Safety.PowerProtect power_protect 调用兼容,算法不是官方闭源实现
Safety.PositionProtect position_protect 调用兼容,保护动作更保守
HighCmd/HighState MQTT Go1/Go1MQTT 类型已提供UDP 传输不支持
Loop/LoopFunc C++ 已提供;官方 Python wrapper 本身未导出
C++ headers/static library 低层 C++ 源码兼容;不承诺二进制 ABI

关键协议差异

官方 SDK 的公开结构不能直接描述本项目实测的 Go1 PRO 线协议。PRO 低层通道需要:

  • 616 字节 LowCmdCRC 位于 612
  • Blowfish ECB 加密;
  • bandWidth 使用实测字节序和值;
  • 858 字节加密状态包中取 856 字节块解密,再解析 807 字节有效状态。

因此兼容层只对齐用户代码接口,不替换本项目现有 codec也不承诺二进制包与官方 EDU SDK 相同。

官方风格用法

import robot_interface as sdk

LOWLEVEL = 0xff
FR_1 = 1

udp = sdk.UDP(LOWLEVEL, 8080, "192.168.123.10", 8007)
safe = sdk.Safety(sdk.LeggedType.Go1)
cmd = sdk.LowCmd()
state = sdk.LowState()
udp.InitCmdData(cmd)

udp.Recv()
udp.GetRecv(state)

cmd.motorCmd[FR_1].q = 1.2
cmd.motorCmd[FR_1].dq = 0.0
cmd.motorCmd[FR_1].Kp = 5.0
cmd.motorCmd[FR_1].Kd = 1.0
safe.PositionLimit(cmd)
safe.PowerProtect(cmd, state, 1)
udp.SetSend(cmd)
udp.Send()

这里故意不使用 sdk.LOWLEVELsdk.FR_1、上下文管理器或 close():这些是本项目 提供的便利能力,但不属于官方 Python wrapper 的导出契约。

C++ 同源码用法

C++ 公共入口、命名空间、结构字段和低层方法签名与官方 v3.8.6 对齐:

#include "unitree_legged_sdk/unitree_legged_sdk.h"
using namespace UNITREE_LEGGED_SDK;

UDP udp(LOWLEVEL, 8090, "192.168.123.10", 8007);
Safety safe(LeggedType::Go1);
LowCmd cmd{};
LowState state{};
udp.InitCmdData(cmd);

业务源码和 #include 不变只切换链接库。PRO 端用本项目 CMake 目标 unitree_legged_sdk,官方 Go1 端用官方仓库的 libunitree_legged_sdk.a。本项目已经用 官方头文件反向编译可移植示例,并用本项目头文件编译官方仓库的五个 C++ 示例。

本项目构建方法:

cmake -S . -B build-cpp -DCMAKE_BUILD_TYPE=Release
cmake --build build-cpp
ctest --test-dir build-cpp --output-on-failure

标准四参数 UDP(LOWLEVEL, ...) 会在库内部完成 PRO 编解码。默认从安装数据目录读取 Blowfish state需要覆盖时设置 GO1_PRO_BLOWFISH_STATE=/path/to/blowfish_state.bin

可移植程序规则

要求同一份源码直接运行时,主控制流程只使用:

  • import robot_interface as sdk
  • sdk.UDPsdk.Safetysdk.LeggedType
  • sdk.LowCmd/LowStateMotorCmd/MotorStateIMU/BmsState
  • InitCmdData/Recv/GetRecv/SetSend/Send
  • 官方结构中公开的字段,例如 motorCmd[i].qmotorState[i].q

所有官方结构对象都应先无参构造,再逐字段赋值。例如使用 motor = sdk.MotorCmd(); motor.q = 1.0,不要写 sdk.MotorCmd(q=1.0)。后者是 Python dataclass 常见写法,但官方 pybind wrapper 不接受构造参数。

枚举也按官方对象使用,例如 sdk.Safety(sdk.LeggedType.Go1)。不要把枚举当作整数比较 或直接写 sdk.Safety(2);本项目的严格兼容入口会和官方一样拒绝后一种写法。

以下接口属于 PRO 扩展,使用后程序不再能在只安装官方 SDK 的环境中直接运行:

  • from go1_pro_sdk import ...
  • MCUClientGo1Go1MQTT
  • LowCmd.set_motor()all_damping()
  • LowState.remoteapply_safety()
  • wake_mcu()safe_stop()、Blowfish 和快速 C++ builder。

推荐把程序分为“官方公共控制核心”和“可选 PRO 增强”两个模块。公共核心不能导入 增强模块;增强模块可以包装公共核心,但必须允许在官方环境中完全不加载。

Send()Recv()SetSend() 的具体正整数来自线协议和底层 socket不属于跨 SDK 稳定契约。初始化时可以用 Recv() > 0 判断是否收到包,但不应比较具体字节数。

首次进入 PRO 低层通道前,应先反复发送 InitCmdData() 产生的 stop sentinel并在 收到第一帧 LowState 后再写入有效位置目标。可移植示例已采用这一时序,避免在状态 仍为全零默认值时直接驱动关节。

UDP(HIGHLEVEL, ..., "192.168.123.161", 8082) 不会被静默映射到 MQTT。两种通道 状态模型和时序不同,兼容层会明确抛出 NotImplementedError。高层控制继续使用 Go1Go1MQTT

尚未完全对齐的风险

  • 官方 PowerProtect 实现在静态库中,头文件只公开签名。本项目当前保护逻辑按 关节力矩限幅和实测过载执行,不等价于官方内部的累计功率算法。
  • 官方 BmsState 是 34 字节,本项目实测的 PRO 状态布局只给 BMS 分配 24 字节; cell_vol 后半部分不能视为完整的官方数据。
  • LowState.tick 当前没有从 PRO 包中解析,仍为默认值;footForcefootForceEst 的偏移来自逆向推断,尚未达到官方字段的同等可信度。
  • head/SN/version/reserve/crc 在本地原生类型中部分使用 bytes,官方 wrapper 使用定长整数数组或整数。命令 builder 接受两种常见赋值形态,但读取后的 Python 类型不保证完全相同。
  • 本项目 LowState 是 PRO 抓包格式的解释,不应按官方 #pragma pack(1) 结构大小 直接做内存映射。
  • C++ 类型布局和公开符号用于源码重编译兼容,不保证本项目库与官方预编译对象之间 的 ABI 互换;切换实现时应重新编译应用。
  • 两个自定义包长 C++ UDP 构造函数和 SetSend(char*) 提供原始 UDP 透传,但不会 自动把任意自定义结构转换为 PRO 私有协议;主动断连时间和 accessible 时间参数 仅保留调用形状。Python 兼容入口仍只保证标准四参数 LOWLEVEL 通道。
  • 官方 SDK 和本项目都提供顶层 robot_interface,同一个 Python 环境不要同时安装 两种实现。应在 PRO 环境安装本项目,在官方机器人环境安装官方 SDK。