GMR 使用经验分享:从人体动作到机器人轨迹

GMR 使用经验分享:从人体动作到机器人轨迹

硅基生命训练师

2026-04-18 发布72 浏览 · 0 点赞 · 0 收藏

一、为什么要使用 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_s14roban_s17kuavo_s52kuavo_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 骨架文件,支持lejulafan1qmainokov

    • 注意:并非每个 --format 下四种机器人都有 IK 配置,需存在 general_motion_retargeting/params.pyIK_CONFIG_DICT["bvh_<format>"][robot]中,不匹配时运行会直接报错并提示可用机器人列表。
  • --motion_fps:控制输出与回放的 PKL 帧率。

  • --bvh_unit:BVH 平移通道的长度单位,支持autommcmm,默认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.pklcsv/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_linkleg_l6_link);值为长度 5 的数组:
    [human_body_name, pos_weight, rot_weight, pos_offset, rot_offset]

  • human_body_name:对应人体部位名。

  • pos_weight/rot_weight:mink FrameTask位置 / 姿态代价权重

  • 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_offsetzarm_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],当只有 wx 非零时,该四元数表述绕该关节局部 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_fpsFrame Time需要保持一致;

2. 出现整机悬空或入地时,优先排查--bvh_unit单位量级是否正确;

3. 上述两步完成后,微调ground_height使机器人脚掌贴合地面;

4. 针对不同动捕数据呈现出的重定向结果,调整pos_weightrot_weightrot_offset,权重与偏置没有「一次调完」的万能值,通过小步改、多段动作对比直至调出满意的重定向轨迹。

以上均是笔者使用 leju-gmr 时的个人经验,不同 BVH 来源、身高比例与机器人外形都需要改变参数以达到最佳效果;若与你的现象不一致,以可复现的最小用例为准慢慢对齐即可,文末分享笔者使用该仓库进行重定向后的轨迹可视化结果。

请前往 登录/注册 即可发表您的看法…