Day6 unitree_G1人形机器人GMR—— MotionInput

可以把 MotionInput 理解成一个"统一插座",GMR不需要知道前面的动作数据到底来自:

  • Axis Studio;
  • Noitom MocapApi;
  • UDP测试程序;
  • 以后增加的BVH文件。

GMR只需要知道:

你给我一个格式正确的 NoitomFrame,我就把它转换成G1动作。
不是 MotionInput 亲自转换,而是 MotionInput 的两个具体实现分别负责转换,然后统一返回 NoitomFrame

先看三者关系

复制代码
MotionInput                 统一规定
├── XsensUdpSource          具体干活
└── NoitomMocapApiSource    具体干活

MotionInput只是规定:

复制代码
无论你内部怎么接收数据,
最后都必须给我返回一个 NoitomFrame。

它类似一份工作要求,不是实际干活的人。

一、先理解原来的问题

系统有两种动作来源。

来源1:Xsens UDP测试输入

程序直接监听一个UDP端口:

复制代码
测试程序/Xsens
    ↓
UDP数据包
    ↓
XsensUdpSource

它收到的是UDP格式的数据,需要自己解析。

来源2:Noitom正式输入

真实动捕服先连接Axis Studio:

复制代码
动捕服
  ↓
Axis Studio
  ↓
TCP 127.0.0.1:7003
  ↓
Noitom MocapApi
  ↓
NoitomMocapApiSource

它收到的是Axis通过Noitom SDK提供的人体Avatar骨骼数据。

这两种输入方式完全不同:

项目 Xsens UDP Noitom MocapApi
网络方式 UDP TCP或UDP
数据格式 自定义UDP数据包 Noitom SDK事件
解析方式 自己解析数据包 调用MocapApi
主要用途 测试、兼容旧程序 正式动捕服
是否依赖Axis 不一定

如果没有统一接口,GMR里面可能会出现这种代码:

复制代码
if (使用Xsens) {
    接收UDP();
    解析UDP();
    执行GMR();
}

if (使用Noitom) {
    连接MocapApi();
    读取Avatar();
    执行GMR();
}

这样会造成:

  • GMR和网络代码混在一起;
  • 两条链路可能各写一套GMR;
  • 修复GMR时要改两个地方;
  • 以后增加第三种输入会越来越乱;
  • 很难判断问题发生在输入还是GMR。

所以我们在输入和GMR之间加了一层统一接口。

二、MotionInput到底是什么

MotionInput不是一种新的网络协议,也不是新的动作格式。

它只是C++里的一个统一接口,大致相当于:

复制代码
class MotionInput
{
public:
    virtual void open() = 0;

    virtual std::optional<NoitomFrame> pollFrame() = 0;

    virtual void close() = 0;
};

它只规定三件事:

复制代码
open()      打开动作数据源
pollFrame() 获取一帧人体动作
close()     关闭数据源

但它没有规定数据必须从哪里来。

这就像一个统一插座:

复制代码
插座规定:
必须提供电

但不关心电来自:
火电、水电、风电还是太阳能

同样:

复制代码
MotionInput规定:
必须提供NoitomFrame

但不关心数据来自:
UDP、Axis、文件还是其他设备

三、两个实现分别做什么

1. XsensUdpSource

它是 MotionInput 的一个实现。

复制代码
class XsensUdpSource : public MotionInput
{
public:
    void open() override;
    std::optional<NoitomFrame> pollFrame() override;
    void close() override;
};

它内部负责:

复制代码
监听UDP端口
  ↓
收到UDP数据包
  ↓
解析人体关节
  ↓
整理成NoitomFrame
  ↓
交给GMR

虽然它叫 XsensUdpSource,但它交给后面的数据已经被整理成统一格式。

输出是:

复制代码
NoitomFrame

2. NoitomMocapApiSource

它也是 MotionInput 的一个实现。

复制代码
class NoitomMocapApiSource : public MotionInput
{
public:
    void open() override;
    std::optional<NoitomFrame> pollFrame() override;
    void close() override;
};

