环境搭建与工具链: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 0.28+ 跟旧版 gym 的 API 有变化,
step返回 5 个值而不是 4 个。如果你看到too many values to unpack,八成是这个原因。 - 渲染模式: 训练时别开
render_mode="human",会拖慢速度。我习惯训练时用None,只在测试时渲染。 - 随机种子: 自定义环境里记得调用
super().reset(seed=seed),否则随机数不可复现,调试起来很痛苦。
好了,工具链就这些。下一章咱们会真正开始写强化学习算法,到时候这些环境就是你的「试验田」。