GMR(General Motion Retargeting)是一套面向人形机器人的动作重定向方法。论文作者为 João Pedro Araújo、Yanjie Ze、Pei Xu、Jiajun Wu 和 C. Karen Liu。它解决的是人体与机器人之间的 embodiment gap:两者的骨骼长度、关节结构、活动范围和静止姿态都不同,人体动作不能直接复制到机器人关节上。

原论文与代码:

[ICRA 2026] Retargeting Matters: General Motion Retargeting for Humanoid Motion Tracking

YanjieZe/GMR

各种 motion imitation 方法,包括 AMP 一类依赖参考动作的训练方法,通常都需要先把人体动捕数据处理成机器人能够使用的 motion data。GMR 位于运动学重定向这一层,之后才是物理仿真中的 motion tracking。

从人体动作重定向到机器人动作跟踪

这篇文章主要记录我对 GMR 五个处理阶段,以及它们在代码中如何衔接的理解。

第一步:人体和机器人关键刚体匹配

这里匹配的是 body,而不是 joint。

人体侧的关键刚体与机器人侧的 link/body 建立对应关系,常见对象包括骨盆、躯干、大腿、小腿、脚、上臂、前臂和手。位置约束比较的是所选 body frame 的原点,也可以在原点上叠加配置的局部位置偏移;朝向约束比较的是人体 body frame 和机器人 body frame 的三维朝向。

这组映射记作 $\mathcal{M}$。映射关系不只是告诉 IK 哪两块刚体对应,还为后面的缩放、坐标系修正和误差权重提供索引。

bvh_lafan1_to_g1.json 是 LAFAN1 BVH 到 Unitree G1 的配置文件。其中每条映射都包含人体 body 名、位置权重、旋转权重、局部位置偏移和旋转偏移。它不是单独服务于某一个阶段,而是把整套重定向需要的配置集中在一起。
bvh动画如下(这里以lafan1为例):
LAFAN1 BVH 人体动作

第二步:静止姿态与坐标系对齐

即使人体 body 和机器人 body 的语义相同,它们各自局部坐标系的定义也可能不同,尤其是局部 $x$、$y$、$z$ 轴的朝向。GMR 会在静止姿态下加入旋转偏移,使人体关键刚体的朝向先与机器人对应刚体对齐;必要时也会加入局部位置偏移。

这些偏移最终会用于两阶段 IK 的目标位姿。如果不先对齐坐标系,同一个“抬腿”动作在两个模型中可能对应完全不同的旋转方向。

配置也可以顺便修正人体与机器人静止体态上的差别。人体自然站立时,腿部轮廓会受到肌肉和骨骼形态影响;机器人没有相同的外形,直接照搬人体关键点位置容易得到不自然的内扣或外扩姿态,肩部也有类似问题。bvh_lafan1_to_g1.json 中的 pos_offsetrot_offset 就承担了这部分修正。

人体与机器人静止姿态差异
效果如下:

静止姿态对齐效果

第三步:人体数据的局部非均匀缩放

人体和机器人各身体部分的长度比例并不一致,所以不能把整个人体动作统一缩小,也不能把每个 body 的世界坐标分别乘上任意比例。前者无法处理上下肢比例差异,后者会破坏各 body 与 root 之间的空间关系。

GMR 对目标位置采用局部非均匀缩放:先计算每个 body 相对人体 root 的位置,再使用该 body 自己的缩放系数,最后加回经过统一缩放的 root 全局平移。论文中的公式是:

其中,$h$ 是当前人体的身高,$h{\text{ref}}$ 是配置缩放系数时假定的参考身高,$s_b$ 是关键刚体 $b$ 的局部缩放系数,$s{\text{root}}$ 是 root 全局平移的缩放系数。

公式的前半部分

表示 body 相对 root 的局部位置。不同 key body 可以使用不同的 $s_b$,例如肩宽、手臂和腿可以分别调整。配置中的系数是标量,因此一个 body 的 $x$、$y$、$z$ 分量会乘以同一个系数。

公式的后半部分

表示 root 的全局平移。它决定整台机器人在世界坐标系中的目标轨迹。当 $b$ 本身就是 root 时,局部项为零,公式简化为:

把 body 的局部形态缩放和 root 的世界轨迹拆开,既能适配人体与机器人的局部比例,又不会让各个 body 因为独立缩放世界坐标而散开。论文还特别强调,root 平移需要统一缩放,否则容易引入脚底滑动。

在当前官方代码中,actual_human_height / human_height_assumption 会先乘进 human_scale_table,随后 scale_human_data() 分别缩放 root 和各 body 相对 root 的位置,计算行为与上面的公式一致。

效果如下:

人体数据局部非均匀缩放效果

第四步:使用旋转约束求 IK 初解

理论上可以让所有关键刚体的位置和朝向同时严格匹配,但人体与机器人结构不同,很多目标无法同时满足。即使前面做过缩放,目标点之间的距离也不一定符合机器人的真实 link 长度。优化一开始就同时拉扯所有目标,很容易落入不理想的局部最优解。

GMR 因此采用两阶段 IK。初解阶段优先匹配所有关键刚体的朝向,只对末端执行器加入位置约束:

$\mathcal M_{ee}$ 是手和脚等末端执行器对应关系的集合,$\mathbf q$ 包含机器人 root 平移、root 旋转和各关节位置,$\mathbf q^-$、$\mathbf q^+$ 是允许的关节范围。