它内部负责:

复制代码
通过MocapApi连接Axis
  ↓
监听TCP 127.0.0.1:7003
  ↓
等待AvatarUpdated事件
  ↓
读取人体骨骼位置和旋转
  ↓
整理成NoitomFrame
  ↓
交给GMR

它的输出同样是:

复制代码
NoitomFrame

四、最关键的地方:输入不同,输出相同

两条链路的原始数据不同:

复制代码
XsensUdpSource
收到的是UDP数据包

NoitomMocapApiSource
收到的是MocapApi Avatar事件

但是经过各自解析以后,它们都必须生成:

复制代码
NoitomFrame

例如,可以把统一后的 NoitomFrame 简单理解为:

复制代码
struct NoitomFrame
{
    uint64_t timestamp_ms;
    uint32_t posture_index;

    std::vector<BodyPose> bodies;
};

里面包含类似:

复制代码
Hips
Spine
Chest
Neck
Head
LeftShoulder
LeftArm
LeftForeArm
LeftHand
RightShoulder
RightArm
RightForeArm
RightHand
LeftUpLeg
LeftLeg
LeftFoot
RightUpLeg
RightLeg
RightFoot

每个身体部位都有:

复制代码
位置 position
旋转 rotation

因此,到达GMR时,两种输入已经没有区别了。

五、GMR看到的是什么

GMR不会看到:

复制代码
TCP端口7003
UDP端口8000
MocapApi
Axis Studio
网络数据包

它只会收到:

复制代码
NoitomFrame frame;

然后执行:

复制代码
RobotFrame robot_frame =
    retargeter.retarget(frame);

换成大白话就是:

复制代码
输入层说:
"这是这一时刻的人体骨骼。"

GMR说:
"我不管它来自哪套动捕设备,
只要关节名称、位置和旋转正确,
我就把它变成G1动作。"

六、运行时怎么选择输入

程序启动时通过参数选择。

正式使用Noitom

实际链路是:

复制代码
动捕服
  ↓
Axis Studio
  ↓
TCP 7003
  ↓
NoitomMocapApiSource
  ↓
NoitomFrame
  ↓
GMR

使用UDP测试

如果运行:

复制代码
-MotionInput udp

程序内部相当于:

复制代码
if (motion_input == "udp") {
    input = std::make_unique<XsensUdpSource>();
}

实际链路变成:

复制代码
UDP测试数据
  ↓
XsensUdpSource
  ↓
NoitomFrame
  ↓
GMR

注意:二者不会同时正式控制GMR。启动时选择谁,当前运行就使用谁。

七、用翻译员来理解

可以把两种输入理解成两个人:

复制代码
Xsens说英语
Noitom说中文

GMR只懂一种"标准语言":

复制代码
NoitomFrame

所以两名翻译员分别负责:

复制代码
XsensUdpSource:
英语 → 标准语言

NoitomMocapApiSource:
中文 → 标准语言

翻译完成以后,GMR只处理标准语言。

复制代码
Xsens原始数据 ─→ XsensUdpSource ─┐
                                ├→ NoitomFrame → GMR
Noitom原始数据 ─→ NoitomSource ──┘

八、为什么统一格式叫NoitomFrame

这个名称确实容易让人误会。

看到:

复制代码
NoitomFrame

容易认为:

它只能装Noitom的数据。

但在当前工程里,它实际上被当作:

复制代码
统一的人体骨骼帧

也就是说,Xsens UDP解析完成后,也会转换成 NoitomFrame

从架构命名角度,更理想的名字可能是:

复制代码
HumanMotionFrame

或者:

复制代码
SkeletonFrame

这样更容易理解:

复制代码
Xsens → SkeletonFrame
Noitom → SkeletonFrame

但我们没有随意改名,因为这个结构已经被GMR等代码使用。改名会扩大修改范围,增加不必要的风险。

所以当前可以这样记:

复制代码
NoitomFrame虽然名字里有Noitom,
但在本项目里承担的是"统一人体骨骼帧"的角色。

九、MotionInput解决了什么问题

