跳到主要内容

4. Gymnasium 快速上手

Gymnasium 不是一个“把机器人放进 3D 世界里跑起来”的仿真器,它更像是强化学习环境的标准接口层。

它解决的问题不是“物理怎么模拟”,而是“智能体如何以统一方式和环境交互”。很多你熟悉的环境,无论底层是 Classic ControlBox2DMuJoCo 还是你自己写的任务,到了 Gymnasium 这里都会收敛成同一套 API:

  • gym.make()
  • env.reset()
  • env.step(action)
  • env.render()
  • env.close()

如果你是第一次接触 Gymnasium,这一节的目标很明确:

  1. 分清 Gymnasium 和 Isaac Sim / MuJoCo 这类仿真器的关系。
  2. 把最常用的安装方式装起来。
  3. 跑通一个最小环境,例如 CartPole-v1
  4. 读懂 reset()step()action_spaceobservation_space
  5. 知道怎么把内置环境过渡到自定义环境。

4.1 Gymnasium 是什么

你可以把 Gymnasium 理解成强化学习世界里的“环境协议”。

它的核心价值主要有四个:

  • 给不同环境提供统一的交互接口,降低切换成本。
  • 内置一批常见参考环境,适合教学、实验和算法验证。
  • 提供 WrapperVector Env 等机制,方便做环境改造和并行采样。
  • 让训练代码和具体环境实现解耦,更容易接入上层算法框架。

这里最容易混淆的一点是:

  • Isaac SimMuJoCo 更偏“世界和物理怎么模拟”
  • Gymnasium 更偏“智能体如何看到状态、执行动作、收到奖励”

两者并不冲突。真实工作流里很常见的一种组合就是:

物理引擎 / 仿真器 -> 封装成 Gymnasium Env -> 训练代码

4.2 安装前先做两个判断

正式开始前,建议先判断两件事。

1. 你是想先学环境 API,还是先学高保真仿真

  • 想先理解强化学习环境循环、状态、动作、奖励:先学 Gymnasium。
  • 想先搭 3D 场景、传感器、机器人资产:先学 Isaac Sim、Gazebo、MuJoCo 等仿真器。
  • 想做机器人学习:通常两者都要学,只是顺序可以不同。

2. 你是先用内置环境,还是马上写自己的环境

  • 第一次上手:先用 CartPole-v1Pendulum-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
  • 想跑 CartPolePendulumMountainCarpip 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。
  • 文档和旧博客里经常混用 gymgymnasium。新项目优先直接使用 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() 创建的环境,默认已经包上了 TimeLimitOrderEnforcingPassiveEnvChecker

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()
🕹 在线试试 · CartPole-v1(浏览器里跑)

运行方式:

python hello_cartpole.py

你应该会看到一个小车左右乱动,杆子很快倒下去。这是正常现象,因为现在的智能体还没有学习,只是在随机试动作。

这段代码里最值得注意的是三件事:

  • render_mode="human" 需要在 gym.make() 时传入,而不是等运行后再补。
  • 第一步一定是 reset(),不能直接 step()
  • 随机动作只能证明环境在正常工作,不代表你已经“训练了一个 agent”。

4.6 看懂动作空间和观测空间

很多时候,环境能不能接进你的算法,关键就看你有没有把 action_spaceobservation_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 读懂 terminatedtruncated

这是新手最常混淆、但又非常关键的一点。

很多旧教程还会写:

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()

这里最重要的直觉有三个:

  • 现在返回的 observationsrewardsterminationstruncations 都是“批量”的。
  • envs.action_space 也变成了面向批量环境的动作空间。
  • 如果你只想看单个子环境的接口,应该关注 envs.single_action_spaceenvs.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,就直接跑环境

比如你只安装了基础包,却直接去跑 Box2DMuJoCo 环境,失败通常是依赖没装齐,不一定是代码逻辑有问题。

坑 6:没区分 terminatedtruncated

入门时可以先用 or 合并,但到了算法实现阶段,最好明确知道哪一种结束信号是任务终止,哪一种只是时间截断。

4.12 一条实用的学习路径

如果你准备认真学 Gymnasium,我建议按这个顺序走:

  1. 先跑通 CartPole-v1,把 reset/step/render/close 主循环看明白。
  2. 再看 action_spaceobservation_space,建立接口直觉。
  3. 试一个连续动作环境,例如 Pendulum-v1
  4. 学会至少一种 Wrapper。
  5. 学会 make_vec(),理解并行环境的返回值。
  6. 最后再开始写自己的环境。

当你能把“随机跑一个内置环境”和“注册一个自定义环境”都各做一遍时,就已经真正跨过 Gymnasium 的入门门槛了。

4.13 本节小结

这一节最重要的结论有四个:

  • Gymnasium 更像环境交互标准,而不是具体物理仿真器。
  • 截至 2026-04-14,Gymnasium 当前稳定版是 1.2.3,并要求 Python >= 3.10
  • 真正有效的上手顺序是 内置环境 -> API 循环 -> space -> 向量化 -> 自定义环境
  • 很多机器人和强化学习项目,最终都会落到 底层仿真器 + Gymnasium 接口 + 训练代码 这条工作流上。

参考资料