
GMR 使用经验分享:从人体动作到机器人轨迹
一、为什么要使用 GMR
人形机器人的动作往往不是在关节空间里直接设计的,而是从人体运动来的。以动捕为例:动捕软件导出的 BVH 每一帧都能给出人体各部位在世界坐标系下的位置与姿态。机器人控制器需要的则是浮动基座(例如与 base_link 或腰部连杆对应的根位姿)以及各关节角,并且这些量必须与机器人模型文件里定义的关节顺序、连杆几何一致。连接「人体运动」与「机器人轨迹」的中间层,就是运动重定向(retargeting)。
GMR 把这一层收束成一条清晰的流水线:统一的人体帧表示(按身体部位名称,每帧一组世界系下的平移 + 旋转)→ 可配置的 IK 匹配表( JSON 里指定人体部位与机器人连杆的对应关系、权重与偏移)→ 在 MuJoCo 模型(MJCF) 上用 mink 做逆运动学求解,得到与模型一致的 qpos(根位姿 + 关节位置)。因此,在更换动捕数据格式或目标机型时,主要工作是:把新数据读成上述统一字典、增改 IK 配置文件、以及(若为新机器人)更换或扩展 MJCF,而不必从零重写整条重定向与求解代码。
二、快速使用 GMR
下面以 Leju-GMR 仓库为例,分享快速使用方法。
1. 获取代码与环境
git clone https://gitee.com/leju-robot/leju-gmr.git # 下载仓库
cd leju-gmr/ # 请根据你的下载路径进入
conda create -n gmr python=3.10 -y # 创建 conda 环境
conda activate gmr
pip install -e . # 以“可编辑模式”安装当前目录中的Python包
该仓库将人体运动学表征(如 BVH 解析后的逐帧部位位姿)通过 GMR 转换为机器人关节轨迹,并使用 MuJoCo 进行可视化。
2. 运行预置 BVH 示例
output/BVH/leju/下已附带示例dance_bj_01_Skeleton_002.bvh,请使用如下命令运行 demo:
python scripts/bvh_to_pkl.py \
--bvh_file output/BVH/leju/dance_bj_01_Skeleton_002.bvh \
--bvh_fps 100 \
--robot roban_s17 \
--motion_fps 50 \
--rate_limit \
--format leju \
--record_video_path
运行成功后会出现 MuJoCo 可视化窗口。程序启动后默认处于暂停状态;在运行脚本的终端,按空格键可在「继续播放 / 暂停」之间切换。


三、进阶使用 GMR
1. 命令详解
1.1 BVH 重定向到机器人并导出 PKL
python scripts/bvh_to_pkl.py \
--bvh_file <path_to_bvh_data> \
--bvh_fps <source_bvh_fps> \
--robot <robot_name> \
--save_path <path_or_dir_to_save_robot_data.pkl> \
--rate_limit \
--format <format> \
--motion_fps <target_motion_fps> \
--bvh_unit <auto|mm|cm|m> \
--record_video_path [video_path]
参数说明:
-
--bvh_file:指定要处理的 BVH 文件路径,此参数为必填参数。 -
--bvh_fps:BVH 数据的源帧率(Hz),应与录制或 BVH 头里Frame Time一致,用于从源序列重采样到目标帧率。 -
--robot:指定重定向的目标机器人型号,该仓库支持roban_s14、roban_s17、kuavo_s52、kuavo_s54。 -
--save_path:输出路径(可选)。输出 PKL 的解析规则为:-
传入目录路径:保存到
<dir>/<robot>/pkl/<bvh_stem>_<fps>.pkl; -
省略:默认保存到
output/<robot>/pkl/<bvh_stem>_<fps>.pkl。
-
-
--rate_limit:控制查看器是否按仿真步长限速播放。-
当未指定
--motion_fps时,默认会限速按--bvh_fps进行播放; -
当已指定
--motion_fps时,会按指定的帧率做回放限速。
-
-
--format:BVH 骨架文件,支持leju、lafan1、qmai、nokov。- 注意:并非每个
--format下四种机器人都有 IK 配置,需存在general_motion_retargeting/params.py中IK_CONFIG_DICT["bvh_<format>"][robot]中,不匹配时运行会直接报错并提示可用机器人列表。
- 注意:并非每个
-
--motion_fps:控制输出与回放的 PKL 帧率。 -
--bvh_unit:BVH 平移通道的长度单位,支持auto、mm、cm、m,默认auto。若已知单位,建议显式指定(如--bvh_unit m)。 -
--record_video_path:进行录制 MP4 播放文件-
仅传
--record_video_path:默认保存到output/<robot>/videos/<pkl_stem>.mp4; -
传具体路径:按指定路径保存视频;
-
不传:不录制视频。
-
1.2 PKL 格式转换为 CSV 格式
PKL 格式的动作数据本身具有独立用途,若需要 CSV 数据,请使用下述脚本:
python scripts/batch_gmr_pkl_to_csv.py --folder <包含_pkl_的文件夹路径>
参数说明:
--folder(必须传入):放有 .pkl 的目录。脚本会扫描该目录下所有 .pkl数据文件,并在同目录下创建 csv/ 子文件夹,为每个 PKL 生成同名 .csv(例如 demo.pkl → csv/demo.csv)。
2. 参数进阶调整
进行重定向时,针对真实动作数据往往需要修改各类参数,笔者在这里进行一些经验分享。
2.1 调整 bvh_unit,与 BVH 平移单位对齐
笔者在使用不同来源的 BVH 数据时,既遇到过整机「飞在天上」,也遇到过「陷入地里」—— 根高度与人体目标相对地面的关系错误。后来经过排查,归因于平移单位与 --bvh_unit 不一致。例如实际是厘米却按米去解,数值被当成「几米、几十米」的位移,人体被抬到极高处;反过来,若不该用毫米却选了 mm(本已是厘米或米,又额外乘了 1/1000),脚端目标容易落到地面以下,看上去就像半截埋进地里。因此无论表现为离地极远还是整体过低,都建议先把--bvh_unit当作第一嫌疑进行修改。
python scripts/bvh_to_pkl.py \
--bvh_file output/BVH/leju/0211_re_005_Skeleton.bvh \
--bvh_fps 120 \
--robot kuavo_s54 \
--motion_fps 50 \
--format leju \
--bvh_unit m \
--rate_limit \
--record_video_path

