第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章 环境搭建知识体系 Gymnasium安装 自定义跟踪环境 渲染与交互 pip安装 & 环境验证 经典控制环境测试 继承Env类 状态/动作空间定义 奖励函数设计 终止条件设置 human模式弹窗 rgb_array录制 matplotlib实时绘制 目标:搭建一个可运行、可调试、可扩展的轨迹跟踪训练环境

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() 里的运动学模型对不对。我习惯在每一步打印状态值,看看数值是否在合理范围内。另外,奖励函数的数值大小也很关键——太大或太小都会影响训练稳定性。

好了,环境搭建就到这里。你想想看,有了这个基础,后面再往上加算法就顺理成章了。代码都在上面,建议你亲手敲一遍,遇到问题再回来对照。

可可精品资料库