没有MotionInput时

复制代码
GMR
├── 自己监听UDP
├── 自己连接MocapApi
├── 自己判断输入类型
├── 自己解析网络数据
└── 再执行IK

GMR要做的事情太多。

有MotionInput以后

复制代码
XsensUdpSource:负责UDP
NoitomMocapApiSource:负责Axis/MocapApi
GMR:只负责人体到机器人的转换
ZMQ:只负责发送转换后的动作

每个模块只负责一件事。

十、它和ZMQ有什么关系

MotionInput负责的是GMR之前:

复制代码
动作从哪里来

ZeroMQ负责的是GMR之后:

复制代码
GMR结果怎么发出去

两个模块位于系统的不同位置:

复制代码
输入侧                              输出侧
MotionInput                         ZMQ
    ↓                                ↑
NoitomFrame → GMR → RobotFrame → protobuf

不要把它们混在一起。

  • MotionInput:电脑怎样获得人体动作。
  • GMR:人体动作怎样变成G1动作。
  • protobuf:G1动作怎样组织成数据。
  • ZMQ:数据怎样传给MuJoCo或机器人。

十一、当前实际运行的正式链路

你刚才穿动捕服测试时,实际使用的是这一条:

复制代码
```mermaid
flowchart LR
    A["动捕服"] --> B["Axis Studio"]
    B --> C["TCP 127.0.0.1:7003"]
    C --> D["Noitom MocapApi"]
    D --> E["NoitomMocapApiSource"]
    E --> F["统一 NoitomFrame"]
    F --> G["输入 FIFO"]
    G --> H["C++ GMR IK"]
    H --> I["G1 RobotFrame"]
    I --> J["protobuf"]
    J --> K["ZeroMQ"]
    K --> L["MuJoCo"]
```

XsensUdpSource这次没有参与运行,它只是保留下来的测试入口:

复制代码
```mermaid
flowchart LR
    A["UDP测试数据"] --> B["XsensUdpSource"]
    B --> C["统一 NoitomFrame"]
    C --> D["同一套 C++ GMR"]
```

十二、最简单的记忆方法

只需要记住下面四句话:

复制代码
MotionInput是统一入口。

XsensUdpSource负责解析UDP测试数据。

NoitomMocapApiSource负责读取Axis正式数据。

无论数据来自哪里,最终都变成NoitomFrame,再交给同一套GMR。

最终最简化结构就是:

复制代码
不同的数据来源
      ↓
不同的输入适配器
      ↓
统一NoitomFrame
      ↓
同一个GMR
      ↓
同一个ZMQ发送链路

它的本质不是增加复杂度,而是把原来混在一起的事情分开。

相关推荐
tudousisi2222 分钟前
8.25第二题训练
c++
星星.7227 分钟前
2026河南萌新联赛第六场(郑州大学)补题B、D、L、J、I
数据结构·c++·算法
智塑未来9 分钟前
金融AI应用解决方案选型先看哪三项
人工智能·金融
深念Y9 分钟前
Fork 仓库瘦身指南:从 90MB 到 11MB
git·github·仓库·历史·瘦身·fork·上游
Csvn9 分钟前
第 3 章 上下文工程
人工智能·aigc
智恒百亿9 分钟前
AI服务器涨价15%背后:5090八卡智算服务器的选型与成本分析
大数据·服务器·人工智能·github
中科同志科技10 分钟前
元器件拆焊真空焊接设备深度解读:工艺流程与优化策略
大数据·人工智能·机器人·汽车
政安晨11 分钟前
政安晨【人工智能随笔】— 从像素到星际:游戏如何塑造了现代AI的二十年演进史 (读DeepMind的EVE宇宙AI研究有感)
人工智能·游戏·ai·智能体·deepmind·人工智能与游戏·智能体与游戏
zww894911112 分钟前
AI导购线上商城搭建实战:从系统架构到部署全流程指南
人工智能·系统架构
鲨鱼辣钊12 分钟前
Github Copilot 研发效能提升实战指南
log4j·github·copilot