直观上,只要大腿、小腿和脚的朝向合理,同时脚能够到达目标位置,髋、膝、踝组成的运动链就能先形成一个结构基本正确的姿态。如果此时还强制要求膝盖位置完全等于人体膝盖位置,人体与机器人腿长不同带来的冲突就会直接进入优化。

论文的通用表述是“手和脚的位置”,但具体启用哪些位置约束仍由配置权重决定。以当前官方 bvh_lafan1_to_g1.json 为例,ik_match_table1 中脚部位置权重非零,而手部位置权重为零,因此这份 G1 配置的初解阶段实际只追踪脚的位置。

姿态误差

位置误差可以直接相减,旋转误差不能用两个旋转矩阵做普通减法。对于人体目标朝向 $R_i^h$ 和当前机器人刚体朝向 $R_j(\mathbf q)$,先求相对旋转:

旋转矩阵满足 $R^{-1}=R^\mathsf{T}$,所以也可以写成:

它描述了从人体目标朝向旋转到当前机器人刚体朝向所需的相对旋转。随后通过 $SO(3)$ 的对数映射,把旋转矩阵转换为三维旋转向量:

向量 $\boldsymbol\phi$ 的方向表示误差旋转轴,模长表示误差角度。假设机器人脚相对目标脚绕 $z$ 轴多转了 $10^\circ$,那么可以写成:

对应的平方误差为:

论文将这项记为 $R_i^h \ominus R_j(\mathbf q)$。优化器使用的就是这类三维切空间误差,而不是直接对旋转矩阵逐元素相减。

权重、初值与 Mink

ik_match_table1 中的位置和旋转权重决定目标冲突时的取舍。权重不会改变机器人的结构,只会改变各项误差在优化目标中的重要程度。脚部位置权重更高时,优化器会更优先调整 root 和腿部,使脚靠近目标,即使其他刚体暂时保留更大的误差。

求解当前帧之前,root 平移使用缩放后的 root 目标位置初始化,root 旋转使用人体 root 朝向中的 yaw 分量初始化。人体骨盆的 roll 和 pitch 可能同时包含动作倾斜、局部姿态、动捕噪声和 rest pose 差异,直接完整复制容易让机器人初值明显倾斜。

GMR 使用基于 MuJoCo 的差分 IK 库 Mink。一个人体与机器人刚体映射会建立一个 FrameTaskFrameTask.set_target() 接收由 $SO(3)$ 旋转和三维平移组成的 $SE(3)$ 目标。多个任务组成 tasks1mink.solve_ik() 求出广义速度,再由 configuration.integrate_inplace() 积分并更新 qpos

官方当前实现会重复求解,直到误差改善量不再超过 0.001,或者达到最多十次迭代。这里的 0.001 是代码中的误差改善阈值,不是单独某个位置或角度误差的物理单位阈值。

最终求解效果如下,可以看到第一步优化只侧重于每个body的朝向和四肢端点的位置,所以其实中间很多点目前还有些位置误差,然后需要交给下一步精细求解

第一阶段 IK 效果

第五步:同时使用旋转和位置约束精调

初解阶段得到结构基本合理的单帧姿态后,精调阶段把它作为初值,对映射集合 $\mathcal M$ 中的关键刚体同时考虑位置和朝向误差:

这一阶段使用 ik_match_table2 中的另一组权重,继续在机器人结构和人体目标之间折中。公式写的是全部关键刚体,但某项权重配置为零时,对应误差实际不会参与优化。

效果如下,可以看到明显要比第四步效果好很多,骨架更加贴合机器人

第二阶段 IK 精调效果

论文中的方法先针对单帧姿态定义。处理完整动作时,会按时间顺序逐帧调用,并把上一帧的重定向结果作为下一帧初解阶段的初值。这样既减少每帧从头搜索的开销,也让相邻帧更连续。

整段动作完成后,再通过正向运动学计算所有机器人刚体在全部帧中的高度,找到全局最低点,并从 root 的全局高度中统一减去该值,用来修正悬空或穿地。这个处理是整段动作的统一高度平移,不会逐帧独立把脚吸到地面。
可以看到修正前,机器人是有一定的浮空的
全局高度修正前
然后修正后,会贴着地面,缓解穿模和腾空的现象
全局高度修正后

步骤与代码部分对应

论文中的五个阶段,在当前 GMR 配置和实现中可以对应为:

处理内容 配置或代码 实际作用
关键刚体匹配 ik_match_table1ik_match_table2 中的 robot body 与 human body 建立 $\mathcal M$,确定人体和机器人哪些刚体互相对应
静止姿态对齐 pos_offsetrot_offset 修正局部坐标轴与静止体态差异
局部非均匀缩放 human_height_assumptionhuman_scale_tablescale_human_data() 缩放 body 相对 root 的位置,同时单独处理 root 全局平移
IK 初解 ik_match_table1tasks1 以旋转约束为主,按非零权重加入末端位置约束
IK 精调 ik_match_table2tasks2 使用另一组位置和旋转权重继续优化

scripts/bvh_to_robot.py 负责读取 LAFAN1 BVH 动作并逐帧调用 GeneralMotionRetargeting.retarget()update_targets() 内部先执行 scale_human_data()offset_human_data(),再通过 FrameTask.set_target() 写入目标 $SE(3)$。retarget() 依次求解 tasks1tasks2,两次得到的广义速度都会积分进同一个 configuration,最终返回当前帧的机器人 qpos

对于 G1,一帧输出可以理解为机器人 base 的三维平移、四元数旋转和关节位置。整段逐帧结果再组成训练或 motion tracking 使用的机器人参考动作。