欢迎回来!在上一篇文章中,我们学习了如何创建一个简单的网格世界环境。今天,我们将在此基础上,探索如何创建更复杂、更接近真实世界问题的环境。让我们开始吧!
1. 引入连续动作空间
在现实世界中,很多问题的动作空间是连续的。比如,控制机器人的力度或者调整温度。让我们将我们的网格世界升级为一个连续的 2D 平面。
import gym
import numpy as np
from gym import spaces
class Continuous2DWorld(gym.Env):
def __init__(self):
super(Continuous2DWorld, self).__init__()
# 动作空间:二维连续空间,范围 [-1, 1]
self.action_space = spaces.Box(low=-1, high=1, shape=(2,), dtype=np.float32)
# 观察空间:agent 的位置,范围 [0, 10]
self.observation_space = spaces.Box(low=0, high=10, shape=(2,), dtype=np.float32)
self.agent_pos = None
self.goal_pos = np.array([8.0, 8.0])
def reset(self):
self.agent_pos = np.random.uniform(0, 10, size=(2,))
return self.agent_pos
def step(self, action):
# 更新 agent 位置
self.agent_pos += action
self.agent_pos = np.clip(self.agent_pos, 0, 10)
# 计算到目标的距离
distance = np.linalg.norm(self.agent_pos - self.goal_pos)
# 计算奖励和是否结束
reward = -distance # 负的距离作为奖励
done = distance < 0.1 # 当距离小于 0.1 时视为到达目标
return self.agent_pos, reward, done, {}
def render(self):
print(f"Agent 位置: {self.agent_pos}, 目标位置: {self.goal_pos}")
在这个新环境中:
- 动作空间是一个 2D 连续空间,表示 agent 在 x 和 y 方向上的移动。
- 观察空间是 agent 在 2D 平面上的位置。
- 奖励是 agent 到目标的负距离,鼓励 agent 尽快接近目标。
2. 设计更复杂的奖励函数
好的奖励函数设计对于学习效果至关重要。让我们为我们的环境设计一个更复杂的奖励函数。
def compute_reward(self):
distance = np.linalg.norm(self.agent_pos - self.goal_pos)
# 基础奖励:负的距离
base_reward = -distance
# 速度奖励:鼓励 agent 保持移动
speed_reward = np.linalg.norm(self.last_action) if hasattr(self, 'last_action') else 0
# 接近目标奖励
proximity_reward = 1.0 if distance < 1.0 else 0.0
# 惩罚项:如果 agent 碰到边界
boundary_penalty = -1.0 if np.any(self.agent_pos == 0) or np.any(self.agent_pos == 10) else 0.0
return base_reward + 0.1 * speed_reward + proximity_reward + boundary_penalty
def step(self, action):
self.last_action = action
self.agent_pos += action
self.agent_pos = np.clip(self.agent_pos, 0, 10)
reward = self.compute_reward()
done = np.linalg.norm(self.agent_pos - self.goal_pos) < 0.1
return self.agent_pos, reward, done, {}
这个新的奖励函数考虑了多个因素:
- 到目标的距离
- agent 的移动速度
- 是否接近目标
- 是否碰到环境边界
这种复杂的奖励函数可以引导 agent 学习更复杂的行为。
3. 添加环境随机性
真实世界是充满不确定性的。让我们给我们的环境添加一些随机性。
class StochasticContinuous2DWorld(Continuous2DWorld):
def __init__(self, noise_level=0.1):
super().__init__()
self.noise_level = noise_level
def step(self, action):
# 添加噪声到动作
noisy_action = action + np.random.normal(0, self.noise_level, size=action.shape)
return super().step(noisy_action)
def reset(self):
self.agent_pos = np.random.uniform(0, 10, size=(2,))
self.goal_pos = np.random.uniform(0, 10, size=(2,)) # 目标位置也随机化
return self.agent_pos
这个版本的环境增加了两种随机性:
- 动作执行时有噪声,模拟现实世界中的不精确控制。
- 每次重置时,目标位置也随机化,增加了任务的难度。
4. 实现障碍物
让我们给我们的 2D 世界添加一些障碍物,使环境更加复杂和真实。
class Obstacle2DWorld(StochasticContinuous2DWorld):
def __init__(self, num_obstacles=5):
super().__init__()
self.num_obstacles = num_obstacles
self.obstacles = []
def reset(self):
self.agent_pos = np.random.uniform(0, 10, size=(2,))
self.goal_pos = np.random.uniform(0, 10, size=(2,))
# 生成随机障碍物
self.obstacles = []
for _ in range(self.num_obstacles):
obs_pos = np.random.uniform(0, 10, size=(2,))
obs_radius = np.random.uniform(0.5, 1.0)
self.obstacles.append((obs_pos, obs_radius))
return self.agent_pos
def step(self, action):
new_pos = self.agent_pos + action
# 检查是否碰到障碍物
for obs_pos, obs_radius in self.obstacles:
if np.linalg.norm(new_pos - obs_pos) < obs_radius:
return self.agent_pos, -1, False, {"collision": True}
self.agent_pos = np.clip(new_pos, 0, 10)
distance = np.linalg.norm(self.agent_pos - self.goal_pos)
reward = -distance
done = distance < 0.1
return self.agent_pos, reward, done, {}
def render(self):
print(f"Agent 位置: {self.agent_pos}")
print(f"目标位置: {self.goal_pos}")
print("障碍物:")
for i, (obs_pos, obs_radius) in enumerate(self.obstacles):
print(f" 障碍物 {i+1}: 位置 = {obs_pos}, 半径 = {obs_radius}")
这个新版本的环境添加了随机生成的圆形障碍物。如果 agent 碰到障碍物,会得到惩罚并停止移动。
5. 使用 Wrapper 增强环境
Gym 提供了 Wrapper 机制,允许我们在不修改原始环境的情况下增加新的功能。让我们创建一个 Wrapper 来记录 agent 的轨迹。
class TrajectoryRecorder(gym.Wrapper):
def __init__(self, env):
super().__init__(env)
self.trajectory = []
def reset(self):
self.trajectory = []
return self.env.reset()
def step(self, action):
observation, reward, done, info = self.env.step(action)
self.trajectory.append(observation)
return observation, reward, done, info
def get_trajectory(self):
return np.array(self.trajectory)
# 使用方法
env = TrajectoryRecorder(Obstacle2DWorld())
这个 Wrapper 记录了 agent 在每个回合中的完整轨迹,可以用于后续的分析或可视化。
总结
在这篇教程中,我们学习了如何将简单的网格世界升级为更复杂、更真实的环境:
- 引入了连续动作空间,更接近真实世界的控制问题。
- 设计了更复杂的奖励函数,引导 agent 学习复杂行为。
- 添加了环境随机性,增加了任务的难度和真实性。
- 实现了障碍物,使环境更加复杂和有挑战性。
- 使用 Wrapper 来增强环境功能,而不需要修改原始环境。