人形机器人舞蹈开发简记

流程

目前主流机器人舞蹈开发主要分为以下几个步骤(超链接均为github):

  1. 获得指定舞蹈的视频或动捕数据.bvh
  2. 将上述数据转为SMPL格式,目前SMPLX更常用,对于视频可以使用TRAMASAP使用)或GVHMR(宇树官方使用),对于动捕直接使用GMR重定向或bvh2smpl(待验证)
  3. 将SMPL数据重定向到机器人的各个关节,可用算法H2O或OmniH2O(ASAP使用),也可GMR(宇树官方使用)
  4. 通过DeepMimic系列RL算法训练(目前BeyondMimic较常用),仿真通常使用IsaacGym(较老)或IsaacLab配合Isaacsim(较新)等
  5. 将.onnx动作策略部署至mujoco进行sim2sim验证
  6. 将.onnx动作策略部署至实机进行sim2real测试,目前推荐代码简洁robomimic-deploy,多合一robojudo

以上1-3为数据处理阶段,目的是给训练算法提供可用的高质量数据。
第4步训练通常被认为是决定最终效果的关键步骤,也是算法工程师主要的工作内容,包括对算法的改进如与最新算法的结合、调参等。一个熟练的训练通常耗时20小时(4090),但新算法的调试可能需要一个月。
之所以使用IsaacLab训练、用mujoco验证是因为前者容易上手训练速度快,后者仿真更精准但学习较难。而最近的mujocolab结合了两者的优势,使用IsaacLab的API和mujoco的物理引擎,其RL算法使用BeyondMimic,其动作数据使用wandb线上存取,并于昨天(2025-10-10)上线了第一个训练案例:G1回旋踢

MJLAB的使用说明

虽然github上已有详细的readme,但其中存在若干不详尽或错误,因此记录

1. 关于mjlab本体

  • 安装方式建议使用Method 1, Option A, 提示错误时按提示加上--dev。
  • run demo没必要。
  • 使用wandb时
    • 在终端输入wandb loginwandb login --relogin,粘贴API key。
    • 在wandb新建一个名为Motions的registry,类型为all types。
    • csv_to_npz.py中的wandb.init(..., entity='<自己的team名字如: unitree>')
    • 训练第一步使用csv_to_npz.py,会在wandb生成一个csv_to_npz的project并将动作存在其下,例如fkdance。
MUJOCO_GL=egl uv run src/mjlab/scripts/csv_to_npz.py  --input-file /path/to/motion.csv  --output-name /path/to/motion.npz  --input-fps 30   --output-fps 50   --render 

更新后的csv_to_npz可以在前后加入过渡动作,以使得开始前和结束后平滑过渡:

python src/mjlab/scripts/csv_to_npz.py --input_file data/tu/unitree_joint_data_tu3_2.csv  --output_name tu3_2 --render --add-start-transition --add-end-transition --pad-duration 1.5 --transition-duration 1.5
  • train时--registry-name设置为unitree/csv_to_npz/fkdance(默认使用最新版,也可以自己指定版本,如fkdance:v0),训练时会生成一个mjlab的project,并在其下生成一个随机码(例如fk1234fk)的run代表本次训练,点进该run,点击overview的页签,其中有一条run path,用于play时的--wandb-run-path
MUJOCO_GL=egl uv run train Mjlab-Tracking-Flat-Unitree-G1 \
--registry-name unitree/csv_to_npz/fkdance \ #npz动作路径
--env.scene.num-envs 4096
  • 离线train脚本:
MUJOCO_GL=egl uv run train Mjlab-Tracking-Flat-Unitree-G1-No-State-Estimation \
--motion_file artifacts/local/conduct1min.npz \
--env.scene.num-envs 4096
  • 中断后继续训练:
uv run train Mjlab-Tracking-Flat-Unitree-G1-No-State-Estimation --motion_file artifacts/local/tu3_2.npz --env.scene.num-envs 4096 --agent.resume True --agent.load_run "2025-12-12_17-48-20"
  • play时修改wandb-run-path即可。
uv run play Mjlab-Tracking-Flat-Unitree-G1 \
--wandb-run-path unitree/mjlab/fk1234fk
  • 联网跑经常面临连接问题,因此可以本地跑策略,要给出.pt文件和.npz动作文件路径:
