第2章:环境搭建——Gymnasium库安装、自定义轨迹跟踪环境、渲染与交互
好,咱们进入实战环节。上一章聊了强化学习解决轨迹跟踪的基本思路,这一章就得把「战场」搭起来。说白了,没有环境,算法就是纸上谈兵。
我个人习惯先把基础设施搞定,再谈算法调优。所以这一章,咱们一步步来:装库、写环境、让小车动起来。
2.1 Gymnasium库安装
Gymnasium 是 Gym 的继承者,目前最主流的强化学习环境库。我建议直接用 pip 安装,干净利落。
pip install gymnasium
pip install gymnasium[box2d] # 如果需要物理仿真环境
pip install gymnasium[classic-control] # 经典控制问题
嗯,这里要注意:如果你用的是 conda 环境,记得先激活虚拟环境再装。我在项目中遇到过因为装错环境,折腾半天才发现 pip 装到了 base 环境里——这种低级错误,犯一次就长记性了。
python -c "import gymnasium; print(gymnasium.__version__)" 验证是否成功。如果没报错,说明环境就绪。
2.2 自定义轨迹跟踪环境
标准环境虽然方便,但轨迹跟踪这种任务,还是得自己写。为什么?因为标准环境里没有「期望轨迹」这个概念。你需要告诉智能体:你应该沿着这条线走,而不是乱跑。
我设计环境时,通常会继承 gymnasium.Env 类,然后实现几个核心方法。下面是一个简化版的自定义环境框架:
import gymnasium as gym
from gymnasium import spaces
import numpy as np
class TrajectoryTrackingEnv(gym.Env):
"""自定义轨迹跟踪环境"""
def __init__(self):
super().__init__()
# 动作空间:线速度、角速度
self.action_space = spaces.Box(
low=np.array([0.0, -1.0]),
high=np.array([1.0, 1.0]),
dtype=np.float32
)
# 观测空间:位置(x,y)、朝向角、速度、目标点偏差
self.observation_space = spaces.Box(
low=np.array([-10, -10, -np.pi, 0, -5, -5]),
high=np.array([10, 10, np.pi, 1, 5, 5]),
dtype=np.float32
)
# 定义期望轨迹(这里用一条正弦曲线)
self.trajectory = self._generate_trajectory()
self.current_step = 0
self.max_steps = 500
def _generate_trajectory(self):
"""生成期望轨迹点"""
x = np.linspace(0, 10, 100)
y = 2 * np.sin(0.5 * x) + 1
return np.column_stack((x, y))
def reset(self, seed=None):
"""重置环境"""
super().reset(seed=seed)
self.current_step = 0
# 随机初始化小车位置
self.state = np.array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
return self.state, {}
def step(self, action):
"""执行动作,返回下一状态、奖励、是否终止等"""
# 更新状态(简化运动学模型)
v, omega = action
x, y, theta, _, _, _ = self.state
dt = 0.1
x_new = x + v * np.cos(theta) * dt
y_new = y + v * np.sin(theta) * dt
theta_new = theta + omega * dt
# 计算与期望轨迹的偏差
target = self.trajectory[self.current_step % len(self.trajectory)]
dx = target[0] - x_new
dy = target[1] - y_new
distance_error = np.sqrt(dx**2 + dy**2)
# 奖励设计:距离越近奖励越高
reward = -distance_error
self.state = np.array([x_new, y_new, theta_new, v, dx, dy])
self.current_step += 1
# 终止条件
terminated = distance_error > 5.0 # 偏离太远
truncated = self.current_step >= self.max_steps # 超时
return self.state, reward, terminated, truncated, {}
def render(self):
"""渲染(后面会详细讲)"""
pass
1. 定义好状态空间和动作空间的边界
2. 奖励函数要能反映「跟踪精度」
3. 终止条件要合理,别让智能体无限跑下去
2.3 渲染与交互
环境写好了,但你看不到小车在跑,那多没意思。渲染就是让环境「可视化」的关键。
Gymnasium 支持多种渲染模式:human(弹窗显示)、rgb_array(返回像素数组)、ansi(文本显示)。我个人最常用 human 模式调试,用 rgb_array 做记录。
下面是一个简单的渲染实现,用 matplotlib 实时绘制小车和轨迹:
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
class TrajectoryTrackingEnv(gym.Env):
# ... 前面的代码不变 ...
def __init__(self):
# ... 初始化代码 ...
self.fig = None
self.ax = None
def render(self, mode='human'):
if mode == 'human':
if self.fig is None:
plt.ion() # 交互模式
self.fig, self.ax = plt.subplots(figsize=(8, 6))
# 绘制期望轨迹
self.ax.plot(self.trajectory[:, 0], self.trajectory[:, 1],
'b--', label='期望轨迹', linewidth=2)
self.ax.set_xlim(-1, 11)
self.ax.set_ylim(-3, 5)
self.ax.grid(True)
self.ax.set_aspect('equal')
self.ax.legend()
# 清除旧的小车位置
if hasattr(self, 'car_patch'):
self.car_patch.remove()
# 绘制当前小车位置(用矩形表示)
x, y, theta, _, _, _ = self.state
self.car_patch = Rectangle(
(x-0.2, y-0.1), 0.4, 0.2,
angle=np.degrees(theta),
color='red', alpha=0.8
)
self.ax.add_patch(self.car_patch)
plt.pause(0.01)
elif mode == 'rgb_array':
# 返回图像数组,用于录制视频
self.fig.canvas.draw()
image = np.frombuffer(self.fig.canvas.tostring_rgb(), dtype='uint8')
image = image.reshape(self.fig.canvas.get_width_height()[::-1] + (3,))
return image
plt.pause() 调用太频繁会导致界面卡死。建议每 3-5 步渲染一次,或者用 plt.pause(0.01) 这种短延时。另外,记得在环境关闭时调用 plt.close() 释放资源。
2.4 知识体系总览
这一章的内容,说白了就是三件事:装库、写环境、让它动起来。下面这张图帮你理清脉络:
2.5 完整测试流程
环境写完了,怎么验证它能不能跑?我一般会写一个简单的随机策略测试脚本:
# test_env.py
env = TrajectoryTrackingEnv()
obs, _ = env.reset()
for i in range(200):
# 随机动作
action = env.action_space.sample()
obs, reward, terminated, truncated, info = env.step(action)
env.render()
if terminated or truncated:
print(f"第{i}步结束,奖励累计:{reward:.2f}")
obs, _ = env.reset()
env.close()
跑这个脚本,你应该能看到一个小红方块沿着蓝色虚线移动。如果偏离太远,环境会自动重置。这就是最基础的「轨迹跟踪」雏形。
step() 里的运动学模型对不对。我习惯在每一步打印状态值,看看数值是否在合理范围内。另外,奖励函数的数值大小也很关键——太大或太小都会影响训练稳定性。
好了,环境搭建就到这里。你想想看,有了这个基础,后面再往上加算法就顺理成章了。代码都在上面,建议你亲手敲一遍,遇到问题再回来对照。