环境搭建与工具链:Gymnasium库安装与使用、自定义机器人仿真环境、PyTorch基础回顾

说实话,每次带新人入门强化学习轨迹规划,我最怕听到的一句话就是:「老师,我代码跑不起来。」

环境搭建这事儿,看着简单,坑是真不少。我自己当年就吃过不少亏——明明照着教程一步步来,结果一运行就报错,查了半天发现是版本不兼容。所以这一章,咱们把工具链彻底捋清楚。

Gymnasium:强化学习界的「乐高积木」

Gymnasium 是 Gym 的继承者,由 Farama Foundation 维护。说白了,它就是一套标准化的强化学习环境接口。你想想看,如果没有统一接口,每个人写自己的环境,那算法怎么复用?

安装很简单,一行命令搞定:

pip install gymnasium

但这里有个坑——千万别直接装 gym。老版的 Gym 已经停止维护了,很多新功能都不支持。我去年有个项目,一开始用了旧版 gym,结果跟新版的 stable-baselines3 死活不兼容,折腾了两天才发现是版本问题。

装完之后,咱们来跑个经典环境试试:

import gymnasium as gym

env = gym.make("CartPole-v1", render_mode="human")
observation, info = env.reset()

for _ in range(100):
    action = env.action_space.sample()  # 随机动作
    observation, reward, terminated, truncated, info = env.step(action)
    
    if terminated or truncated:
        observation, info = env.reset()

env.close()

看到没?核心就四个方法:make、reset、step、close。所有 Gymnasium 环境都遵循这个接口,这就是标准化的力量。

小技巧: 用 gymnasium.envs.registry 可以查看所有可用的环境。我习惯先列出来,再挑合适的。

自定义机器人仿真环境

标准环境虽好,但做轨迹规划时,你往往需要自己的机器人模型。比如我做过一个六轴机械臂的项目,标准环境里根本没有,只能自己写。

自定义环境的核心是继承 gymnasium.Env 类,然后实现几个关键方法:

import gymnasium as gym
from gymnasium import spaces
import numpy as np

class SimpleRobotEnv(gym.Env):
    def __init__(self):
        super().__init__()
        # 动作空间:关节角度变化 [-0.1, 0.1]
        self.action_space = spaces.Box(
            low=-0.1, high=0.1, shape=(2,), dtype=np.float32
        )
        # 观测空间:关节角度和末端位置
        self.observation_space = spaces.Box(
            low=-np.pi, high=np.pi, shape=(4,), dtype=np.float32
        )
        self.joint_angles = np.array([0.0, 0.0])
        
    def reset(self, seed=None, options=None):
        super().reset(seed=seed)
        self.joint_angles = np.array([0.0, 0.0])
        return self._get_obs(), {}
    
    def step(self, action):
        self.joint_angles += action
        # 计算末端位置(正运动学)
        end_effector = self._forward_kinematics(self.joint_angles)
        reward = -np.linalg.norm(end_effector - self.target)
        terminated = reward > -0.01
        return self._get_obs(), reward, terminated, False, {}
    
    def _get_obs(self):
        end_effector = self._forward_kinematics(self.joint_angles)
        return np.concatenate([self.joint_angles, end_effector])
    
    def _forward_kinematics(self, angles):
        # 简化的2关节正运动学
        x = np.cos(angles[0]) + np.cos(angles[0] + angles[1])
        y = np.sin(angles[0]) + np.sin(angles[0] + angles[1])
        return np.array([x, y])
注意: 定义 observation_space 和 action_space 时,数据类型一定要用 np.float32。我曾经用 float64,结果跟 PyTorch 的默认类型对不上,训练时各种隐式转换,速度慢了一倍。

嗯,这里要特别说一下 reward 设计。很多新手喜欢把 reward 设得很复杂,其实没必要。我个人的经验是:先让机器人能完成任务,再优化效率。比如上面这个例子,只用末端距离作为 reward,简单直接。

PyTorch 基础回顾:够用就行

做强化学习轨迹规划,PyTorch 不需要你精通所有功能。说白了,你只要掌握三样东西:张量操作、神经网络搭建、梯度计算。

张量操作:

import torch

# 创建张量
state = torch.tensor([0.1, 0.2, 0.3], dtype=torch.float32)
# 转到 GPU(如果有的话)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
state = state.to(device)

# 批量处理(强化学习中很常见)
batch_states = torch.randn(32, 4)  # 32个样本,每个4维

神经网络搭建:

import torch.nn as nn
import torch.nn.functional as F

class PolicyNetwork(nn.Module):
    def __init__(self, state_dim, action_dim):
        super().__init__()
        self.fc1 = nn.Linear(state_dim, 128)
        self.fc2 = nn.Linear(128, 128)
        self.fc3 = nn.Linear(128, action_dim)
        
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        return self.fc3(x)  # 输出动作

你看,就这么简单。三层全连接,激活函数用 ReLU,输出层不加激活(因为动作可以是任意值)。

梯度计算与优化:

optimizer = torch.optim.Adam(policy.parameters(), lr=3e-4)
loss_fn = nn.MSELoss()

# 训练循环
for epoch in range(100):
    optimizer.zero_grad()
    predicted = policy(states)
    loss = loss_fn(predicted, targets)
    loss.backward()
    optimizer.step()
核心要点: 强化学习中,loss.backward() 之前一定要 optimizer.zero_grad()。我见过太多人忘记这步,梯度不断累加,模型直接炸掉。

本章知识体系

下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个「地图」,随时回来对照:

环境搭建与工具链知识体系 Gymnasium 库 自定义仿真环境 PyTorch 基础 安装配置 标准接口 环境注册 继承 Env 类 空间定义 Reward 设计 张量操作 网络搭建 梯度优化 三者结合 → 强化学习轨迹规划流水线

这张图把本章内容串起来了。左边是环境,中间是自定义逻辑,右边是深度学习框架。三者缺一不可。

避坑指南

最后,分享几个我踩过的坑:

  • 版本兼容性: Gymnasium 0.28+ 跟旧版 gym 的 API 有变化,step 返回 5 个值而不是 4 个。如果你看到 too many values to unpack,八成是这个原因。
  • 渲染模式: 训练时别开 render_mode="human",会拖慢速度。我习惯训练时用 None,只在测试时渲染。
  • 随机种子: 自定义环境里记得调用 super().reset(seed=seed),否则随机数不可复现,调试起来很痛苦。

好了,工具链就这些。下一章咱们会真正开始写强化学习算法,到时候这些环境就是你的「试验田」。

可可精品资料库