You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Stable-Baselines3 PPO智能体在自定义抛射环境中无法学习问题排查

PPO智能体卡在动作下限无法学习的问题分析与解决方案

问题概述

使用Stable-Baselines3的PPO算法在Pymunk构建的抛射环境中训练时,出现以下异常:

  • 动作始终固定在下限值10.0,无任何探索变化
  • 奖励全程保持恒定,ep_rew_mean无提升趋势
  • 熵损失下降过快,策略标准差变大但动作无更新

核心原因分析

1. 单步终止设计(done=True)破坏PPO学习机制

PPO依赖连续轨迹片段计算优势函数和更新策略,但当前环境每执行一次动作就直接设置done=True,导致:

  • 无法积累有效经验轨迹,优势估计完全失效
  • 智能体没有机会从后续动作反馈中学习,只能停留在初始动作或随机探索的局部点

2. 奖励信号弱且区分度低

当前奖励为-error / target_x,范围仅在[-1, 0]之间:

  • 梯度信号微弱,智能体难以感知动作变化带来的奖励差异
  • 无正向激励,无法引导智能体向更优动作探索

3. 观测空间存在无效信息

  • 重置环境时last_landing_x设为0.0,导致初始观测的着陆位置和误差信息完全无效
  • 角度归一化使用90度作为分母,但动作范围是10-80度,归一化后范围仅为[0.11, 0.89],信息利用率低

4. 探索策略配置不合理

虽然设置了ent_coef=0.05,但单步环境下熵损失快速下降,智能体很快放弃探索;log_std_init=1.0可能导致初始策略标准差过大,反而抑制有效探索。

针对性解决方案

1. 重构环境为多步回合制

修改环境终止逻辑,让每个回合包含多步动作,给PPO提供足够的轨迹片段:

class ProjectileEnv(Env):
    def __init__(self):
        super().__init__()
        # 新增最大回合步数
        self.max_steps_per_episode = 15
        self.current_step = 0
        # ... 原有初始化代码(min_angle、max_angle等)

    def reset(self):
        self.last_angle = np.random.uniform(self.min_angle, self.max_angle)
        # 初始着陆位置设为发射点100,保证初始观测有效
        self.last_landing_x = 100.0
        self.current_step = 0
        self.prev_error = abs(self.last_landing_x - self.target_x)
        self.state = self._get_state()
        return self.state

    def step(self, action):
        angle = float(action[0])
        self.last_angle = angle
        simulation_result = self.simulation.run(
            angle=angle, speed=500, apply_air_resistance=True, step=1/120, record_trajectory=False
        )
        self.last_landing_x = simulation_result["landing_x"]
        error = abs(self.last_landing_x - self.target_x)
        
        # 优化奖励:扩展到[-1,1]区间,增加误差减小的正向激励
        normalized_error = error / self.target_x
        reward = 1 - 2 * normalized_error  # 误差为0时奖励1,误差最大时奖励-1
        if error < self.prev_error:
            reward += 0.2  # 误差减小时额外奖励
        self.prev_error = error
        
        self.state = self._get_state()
        self.current_step += 1
        # 仅当达到最大步数时终止回合
        done = self.current_step >= self.max_steps_per_episode
        return self.state, reward, done, {}

2. 优化观测空间信息

调整观测归一化逻辑,确保所有状态信息有效且分布合理:

def _get_state(self):
    # 动作范围10-80,直接用动作区间归一化到[0,1]
    angle_norm = (self.last_angle - self.min_angle) / (self.max_angle - self.min_angle)
    # 着陆位置按环境宽度1500归一化
    landing_x_norm = self.last_landing_x / 1500.0
    # 误差按目标距离归一化到[0,1]
    error_norm = abs(self.last_landing_x - self.target_x) / self.target_x
    return np.array([angle_norm, landing_x_norm, error_norm], dtype=np.float32)

3. 调整PPO策略参数增强探索

修改训练代码中的策略配置,提升探索意愿:

from stable_baselines3 import PPO
from learning.env import ProjectileEnv

env = ProjectileEnv()
model = PPO(
    "MlpPolicy",
    env,
    verbose=1,
    ent_coef=0.1,  # 增大熵系数,鼓励探索
    policy_kwargs={"log_std_init": 0.5},  # 降低初始标准差,避免过度随机
    clip_range=0.2,
    learning_rate=3e-4,
    n_steps=2048  # 增大轨迹片段长度,提升优势估计精度
)

model.learn(total_timesteps=100000)  # 增加训练步数

# 测试模型
obs = env.reset()
total_reward = 0
for _ in range(env.max_steps_per_episode):
    action, _ = model.predict(obs)
    obs, reward, done, info = env.step(action)
    total_reward += reward
    print(f"动作: {action[0]:.2f}, 奖励: {reward:.2f}")
    if done:
        break
print(f"总奖励: {total_reward:.2f}")

4. 验证仿真逻辑正确性

手动测试不同角度的着陆位置,确保物理仿真符合预期:

from simulation.simul import Simulation

sim = Simulation(1500, 500)
# 测试不同角度的着陆位置
angles = [10, 30, 45, 60, 80]
for angle in angles:
    res = sim.run(angle=angle, speed=500)
    print(f"角度{angle}度: 着陆位置X={res['landing_x']:.2f}")

确保着陆位置随角度变化呈现合理的抛物线趋势(先增后减),排除仿真逻辑错误导致的学习障碍。

内容的提问来源于stack exchange,提问作者Jhj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 17:08:08