Gymnasium MuJoCo 连续控制环境完整实战指南:从 5 行代码到造出自己的机器人
【免费下载链接】GymnasiumA standard API for single-agent reinforcement learning environments, with popular reference environments and related utilities (formerly Gym)项目地址: https://gitcode.com/GitHub_Trending/gy/Gymnasium
如果你只训练过离散环境,Gymnasium 的 MuJoCo 环境就是连续控制的入口:几行 Python 就能让一只小猎豹跑起来,一套完整训练闭环能看着奖励曲线从零爬升。本文带你从能跑起来的代码,一路走到自定义环境。
五分钟跑通第一个 MuJoCo 环境:HalfCheetah 的最小循环 🐆
假设你刚装好依赖:
pip install "gymnasium[mujoco]"现在打开 Python,把下面这段原样跑一遍:
import gymnasium as gym env = gym.make("HalfCheetah-v5") obs, info = env.reset(seed=42) print(obs.shape) # (17,) print(info) # {'x_position': -0.262...} action = env.action_space.sample() # 随机采样一个动作 obs, reward, terminated, truncated, info = env.step(action) print(reward) # 例如 0.42 print(info) # 含 x_position / x_velocity / reward_forward / reward_ctrl env.close()逐行拆一下发生了什么:
gym.make("HalfCheetah-v5")从注册表实例化环境。它是一只二维的"半截猎豹":躯干和头固定,6 个关节(前后大腿、小腿、脚)能施加扭矩,目标就是往右跑得越快越好。env.reset(seed=42)把机器人放回初始姿态并加一点随机噪声,返回(obs, info)。注意 0.26 之后reset()返回的是二元组而不是单个观测。obs.shape是(17,),一个 float64 向量。它其实就是"位置 + 速度"的拼接:7 个关节角度加躯干高度等 8 个 qpos(位置),9 个 qvel(速度)。env.action_space.sample()在Box(-1, 1, (6,))里随机抽一个 6 维向量——相当于给 6 个关节随机拧一下力矩。step()返回五元组:新观测、标量奖励、terminated(任务是否"失败"结束)、truncated(是否到时间上限)、info(诊断信息)。
这就是全部循环:观测进、动作出、拿奖励。训练强化学习,就是让这个"随机拧一下"逐步变成"知道怎么拧"。
读懂环境信号:HalfCheetah 的 17 维观测里装了什么
刚拿到obs时最容易犯的错误,是把它当成"一团数字"。其实 Gymnasium 的 MuJoCo 环境里,观测和动作都有清晰的物理含义。
| 信号 | 形状 / 范围 | 含义 |
|---|---|---|
obs | (17,), float64 | 前 8 项是 qpos(各关节角度、躯干高度等位置),后 9 项是 qvel(对应速度) |
action | Box(-1, 1, (6,)), float32 | 6 个关节的归一化扭矩,环境内部再映射成牛顿米 |
reward | 标量 | 前进速度奖励 - 0.1 × ‖action‖²,往前跑加分、用力过猛扣分 |
terminated | 恒为False | HalfCheetah 永远不会"摔倒",只有 1000 步后truncated=True |
三个值得注意的设计细节:
位置被故意藏起来了。17 维里没有躯干的前后坐标rootx——因为奖励本身就算自前进速度,直接喂位置等于把答案抄给智能体。想拿位置,看info["x_position"]即可。这个取舍可以开关:gym.make("HalfCheetah-v5", exclude_current_positions_from_observation=False)会得到 18 维观测。
奖励是拆解过的。每步info里都有reward_forward和reward_ctrl两项。调试"为什么奖励上不去"时,先看这两项谁在拖后腿,比盯着总奖励猜要快得多。
v5 帮你标注了观测结构。env.observation_structure直接告诉你哪些维度是 qpos、哪些是 qvel,做自定义 wrapper 时非常省事。
对比另一个环境更能体会差异:InvertedPendulum-v5的奖励是"每保持直立 0.2 弧度以内一步 +1",且摔倒立刻terminated=True。同样是"位置+速度"的观测,任务性质从"耐力跑"变成了"走钢丝"。
环境选型指南:从哪个 MuJoCo 环境开始训练
第一次做连续控制,最忌讳一上来就冲 Humanoid——21 维动作、全身协调,奖励曲线能让你怀疑人生。选环境的依据其实就三条:动作维度、有没有摔倒终止、你想对标什么论文。
先定动作维度预算。维度直接决定训练难度:
- 1~2 维:
InvertedPendulum-v5(1 维)、InvertedDoublePendulum-v5(2 维)、Reacher-v5(2 维) - 3~6 维:
Hopper-v5(3 维)、Pusher-v5(3 维)、Swimmer-v5(5 维)、HalfCheetah-v5(6 维)、Walker2d-v5(6 维) - 8 维及以上:
Ant-v5(8 维)、Humanoid-v5/HumanoidStandup-v5(21 维)
再看有没有"摔倒"终止。HalfCheetah永不终止,奖励曲线平滑,最适合当第一个 locomotion 任务;Hopper、Walker2d、Humanoid一旦摔倒就提前截断,回合长度忽长忽短,奖励曲线天然带噪声——这对调算法是好事,但对刚上手的人是干扰。
最后看你想要什么。验证代码管线、快速看到收敛:选InvertedPendulum-v5(4 维观测、1 维动作,仓库官方教程就用它做 REINFORCE 示例,见 docs/tutorials/training_agents/mujoco_reinforce.py)。想学奔跑控制:HalfCheetah-v5。想摸 3D 操作(manipulation):Reacher-v5入门、Pusher-v5进阶。要发论文做对比:Ant-v5和Humanoid-v5是 PPO/SAC 论文的标配基准。
一句话决策链:调试期用 InvertedPendulum,正式开跑用 HalfCheetah,冲基准上 Ant/Humanoid。另外所有环境都有 v4/v5 两代,新研究直接用-v5(mujoco>=2.3.3,特性最全);要复现旧文献结果再考虑 v4。
采样提速三板斧:渲染模式、向量化与无头后端 ⚡
训练 MuJoCo 环境时,真正的瓶颈往往不是算法而是环境采样速度。三板斧按收益排序:
第一斧:训练时别开渲染。窗口渲染会实打实地拖慢每一步:
train_env = gym.make("HalfCheetah-v5") # 训练:不渲染,最快 eval_env = gym.make("HalfCheetah-v5", render_mode="human") # 评估:弹窗看效果想留训练过程当证据,用视频录制代替实时窗口:
from gymnasium.wrappers import RecordVideo video_env = RecordVideo( gym.make("HalfCheetah-v5", render_mode="rgb_array"), video_folder="./videos", )第二斧:向量化环境并行采样。PPO 这类算法一个 update 要吃上千步样本,8 个环境并行跑比单环境快得多:
from gymnasium.vector import SyncVectorEnv envs = SyncVectorEnv([lambda: gym.make("HalfCheetah-v5") for _ in range(8)]) obs, infos = envs.reset(seed=42) actions = envs.action_space.sample() # 一次给 8 个环境各抽一个动作 obs, rewards, terminateds, truncateds, infos = envs.step(actions) print(obs.shape) # (8, 17)多核机器上可以再换成AsyncVectorEnv,用多进程进一步并行。
第三斧:无头图形后端。服务器上根本没有显示器,默认后端会报错或卡死。MuJoCo 用MUJOCO_GL环境变量切换后端:
| 后端 | 设置方式 | 适用场景 |
|---|---|---|
| GLFW | MUJOCO_GL=glfw(默认) | 本地开发,带窗口的 GPU 渲染 |
| EGL | MUJOCO_GL=egl | 服务器无头 + GPU,推荐 |
| OSMesa | MUJOCO_GL=osmesa | 无 GPU 的纯 CPU 渲染,兼容性最好 |
export MUJOCO_GL=egl python train.py三者叠加之后,一个 4 环境并行的 PPO 训练,环境侧基本不再是短板。
完整训练闭环:用 PPO 训练 HalfCheetah 并读懂奖励曲线
采样提速之后,来看一个能落地的完整闭环:训练 → 监控 → 评估。算法选 PPO,工具选 Stable-Baselines3,它是 Gymnasium 生态里最顺手的连续控制搭档。
训练。核心就这几行:
import gymnasium as gym from stable_baselines3 import PPO from stable_baselines3.common.callbacks import EvalCallback env = gym.make("HalfCheetah-v5") model = PPO("MlpPolicy", env, n_steps=1024, batch_size=64) model.learn(total_timesteps=1_000_000)监控。用EvalCallback周期性在独立评估环境里打分并保存最优权重,比盯着训练奖励靠谱:
eval_env = gym.make("HalfCheetah-v5") cb = EvalCallback(eval_env, best_model_save_path="./best", eval_freq=10_000) model.learn(1_000_000, callback=cb)评估。评估一定多跑几个回合取平均,单回合在 MuJoCo 环境里噪声很大:
rewards = [] for _ in range(10): obs, _ = env.reset() total, done = 0.0, False while not done: action, _ = model.predict(obs) obs, r, terminated, truncated, _ = env.step(action) total += r done = terminated or truncated rewards.append(total) print(f"10 回合平均奖励: {sum(rewards)/10:.1f}")怎么读奖励曲线。下面这张是 Gymnasium 官方教程里 REINFORCE 训练InvertedPendulum的曲线(5 条运行叠加),形态对所有连续控制任务通用:
- 前 1000 回合贴着 0:策略基本在瞎探索,别急着调参。
- 中段陡升:学到了"基本姿势"。曲线陡峭程度取决于任务难度,InvertedPendulum 五千多回合就能摸到满分 1000 附近,HalfCheetah 则要在百万步级别才见平台期。
- 多条曲线之间的散度是随机性,不是 bug——初始状态噪声 + 策略初始化噪声都会体现在这。
- 后半段围绕一个平台震荡:那是策略上限了,继续加步数收益递减,该去看评估环境里的平均数而不是训练曲线本身。
踩坑排查手册:MuJoCo 环境的 5 个高频问题
Q1:种子都固定了,为什么两次跑出来还是不一样?env.reset(seed=42)只固定了初始状态噪声。策略网络的初始化是另一套随机源,还要torch.manual_seed(42)/np.random.seed(42)一起管。另外 MuJoCo 官方说明过:不同版本的mujoco包因浮点运算顺序,结果会有微小差异——严格复现要连mujoco的版本号一起锁死。
Q2:服务器上报错或渲染卡死怎么办?十有八九是后端问题。服务器没有窗口系统,默认的 GLFW 会出问题:export MUJOCO_GL=egl用 GPU 无头渲染,或MUJOCO_GL=osmesa走 CPU。再检查一点:训练脚本里是不是不小心写了render_mode="human",有窗口的渲染能把采样拖慢几倍。
Q3:奖励变 NaN、terminated全 True,机器人"炸了"?这是状态数值溢出(MuJoCo 环境对非有限状态直接判终止)。按顺序排查:网络输出没压缩到[-1, 1](用tanh或动作缩放 wrapper);观测没归一化(gymnasium.wrappers.NormalizeObservation);学习率过大。三者占齐前两个,大多数"爆炸"都能救回来。
Q4:随机策略的奖励为什么低得离谱?正常。随机动作下的 HalfCheetah 基本在原地扑腾,平均奖励接近 0 甚至为负。想快速 sanity check,直接看info["x_position"]是否随回合缓慢漂移,比看总奖励直观。
Q5:不同环境的奖励数值不能直接比?对。InvertedPendulum一步最多 +1,满回合 1000 分;HalfCheetah满回合几百;Humanoid是几百上千的量级。跨环境谈"我的算法更强"之前,先统一到各自的评估协议(多回合平均、固定种子),否则比的是奖励标度。
造一个自己的 MuJoCo 环境:XML 模型 + 继承 MujocoEnv
当你想换一个机器人、改一个任务规则,就有两条路,难度差一个数量级。
轻量路线:只换模型。v5 的环境类全部支持xml_file参数,直接把自己的 XML 塞给现成环境:
env = gym.make("HalfCheetah-v5", xml_file="my_cheetah.xml")观测空间会按新模型的 qpos/qvel 维度自动重建,不用改 Python 代码。参考模型都在 gymnasium/envs/mujoco/assets/ 下,XML 骨架长这样:
<mujoco> <option timestep="0.01" gravity="0 0 -9.81"/> <worldbody> <light pos="0 0 3" dir="0 0 -1"/> <geom name="floor" type="plane" size="5 5 0.1"/> <body name="cart"> <joint name="slide" type="slide" axis="1 0 0"/> <geom type="capsule" size="0.1 0.2" mass="1"/> </body> </worldbody> <actuator><motor joint="slide" gear="3"/></actuator> </mujoco>三个关键标签:<joint>给自由度(每对 qpos/qvel),<geom>定义碰撞与外观,<actuator>决定动作维度和控制强度。
重量路线:自定义环境类。换任务规则(比如"推到指定位置给奖励")时,继承MujocoEnv并实现观测、奖励、终止、重置几个钩子,物理推进直接复用基类的do_simulation:
import numpy as np from gymnasium.envs.mujoco import MujocoEnv class MyCartEnv(MujocoEnv): def __init__(self): super().__init__(xml_file="my_cart.xml", frame_skip=5) def _get_obs(self): return np.concatenate([self.data.qpos, self.data.qvel]) def _get_rew(self, x_velocity, action): return x_velocity - 0.1 * np.sum(np.square(action)), {} def _get_terminated(self): return bool(np.any(~np.isfinite(self.data.qpos))) def step(self, action): before = self.data.qpos[0] self.do_simulation(action, self.frame_skip) x_vel = (self.data.qpos[0] - before) / self.dt reward, info = self._get_rew(x_vel, action) return (self._get_obs(), reward, self._get_terminated(), False, info)完整实现对照 gymnasium/envs/mujoco/mujoco_env.py 和 gymnasium/envs/mujoco/half_cheetah_v5.py 即可上手,钩子命名与官方环境完全一致。
一张表收尾:Gymnasium MuJoCo 环境要点速查
| 事项 | 写法 |
|---|---|
| 安装依赖 | pip install "gymnasium[mujoco]" |
| 创建环境 / 传参 | gym.make("Ant-v5", render_mode="rgb_array") |
| 查空间 | env.observation_space/env.action_space |
| 固定初始状态 | env.reset(seed=42) |
| 并行采样 | gymnasium.vector.SyncVectorEnv/AsyncVectorEnv |
| 录制训练视频 | gymnasium.wrappers.RecordVideo |
| 观测归一化 / 动作缩放 | NormalizeObservation/RescaleAction |
| 无头 GPU 渲染 | export MUJOCO_GL=egl |
继续深入可以翻这几处(仓库内相对路径):
- MuJoCo 环境总览与渲染参数:docs/environments/mujoco.md
- 环境类与基类源码:gymnasium/envs/mujoco/
- 环境注册表(所有可用 id):gymnasium/envs/init.py
- 官方 REINFORCE 训练教程:docs/tutorials/training_agents/mujoco_reinforce.py
- 向量化环境实现:gymnasium/vector/
从InvertedPendulum的第一条曲线,到Humanoid的全身协调,路径都在这几节里了。现在去把MUJOCO_GL=egl设上,让猎豹先跑起来。
【免费下载链接】GymnasiumA standard API for single-agent reinforcement learning environments, with popular reference environments and related utilities (formerly Gym)项目地址: https://gitcode.com/GitHub_Trending/gy/Gymnasium
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考