主题
为什么关注 mjlab
机器人强化学习训练通常同时追求两件事:仿真要足够快,能够持续提供大量交互数据;模型又要足够透明,出现抖动、穿模、接触异常或数值发散时,能够迅速定位问题。
Isaac Gym 和 Isaac Lab 已经形成了成熟的 GPU 并行训练工作流,但其资产和运行方式与 Isaac Sim、USD 和 Omniverse 生态结合较深。MuJoCo 的使用方式更直接,机器人结构、关节、碰撞体、传感器和执行器都可以在 MJCF 中清楚地查看和修改。不过,如果直接基于 MuJoCo 搭建强化学习项目,并行环境、任务配置和算法接口往往还需要额外组织。
mjlab 尝试把两条路线的优势放到同一套工程里:底层使用 MuJoCo Warp 在 GPU 上并行推进仿真,上层借鉴 Isaac Lab 的 manager-based 思路组织机器人、动作、观测、奖励和终止条件。
从使用者角度看,mjlab 打通的是这样一条训练链路:
plain text
MuJoCo 机器人模型
-> mjlab 任务配置
-> MuJoCo Warp 并行仿真
-> 强化学习训练
-> 策略回放与导出这条链路的价值不只在于“让 MuJoCo 跑在 GPU 上”,更重要的是把模型、任务和训练流程分开管理。已有任务可以直接运行;更换机器人时,修改也能集中在资产和任务配置中,而不必重新编写整套训练循环。
mjlab 如何组织机器人训练
一个 mjlab 任务,本质上是在回答三个问题:
仿真中放什么机器人和场景;
策略控制什么、观察什么、优化什么;
训练器如何读取环境并更新策略。
这三部分组合起来,才构成一个可以训练的机器人任务。
plain text
flowchart LR
A["MJCF 机器人模型"] --> B["mjlab 任务配置"]
B --> C["MuJoCo Warp 并行仿真"]
C --> D["RL Runner / PPO"]
D --> E["Checkpoint"]
E --> F["Play / Evaluation"]机器人模型仍然由 MuJoCo/MJCF 描述,其中包含 body、joint、geom、site、sensor 和 actuator 等信息。mjlab 负责在模型之上组织任务:策略输出如何作用到关节,哪些状态进入观测,奖励如何计算,什么情况下结束当前 episode。
MuJoCo Warp 则负责并行推进大量环境。对于 locomotion、速度跟踪和动作跟踪这类任务,采样效率会直接影响训练速度。训练器拿到环境输出后,完成 rollout、优势估计、策略更新和 checkpoint 保存。
在 unitree_rl_mjlabhttps://github.com/unitreerobotics/unitree\_rl\_mjlab 中,这些配置最终通过 task id 组织起来。训练命令里的任务名并不是单纯的字符串,而是一组环境配置、机器人配置和训练配置的入口。换机器人或换任务时,训练脚本本身通常不需要改,真正变化的是 task id 背后加载的内容。
这也是 mjlab 更适合工程扩展的地方:跑已有任务时,可以直接复用完整链路;接入自己的机器人时,则主要处理模型、执行器、关节映射、接触定义和动作数据。
环境部署:跑通 unitree_rl_mjlab
安装训练环境
本文使用 Ubuntu 22.04、Python 3.11,Conda以及带有远程桌面,方便,流畅。
这里我用算力自由平台推荐镜像:https://www.gpufree.cn/images/100113

