上一篇理论篇主要聊了 GMR 的方法逻辑:关键体匹配、默认姿态对齐、非均匀局部缩放,以及两阶段 IK。理论上看起来比较清楚,但真正接入自己的机器人时,重点并不是"能不能跑通脚本",而是:如何配置出一套稳定、可复用、质量还不错的 smplx_to_robot 映射表。
本文主要记录一次将自定义机器人接入 GMR 的实践流程和调试经验,不深扒代码细节,重点放在工程配置和一些实际踩坑点上。
理论篇参考上一篇文章:传送门
GMR 项目主页:https://jaraujo98.github.io/retargeting_matters/
代码仓库:https://github.com/YanjieZe/GMR
一、第一次接入自定义机器人的基本流程
第一次给自己的机器人做重定向,大致流程如下:
text
1. 放入自己的机器人 URDF
2. 补齐需要参考的连杆点,例如手、头、脚趾等
3. 将 URDF 转为 XML / MJCF 格式
4. 创建对应机器人的 ik_configs 配置文件
5. 在脚本加载项中注册机器人名称和模型路径
6. 根据机器人结构修改 ik_configs,配置映射部位和比例
7. 在两张 IK match 表中配置映射关系、权重、位置偏移和旋转偏移
8. 选择几段代表性动作进行重定向
9. 观察动作质量和匹配误差
10. 根据结果调整权重、offset 和 scale
这里最麻烦的是第一次配置。只要机器人配置稳定下来,后续换新的动作数据时,流程会简单很多,基本就是放入动作、运行重定向、检查结果。
整体上,这也和论文中的流程是一致的:
text
1. 找到人体和机器人的对应部位
2. 调整默认姿态和方向
3. 调整缩放比例
4. 配置两阶段 IK 权重
5. 计算得到机器人动作

二、URDF 不一定够,需要补参考连杆
GMR 的匹配对象是人体骨架 body 和机器人 link。
所以有些机器人虽然模型能正常仿真,但未必有足够适合重定向的参考 link。
比较常见需要补的点包括:
text
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 的一段配置为例:
python
"left_knee_link": [
"left_knee",
0,
10,
[
0.0,
0.0,
0.0
],
[
0.5,
-0.5,
-0.5,
-0.5
]
],
这一项从上到下分别是:
text
机器人连杆
人体骨架部位
位置权重
旋转权重
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,就不会对重定向结果产生强约束。
所以配置映射表时,不要只看有没有对应关系,更要看权重是否合理。
五、人体关节很多,但不需要全都对齐
可用的人体关节很多,例如:
python
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"
]
但实际配置时,不建议一股脑全部高权重对齐。约束太多容易过拟合,表现出来可能是:
text
动作抖动
局部关节异常扭动
腿部或手臂姿态不自然
为了满足某个点导致整体姿态变形
我的理解是,映射选择应该优先考虑三个问题:
text
1. 机器人是否真的有这个部位?
2. 这个部位对动作质量是否关键?
3. 机器人是否有能力合理实现这个约束?
比如机器人没有头部自由度,却高权重跟踪 Head 的方向,可能会导致机器人通过扭腰或扭 torso 去代偿头部旋转,结果反而很怪。
脚部则相反,通常值得重点关注。因为脚部不仅影响动作像不像,还直接影响接触、落地和稳定性。
例如,实践中,使用 foot 作为脚部主要参考点,效果通常比只用 ankle 更好一些。
原因大概是:ankle 更像一个点,而 foot 更能表达脚掌方向。虽然 ankle 到 foot 之间可能没有额外自由度,但 foot 的方向信息对步态很有帮助,尤其是转身、跑步、落脚这类动作。
也可以 foot 和 ankle 都用,但权重不要太激进。脚部约束过强时,腿部姿态也可能变僵。
只使用 ankle、只使用 foot的效果对比


六、四元数偏移:本质是坐标系对齐
四元数 offset 是调试中最容易困惑的部分。
比如常见的:
python
[0.5, -0.5, -0.5, -0.5]
它并不是经验玄学,而是在做坐标系对齐。
GMR 中四元数格式是 wxyz,这一点要特别注意。
原始动作骨架的默认状态中,关节四元数基本都是:
python
[1, 0, 0, 0]
但这只说明它在骨架自己的坐标系下没有旋转,并不代表它和机器人 link 的局部坐标系一致。
观察原始骨架可以看到,它可能是面朝上躺平的大字形态,且轴系大致表现为:
text
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 保持一致,所以需要单独配置:
python
"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 的手肘还带有一定弯曲。因此肩部配置会使用类似:
python
"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 更直观,但更依赖具体机器人和动作数据。
影响因素包括:
text
人体骨架身高和比例
机器人腿长、臂长、躯干比例
机器人 link 原点定义
foot / ankle / toe 的选择
不同动作库的骨架差异
root 缩放比例
目前比较有效的方法还是构建可视化脚本,反复观察:
text
原始人体骨架
偏移后的目标骨架
机器人 FK 后的实际 link
关键点位置误差
关键点旋转误差
对于一些怎么都对不齐的点,不建议死磕。可以降低位置权重,保留旋转约束,或者干脆弱化这个部位。
本质上,GMR 不是要让每个点都完美贴合,而是要让关键部位合理贴合。
原始动作骨架、未偏移结果、偏移后结果(但骨架保留原始效果以便表达区别)三图对比:

