基于gym的多智能体追逃博弈强化学习仿真平台实现
发布时间:2026/8/31 22:22:35来源:尧图网络
简介本资源是一个基于OpenAI Gym框架构建的多智能体追逃博弈强化学习仿真平台面向计算机、人工智能及相关专业本科生专为课程设计与期末大作业打造解决多智能体协同决策、对抗策略建模与环境交互实现等核心实践难点。压缩包共40个文件含27个Python源码覆盖3D/2D追逃环境定义、FlightGear与JSBSim飞行仿真接口、主训练脚本及多组测试用例、9个编译缓存文件、1个依赖说明txt、1个README.md文档和1张环境示意图png整体仅79KB轻量易部署。已有145人下载学习代码经导师指导并获评98分高分项目提供完整可运行的多智能体RL训练闭环从自定义gym环境封装、状态-动作空间设计、奖励函数构造到PPO/MADDPG等算法适配接口结构清晰、注释充分便于快速复现与二次开发。 先说明一下这类“追逃博弈”场景在很多领域都有真实投影多无人机协同围捕、仓储机器人分拨调度、游戏里的NPC追逐逻辑甚至安防领域的入侵者拦截。它们的共同点是多个智能体在同一个动态环境里一边合作一边对抗最终形成一套策略。而用Python、gym、多智能体、强化学习这几个关键词组合起来就能搭出一个完整的仿真训练平台。这套平台可以用来做课程设计、毕业设计也可以作为多智能体强化学习算法研究的测试床。今天我就把这个项目的核心设计、环境源码结构、算法选型、训练调参和踩坑记录完整拆开讲一遍。我最初做这个项目的时候目标很明确不只要有一个能跑的demo还要在答辩和演示时拿得出手。所以整个平台必须有三个东西一是标准化的gym环境接口二是可配置的多智能体追逃场景三是能稳定训练出效果的强化学习算法。如果你也在做类似题目或者想把手里的“空壳代码”补成完整的项目这篇内容应该能帮你省下大量试错时间。1. 追逃博弈怎么做成强化学习问题1.1 为什么选择gym框架而不是自己写一套环境循环很多人一开始会纠结我直接用numpy写一个游戏循环不就行了为什么要套gym但从实际开发经验看gym提供的不是“限制”而是“标准接口”。有了reset、step、render这套约定你后面接任何算法都很顺写DQN时只需要调env.reset()和env.step(action)不用自己反复设计状态更新和回合结束逻辑。更重要的是很多评估指标、可视化工具、以及Stable-Baselines3这类现成算法库都是围绕gym接口设计的项目到后期想跑对比实验会非常省事。不过这里有一个细节要提前踩平现在gym生态已经发生了一次迁移老代码用gym新代码推荐gymnasium。两者API大体一致但有些细节不同。建议项目里做一个薄封装层不让算法代码直接依赖具体库这样无论是gym还是gymnasium都能平滑切换。这也是一个可以在答辩时讲的“架构亮点”。1.2 追逃问题的数学建模与状态空间定义追逃博弈本质上是一个动态博弈问题。场地里有两类角色追捕者Pursuer和逃亡者Evader。我们的目标是一方学会协作围捕另一方学会灵活逃脱。以经典的2捕1逃为例场地是一个二维连续空间每个智能体有位置和速度信息。状态空间需要包含三个层次的信息自身状态坐标x、y速度vx、vy相对状态目标相对自身的位置差dx、dy以及相对速度边界感知与场地边界的距离或者至少是“是否接近边界”的标记如果做的是全局可观测版本观测向量直接拼接所有追捕者和逃亡者的信息如果做部分可观测版本则每个智能体只能看到自己一定范围内的信息。按我踩过的经验基础版本先用全局可观测后面再设置一个“局部观测开关”作为项目亮点。状态维度可以参考这样设计智能体类型观测内容维度追捕者自身位置、速度 逃亡者相对位置 逃亡者速度8逃亡者自身位置、速度 所有追捕者相对位置按距离排序10可选扩展边界距离、视野遮挡标记、最近邻居距离若干维度不是越大越好我见过很多项目堆了几十个维度训练速度直线下降最后效果反而不如十几个维度稳定。1.3 智能体的动作空间设计离散还是连续追逃问题的动作空间可以做成离散也可以做成连续。离散动作用上下左右加原地不动每步移动一个固定步长实现最简单训练也最稳定适合作为基线版本。连续动作用速度方向和速度大小作为输出更贴近真实机器人控制但需求算法更复杂比如用DDPG或MADDPG。我的项目里两种都做了但默认跑的是离散版本。说实话除非你的课题明确要求“连续控制”否则优先离散能省很多事。教学演示场景里离散动作画出来的轨迹更清晰也更容易解释每个时间步的决策逻辑。2. 环境实现追逃环境源码的核心细节2.1 reset、step、render三个核心接口的实现逻辑写这个环境时最核心的就是把reset和step的细节做到可复现、可调参。reset时要随机初始化所有智能体的位置但不能重叠也不能离得太近导致一开局就捕获。我常用的策略是先把逃亡者放在场地中心附近再把追捕者放在场地边缘相对位置。step函数更复杂需要按顺序处理这么几件事更新动作、更新位置、检测边界、检测捕获、计算奖励、判断回合终止。很多人第一次写容易把检测边界和检测捕获的顺序搞反导致智能体出界后依然能捕获目标逻辑上很诡异。因为这是一个多智能体环境step接收的是所有智能体的动作列表返回的也是每个智能体各自的观测、奖励和终止状态这一点跟单智能体环境差异很大。2.2 奖励函数设计捕食者和逃亡者分别怎么设置奖励函数是整个追逃博弈的灵魂。我调试项目时花时间最多的不是算法而是奖励。先看追捕者的奖励设计r_pursuer capture_reward distance_reward out_of_bound_penalty time_penalty这里最关键的是distance_reward。直接给“离目标更近”的正奖励公式是(distance_before - distance_now)乘以一个系数。这个“相对距离变化量”比“绝对距离”要稳定得多。如果只设绝对距离奖励智能体很容易找到一个角落站定不动因为这也能获得较高奖励但它对围捕目标毫无帮助。逃亡者的奖励则是反向设计r_evader escape_reward timeout_bonus caught_penalty out_of_bound_penalty逃亡者希望与追捕者的平均距离增大希望拖到回合结束还不被捕获。timeout_bonus是逃亡者奖励中很重要的一环没有这个奖励它倾向于原地不动因为反正也跑不掉少做少错。2.3 捕食者捕获判定与回合终止条件捕获判定不能做太复杂。常见的做法是计算追捕者与逃亡者的欧氏距离小于某个阈值就判定捕获成功。这个阈值通常是智能体半径之和再乘一个宽松系数。举个例子两个智能体半径都是0.1那么捕获距离设为0.2到0.3比较合理。太精确的碰撞检测会导致训练很难收敛因为智能体在一帧里跨过目标边缘是常有的事。回合终止有三个条件捕获成功、回合步数超限、逃亡者出界可选。捕获成功时记录一个“捕获标志”超时则视为逃亡者逃脱。这两个事件的统计频率就是后面评估算法性能的核心指标。2.4 数值细节与稳定性步长、速度、边界碰撞连续时间环境里步长dt与每步位移量必须匹配。我常用场地范围是[-1, 1]乘[-1, 1]每步位移量0.1相当于一个回合最多200步智能体从场地一端跑到另一端要20步。这个比例下追捕和逃跑都有足够的策略空间又不会因为场地太大导致追捕者永远碰不到逃亡者。边界处理有两种方式硬边界和软反射。硬边界是位置被clamp住速度清零软反射是类似台球的反弹效果。我建议用软反射因为硬边界会导致智能体在角落出现奇怪的抖动行为。另外惩罚参数不要给太大否则智能体会为了“不出界”而彻底放弃追捕形成一种非常保守但指标糟糕的行为。3. 训练平台源码与算法实现3.1 项目目录结构与模块划分源码的组织方式对项目质量影响很大尤其答辩时老师很可能翻你的目录结构看规范性。我建议这样组织pursuit_evasion/ ├── envs/ │ ├── pursuit_evasion_env.py │ └── __init__.py ├── agents/ │ ├── dqn_agent.py │ ├── maddpg_agent.py │ └── __init__.py ├── train.py ├── evaluate.py ├── utils/ │ ├── memory.py │ ├── logger.py │ └── visualizer.py └── config.yamlconfig.yaml里放所有可调参数包括场地大小、智能体数量、回合上限、学习率、奖励系数等。把参数从代码里抽出来看似多了一步但调参时极其方便。3.2 环境源码示例追逃gym环境的骨架先放一段最核心的环境骨架代码能帮你快速建立一个整体认知import gym from gym import spaces import numpy as np class PursuitEvasionEnv(gym.Env): def __init__(self, n_pursuers2, n_evaders1, max_steps200): super().__init__() self.map_size 2.0 self.n_pursuers n_pursuers self.n_evaders n_evaders self.max_steps max_steps self.capture_dist 0.2 self.step_size 0.1 # 每个智能体用离散动作0上 1下 2左 3右 4原地 self.action_space spaces.Discrete(5) obs_dim 8 self.observation_space spaces.Box( low-10.0, high10.0, shape(obs_dim,), dtypenp.float32 ) def reset(self): self.pursuers self._random_positions(self.n_pursuers) self.evader self._random_positions(1)[0] self.steps 0 return self._get_obs() def step(self, actions): # actions: dict, keys是agent id self._apply_actions(actions) self.steps 1 captured self._check_capture() terminated captured or self.steps self.max_steps rewards self._compute_rewards(captured) infos {captured: captured, steps: self.steps} obs self._get_obs() return obs, rewards, terminated, infos def _get_obs(self): # 拼接所有智能体的观测具体拼接规则按类型区分 pass注意step返回的是所有智能体的奖励列表不是单个数。这个细节是单智能体与多智能体代码之间的一个典型分水岭很多人从单智能体改到多智能体时在这里卡住。3.3 智能体训练逻辑独立DQN的实现要点多智能体强化学习的入门算法我推荐独立DQNIndependent DQNIDQN。它本质上就是每个智能体各自维护一个DQN只是它们共享同一个环境。这个算法虽然简单但只要环境设计合理就能产生不错的围捕效果。其实现要点有三个第一是经验回放内存。每个智能体独立存储自己的(obs, action, reward, next_obs, done)元组训练时从自己的记忆里采样。第二是目标网络。每个智能体有online网络和目标网络每隔一定步数把online权重硬拷贝到目标网络。第三是探索策略。用epsilon-greedyepsilon从1.0开始在训练过程中线性衰减到0.05左右。实现时还可以做参数共享两个追捕者用同一个神经网络参数共享经验回放。这样训练速度会快很多因为捕食者的经验本质上是可以互相借鉴的。代价是智能体之间可能出现同质化行为但在这个项目里影响不大因为两个捕食者本来就是同构的。3.4 训练主循环多智能体回合训练的实现方式训练主循环的核心是“每回合重置环境每个时间步收集所有智能体的动作统一送入环境拿到多智能体各自的奖励分别存入各自记忆”。for episode in range(config[episodes]): obs env.reset() done False while not done: actions {} for agent_id, agent in agents.items(): actions[agent_id] agent.act(obs[agent_id]) next_obs, rewards, done, info env.step(actions) for agent_id, agent in agents.items(): agent.store_transition( obs[agent_id], actions[agent_id], rewards[agent_id], next_obs[agent_id], done, ) agent.update() obs next_obs这个循环看起来很简单但有一个隐藏问题多智能体环境下单个智能体的观测并不满足马尔可夫性质因为环境的变化还受到其他智能体策略的影响。在强化学习里这叫“非平稳环境”。独立DQN能跑通但理论上不是最终解法。如果你想在项目里体现深度可以在算法对比部分加入MADDPG。3.5 MADDPG的扩展思路集中式训练分布式执行MADDPGMulti-Agent Deep Deterministic Policy Gradient是目前多智能体强化学习里的经典算法核心思路是“集中式训练分布式执行”。训练阶段Critic网络能看到所有智能体的状态和动作相当于开了一个上帝视角来评估当前局势执行阶段Actor网络只用自身观测来决定动作。这种设计缓解了环境非平稳的问题因为它训练时考虑了其他人的行为。做MADDPG版本的追逃博弈一个比较稳妥的路径是先在连续动作空间下实现基础版用2个追捕者加1个逃亡者验证逻辑然后逐步增加逃亡者数量。MADDPG的训练稳定性比IDQN差一些需要调更多超参但它明显更适合展示“多智能体算法对比”的实验部分。4. 跑通的训练效果与调参实战4.1 评估指标怎么设计才客观训练之外更关键的是如何证明算法有效。我主要用三个指标捕获率测试时100个回合中成功捕获的比例平均捕获用时成功捕获回合的平均步数逃逸成功率逃亡者跑满回合上限的比例这三个指标要交叉看。如果捕获率高但平均用时很长可能意味着捕食者学会了“围”但没有学会“攻”只是在某个角落把目标堵死。如果逃逸成功率很高大概率是奖励参数或探索策略出了问题。4.2 经典失败案例追捕者原地转圈我调参时遇到最经典的问题是两个追捕者学会了在同一水平线上来回走完全不去理会逃亡者。原因是距离变化奖励的权重太小而时间惩罚稍微压过了探索收益智能体找了一个“不做事”的局部最优。解决方法是把距离变化奖励系数从1.0提高到2.5同时给“接近逃亡者”的额外bonus。调整后学习曲线在1000个回合内就出现了明显爬升趋势。另一个常见问题是捕食者都追同一个逃亡者因为两个捕食者共享网络参数学习到完全相同的策略总是走同一条路径去追。展示初始化轨迹时两条轨迹重合视觉上非常难看。缓解方法是在捕食者奖励里加一个“和队友保持距离”的小惩罚。但这也会引入新的超参需要仔细调节否则捕食者会为了保持距离而放弃围捕。4.3 训练曲线怎么看early success的陷阱强化学习训练中一个常见误区是只观察前几百个回合的收益曲线。追踪博弈特别容易前期表现不错因为初始位置随机很多回合里追捕者一开局就离逃亡者很近很容易捕获成功。但这不代表策略学到了。更可靠的指标是测试模式下、固定初始位置的捕获率变化曲线。训练过程中每500个回合跑一次固定测试集记录捕获率曲线这样看趋势才不会被“early success”骗到。曲线震荡也是正常现象。epsilon从高到低衰减的过程中策略会经历一个“探索多、效果差”到“探索少、效果稳”的过渡期。如果震荡持续且不收敛优先检查奖励尺度和学习率而不是怀疑算法本身。5. 常见问题与排查技巧实录5.1 多智能体训练中的典型问题速查表我把调试过程中遇到的问题整理成了速查表对这些项目的学员可以直接对照排查问题现象可能原因解决方向捕食者原地绕圈距离变化奖励权重过低加大距离变化系数加时间惩罚捕食者追同一个目标参数共享导致同质化增加队员间距惩罚或采用集中式Critic训练Loss爆炸奖励尺度差异过大奖励归一化到[-1, 1]降低学习率逃亡者撞墙后卡住硬边界惩罚过重改用软反射边界降低出界惩罚捕获率高但平均用时很长策略偏向堵角落检查奖励是否真的鼓励“接近”而非“堵住”训练曲线剧烈震荡epsilon衰减过快延长探索阶段或使用自适应探索率5.2 环境调试的调试顺序建议我的调试顺序很有讲究很适合分享给第一次做多智能体项目的同学。不要一上来就训练算法。如果算法不收敛你根本分不清是环境逻辑错误还是算法问题。我的做法是先写好一个规则策略追捕者直接以当前位置朝逃亡者方向移动一格。如果这个固定策略能在统计上达到60%的捕获率说明环境逻辑没问题可以开始训练强化学习算法。训练侧也按步骤来先跑500个回合观察奖励这个数值是否在增长Loss是否在下降。如果这两个数值都没有起色先不要急着调超参数加一个监控来记录平均步数和平均奖励变化趋势。如果这两个数值有明显变化再考虑调参。5.3 可视化让答辩演示更出彩多智能体追逃博弈项目的视觉表现力非常强一定要做好可视化。最简单的做法是matplotlib画轨迹图用不同颜色的线条标出捕食者和逃亡者的运动路径并在捕获点画一个大圆点标记。进阶做法是用matplotlib的animation模块生成回放视频能够直观看到“围捕”过程。我建议在测试阶段给每个回合保留一份轨迹数据到答辩前整理成四宫格对比图训练前、训练中、训练后的追捕效果以及逃亡者的最佳逃脱轨迹。如果能把这些可视化数据放进项目的README里整个项目的完成度会瞬间上一个档次。老师看代码看半天可能看不出水平但一张训练曲线加一张捕获过程动画比什么都直观。6. 写在最后的一点经验我做这个项目时最大的体会是多智能体追逃博弈项目的难点不在算法本身而在环境设计的每个细节。一个奖励函数的系数差0.5训练结果可能天差地别。如果你时间有限我建议把精力集中三件事上先把环境状态转移逻辑做扎实确保每一步的观测、奖励、终止判断完全可解释再把奖励写简不要贪心堆一堆参数先让单智能体能稳定追到目标最后再上多智能体协调机制。如果你打算继续扩展可以考虑给环境加障碍物、让逃避者使用预训练的对抗策略或者引入通信机制这些方向都能让项目从“课程作业”变成“研究平台”。希望这篇拆解能帮你的项目少走几步弯路。本文还有配套的精品资源点击获取
网站建设高端定制企业官网