从CartPole到Pendulum用Python代码‘玩转’Gym两大经典控制环境强化学习作为机器学习的重要分支其魅力在于让智能体通过与环境交互来自主学习最优策略。而OpenAI Gym作为最流行的强化学习实验平台提供了丰富的训练环境。本文将带您深入探索Gym中两个经典控制问题——CartPole-v1和Pendulum-v1通过完整的Python代码示例让您亲身体验强化学习环境的运行机制。1. 环境搭建与基础API在开始之前确保已安装最新版gym库pip install gym0.26.2Gym环境的核心交互流程遵循重置-执行-观察循环import gym # 创建环境 env gym.make(CartPole-v1) # 初始化环境 observation env.reset() for _ in range(1000): # 渲染当前状态 env.render() # 随机选择动作 action env.action_space.sample() # 执行动作并获取反馈 observation, reward, done, info env.step(action) # 检查是否终止 if done: observation env.reset() # 关闭环境 env.close()每个环境都提供以下关键属性属性描述示例值observation_space状态空间定义Box(4,)action_space动作空间定义Discrete(2)reward_range奖励值范围(0,1)2. CartPole-v1平衡杆问题解析CartPole-v1是一个经典的物理控制问题目标是让小车上的杆子保持直立不倒下。让我们深入分析其核心要素2.1 状态空间详解CartPole的状态由4个连续值组成小车位置水平坐标范围[-4.8,4.8]小车速度移动速率无固定界限杆子角度偏离垂直方向的角度范围[-0.418,0.418]弧度杆子角速度角度变化率无固定界限通过以下代码可以查看状态空间的具体参数env gym.make(CartPole-v1) print(状态空间:, env.observation_space) print(动作空间:, env.action_space)2.2 动作与奖励机制CartPole采用离散动作空间0向左施加力1向右施加力奖励设计非常简单每存活一步获得1奖励。终止条件包括杆子倾斜超过12度小车移动超出边界持续500步v1版本下面是一个完整的交互示例包含状态可视化import numpy as np import matplotlib.pyplot as plt def run_cartpole(episodes10): env gym.make(CartPole-v1) rewards [] for ep in range(episodes): obs env.reset() ep_reward 0 states [] while True: states.append(obs) action env.action_space.sample() obs, reward, done, _ env.step(action) ep_reward reward if done: rewards.append(ep_reward) visualize_states(np.array(states), ep) break env.close() return rewards def visualize_states(states, ep_num): plt.figure(figsize(10,6)) labels [Position, Velocity, Angle, Angular Velocity] for i in range(4): plt.plot(states[:,i], labellabels[i]) plt.title(fEpisode {ep_num} - State Trajectory) plt.xlabel(Time Step) plt.ylabel(Value) plt.legend() plt.grid() plt.show()3. Pendulum-v1钟摆控制挑战Pendulum-v1比CartPole更具挑战性需要控制钟摆使其保持直立状态。与CartPole不同Pendulum采用连续动作空间。3.1 状态表示解析Pendulum的状态空间包含3个值cos(theta)角度θ的余弦值sin(theta)角度θ的正弦值theta_dot角速度这种表示方式避免了角度周期性带来的不连续问题。θ0表示钟摆垂直向下范围是[-π,π]弧度。3.2 连续动作空间Pendulum的动作是施加在钟摆上的扭矩范围[-2,2]env gym.make(Pendulum-v1) print(动作空间:, env.action_space) # Box(-2.0, 2.0, (1,), float32)奖励函数设计更为复杂reward -(θ² 0.1*θ_dot² 0.001*action²)这意味着角度θ越接近0垂直向下奖励越高角速度和动作幅度越小越好下面是一个Pendulum的完整交互示例def run_pendulum(episodes5): env gym.make(Pendulum-v1) for ep in range(episodes): state env.reset() total_reward 0 frames [] for t in range(200): # 最大200步 # 随机动作 action env.action_space.sample() # 执行动作 next_state, reward, done, _ env.step(action) total_reward reward # 渲染并保存帧 frames.append(env.render(modergb_array)) if done: break print(fEpisode {ep1}, Total Reward: {total_reward:.2f}) display_animation(frames) env.close() def display_animation(frames): from IPython.display import HTML import matplotlib.animation as animation fig plt.figure(figsize(6,6)) plt.axis(off) img plt.imshow(frames[0]) def animate(i): img.set_array(frames[i]) return img, anim animation.FuncAnimation(fig, animate, frameslen(frames), interval50) plt.close() return HTML(anim.to_html5_video())4. 高级技巧与性能优化在实际应用中我们通常需要超越随机动作的策略。以下是几个实用技巧4.1 状态预处理对于Pendulum将状态转换为更直观的角度表示def pendulum_state_to_angle(state): cos_theta, sin_theta, theta_dot state theta np.arctan2(sin_theta, cos_theta) return np.array([theta, theta_dot])4.2 简单控制策略基于规则的策略可以作为baselinedef simple_pendulum_policy(state): theta, theta_dot pendulum_state_to_angle(state) # PD控制器 kp 15.0 # 比例系数 kd 1.0 # 微分系数 action kp * theta kd * theta_dot # 限制在[-2,2]范围内 return np.clip(action, -2, 2).reshape(1,)4.3 性能评估指标建立评估函数比较不同策略def evaluate_policy(env, policy, n_episodes10): total_rewards [] for _ in range(n_episodes): state env.reset() episode_reward 0 while True: action policy(state) state, reward, done, _ env.step(action) episode_reward reward if done: total_rewards.append(episode_reward) break return { mean_reward: np.mean(total_rewards), std_reward: np.std(total_rewards), max_reward: np.max(total_rewards), min_reward: np.min(total_rewards) }5. 从模拟到实战训练建议当您准备开始训练真正的强化学习算法时考虑以下实践要点帧堆叠连续几帧作为输入捕捉动态信息奖励塑形设计中间奖励加速学习超参数调优学习率、折扣因子等对性能影响显著# 示例帧堆叠实现 class FrameStack: def __init__(self, env, k4): self.env env self.k k self.frames deque([], maxlenk) def reset(self): obs self.env.reset() for _ in range(self.k): self.frames.append(obs) return self._get_obs() def step(self, action): obs, reward, done, info self.env.step(action) self.frames.append(obs) return self._get_obs(), reward, done, info def _get_obs(self): return np.concatenate(self.frames, axis0)