八、两阶段 IK:表一粗对齐,表二细对齐
GMR 中通常会有两张 IK match 表,对应两阶段 IK。
第一阶段主要是粗对齐。重点是:
text
root / pelvis 方向位置合理
torso 方向合理
双足位置合理
主要 end-effector 大致到位
其他关节旋转方向不要太离谱
第一阶段的目标不是精修动作,而是得到一个比较稳定的初始姿态。
如果第一阶段已经跑偏,第二阶段很容易在错误姿态附近继续优化。
第二阶段是细对齐。一般会加入更多 key body,并适当提高部分旋转或位置约束,让动作更贴近源动作。
可以简单理解为:
text
表一:先把机器人摆对
表二:再把动作修细
所以两张表不要配置成完全一样。
表一应该抓重点,表二可以更细,但也不能过度激进。否则动作细节可能更像了,但抖动、自碰撞和关节突变也更容易出现。
九、个人调试经验
整理一些目前比较有用的经验:
1. foot 通常比 ankle 更适合作为脚部主约束
foot 对脚掌方向更敏感,步行和转身效果会更好。ankle 可以辅助,但单独用 ankle 时脚掌方向约束偏弱。
2. 没有自由度的部位,权重要低
比如没有头部自由度,就不要高权重跟踪 Head 的方向。否则机器人可能通过 torso 或腰部去代偿,动作会变得很奇怪。
3. 不同动作库可能需要不同 scale 或 offset
不同动作库的骨架定义、身高比例、坐标系和帧率都可能不同。不要默认一套参数可以适配所有来源的数据。
4. 不要只用一个动作验证配置
一个动作调得很好,不代表配置稳定。建议在同一个动作库/作者下,至少准备几类动作:
text
普通步行
转身
跑步
上肢动作
大幅度动态动作
多动作都能接受,配置才比较可信。
十、几个待研究问题
目前还有一些问题没有完全搞清楚,后续值得继续看。
1. 权重和 scale 的影响能否量化?
现在调参主要依赖观察和枚举,成本比较高。后面可以考虑引入一些自动指标:
text
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仓库里则是区分了高频低频的训练环境。这里需要继续确认:
text
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 输出整理成重定向系统使用的格式:
python
{
"pelvis": (position, quaternion),
"left_hip": (position, quaternion),
"left_knee": (position, quaternion),
...
}
其中四元数格式为 [w, x, y, z]。这个阶段也会根据目标帧率进行时间对齐或重采样,并返回实际使用的 aligned_fps。
逐帧重定向
脚本逐帧取出 SMPL-X 处理后的人体数据:
python
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。输出结构如下:
python
{
"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 的映射、权重、缩放和坐标系校准。
- 输出结果不是力控或轨迹控制命令,而是逐帧机器人运动学状态。
整体流程可以概括为:
text
SMPL-X .npz
-> SMPL-X body model 前向计算
-> 人体关节位置/姿态序列
-> 缩放与坐标系校准
-> 机器人 IK 求解
-> 机器人 root + joint motion
-> .pkl 动作文件
总结
把自己的机器人接入 GMR,核心工作不是跑脚本,而是配置好 smplx_to_robot 映射表。
这张表里最重要的是:
text
映射哪个机器人 link
参考哪个人体 body
位置权重是多少
旋转权重是多少
xyz offset 如何设置
quaternion offset 如何设置
其中,映射关系决定机器人"看哪里",权重决定机器人"有多在意",offset 和 scale 决定人体动作目标是否真的适合当前机器人。
我的整体感受是,GMR 的工程配置本质上是在做一次折中:
既不能让机器人完全无视人体动作,也不能让它死追所有人体关节。比较好的结果通常来自合理取舍:关键部位强约束,非关键部位弱约束;可实现的部位认真跟踪,不可实现的部位适当放过。
当这套配置稳定之后,GMR 就会变成一个比较实用的动作数据生产工具。对于后续 motion tracking、模仿学习或者遥操作数据处理来说,前面这一步调得越稳,后面的训练就越省心。