4. Gymnasium 快速上手
Gymnasium 不是一个“把机器人放进 3D 世界里跑起来”的仿真器,它更像是强化学习环境的标准接口层。
它解决的问题不是“物理怎么模拟”,而是“智能体如何以统一方式和环境交互”。很多你熟悉的环境,无论底层是 Classic Control、Box2D、MuJoCo 还是你自己写的任务,到了 Gymnasium 这里都会收敛成同一套 API:
gym.make()env.reset()env.step(action)env.render()env.close()
如果你是第一次接触 Gymnasium,这一节的目标很明确:
- 分清 Gymnasium 和 Isaac Sim / MuJoCo 这类仿真器的关系。
- 把最常用的安装方式装起来。
- 跑通一个最小环境,例如
CartPole-v1。 - 读懂
reset()、step()、action_space和observation_space。 - 知道怎么把内置环境过渡到自定义环境。
4.1 Gymnasium 是什么
你可以把 Gymnasium 理解成强化学习世界里的“环境协议”。
它的核心价值主要有四个:
- 给不同环境提供统一的交互接口,降低切换成本。
- 内置一批常见参考环境,适合教学、实验和算法验证。
- 提供
Wrapper、Vector Env等机制,方便做环境改造和并行采样。 - 让训练代码和具体环境实现解耦,更容易接入上层算法框架。
这里最容易混淆的一点是:
Isaac Sim、MuJoCo更偏“世界和物理怎么模拟”Gymnasium更偏“智能体如何看到状态、执行动作、收到奖励”
两者并不冲突。真实工作流里很常见的一种组合就是:
物理引擎 / 仿真器 -> 封装成 Gymnasium Env -> 训练代码
4.2 安装前先做两个判断
正式开始前,建议先判断两件事。
1. 你是想先学环境 API,还是先学高保真仿真
- 想先理解强化学习环境循环、状态、动作、奖励:先学 Gymnasium。
- 想先搭 3D 场景、传感器、机器人资产:先学 Isaac Sim、Gazebo、MuJoCo 等仿真器。
- 想做机器人学习:通常两者都要学,只是顺序可以不同。
2. 你是先用内置环境,还是马上写自己的环境
- 第一次上手:先用
CartPole-v1、Pendulum-v1这类内置环境。 - 已经有明确任务定义:再进入自定义环境。
原因很简单:如果你连 reset/step 循环还没建立直觉,一上来就写自己的环境,很容易把“环境逻辑问题”和“算法问题”混在一起。
4.3 截至 2026-04-14,版本怎么装
截至
2026-04-14,PyPI 上 Gymnasium 的最新稳定版是1.2.3,发布时间是2025-12-18;PyPI 页面同时标明它要求Python >= 3.10。
对大多数第一次上手的人,我建议这样选:
- 只想先理解 API:
pip install gymnasium - 想跑
CartPole、Pendulum、MountainCar:pip install "gymnasium[classic-control]" - 想跑
Box2D环境:pip install "gymnasium[box2d]" - 想跑
MuJoCo环境:pip install "gymnasium[mujoco]"
如果你只是想把最小例子跑通,先装 classic-control 就够了。
Windows
py -3.11 -m venv env_gymnasium
env_gymnasium\Scripts\activate
python -m pip install --upgrade pip
pip install "gymnasium[classic-control]"
macOS / Linux
python3.11 -m venv env_gymnasium
source env_gymnasium/bin/activate
python -m pip install --upgrade pip
pip install "gymnasium[classic-control]"
几个实用提醒:
- 如果你只安装了
gymnasium,某些需要额外依赖的环境会创建失败,这通常不是 Gymnasium 本体坏了,而是缺少对应 extra。 - 文档和旧博客里经常混用
gym和gymnasium。新项目优先直接使用gymnasium。 - 新手第一步不必追求“装全家桶”,先把
classic-control跑通最重要。
4.4 Gymnasium 的核心心智模型
Gymnasium 上手最快的方法,不是先记一堆环境名,而是先记住这 6 个核心概念。
1. Env
环境对象本身。它定义了:
- 当前状态如何组织
- 动作如何输入
- 奖励如何返回
- 什么时候一局结束
2. reset()
开始新 episode。它返回:
observation, info = env.reset()
也就是说,Gymnasium 不是只返回观测,还会返回一个 info 字典。
3. step(action)
推进环境一步。它返回:
observation, reward, terminated, truncated, info = env.step(action)
这里最重要的是别再把它想成旧版 Gym 里的 done。
terminated:任务本身结束了,例如杆子倒了、目标达成了truncated:被时间上限或外部限制截断了
对入门循环来说,通常会写成:
episode_over = terminated or truncated
4. action_space
动作空间定义了智能体“允许做什么”。
常见类型有:
Discrete(n):离散动作,例如左/右Box(...):连续动作,例如关节力矩、速度命令
5. observation_space
观测空间定义了环境“会返回什么”。
它可能是:
- 一维向量
- 图像张量
- 字典结构
6. Wrapper
Wrapper 可以在不改原环境源码的情况下修改交互形式,比如:
- 改观测格式
- 裁剪动作
- 改奖励
- 增加时间信息
Gymnasium 官方文档也明确提到,大多数通过 gym.make() 创建的环境,默认已经包上了 TimeLimit、OrderEnforcing 和 PassiveEnvChecker。
4.5 第一个环境:让 CartPole 随机跑起来
这是最值得先做的实验,因为它能帮你把 Gymnasium 的主循环一次性看明白。
下面是一个最小可运行脚本。它会创建 CartPole-v1 环境,然后每一步都随机采样动作:
import gymnasium as gym
env = gym.make("CartPole-v1", render_mode="human")
observation, info = env.reset(seed=42)
total_reward = 0.0
episode_over = False
while not episode_over:
action = env.action_space.sample()
observation, reward, terminated, truncated, info = env.step(action)
total_reward += reward
episode_over = terminated or truncated
print(f"Episode finished! Total reward: {total_reward}")
env.close()
运行方式:
python hello_cartpole.py
你应该会看到一个小车左右乱动,杆子很快倒下去。这是正常现象,因为现在的智能体还没有学习,只是在随机试动作。
这段代码里最值得注意的是三件事:
render_mode="human"需要在gym.make()时传入,而不是等运行后再补。- 第一步一定是
reset(),不能直接step()。 - 随机动作只能证明环境在正常工作,不代表你已经“训练了一个 agent”。
4.6 看懂动作空间和观测空间
很多时候,环境能不能接进你的算法,关键就看你有没有把 action_space 和 observation_space 看明白。
import gymnasium as gym
env = gym.make("CartPole-v1")
print("action_space:", env.action_space)
print("observation_space:", env.observation_space)
observation, info = env.reset(seed=42)
print("initial observation:", observation)
print("sample action:", env.action_space.sample())
env.close()
对 CartPole-v1 来说:
- 动作空间是
Discrete(2),通常对应“向左推”和“向右推” - 观测空间是一个
Box,里面包含小车位置、速度、杆角度、角速度
这一步真正要建立的认知是:
算法输出的动作 必须匹配 action_space
环境返回的观测 必须匹配 observation_space
如果这两个接口对不上,训练代码往往还没开始学就已经报错了。
4.7 读懂 terminated 和 truncated
这是新手最常混淆、但又非常关键的一点。
很多旧教程还会写:
observation, reward, done, info = env.step(action)
但在 Gymnasium 里,应该明确区分成:
observation, reward, terminated, truncated, info = env.step(action)
你可以这样理解:
terminated:环境从任务逻辑上结束truncated:环境被时间限制或其他外部条件截断
为什么这件事重要?
因为在训练代码里,这两种结束信号有时应该被不同地处理。最典型的例子就是:时间上限到了,不代表任务一定“失败”;它只是这一局被强制停下来了。
对初学者来说,先记住这条实用规则就够:
- 写交互循环时,用
terminated or truncated - 写算法细节时,再去区分这两类结束信号
4.8 第二个实验:并行跑多个环境
当你开始训练强化学习算法时,单环境往往不够快。Gymnasium 提供了 Vector Env 机制,把多个环境按批次一起跑。
一个最小例子如下:
import gymnasium as gym
envs = gym.make_vec("CartPole-v1", num_envs=4, vectorization_mode="sync")
observations, infos = envs.reset(seed=42)
for _ in range(100):
actions = envs.action_space.sample()
observations, rewards, terminations, truncations, infos = envs.step(actions)
envs.close()
这里最重要的直觉有三个:
- 现在返回的
observations、rewards、terminations、truncations都是“批量”的。 envs.action_space也变成了面向批量环境的动作空间。- 如果你只想看单个子环境的接口,应该关注
envs.single_action_space和envs.single_observation_space。
Gymnasium 官方文档还特别提醒,向量化环境通常会在 episode 结束后自动重置子环境,所以你后面接训练代码时,需要清楚自己使用的是哪种 autoreset 语义。
4.9 从内置环境到你自己的任务
当你已经能顺畅使用 CartPole 这类内置环境后,下一步就应该尝试把“任务描述”写成自己的环境。
最小骨架通常长这样:
import numpy as np
import gymnasium as gym
from gymnasium import spaces
class LineReachEnv(gym.Env):
metadata = {"render_modes": [], "render_fps": 4}
def __init__(self, goal=5):
self.goal = goal
self.action_space = spaces.Discrete(2)
self.observation_space = spaces.Box(
low=0,
high=goal,
shape=(1,),
dtype=np.int32,
)
self.state = 0
def reset(self, seed=None, options=None):
super().reset(seed=seed)
self.state = 0
observation = np.array([self.state], dtype=np.int32)
info = {}
return observation, info
def step(self, action):
if action == 1:
self.state += 1
else:
self.state -= 1
self.state = int(np.clip(self.state, 0, self.goal))
terminated = self.state == self.goal
truncated = False
reward = 1.0 if terminated else -0.01
observation = np.array([self.state], dtype=np.int32)
info = {}
return observation, reward, terminated, truncated, info
然后你可以把它注册成一个标准环境:
import gymnasium as gym
gym.register(
id="tutorial/LineReach-v0",
entry_point=LineReachEnv,
max_episode_steps=20,
)
env = gym.make("tutorial/LineReach-v0")
你需要理解自定义环境最核心的四件事:
action_space定义合法动作observation_space定义合法观测reset()负责开始一局step()负责推进一步并返回奖励与结束信号
如果你怀疑自己的环境定义有问题,可以先做一件非常值回票价的事:
from gymnasium.utils.env_checker import check_env
check_env(LineReachEnv())
这能帮你提前抓住很多接口层面的低级错误。
4.10 Gymnasium 还能帮你做什么
当你迈过最小环境这一步之后,Gymnasium 的价值会越来越明显,常见进阶方向主要有四类。
1. 快速验证 RL 算法
- 换环境方便
- 接口统一
- 适合做最小实验和回归测试
2. 包装已有仿真任务
很多机器人任务会把底层仿真器封装成 Gymnasium 接口,让上层训练代码不用关心具体物理引擎。
3. 并行采样
当训练吞吐变成瓶颈时,Vector Env 会是非常常见的一步优化。
4. 构建自己的 Benchmark
如果你在做特定机器人任务、控制问题或具身任务,Gymnasium 提供了一套非常清晰的环境组织方式,方便你把任务标准化下来。
4.11 新手最常踩的坑
坑 1:把 Gymnasium 当成算法库
Gymnasium 主要解决的是环境接口问题,不负责直接给你 PPO、DQN、SAC 这些训练实现。
坑 2:还在用旧版 done
如果你照抄旧 Gym 教程里的四元组返回值,很容易在新版代码里踩坑。
坑 3:忘了先 reset()
Gymnasium 官方文档专门把这件事列为新手常见错误。环境必须先 reset,再 step。
坑 4:以为 env.action_space.sample() 就是在训练
这只是随机采样动作,用来验证环境是否工作正常,不是学习过程本身。
坑 5:没装对应 extra,就直接跑环境
比如你只安装了基础包,却直接去跑 Box2D 或 MuJoCo 环境,失败通常是依赖没装齐,不一定是代码逻辑有问题。
坑 6:没区分 terminated 和 truncated
入门时可以先用 or 合并,但到了算法实现阶段,最好明确知道哪一种结束信号是任务终止,哪一种只是时间截断。
4.12 一条实用的学习路径
如果你准备认真学 Gymnasium,我建议按这个顺序走:
- 先跑通
CartPole-v1,把reset/step/render/close主循环看明白。 - 再看
action_space和observation_space,建立接口直觉。 - 试一个连续动作环境,例如
Pendulum-v1。 - 学会至少一种 Wrapper。
- 学会
make_vec(),理解并行环境的返回值。 - 最后再开始写自己的环境。
当你能把“随机跑一个内置环境”和“注册一个自定义环境”都各做一遍时,就已经真正跨过 Gymnasium 的入门门槛了。
4.13 本节小结
这一节最重要的结论有四个:
- Gymnasium 更像环境交互标准,而不是具体物理仿真器。
- 截至
2026-04-14,Gymnasium 当前稳定版是1.2.3,并要求Python >= 3.10。 - 真正有效的上手顺序是
内置环境 -> API 循环 -> space -> 向量化 -> 自定义环境。 - 很多机器人和强化学习项目,最终都会落到
底层仿真器 + Gymnasium 接口 + 训练代码这条工作流上。