GMR 工程实践笔记:把自己的机器人接入动作重定向流程
上一篇理论篇主要聊了 GMR 的方法逻辑:关键体匹配、默认姿态对齐、非均匀局部缩放,以及两阶段 IK。理论上看起来比较清楚,但真正接入自己的机器人时,重点并不是“能不能跑通脚本”,而是:如何配置出一套稳定、可复用、质量还不错的smplx_to_robot映射表。
本文主要记录一次将自定义机器人接入 GMR 的实践流程和调试经验,不深扒代码细节,重点放在工程配置和一些实际踩坑点上。
理论篇参考上一篇文章:传送门
GMR 项目主页:https://jaraujo98.github.io/retargeting_matters/
代码仓库:https://github.com/YanjieZe/GMR
一、第一次接入自定义机器人的基本流程
第一次给自己的机器人做重定向,大致流程如下:
1. 放入自己的机器人 URDF 2. 补齐需要参考的连杆点,例如手、头、脚趾等 3. 将 URDF 转为 XML / MJCF 格式 4. 创建对应机器人的 ik_configs 配置文件 5. 在脚本加载项中注册机器人名称和模型路径 6. 根据机器人结构修改 ik_configs,配置映射部位和比例 7. 在两张 IK match 表中配置映射关系、权重、位置偏移和旋转偏移 8. 选择几段代表性动作进行重定向 9. 观察动作质量和匹配误差 10. 根据结果调整权重、offset 和 scale这里最麻烦的是第一次配置。只要机器人配置稳定下来,后续换新的动作数据时,流程会简单很多,基本就是放入动作、运行重定向、检查结果。
整体上,这也和论文中的流程是一致的:
1. 找到人体和机器人的对应部位 2. 调整默认姿态和方向 3. 调整缩放比例 4. 配置两阶段 IK 权重 5. 计算得到机器人动作二、URDF 不一定够,需要补参考连杆
GMR 的匹配对象是人体骨架 body 和机器人 link。
所以有些机器人虽然模型能正常仿真,但未必有足够适合重定向的参考 link。
比较常见需要补的点包括:
head left_hand / right_hand left_toe / right_toe torso reference point这些点不一定需要自由度,可以用 fixed joint 加到 URDF 里。它们的作用不是改变机器人运动能力,而是提供更明确的空间参考。
比如脚部,如果只有 ankle,没有 toe 或 foot 参考点,那么脚掌方向的约束会比较弱。对于步行、转身、跑步这类动作,脚掌方向其实非常关键。
一个G1的参考点示意图:
三、核心配置:smplx_to_robot映射表
GMR 配置里最重要的是人体骨架到机器人 link 的映射表。以 G1 的一段配置为例:
"left_knee_link":["left_knee",0,10,[0.0,0.0,0.0],[0.5,-0.5,-0.5,-0.5]],这一项从上到下分别是:
机器人连杆 人体骨架部位 位置权重 旋转权重 xyz 位置偏移 wxyz 四元数旋转偏移也就是:
| 字段 | 含义 |
|---|---|
left_knee_link | 机器人参与 IK 匹配的 link |
left_knee | 人体骨架中对应的 body |
0 | 位置跟踪权重 |
10 | 旋转跟踪权重 |
[0.0, 0.0, 0.0] | 位置偏移 |
[0.5, -0.5, -0.5, -0.5] | 旋转偏移,格式为 wxyz |
这个表基本决定了机器人最终会“相信”人体骨架中的哪些点,以及相信到什么程度。
四、权重为 0,就是不跟踪
在 IK match 表中,权重非常直接。
如果某个部位的位置权重和旋转权重都设为 0,那么这个部位实际上就不会参与跟踪。比如把左手肘和左手腕权重都设为 0,可以看到机器人左臂基本保持 URDF 默认姿态,不会主动追踪人体动作。
左手肘和左手腕权重设为 0 后的重定向效果:
这点在调试时很有用。
不是所有人体关节都应该强行对齐,尤其是机器人没有对应自由度,或者 link 定义和人体差异较大时,高权重反而容易制造问题。
一个典型例子是 G1 的torso_link - spine3。即便两者空间位置相差比较大,只要位置权重设为 0,就不会对重定向结果产生强约束。
所以配置映射表时,不要只看有没有对应关系,更要看权重是否合理。
五、人体关节很多,但不需要全都对齐
可用的人体关节很多,例如:
body_joint_names=["Pelvis","Left_Hip","Right_Hip","Spine1","Left_Knee","Right_Knee","Spine2","Left_Ankle","Right_Ankle","Spine3","Left_Foot","Right_Foot","Neck","Left_Collar","Right_Collar","Head","Left_Shoulder","Right_Shoulder","Left_Elbow","Right_Elbow","Left_Wrist","Right_Wrist","Left_Hand","Right_Hand"]但实际配置时,不建议一股脑全部高权重对齐。约束太多容易过拟合,表现出来可能是:
动作抖动 局部关节异常扭动 腿部或手臂姿态不自然 为了满足某个点导致整体姿态变形我的理解是,映射选择应该优先考虑三个问题:
1. 机器人是否真的有这个部位? 2. 这个部位对动作质量是否关键? 3. 机器人是否有能力合理实现这个约束?比如机器人没有头部自由度,却高权重跟踪Head的方向,可能会导致机器人通过扭腰或扭 torso 去代偿头部旋转,结果反而很怪。
脚部则相反,通常值得重点关注。因为脚部不仅影响动作像不像,还直接影响接触、落地和稳定性。
例如,实践中,使用foot作为脚部主要参考点,效果通常比只用ankle更好一些。
原因大概是:ankle更像一个点,而foot更能表达脚掌方向。虽然 ankle 到 foot 之间可能没有额外自由度,但 foot 的方向信息对步态很有帮助,尤其是转身、跑步、落脚这类动作。
也可以 foot 和 ankle 都用,但权重不要太激进。脚部约束过强时,腿部姿态也可能变僵。
只使用 ankle、只使用 foot的效果对比
六、四元数偏移:本质是坐标系对齐
四元数 offset 是调试中最容易困惑的部分。
比如常见的:
[0.5,-0.5,-0.5,-0.5]它并不是经验玄学,而是在做坐标系对齐。
GMR 中四元数格式是wxyz,这一点要特别注意。
原始动作骨架的默认状态中,关节四元数基本都是:
[1,0,0,0]但这只说明它在骨架自己的坐标系下没有旋转,并不代表它和机器人 link 的局部坐标系一致。
观察原始骨架可以看到,它可能是面朝上躺平的大字形态,且轴系大致表现为:
z 轴朝身体前方 y 轴垂直身体 x 轴朝身体侧方[1, 0, 0, 0]应用[0.5, -0.5, -0.5, -0.5]后,可以看到原本朝前的 z 轴被旋到 x 轴方向,原本朝左的 x 轴被旋到 y 轴方向,这样更接近常见机器人模型的 link 坐标定义。
这里最重要的原则是:
四元数 offset 不是为了让某一帧动作看起来顺眼,而是为了在默认姿态下对齐机器人 link 的局部坐标系。
虽然[0.5, -0.5, -0.5, -0.5]在很多位置上有效,但不是所有 link 都适用。
比如 G1 的 hip 部位使用的是right_hip_roll_link,它的局部轴系并不和 torso 保持一致,所以需要单独配置:
"right_hip_roll_link":["right_hip",0,10,[0.0,0.0,0.0],[0.4267755048530407,-0.5637931078484661,-0.5637931078484661,-0.4267755048530407]],
hip 使用特殊offset 后的效果:
可以发现,这套偏移是更符合机器人的坐标系设定的,这说明 offset 要看机器人 link 自己的坐标系,而不是无脑都套用同一套偏移。
手臂也类似。人体骨架通常是大字型姿态,而机器人默认一般是垂手姿态,G1 的手肘还带有一定弯曲。因此肩部配置会使用类似:
"left_shoulder_yaw_link":["left_shoulder",0,10,[0.0,0.0,0.0],[0.70710678,0.0,-0.70710678,0.0]],在这个配置下,如果把机器人 shoulder roll 调到 90°,让手臂打直,会发现这个偏移量和机器人坐标系是匹配的,包括后续的小臂和手部也同理。
不过这里还有一个我暂时没有完全确认的问题:目前这种“手动旋转机器人后再观察匹配”的方式,更像是一个工程辅助验证方法,而不一定是严格的代码设计逻辑。仓库中并没有明确说明需要先旋转机器人再匹配,因此这里还需要继续研究坐标变换的真实顺序。
七、xyz offset 和 scale:直观,但最费时间
相比四元数,xyz offset 和 scale 更直观,但更依赖具体机器人和动作数据。
影响因素包括:
人体骨架身高和比例 机器人腿长、臂长、躯干比例 机器人 link 原点定义 foot / ankle / toe 的选择 不同动作库的骨架差异 root 缩放比例目前比较有效的方法还是构建可视化脚本,反复观察:
原始人体骨架 偏移后的目标骨架 机器人 FK 后的实际 link 关键点位置误差 关键点旋转误差对于一些怎么都对不齐的点,不建议死磕。可以降低位置权重,保留旋转约束,或者干脆弱化这个部位。
本质上,GMR 不是要让每个点都完美贴合,而是要让关键部位合理贴合。
原始动作骨架、未偏移结果、偏移后结果(但骨架保留原始效果以便表达区别)三图对比:
八、两阶段 IK:表一粗对齐,表二细对齐
GMR 中通常会有两张 IK match 表,对应两阶段 IK。
第一阶段主要是粗对齐。重点是:
root / pelvis 方向位置合理 torso 方向合理 双足位置合理 主要 end-effector 大致到位 其他关节旋转方向不要太离谱第一阶段的目标不是精修动作,而是得到一个比较稳定的初始姿态。
如果第一阶段已经跑偏,第二阶段很容易在错误姿态附近继续优化。
第二阶段是细对齐。一般会加入更多 key body,并适当提高部分旋转或位置约束,让动作更贴近源动作。
可以简单理解为:
表一:先把机器人摆对 表二:再把动作修细所以两张表不要配置成完全一样。
表一应该抓重点,表二可以更细,但也不能过度激进。否则动作细节可能更像了,但抖动、自碰撞和关节突变也更容易出现。
其他补充
不同数据格式骨架
左边是smplx右边是bvh,数据细节来看,不经映射的位置要调整,并且坐标轴也是需要彻底重新调整。
个人调试经验
整理一些目前比较有用的经验:
1. foot 通常比 ankle 更适合作为脚部主约束
foot 对脚掌方向更敏感,步行和转身效果会更好。ankle 可以辅助,但单独用 ankle 时脚掌方向约束偏弱。
2. 没有自由度的部位,权重要低
比如没有头部自由度,就不要高权重跟踪Head的方向。否则机器人可能通过 torso 或腰部去代偿,动作会变得很奇怪。
3. 不同动作库可能需要不同 scale 或 offset
不同动作库的骨架定义、身高比例、坐标系和帧率都可能不同。不要默认一套参数可以适配所有来源的数据。
4. 不要只用一个动作验证配置
一个动作调得很好,不代表配置稳定。建议在同一个动作库/作者下,至少准备几类动作:
普通步行 转身 跑步 上肢动作 大幅度动态动作多动作都能接受,配置才比较可信。
十、几个待研究问题
目前还有一些问题没有完全搞清楚,后续值得继续看。
1. 权重和 scale 的影响能否量化?
现在调参主要依赖观察和枚举,成本比较高。后面可以考虑引入一些自动指标:
key body position error rotation error foot sliding ground penetration self-intersection joint velocity spike如果这些指标能自动统计,调参效率会高很多。
2. 其他动作格式效果如何?
目前主要关注现有仓库主力支持的smplx格式数据。对于遥操数据、视频恢复动作、其他 mocap 格式,还需要单独测试。
3. 动作频率对训练效果有什么影响?
GMR 脚本里写死了 30 fps,而宇树官方动作数据可能是 50 Hz,bymimic仓库里则是区分了高频低频的训练环境。这里需要继续确认:
retarget 后是否需要插值? 频率变化如何影响 tracking 效果?这个问题对后续训练很关键。
4. 是否需要为每个动作库单独配置 scale?
不同动作库录制者体型、骨架标准可能不同。理论上可能需要不同 scale,但也可能动作数据内部已经做了统一标准化。这个需要更多动作源验证。
5. table2 里的rot_offset是否真的生效?
代码分析时发现,第二阶段表里的rot_offset似乎没有被实际套用。这里还不能确定是论文设计如此,还是代码实现遗漏。
十一、SMPL-X 到机器人动作重定向工程流程
最后,我们再快速过一下重定向过程中都干了什么。
输入数据
输入是一个 SMPL-X 动作.npz文件,通常来自 AMASS/OMOMO 等数据集。脚本主要使用其中的:
pose_body: 每帧身体关节轴角姿态,形状通常为(N, 63)。root_orient: 每帧根节点全局朝向,形状为(N, 3)。trans: 每帧根节点全局平移,形状为(N, 3)。betas: 人体形状参数。gender: SMPL-X body model 性别选择。mocap_frame_rate: 原始动作帧率。
这些参数本身还不是“骨架点位序列”,而是 SMPL-X 参数化人体模型的输入。
SMPL-X 前向计算
load_smplx_file()会根据输入文件中的gender和betas创建 SMPL-X body model,并把root_orient、pose_body、trans输入模型,得到每一帧的人体关节位置和完整姿态。
之后get_smplx_data_offline_fast()将 SMPL-X 输出整理成重定向系统使用的格式:
{"pelvis":(position,quaternion),"left_hip":(position,quaternion),"left_knee":(position,quaternion),...}其中四元数格式为[w, x, y, z]。这个阶段也会根据目标帧率进行时间对齐或重采样,并返回实际使用的aligned_fps。
逐帧重定向
脚本逐帧取出 SMPL-X 处理后的人体数据:
smplx_data=smplx_data_frames[i]qpos=retarget.retarget(smplx_data)retarget()内部主要做几件事:
- 将人体骨架按目标机器人尺度进行缩放。
- 对人体匹配点应用位置偏移和旋转偏移。
- 将处理后的人体点位和朝向设置为 IK 任务目标。
- 使用
mink.solve_ik()求解机器人当前帧的qpos。
返回的qpos是 MuJoCo 机器人状态,通常包含:
qpos[:3]: 机器人根节点位置。qpos[3:7]: 机器人根节点四元数,格式为[w, x, y, z]。qpos[7:]: 机器人各关节角度。
输出数据
最后脚本会把所有帧的机器人动作保存为.pkl。输出结构如下:
{"fps":aligned_fps,"root_pos":root_pos,"root_rot":root_rot,"dof_pos":dof_pos,"local_body_pos":None,"link_body_list":None,}其中:
root_pos:(T, 3),机器人根节点位置。root_rot:(T, 4),机器人根节点四元数。保存时会从 MuJoCo 的[w, x, y, z]转为[x, y, z, w]。dof_pos:(T, robot_dof),机器人关节位置序列。fps: 动作播放帧率。
这里的dof_pos是关节角,单位为 rad。以 G1 29DoF 为例,dof_pos的形状是(T, 29),每一帧包含 29 个关节角,列顺序由机器人 XML 中的 joint 顺序决定。
这个.pkl就是后续播放、训练或进一步转换的机器人动作数据。
技术要点
- SMPL-X 负责从参数化人体动作恢复人体关节位置和朝向。
- MuJoCo 提供目标机器人运动学模型。
mink将人体目标点和机器人 link 之间的匹配关系转化为 IK 优化问题。- IK 配置负责定义人体部位到机器人 link 的映射、权重、缩放和坐标系校准。
- 输出结果不是力控或轨迹控制命令,而是逐帧机器人运动学状态。
整体流程可以概括为:
SMPL-X .npz -> SMPL-X body model 前向计算 -> 人体关节位置/姿态序列 -> 缩放与坐标系校准 -> 机器人 IK 求解 -> 机器人 root + joint motion -> .pkl 动作文件总结
把自己的机器人接入 GMR,核心工作不是跑脚本,而是配置好smplx_to_robot映射表。
这张表里最重要的是:
映射哪个机器人 link 参考哪个人体 body 位置权重是多少 旋转权重是多少 xyz offset 如何设置 quaternion offset 如何设置其中,映射关系决定机器人“看哪里”,权重决定机器人“有多在意”,offset 和 scale 决定人体动作目标是否真的适合当前机器人。
我的整体感受是,GMR 的工程配置本质上是在做一次折中:
既不能让机器人完全无视人体动作,也不能让它死追所有人体关节。比较好的结果通常来自合理取舍:关键部位强约束,非关键部位弱约束;可实现的部位认真跟踪,不可实现的部位适当放过。
当这套配置稳定之后,GMR 就会变成一个比较实用的动作数据生产工具。对于后续 motion tracking、模仿学习或者遥操作数据处理来说,前面这一步调得越稳,后面的训练就越省心。