uv run play Mjlab-Tracking-Flat-Unitree-G1-No-State-Estimation-Play \
--checkpoint_file logs/rsl_rl/g1_tracking/2025-11-14_11-45-58/model_14999.pt \
--motion_file artifacts/fkdance\:v0/motion.npz 
agent配置文件

路径:/mjlab/src/mjlab/tasks/tracking/config/g1/rl_cfg.py
可修改save_interval, max_iterations等。

PS
  • 训练时任务可用Mjlab-Tracking-Flat-Unitree-G1-No-State-Estimation,其obs为154维,少了motion anchor的pos以及base_lin_vel。
  • 单独运行csv_to_npz.py时本地是没有.npz文件的,还需要下载一下,创建一个download_npz.py如下:
import wandb
run = wandb.init()
artifact = run.use_artifact('unitree/csv_to_npz/fkdance:v0', type='motions')
artifact_dir = artifact.download()

原始视频通过GVHMR以及GMR、mjlab的处理流程

  1. 按readme安装GVHMR之后使用代码转换
python tools/demo/demo.py  --video=../motion_data/..

注意:之前同名的输出results会影响再次输出,因此要先删掉。

  1. 按readme安装GMR之后运行。
python scripts/gvhmr_to_robot.py \
--gvhmr_pred_file ../GVHMR/outputs/demo/tennis/hmr4d_results.pt \
--robot unitree_g1 --record_video \
--save_path outputs/tennis_motion.pkl # 自定义保存路径
  1. 通过mjlab转换pkl文件为csv:
python src/mjlab/scripts/pkl_to_csv_gmr.py \
--pkl-file ../motion_data/..pkl \
--csv-file ../motion_data/cartwheel.csv \
--add-start-transition --add-end-transition \
--transition-duration 1.0 \
--pad-duration 1.0 

注意:mjlab的仓库中的pkl_to_csv.py有问题(2025-11-26),其中第85、86行的.frames .fps要改成dict调用key的方式fps = data["fps"]。对于gmr转换的pkl,需要将81-93行改为:

  with open(pkl_file, "rb") as f:
    # 直接读取你的 pkl 字典(无需 RobustUnpickler,因为你的 pkl 是标准字典)
    data = pickle.load(f)

  # 从你的 pkl 字典中提取关键字段
  fps = data["fps"]
  root_pos = data["root_pos"]  # (N, 3):x,y,z
  root_rot = data["root_rot"]  # (N, 4):你的 pkl 中是 xyzw 四元数
  dof_pos = data["dof_pos"]    # (N, D):关节角度(D 是机器人自由度,如 G1 是 20 左右)

  # 关键:重组为脚本需要的 frames 数组(格式:每一行 = 3位姿 + 3旋转 + D关节)
  # 第一步:将你的四元数(xyzw)转换为脚本需要的“轴角”(3维向量)
  print("Converting quaternion (xyzw) to axis-angle (3D vector)...")
  root_rot_axisangle = []
  for quat in root_rot:
    # 四元数格式:x,y,z,w(你的 pkl 中存储的格式)
    rotation = Rotation.from_quat(quat)  # 直接用 scipy 转换
    rot_vec = rotation.as_rotvec()       # 转为轴角(3维向量)
    root_rot_axisangle.append(rot_vec)
  root_rot_axisangle = np.array(root_rot_axisangle)  # (N, 3)

  # 第二步:拼接成 frames 数组(顺序:root_pos(3) + 轴角(3) + 关节角度(D))
  frames = np.concatenate([root_pos, root_rot_axisangle, dof_pos], axis=1)
  original_duration = frames.shape[0] / fps

  print(f"Loaded motion with shape: {frames.shape}")
  print(f"FPS: {fps}")
  print(
    f"Original duration: {original_duration:.2f} seconds ({frames.shape[0]} frames)"
  )

AMASS通过GMR转换

以CMU为例(fps=120)
GMR环境转原始数据为pkl,此时脚本默认fps=30:

python scripts/smplx_to_robot.py --smplx_file ../motion_data/CMU/05/05_05_stageii.npz --robot unitree_g1 --save_path ../motion_data/pkl/amass0505.pkl --rate_limit --record_video 