python scripts/bvh_to_pkl.py \
--bvh_file output/BVH/leju/0211_re_005_Skeleton.bvh \
--bvh_fps 120 \
--robot kuavo_s54 \
--motion_fps 50 \
--format leju \
--bvh_unit cm \
--rate_limit \
--record_video_path

python scripts/bvh_to_pkl.py \
--bvh_file output/BVH/leju/0211_re_005_Skeleton.bvh \
--bvh_fps 120 \
--robot kuavo_s54 \
--motion_fps 50 \
--format leju \
--bvh_unit mm \
--rate_limit \
--record_video_path

2.2 IK 配置 JSON 参数详解
在general_motion_retargeting/ik_configs/目录下存放各机器人 IK 配置文件(.json)。运行bvh_to_pkl.py时,由params.py里的IK_CONFIG_DICT根据bvh_<format>与robot选中其中具体哪一个配置文件加载。
顶层标量:
| 字段 | 含义 |
|---|---|
robot_root_name |
机器人根连杆在 MuJoCo 中的 body 名(如 base_link)。 |
human_root_name |
人体骨架根关节名(如 Hips),与 BVH 解析后字典里的键一致。 |
ground_height |
标量,在构造任务时转为竖直向量 [0, 0, ground_height]。 |
human_height_assumption |
配置编制时假设的参考身高(米)。 |
use_ik_match_table1 / use_ik_match_table2 |
是否启用第一 / 第二套 IK 任务表。 |
ik_match_table1/ik_match_table2:
-
对象:键为 MuJoCo 机器人 body 名(如
base_link、leg_l6_link);值为长度 5 的数组:
[human_body_name, pos_weight, rot_weight, pos_offset, rot_offset] -
human_body_name:对应人体部位名。 -
pos_weight/rot_weight:minkFrameTask的位置 / 姿态代价权重。 -
pos_offset:位置偏置/固定位移。 -
rot_offset:四元数[w, x, y, z],本质是固定旋转偏移。
{
"ground_height": 0.0,
// 竖直方向统一标定(米)。
"human_height_assumption": 1.57,
// 假设参考身高(米)。
"human_scale_table": {
"Hips": 1.0,
"Spine": 1.0,
"LeftShoulder":1.0,
// … 其余键为人体骨骼名,值为该骨的局部位置缩放系数。
},
"ik_match_table1": {
// 键:机器人 MuJoCo body 名;值:五元组
// [人体骨骼名, 位置权重, 姿态权重, 位置偏置[x,y,z], 旋转偏置四元数[w,x,y,z]]
"zarm_l1_link": [
"LeftShoulder",
120,
10,
[0.0, 0.0, 0.0],
[0.5, -0.5, -0.5, -0.5]
]
}
}
2.3 地面高度对齐
--bvh_unit单位对齐后进行重定向,笔者发现在重定向时会存在脚底和地面差一截或穿进地面的情况,这时优先去修改 IK 配置中的 ground_height。每一套general_motion_retargeting/ik_configs/*.json里都有ground_height(单位是米),它会参与构造位姿时的位置偏移(减去ground_height对应的竖直分量),这是为「仿真地面 / 机器人基座」留的修正余量。
调整时,若机器人整体仍偏上或偏下一点点,对应当前 --format + --robot 的 JSON 文件,把 ground_height 小幅增减,看脚底与地面对齐是否改善。
"robot_root_name": "base_link",
"human_root_name": "Hips",
"ground_height": 0.15,
"human_height_assumption": 1.42,
"use_ik_match_table1": true,
"use_ik_match_table2": false,

此时脚掌位于地面之下,初次进行调整。
"robot_root_name": "base_link",
"human_root_name": "Hips",
"ground_height": 0.08,
"human_height_assumption": 1.42,
"use_ik_match_table1": true,
"use_ik_match_table2": false,

可以发现经过调整后,脚掌已经升高到地面以上,如果我们再次减小ground_height。
"robot_root_name": "base_link",
"human_root_name": "Hips",
"ground_height": 0.03,
"human_height_assumption": 1.42,
"use_ik_match_table1": true,
"use_ik_match_table2": false,

此时发现机器人悬浮在了地面之上。
调整ground_height的目的,是让机器人在重定向后保持正确的「脚底和地面」关系。笔者在这里进行调整,是为了后续的强化学习训练过程,保证训练环境与生成数据时的「地面」一致。
2.4 调整与胸部的碰撞(蹭胸、穿模)
重定向时笔者偶尔会碰到手掌或上臂贴胸、甚至穿进躯干的情况。此时一般会先动左右肩的旋转偏置 rot_offset(zarm_l1_link / zarm_r1_link 对应 LeftShoulder / RightShoulder):它主要对齐上臂相对躯干的轴向,同时会连带改变肘、掌与胸前的间隙。
下面这一组参数配置,会出现穿模情况。
"zarm_l1_link": [
"LeftShoulder",
120,
10,
[0.0, 0.0, 0.0],
[0.2588, -0.5577, -0.5577, -0.5577]
],
"zarm_r1_link": [
"RightShoulder",
120,
10,
[0.0, 0.0, 0.0],
[0.2588, -0.5577, -0.5577, -0.5577]
],


在同一转轴上略微增大旋转角后,发现穿模情况消失。
"zarm_l1_link": [
"LeftShoulder",
120,
10,
[0.0, 0.0, 0.0],
[0.5, -0.5, -0.5, -0.5]
],
"zarm_r1_link": [
"RightShoulder",
120,
10,
[0.0, 0.0, 0.0],
[0.5, -0.5, -0.5, -0.5]
],


若继续加大同一方向的旋转偏置,会出现抬臂偏高、动作显得「架着」,美观和协调性变差。所以笔者习惯进行小步微调,多看几段不同动作确认效果。
"zarm_l1_link": [
"LeftShoulder",
120,
10,
[0.0, 0.0, 0.0],
[-0.4226, -0.5227, -0.5227, -0.5227]
],
"zarm_r1_link": [
"RightShoulder",
120,
10,
[0.0, 0.0, 0.0],
[-0.4226, -0.5227, -0.5227, -0.5227]
],


2.5 调整手臂和腰侧间隙
笔者在做重定向时遇到过这样一种情况:肘内侧始终贴着躯干两侧,手臂像一直「夹着」身体,摆臂时上臂或肘与腰侧反复摩擦,严重时还会穿模。此时笔者会修改zarm_l3_link / zarm_r3_link(对应 LeftArm / RightArm)上的旋转偏置。
下面以左臂 zarm_l3_link 为例说明(右臂 zarm_r3_link 也可按同一字段调,但左右不必对称)。rot_offset 为 [w, x, y, z],当只有 w 与 x 非零时,该四元数表述绕该关节局部 X 轴的固定旋转。
笔者使用下面这组参数,此时左臂在仿真里更贴躯干、腰侧间隙偏小,摆臂时易与腰侧摩擦。
"zarm_l3_link": [
"LeftArm",
0,
10,
[0.0, 0.0, 0.0],
[0.7071, -0.7071, 0.0, 0.0]
],

此时对旋转偏置进行调整,减小了偏置角度,可见腰侧间隙增大,摩擦减轻。
"zarm_l3_link": [
"LeftArm",
0,
10,
[0.0, 0.0, 0.0],
[0.9239, -0.3827, 0.0, 0.0]
],

若继续大幅改动,可能出现肘部外顶、姿态发僵等不协调观感。笔者建议小步微调,用多段动作与录屏对比。
"zarm_l3_link": [
"LeftArm",
0,
10,
[0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0]
],

四、小结
笔者写到这里,若只记「排障顺序」,目前的习惯是:
1. 源帧率与单位统一:--bvh_fps与Frame Time需要保持一致;
2. 出现整机悬空或入地时,优先排查--bvh_unit单位量级是否正确;
3. 上述两步完成后,微调ground_height使机器人脚掌贴合地面;
4. 针对不同动捕数据呈现出的重定向结果,调整pos_weight、rot_weight、rot_offset,权重与偏置没有「一次调完」的万能值,通过小步改、多段动作对比直至调出满意的重定向轨迹。
以上均是笔者使用 leju-gmr 时的个人经验,不同 BVH 来源、身高比例与机器人外形都需要改变参数以达到最佳效果;若与你的现象不一致,以可复现的最小用例为准慢慢对齐即可,文末分享笔者使用该仓库进行重定向后的轨迹可视化结果。