创建 Python 环境:
bash
conda create -n unitree_rl_mjlab python=3.11
conda activate unitree_rl_mjlab安装基础依赖:
bash
sudo apt install -y \
libyaml-cpp-dev \
libboost-all-dev \
libeigen3-dev \
libspdlog-dev \
libfmt-dev安装工程使用的 MuJoCo Warp 版本:
bash
pip install "mujoco-warp@git+https://github.com/google-deepmind/mujoco_warp@9491175b7cbea87e28d3e3e67733095317c33398"进入工程根目录安装本地包:
bash
pip install -e .pip install -e . 会让当前 Python 环境直接使用本地源码。后续修改机器人资产或任务配置后,不需要重复安装。
检查任务注册
安装完成后,先查看当前工程中已经注册的任务:
bash
python scripts/list_envs.py如果任务列表能够正常显示,说明 Python 环境、本地包和任务导入基本正常。后续训练命令中的第一个参数就是 task id,它决定加载哪一套机器人、环境和训练配置。
速度跟踪训练
速度跟踪适合用来验证最基础的训练链路。以 Unitree G1 平地速度跟踪为例:
bash
python scripts/train.py \
Mjlab-Velocity-Flat-Unitree-G1 \
--env.scene.num-envs=4096第一次运行时,不必直接使用 4096 个环境。可以先改成 128 或 256,确认训练能够启动、日志能够写入、checkpoint 能够保存,再逐步扩大并行规模。
多 GPU 训练示例:
bash
python scripts/train.py \
Mjlab-Velocity-Flat-Unitree-G1 \
--gpu-ids 0 1 \
--env.scene.num-envs=4096动作跟踪训练
motion tracking 任务需要额外提供参考动作。以 G1 的 dance1_subject2 为例,先将 CSV 转换为训练使用的 NPZ:
bash
python scripts/csv_to_npz.py \
--input-file mjlab/motions/g1/dance1_subject2.csv \
--output-name dance1_subject2.npz \
--input-fps 30 \
--output-fps 50然后启动训练:
bash
python scripts/train.py \
Mjlab-Tracking-Flat-Unitree-G1 \
--motion-file mjlab/motions/g1/dance1_subject2.npz \
--env.scene.num-envs=4096训练日志默认保存在:
plain text
logs/rsl_rl/<experiment_name>/<run_time>/其中通常包括 checkpoint、环境配置、训练配置和导出的策略文件。
回放训练结果
速度跟踪任务回放:
bash
python scripts/play.py \
Mjlab-Velocity-Flat-Unitree-G1 \
--checkpoint-file logs/rsl_rl/g1_velocity/<run>/model_<iter>.pt动作跟踪任务还需要提供同一个 motion 文件:
bash
python scripts/play.py \
Mjlab-Tracking-Flat-Unitree-G1 \
--motion-file mjlab/motions/g1/dance1_subject2.npz \
--checkpoint-file logs/rsl_rl/g1_tracking/<run>/model_<iter>.pt回放时不要只看机器人是否“动起来了”。足端是否滑动、躯干是否高频抖动、动作相位是否和参考 motion 一致,往往比单个 reward 数值更能说明策略质量。
不同版本的参数名可能略有差异,例如 --motion-file 和 --motion_file。遇到参数错误时,直接查看当前脚本:
bash
python scripts/train.py --help
python scripts/play.py --help用 mjlab 训练自己的机器人
跑通已有任务之后,再接入自己的机器人。这里通常不需要重写 PPO,也不需要重新实现并行环境。真正需要处理的是机器人和任务之间的接口。
先选一个合适的基线任务
接入新机器人时,最好先选择结构和目标都比较接近的已有任务。
如果目标是训练基础步态,可以从 velocity tracking 开始;如果已经有参考动作,希望训练人形机器人模仿动作,则可以从 motion tracking 开始。这样可以复用已经验证过的训练入口、runner、日志、奖励结构和回放流程。
以人形 motion tracking 为例,G1 任务可以保留动作跟踪主流程,但所有直接指向 G1 的机器人信息都需要替换。
理解 task id 背后的配置
在 unitree_rl_mjlab 中,task id 对应的并不是某一个脚本,而是一组完整配置,通常包括:
训练环境配置;
回放环境配置;
机器人和场景配置;
动作、观测、奖励和终止条件;
PPO 配置;
runner 类型和日志目录。
训练脚本根据 task id 加载这些配置,再用命令行参数覆盖环境数量、motion 文件或 checkpoint 路径。
所以,接入自己的机器人时,真正需要理解的不是 scripts/train.py 这一层,而是 task id 背后到底加载了什么。
替换机器人模型
只替换 XML 通常不够。原任务中的关节名称、刚体名称、接触对象和默认姿态仍然指向旧机器人。
自定义机器人至少需要重新检查:
mjlab 不会自动理解一台新机器人,但它把这些修改集中在资产和任务配置中,而不是让它们散落到训练循环里。
Adam Lite 适配示例
在本文中使用PNDbotics Adam Lite 作为自定义机器人接入案例。整体思路是复用已有 G1 motion tracking 的训练链路,将所有与机器人本体相关的内容替换为 Adam。
Adam 资产放在:
plain text
mjlab/asset_zoo/robots/pnd_adam_lite/
adam_constants.py
xml/
adam_lite.xml
assets/adam_lite.xml 描述 MuJoCo 模型,adam_constants.py 负责将其封装成 mjlab 可以加载的 robot cfg。
模型能够正常加载后,还需要继续处理执行器和任务配置。原 G1 任务中的 action joint、tracking body、anchor body 和 foot contact 都不能直接沿用。
控制接口上,Adam 原始 XML 中的 actuator 与 tracking 任务使用的动作定义并不完全一致。这里改为 position actuator,并根据各关节的 effort 和 stiffness 设置 action scale。策略输出可以理解为:
plain text
q_target = q_default + scale × actionAdam 各身体部位的运动范围不同,因此腿部、腰部、手腕和其他上肢的动作尺度需要分别限制。腿部需要保留足够控制范围,腰部和上肢则适当收紧,避免探索阶段动作过大,破坏整体平衡。
任务配置中,还需要将 G1 相关名称替换为 Adam 对应名称:
Adam 任务中还提供了 No-State-Estimation 版本,通过移除 motion_anchor_pos_b、base_lin_vel 等额外状态量,减少策略对状态估计的依赖。
重新处理 motion 数据
G1 的 NPZ 不能直接用于 Adam,因为 motion tracking 不只依赖关节角,还会使用由机器人模型正向运动学得到的 body position、body orientation 和 body velocity。
Adam 的动作数据处理流程是:
plain text
CSV 动作
-> Adam 关节顺序映射
-> 按训练频率插值
-> 在 MuJoCo 中重放
-> 获取 body 位姿和速度
-> 保存为 NPZ转换命令:
bash
python scripts/csv_to_npz_adam.py \
--input-file mjlab/motions/adam/dance1_subject2.csv \
--output-name dance1_subject2.npz \
--input-fps 30 \
--output-fps 50这里最容易出问题的是关节顺序、四元数顺序、FPS 和初始姿态。这些错误不一定会让程序直接崩溃,但会表现为姿态错位、脚底接触异常、tracking reward 长期偏低,或者 episode 一开始就终止。
注册任务并开始训练
完成模型、控制接口、任务配置和 motion 数据后,就可以注册 Adam 任务:
plain text
Mjlab-Tracking-Flat-PNDbotics-Adam-Lite
Mjlab-Tracking-Flat-PNDbotics-Adam-Lite-No-State-Estimation训练 no-state-estimation 版本:
bash
python scripts/train.py \
Mjlab-Tracking-Flat-PNDbotics-Adam-Lite-No-State-Estimation \
--motion-file mjlab/motions/adam/dance1_subject2.npz \
--env.scene.num-envs=4096第一次验证时,建议先降低并行环境数量和训练迭代:
bash
python scripts/train.py \
Mjlab-Tracking-Flat-PNDbotics-Adam-Lite-No-State-Estimation \
--motion-file mjlab/motions/adam/dance1_subject2.npz \
--env.scene.num-envs=128 \
--agent.max-iterations=1000回放 checkpoint:
bash
python scripts/play.py \
Mjlab-Tracking-Flat-PNDbotics-Adam-Lite-No-State-Estimation \
--motion-file mjlab/motions/adam/dance1_subject2.npz \
--checkpoint-file logs/rsl_rl/adam_lite_tracking/<run>/model_<iter>.pt比较稳妥的验证顺序是:
plain text
模型加载
-> zero action
-> random action
-> 小规模训练
-> checkpoint 回放
-> 扩大并行环境这样可以逐步区分模型问题、动作接口问题和强化学习问题。很多看起来像“PPO 不收敛”的现象,最后都能追溯到关节映射、接触体、执行器参数或参考动作数据。
相关代码可以参考:https://github.com/senlanke/unitree\_rl\_mjlab
mjlab 真正好用在哪里
保留 MuJoCo 建模方式,同时获得 GPU 并行能力
mjlab 最突出的特点,是让机器人继续使用 MJCF 建模,同时通过 MuJoCo Warp 获得 GPU 并行采样能力。
开发者仍然可以直接检查 body、joint、geom、sensor 和 actuator,训练阶段又能同时运行大量环境。对于已经积累 MuJoCo 资产的项目,这比重新迁移整套机器人模型更直接。
工程依赖较轻,实验迭代更快
mjlab 将重点放在机器人模型、任务配置和强化学习训练上,不需要每次启动完整仿真平台。修改模型、奖励或观测之后,可以更快进入下一轮训练和回放。
这种优势在频繁调试执行器、接触参数和观测设计时尤其明显。
训练异常可以直接回到 MuJoCo 模型层排查
机器人强化学习中的很多问题并不来自 PPO,而是来自模型和接口:
actuator 类型不匹配;
action scale 不合理;
joint/body 名称错误;
足端接触体选错;
默认姿态与参考动作不一致。
mjlab 保留了 MuJoCo 原生模型和数据结构,出现异常时可以直接从 MJCF、MjModel 和 MjData 层检查,而不必先穿过复杂的资产转换链路。
对于希望继续使用 MuJoCo 资产,同时获得模块化任务组织和 GPU 并行训练能力的机器人项目,mjlab 是一条值得尝试的路线。
参考资料