mjlab转pkl_to_csv:

python src/mjlab/scripts/pkl_to_csv_gmr.py --pkl-file ../motion_data/pkl/amass0505.pkl --csv-file ../motion_data/csv/amass0505.csv --add-start-transition --add-end-transition --transition-duration 1.0 --pad-duration 1.0

mjlab转csv_to_npz,fps转为50:

MUJOCO_GL=egl uv run src/mjlab/scripts/csv_to_npz.py  --input-file ../motion_data/csv/amass0505.csv  --output-name artifacts/local/amass0505  --input-fps 30   --output-fps 50   --render

以上fps的先降低后升高能够消除原始数据的波动,不必修改。

AMASS数据集中CMU数据集

官方网站中包含了对该数据集的说明。数据格式是SMPL系列,分为140+个包,命名中只有序号,具体动作内容可在网站查询。其中87~90包含一些酷炫动作。使用gmr可直接转换为pkl并保存视频。

数据集

AMASS数据集

*amass.npz 是 AMASS 数据集(Animation of 3D Human Motion)中常见的压缩文件格式,存储了人体运动捕捉(MoCap)数据(如关节角度、位置、旋转等)。.npz 是 NumPy 的压缩归档格式,可通过 Python 的 numpy 库直接读取。

import numpy as np

# 读取 .npz 文件
amass_data = np.load("amass.npz")  # 替换为你的文件路径
# 查看文件中包含的所有数据键(keys)
print("包含的数据集键:", amass_data.files)

可能的结果: 包含的数据集键: ['trans', 'poses', 'betas', 'dmpls', 'gender', 'mocap_framerate', ...]
其中

  • trans 根节点(通常是骨盆)的平移数据(3D 位置,单位:米) (N, 3) N 为帧数
  • poses 人体关节的旋转数据(轴角格式,前 3 个值是根节点旋转,后续每 3 个值对应一个关节) (N, 156) 52 个关节 ×3
  • betas 人体形状参数(SMPL 模型的体型参数,前 10 个为主) (10,) 或 (N, 10)
  • dmpls 动态形状参数(可选,用于精细刻画运动中的体型变化) (N, 8) 或 (N, 12)
  • gender 性别标签('male'/'female'/'neutral') 字符串
  • mocap_framerate 运动捕捉的帧率(每秒帧数) 整数(如 100)
  • frame_time 每帧的时间间隔(秒),通常为 1/mocap_framerate 浮点数(如 0.01)
  • id 运动数据的唯一标识(如动作名称) 字符串

从加载的 amass_data 中提取具体数据,例如获取关节旋转和根节点平移:

# 提取核心数据
trans = amass_data["trans"]  # 根节点平移 (N, 3)
poses = amass_data["poses"]  # 关节旋转 (N, 156)
betas = amass_data["betas"]  # 体型参数 (10,)
fps = amass_data["mocap_framerate"]  # 帧率
gender = amass_data["gender"]  # 性别

# 打印数据形状
print(f"总帧数:{trans.shape[0]}")
print(f"根节点平移形状:{trans.shape}")
print(f"关节旋转形状:{poses.shape}")
print(f"帧率:{fps} FPS")
print(f"性别:{gender}")

若要直观查看运动序列,可结合 SMPL 模型和可视化工具(如 pyrender):

import pyrender
import trimesh
from smplx import SMPL  # 需要安装 smplx 库(处理人体模型)

# 加载 SMPL 模型(需提前下载 SMPL 模型文件,如 smplh中性模型)
smpl_model = SMPL(
    model_path="path/to/smpl/models",  # 替换为你的 SMPL 模型路径
    gender=gender.decode() if isinstance(gender, bytes) else gender,
    batch_size=1
)

# 提取第 i 帧的姿势和形状
i = 100  # 第100帧
root_rot = poses[i, :3]  # 根节点旋转(轴角)
body_poses = poses[i, 3:66]  # 身体关节旋转(前21个关节,63维)
betas_i = betas[:10]  # 前10个形状参数

# 用 SMPL 模型生成网格
output = smpl_model(
    betas=betas_i[None, :],  # 扩展为 batch 维度
    global_orient=root_rot[None, :],  # 根节点旋转
    body_pose=body_poses[None, :]  # 身体关节旋转
)
vertices = output.vertices[0].detach().numpy()  # 顶点坐标
faces = smpl_model.faces  # 面索引

# 用 trimesh 和 pyrender 可视化
mesh = trimesh.Trimesh(vertices=vertices, faces=faces)
scene = pyrender.Scene()
mesh_node = pyrender.Mesh.from_trimesh(mesh)
scene.add(mesh_node)
viewer = pyrender.Viewer(scene, use_raymond_lighting=True)

注意:可视化需要 SMPL 模型文件(需从 SMPL 官网 申请)和 smplx 库(pip install smplx)。

SMPL数据集

SMPL(Skinned Multi-Person Linear Model)是一种参数化人体模型,通过少量参数(形状、姿势)即可生成逼真的 3D 人体网格,广泛用于计算机视觉(如人体姿态估计)、动画生成、机器人模仿学习等领域。“SMPL 格式数据” 指适配该模型的输入 / 输出数据,核心围绕 “形状参数” 和 “姿势参数” 展开,同时包含模型结构相关的固定信息(如顶点、骨骼权重)。
公开SMPL数据集下载

一、SMPL 模型的核心参数(数据核心)

SMPL 模型的输入数据主要分为两类:形状参数(Betas)姿势参数(Pose),二者共同决定人体的 3D 形态。对于通用的机器人运动,还有一个根部平移(Trans)

1. 形状参数(Betas,β)

  • 作用:定义人体的静态体型特征(如身高、胖瘦、肩宽、腿长等),刻画个体差异。
  • 维度:默认 10 维(部分扩展模型如 SMPL-H/SMPL-X 支持 20 维),前 3-5 维贡献了 95% 以上的体型差异,后续维度对应细微特征(如手指粗细)。
  • 数据格式
    • 单个个体:1D 数组 (10,)(20,)(固定体型,如 “标准男性”“标准女性”)。
    • 序列数据(如运动捕捉):2D 数组 (N, 10)(N 为帧数,若体型不变,所有帧的 Betas 相同)。
  • 取值范围:通常在 [-3, 3] 之间(超出范围可能生成不自然体型)。

2. 姿势参数(Pose,θ)

  • 作用:定义人体的动态姿态(如关节旋转、身体朝向),决定人体的动作状态。
  • 维度与结构
    • SMPL 模型包含 24个关节(根节点 + 23 个身体关节,不含手指),每个关节的旋转用轴角(Axis-Angle) 表示(3 个数值,描述旋转轴和角度),因此总维度为 24×3=69
    • 姿势参数通常拆分为两部分:
      1. 全局朝向(Global Orientation):前 3 个值,描述人体根节点(通常是骨盆)在世界坐标系中的旋转(如面向 X 轴正方向、侧身等)。
      2. 身体关节旋转(Body Pose):后 69 个值(23个关节 ×3),描述颈部、肩部、髋部、膝盖等关节的旋转。
  • 数据格式
    • 单帧姿势:1D 数组 (72,)
    • 运动序列:2D 数组 (N, 72)(N 为帧数,每帧对应一个姿势)。
  • 注意:轴角格式需通过罗德里格斯公式(Rodrigues' Formula)转换为旋转矩阵,才能用于计算关节变换。

3. 根部平移(Trans,p)

维度同上

二、SMPL 模型的输出数据(网格与骨骼)

输入 Betas 和 Pose 后,SMPL 模型会输出人体的 3D 网格和骨骼信息,是后续可视化、动画生成的核心数据。

1. 顶点网格(Vertices)

  • 作用:3D 人体的表面网格,由大量顶点组成(SMPL 默认 6890 个顶点)。
  • 数据格式
    • 单帧网格:2D 数组 (6890, 3)(每个顶点对应 3 个坐标值 (x, y, z),世界坐标系)。
    • 运动序列:3D 数组 (N, 6890, 3)(N 为帧数,每帧对应一个网格)。
  • 用途:直接用于可视化(如通过pyrenderMeshLab渲染人体)、碰撞检测(机器人交互场景)。

2. 骨骼权重(Blend Weights)

  • 作用:描述每个顶点受哪些关节旋转的影响(蒙皮权重),用于计算关节运动时顶点的位置变化(线性混合蒙皮,Linear Blend Skinning)。
  • 数据格式:2D 数组 (6890, 23)(每个顶点对应 23 个关节的权重值,权重和为 1)。
  • 特点:SMPL 模型的骨骼权重是固定的(随模型文件存储,不随姿势 / 形状变化),无需实时计算。

3. 关节位置(Joints)

  • 作用:人体骨骼的关节中心点坐标(如骨盆、肩关节、膝关节),用于骨骼结构分析、运动学计算。
  • 数据格式
    • 单帧关节:2D 数组 (23, 3)(23 个关节,每个关节的 3D 坐标)。
    • 运动序列:3D 数组 (N, 23, 3)
  • 分类
    • 局部关节位置:相对于父关节的坐标(运动学树结构)。
    • 全局关节位置:在世界坐标系中的坐标(需结合全局朝向和根节点平移计算)。

三、SMPL 相关扩展模型的格式差异

SMPL 有多个扩展版本,数据格式在基础上略有调整,需注意区分:

模型版本 核心差异 姿势参数维度 顶点数 适用场景
SMPL 基础人体模型(无手指、表情) 69(23 关节 ×3) 6890 全身运动捕捉、动画生成
SMPL-H 增加手部模型(15 个手指关节) 165(55 关节 ×3) 6890 需精细手部动作的场景(如手势识别)
SMPL-X 增加面部表情(50 个面部关节)+ 手部 225(75 关节 ×3) 10475 表情 + 手部 + 身体的完整人体建模(如虚拟人)
SMPL-A 适配动物模型(如狗、马) 随动物骨骼数量变化 随模型变化 动物运动捕捉、仿真

四、SMPL 格式数据的存储与读取

SMPL 格式数据通常以以下方式存储,读取时需对应模型版本:

1. 模型文件(固定结构)

SMPL 模型的固定参数(如基础形状顶点、骨骼权重、关节父节点关系)存储在 .pkl(Python pickle)文件中,需从官方网站申请(需学术许可)。常见文件包括:

  • SMPL_NEUTRAL.pkl(中性体型)、SMPL_MALE.pkl(男性)、SMPL_FEMALE.pkl(女性)。
  • 文件内包含的关键键(key):
    • v_template:基础形状顶点 (6890, 3)
    • weights:骨骼权重 (6890, 23)
    • kintree_table:骨骼运动树(父关节索引)。
    • shapedirs:形状基函数(用于计算 Betas 对顶点的影响)。
    • posedirs:姿势基函数(用于计算 Pose 对顶点的影响)。

2. 运动数据文件(动态参数)

SMPL 格式的运动数据(Betas+Pose + 根节点平移)常存储在 .npz(NumPy 压缩文件)或 .json 中,例如 AMASS 数据集、MPI-INF-3DHP 数据集:

  • 典型 .npz 文件包含的键:
    • betas:形状参数 (10,)(N, 10)
    • poses:姿势参数 (N, 69)(SMPL)或 (N, 165)(SMPL-H)。
    • trans:根节点平移 (N, 3)(描述人体在世界坐标系中的位置变化,如行走时的位移)。
    • mocap_framerate:帧率(如 30 FPS、100 FPS)。

3. 读取工具

  • Python 库

    • smplx:官方推荐的 SMPL 系列模型库,支持加载模型、输入参数生成网格(pip install smplx)。
    • numpy:读取 .npz 格式的运动数据(np.load("smpl_data.npz"))。
    • trimesh/pyrender:可视化 SMPL 顶点网格(渲染 3D 人体)。
  • 示例代码(用smplx加载模型并生成网格):

    import numpy as np
    from smplx import SMPL
    
    # 1\. 加载SMPL模型(需指定模型文件路径)
    smpl = SMPL(
        model_path="path/to/smpl/models",  # 存放.pkl文件的目录
        gender="neutral",  # 中性/男性/女性
        batch_size=1  # 批量处理帧数(1表示单帧)
    )
    
    # 2\. 准备输入参数(示例:标准姿势,轻微胖体型)
    betas = np.array([[0.5, 0.2, 0, 0, 0, 0, 0, 0, 0, 0]], dtype=np.float32)  # 形状参数(10维)
    global_orient = np.array([[0, 0, 0]], dtype=np.float32)  # 全局朝向(无旋转)
    body_pose = np.array([[0]*66], dtype=np.float32)  # 身体关节(无旋转,标准站立)
    
    # 3\. 生成人体网格
    output = smpl(
        betas=betas,
        global_orient=global_orient,
        body_pose=body_pose
    )
    
    # 4\. 提取输出数据
    vertices = output.vertices[0].detach().numpy()  # 顶点网格 (6890, 3)
    joints = output.joints[0].detach().numpy()      # 关节位置 (23, 3)
    print(f"顶点数:{vertices.shape[0]}, 关节数:{joints.shape[0]}")
    
    

五、SMPL 格式数据的核心应用场景

  1. 人体姿态估计:从 2D 图像 / 视频中预测 SMPL 的 Pose 参数,反推 3D 人体姿态。
  2. 运动捕捉数据处理:将 MoCap 设备(如 Vicon)的原始关节数据转换为 SMPL Pose,用于动画生成。
  3. 机器人模仿学习:让机器人通过 SMPL 格式的人类运动数据,学习抓取、行走等动作。
  4. 虚拟人 / 元宇宙:结合 SMPL-X 的表情和手部参数,构建可交互的虚拟人形象。

总结

SMPL 格式数据的核心是 “少参数控制多维度人体形态”,关键在于理解:
输入:Betas(体型)+ Pose(姿态)+ trans(根节点平移);
输出:Vertices(网格)+ Joints(关节)+ weights(骨骼权重);
扩展:SMPL-H/X 等版本增加了手指、表情参数,需匹配对应维度的 Pose 数据。
处理时需注意模型版本与数据维度的匹配,常用smplx库加载模型、numpy读取数据,结合可视化工具验证结果。


算法

TRAM: Global Trajectory and Motion of 3D Humans from in-the-wild Videos

TRAM(Global Trajectory and Motion of 3D Humans from in-the-wild Videos)是由宾夕法尼亚大学研究团队提出的开源4D人体捕捉系统,专注于从非受控的真实场景视频中精准重建3D人体的全局轨迹与运动状态,为计算机视觉领域的人体运动分析提供了创新解决方案。

代码

核心目标

在计算机视觉领域,从普通视频中捕捉人体运动时,若不考虑相机运动,多数方法仅能在相机坐标系中重建人体,难以结合环境准确分析人体动作。TRAM旨在解决这一问题,通过两阶段方法实现世界空间中3D人体运动的精准恢复,既捕捉人体运动学姿态,又获取其在世界空间中的全局轨迹。对于人型机器人舞蹈的任务,可以根据人形动作视频得到SMPL的全局动作轨迹。

工作原理与流程

TRAM集成多种先进技术,通过多步骤协作实现3D人体运动重建,具体流程如下:

  1. 相机位姿估计:运行改进的DROID-SLAM算法,对人体区域进行遮罩处理,降低动态人体对相机运动估计的干扰,同时结合ZoeDepth预测视频深度信息,提升场景重建精度,并利用场景背景推导运动比例尺,确定相机在全局坐标系中的轨迹与姿态。
  2. 人体检测跟踪与运动捕捉:采用Detectron2、Segment-Anything进行人体检测与分割,结合DEVA-Track-Anything实现复杂场景下的人体跟踪;再通过VIMO视频变换模型,以恢复的相机为度量尺度参考帧,回归人体运动学运动,精准估计3D人体姿态。
  3. 结果整合与输出:整合相机位姿数据与人体运动数据,将人体运动映射到全局坐标系,渲染输出包含3D人体全局轨迹与运动的最终视频,所有中间结果与最终数据会保存在对应文件夹中。

如果需要复现视频的人形机器人动作,在通过TRAM获得SMPL数据集后,可以利用isaacgym中的maskedmimic模仿SMPL中的动作,通过仿真即可清洗掉其中不可能实现的动作(sim2data,验证其动作在仿真中可行)。后将其重定向为机器人的动作,此部分技术代表为H2O与OmniH2O(human2humanoid代码),具体原理可参照此文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容