ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

多智能体强化学习环境仓库解析:从接口设计到训练接入

2026/9/11 23:36:22 拓冰建站 浏览量
多智能体强化学习环境仓库解析:从接口设计到训练接入 简介一份专为多智能体强化学习MARL设计的环境工具包面向强化学习研究者、算法工程师与学生用于在统一平台中开发、训练和对比多智能体协作与竞争策略。环境覆盖灭火、找宝藏、抓猪、足球、移动箱子、无人机、清洁、仓库等典型任务场景支持多个智能体并行学习并通过环境反馈与其他智能体行为共同演化策略。压缩包共包含71个文件其中36个Python源码文件提供各场景的环境实现与测试入口13个PDF文档说明任务设计与算法原理13个GIF演示直观展示运行效果另有若干PNG图片与依赖库文件整体体积仅3.65MB轻量易部署。目前已有671人学习使用。通过阅读源码和实验文档读者可快速理解MARL环境构建逻辑并基于这些场景开展课程实验、算法对比或论文复现各模块均提供独立环境与测试脚本便于按需扩展是入门与进阶多智能体强化学习的实用参考。1. 为什么我会把这个 multi-agent 强化学习环境仓库留下来做多智能体强化学习时最让人头疼的往往不是算法本身的公式而是一组能让多个智能体稳定交互的环境。论文里描述的场景要么交互复杂要么没有开源实现自己从头写又要处理地图渲染、回合重置、奖励平衡一堆琐事。这个仓库把 13 个多智能体场景打包在一起每个场景都有 env_xxx.py 和配套的 test_xxx.py还有对应的 PDF 设计说明和运行演示 GIF覆盖了救火、寻宝、抓猪、足球、无人机避让、仓库调度、救援等典型任务。对想快速验证 DQN、PPO 或 Actor-Critic 类算法在 multi-agent 环境上效果的研究者和工程师来说这是一个直接可用的多智能体强化学习环境不需要再重复造地图轮子。仓库里每个环境都保持相对独立你既可以把整个项目当作算法评测基准也可以只挑其中一个环境做深入改造。比如想研究协作机制就直接打开 env_Cleaner.py 和 env_FireFighter.py想对比竞争场景就去看 env_Soccer.py 和 env_OppositeV2.py。下面我会从环境接口设计、场景分类、训练脚本接入三个层面拆解这个资源最后一个部分给出自定义环境和验证环境的完整思路。2. 环境接口设计从 env_xxx.py 看懂 state-action 与 reward 规范2.1 reset/step 是多智能体环境的统一入口打开任意一个 env_xxx.py都会发现它不是严格意义上的 OpenAI Gym Environment而是一组更松散的 Python 类。拿 env_Cleaner.py 来说类里通常包含 init(world_size, obstacles, n_agents) 这类构造参数以及 reset() 和 step(actions)。与单智能体环境不同step 里的 actions 是一个列表长度等于智能体数量每个智能体对应一个动作编号。reset 返回的是所有智能体的初始位置和一个地图状态。我一般拿到新环境的第一件事是打开同目录的 test_xxx.py看它怎么调用环境。几乎所有 test 脚本都遵循同一个模板先实例化环境再循环 reset、step最后把渲染结果打印或保存。下面是一个根据仓库中常见 test_FireFighter.py 风格整理的调用框架# test_FireFighter.py 的简化调用结构 import numpy as np from env_FireFighter import FireFighterEnv # 具体类名以文件为准 env FireFighterEnv(size7, fires3, agents2) obs env.reset() # obs 里通常包含各智能体位置、火点位置、时间步 for step in range(100): actions [env.action_space_sample() for _ in range(env.n_agents)] next_obs, rewards, done, info env.step(actions) env.render() # 有的环境用字符画有的用 matplotlib 画网格 if done: break这段代码说明三件事环境的动作必须一次性传入所有智能体的决策step 返回的 rewards 也是一个列表与智能体一一对应done 表示整个回合是否结束而不是单个智能体是否完成。参数 size、fires、agents 是这个仓库环境常见的构造参数实际名字以各环境的 PDF 为准。了解这些之后接算法时只需要把actions换成策略网络的输出即可。2.2 状态空间与动作空间在代码里怎么表示这个仓库的环境基本都是网格世界动作空间通常是离散的 4 个方向上、下、左、右少数环境会有“停留”或“推箱子”等额外动作。以 env_MoveBox.py 为例智能体不仅可以选择移动方向还可能有一种“推动”动作这会让动作空间变成 5 或 6 维。而 env_OppositeV2.py 这类对抗环境动作空间也会保持对称保证两个智能体有相同的决策自由度。状态空间则分为两类一类是相对位置例如 env_GoTogether 中两个智能体需要汇合观测可能直接给两者的相对坐标另一类是完整地图例如 env_Drones 和 env_Warehouse 会把整张栅格地图拍平成向量网络输入维度等于grid_width * grid_height * channelschannels 对应障碍物、智能体、目标点等图层。下表是我根据仓库内 PDF 说明整理的常见动作定义习惯动作编号含义典型环境0向上移动所有网格环境1向下移动所有网格环境2向左移动所有网格环境3向右移动所有网格环境4保持不动/抓取CatchPigs、Soccer5推动箱子MoveBox这个表只是通用约定具体到每个环境我建议直接打印env.action_space或阅读对应 PDF因为仓库中部分环境允许地形交互未必完全遵循这张表。比如 Soccer 里的动作可能还包含“射门”方向不能想当然地当成四方向导航。2.3 reward 与 done 的设计差异以 FireFighter 和 Cleaner 为例多智能体环境里 reward 设计决定了协作还是竞争。FireFighter 的典型设定是 n 个消防员扑灭 n 处火点每个时间步所有智能体共享一个整体奖励比如每扑灭一处火给 10 分步进惩罚为 -0.1。这样设计会让两个智能体天然倾向于分工因为火点被任意一个扑灭全队都受益。对应到 Q 学习里每个智能体看到的reward是相同的但各自状态不同所以要小心多智能体 credit assignment 问题——当全场只有总奖励时某个智能体很难知道自己这一步对团队成功贡献了多少。Cleaner 环境则不太一样。它的目录下还有 maze.py 和 disjointSet.pymaze 负责生成连通迷宫disjointSet 用于判断清扫区域是否被完全覆盖。这个环境的 reward 往往按被清扫格子的数量计算每个格子第一次被扫到才有正奖励重复清扫只有很小的负奖励。此时多个清洁员如果共享同一张地图相互之间是“隐性竞争”关系——谁先扫到某格谁拿到奖励。我在调试这种环境时会先把累计 reward 打出来如果总奖励一直不变多半是 done 条件或者 map 存储更新出了问题。注意判断一个环境是协作还是竞争不能只看名字要去看 step 函数里 reward 是怎么求和或分配的。同类任务改一行 reward 就能让性质反转。2.4 一个通用的交互循环模板为了把任意一个 env_xxx.py 接到强化学习算法上我会写一个独立的 runner而不是直接改环境源码。下面这个模板兼容前面提到的所有环境它假设环境有n_agents属性动作按批次传入def rollout_episode(env, policy, max_steps200): obs env.reset() total_rewards [0.0] * env.n_agents for _ in range(max_steps): actions policy.select_actions(obs) # 返回长度等于 n_agents 的离散动作 obs, rewards, done, info env.step(actions) for i, r in enumerate(rewards): total_rewards[i] r if done: break return total_rewards, info其中policy.select_actions是通用接口可以替换成随机策略、DQN 的 epsilon-greedy 或 PPO 的 actor 网络输出。注意 rewards 是以 list 返回的不能直接当标量累加。这个模板解决了我接入新环境时 90% 的重复代码问题剩下 10% 是不同环境对 done 和 info 定义不同需要在测试脚本里专门对齐。3. 场景分类解析协作、竞争与混合任务怎么在多智能体环境里建模3.1 协作型场景CatchPigs、GoTogether 和 RescueCatchPigs 字面意思是“抓猪”从仓库里的 gif 看两个智能体需要把猪赶到某个区域。这种任务必须协作因为单边驱赶很难把猪逼到拐角。实现层面猪的位置一般也维护在地图数组里智能体走到猪相邻格时会触发“惊吓”机制让猪往反方向跑。因此 reward 不应该只按“是否抓住”给通常会加“接近猪”的中间奖励避免智能体学成互相乱转。在代码里你会在 step 函数中看到对猪位置的条件判断这里容易出的 bug 是猪被多个智能体同时惊吓时移动方向冲突我一般会用方向叠加后再归一化的做法处理。GoTogether 则是一个汇合任务两个智能体从不同起点出发目标是同时站到同一格。这个环境的经典陷阱是提前汇合所以 done 条件往往是“两者同格且时间步大于某个阈值”而不是第一次同格就停止。Reward 设计上可以给“距离缩小”的稠密奖励也可以只给最终成功 1、不成功 0 的稀疏奖励用来对比不同算法对 reward 密度的敏感度。如果训练时发现两个智能体一开始就往对方起点冲说明它们没有学到“等一等再汇合”这个时序约束此时检查 done 条件是否过于宽松。Rescue 环境在仓库里同时存在 Python2 和 Python3 两套代码说明它经历过跨版本迁移。救援场景一般是有若干被困者分布在危险区域智能体把它们搬运到安全区。这个任务的状态空间相对较大因为要同时表征被困者位置、智能体位置和危险区扩散范围。如果在旧版 Python2 代码上训练建议先迁移到 Python3重点检查xrange、字典迭代和 matplotlib 渲染接口的变化。我在跑这个环境时会先固定随机种子确保两次 reset 的地图一致否则后续调参时很难判断算法进步来自策略优化还是地图难度波动。3.2 竞争型场景Soccer 和 Opposite 的对抗关系Soccer 是典型的零和竞争。仓库内附了 redbot.png 和 bluebot.png 两张机器人图标说明它用 pygame 或 matplotlib 做可视化较多。足球比赛里一个智能体控球时另一个要抢断reward 本质上是对称的进球方 1失球方 -1。如果两个智能体使用同一套参数初始化很容易出现“互相抵消”的学习信号我一般会在训练时引入自博弈或者给双方使用不同的探索率。比如红色方使用 epsilon0.1蓝色方使用 epsilon0.3这样至少一方能产生较丰富的经验避免两个策略同时停滞。Opposite 从名字就能看出是“相对”任务两个智能体很可能被放置在对称位置执行相反的导航路径。这种环境最适合测试竞争型 multi-agent 算法的稳定性。它的难点在于单纯扩大经验回放池会混入大量对手不同策略下的数据导致策略震荡。常见做法是使用 league 式的多对手采样但我手头实验时发现先用固定规则对手做预训练再切换到自博弈收敛会快得多。另外注意读取 env_OppositeV2.py 时V2 可能已经修正了 V1 的奖励对称性问题如果你的实验对公平性敏感优先用新版。3.3 单智能体与多智能体的边界SingleCatchPigs 和 MoveBox仓库里还有 SingleCatchPigs与 CatchPigs 形成对比。Single 版本只有一个智能体去抓猪猪的行为如果是固定规则那它本质上是一个单智能体问题完全可以用 DQN 直接解决不需要 MARL 算法。但这里把它也收进来好处是可以直接对比“单智能体训练”和“双智能体训练”在同一场景下的表现差异。我在课程设计中经常让学生先跑 SingleCatchPigs再切换到 CatchPigs观察协作带来的收益和训练难度的上升。这种对比实验只需要修改环境参数不需要改动算法代码非常适合写进实验报告。MoveBox 则处在单/多智能体的模糊地带如果只有一个箱子且只有一个智能体推那它是单智能体但仓库中 gif 显示 MoveBox 似乎存在两个智能体合作把箱子推向目标。这种“可调智能体数量”的环境很适合做 scalability 实验——同样一个算法智能体数量从 1 变到 3学习曲线会明显变差这本身就是一个值得写进报告里的结论。需要注意的是MoveBox 的观测里通常包含箱子的相对坐标如果多个智能体同时推箱子步长和碰撞检测都会影响训练稳定性跑之前先单步调试确认奖励方向一致。3.4 环境横向对比与选型建议下面这张表汇总了我对主要环境的理解方便根据实验目的快速选择环境环境智能体数任务性质适合验证的算法特性FireFighter2~3协作公共 reward 下的 credit assignmentGoTogether2协作稀疏 reward 下的延时汇合Rescue2~4协作动态障碍与多目标调度CatchPigs2协作多智能体围捕策略SingleCatchPigs1单智能体作为 MARL 的基线对比Soccer2竞争自博弈与对手建模Opposite2竞争对称策略与算法稳定性MoveBox1~2混合智能体数量扩展性Drones3混合碰撞避免与路径规划Warehouse2~5协作多智能体资源分配Cleaner2混合公共 vs 个体 reward 对比需要注意我并不建议直接把表格里的特点当作定论因为仓库里很多环境同时支持修改参数例如 Cleaner 可以通过改变 reward 模式切换协作和竞争选型时必须结合测试脚本里的默认参数。选环境时优先看 PDF 说明里的重点章节比如 Cleaner.pdf 会写明迷宫生成算法这比读源码更快。4. 训练脚本的接入与调试把 test_xxx.py 接到 DQN/PPO 上4.1 先跑通测试脚本拿到压缩包后进入 master 目录对每个环境直接跑一条命令就行。比如cd Multi-Agent-Reinforcement-Learning-Environment-master python test_FireFighter.py注意环境依赖。仓库里大量使用 numpy、matplotlib少数用到 pygame。如果提示 ModuleNotFoundError: No module named pygame用 pip install pygame 安装即可。运行 test_Soccer.py 前要保证当前目录下有 redbot.png、bluebot.png 这两张图否则渲染函数会报 IOError。同样其他 test 脚本也会隐式依赖当前目录所以不要从别的目录运行脚本尽量cd到对应环境目录。跑通之后观察输出图像是否符合预期比如 FireFighter 里火点是否被正确渲染、智能体是否能在有限步内完成任务。4.2 把环境接到 DQN / PPO 算法上由于环境不是 gym 标准接口接入算法时我会写一个 adapter。以 PPO 为例用 gym 一样的格式封装 env# make_env.py 适配器把 test 脚本里用到的环境包装成 gym-like 接口 import gym from gym import spaces import numpy as np class MARLWrapper(gym.Env): def __init__(self, env, n_agents): super().__init__() self.env env self.n_agents n_agents # 假设所有智能体观测量是固定长度向量动作为 0~4 的离散值 self.observation_space spaces.Box(low0, high1, shape(128,), dtypenp.float32) self.action_space spaces.Discrete(5) def reset(self): obs self.env.reset() return np.asarray(obs, dtypenp.float32).flatten()[None, :] def step(self, actions): # actions 可以是 shape(n_agents,) 的整数数组 obs, rewards, done, info self.env.step(actions.tolist()) obs_flat np.asarray(obs, dtypenp.float32).flatten()[None, :] return obs_flat, np.mean(rewards), done, info这段代码有几个值得注意的地方多智能体环境的 obs 通常是 n_agents 个独立观测这里简单拼接后当作单个 obs 使用reward 用np.mean(rewards)做平均这只适用于公共 reward 的协作任务竞争环境下不能这么处理否则两个智能体的 reward 直接抵消训练不出来。观测空间固定为 128 维只是一个示例实际要以环境里的状态向量长度为准否则 DQN 网络输入层维度会错。如果用 stable-baselines3只需要把上面的 wrapper 传给PPO(MlpPolicy, env)即可。但要记住 SB3 默认只处理单智能体MARL 场景需要自己维护多个策略副本或者把多智能体问题转成 centralized training这部分超出环境本身的范畴需要另外设计。我一般在调试阶段用这个 wrapper 确认算法能跑真正做实验时再换成独立的 multi-agent rollout 模块。4.3 多智能体 rollout 中的数据格式在写 collect rollout 代码时我一般会保存每个时间步的完整 transition所有智能体的 obs、actions、rewards、next_obs、done。使用 numpy 数组一次性存储而不是 Python list否则在 1e5 步规模上内存会打爆。def collect_trajectory(env, policy, max_steps1000): obs env.reset() obs_dim 128 # 以实际环境为准 trajectory { obs: np.zeros((max_steps, env.n_agents, obs_dim), dtypenp.float32), actions: np.zeros((max_steps, env.n_agents), dtypenp.int32), rewards: np.zeros((max_steps, env.n_agents), dtypenp.float32), } for t in range(max_steps): actions policy(obs) next_obs, rewards, done, _ env.step(actions) trajectory[obs][t] obs trajectory[actions][t] actions trajectory[rewards][t] rewards obs next_obs if done: break return trajectory这个结构可以直接用于后续 computing returns 或 advantage。注意done是全局回合结束不是每个 agent 自身的 done所以存储时不需要按 agent 拆分 done只用判断是否退出循环即可。在多智能体 PPO 中计算 advantage 时要小心如果多个 agent 各自估算 value价值函数的输入是全局 obs 拼接或共享状态不能独立地用每个 agent 的 reward 去算否则会忽略队友影响。4.4 常见运行问题和排查方法结合我跑这些环境时遇到的坑列几个常见问题屏幕一闪而过很多 test 脚本是即时渲染循环没有 sleep加上 matplotlib 的 interactive mode 未开启。改成plt.pause(0.05)即可。Python2 代码Rescue 环境有 Python2 目录在 Python3 下会报print语法错误使用 2to3 工具转换后再跑。渲染模式报错部分环境在无显示器的服务器上调用matplotlib.pyplot会报_tkinter.TclError在脚本头部添加import matplotlib; matplotlib.use(Agg)可以解决。步数过多不结束检查环境没有设计最大步数测试脚本里的for step in range(100)是唯一限制算法训练时需要自己按 max_steps 截断。奖励维度不匹配如果 rewards 返回的是标量而代码按 list 处理会报floatobject is not subscriptable。先打印 type(rewards) 再解析。5. 扩展自己的 MARL 环境接口验证与 rollout 检查5.1 参照 Cleaner 新建一个简单环境如果想把自定义任务包装成同风格环境最简单的方式是复制 env_Cleaner.py 作为模板。Cleaner 的代码已经把地图从文件加载、智能体移动、格子清扫、reward 计算、回合终止这几件事分开了。你只需要替换地图生成逻辑和 agent 行为规则。具体来说保留 reset 和 step 两个方法在 step 里循环每个 agent 执行移动再统一更新全局状态reward 计算放在所有 agent 移动完之后而不是每移动一个就加一次这样可以保持和前文一致的“全局奖励”。自定义环境时还要定义自己的动作映射。建议沿用仓库的 0~4 四方向约定未来做横向对比时会省去很多麻烦。状态表示上不要把智能体位置直接放进二维坐标容易让网络对地图尺寸过拟合。我一般会生成一个 one-hot 图层把每个智能体、目标、障碍物分别叠在独立的 channel 上这样再接 CNN 或 MLP 都有较好的泛化性。5.2 用随机策略做环境自检在训练任何算法之前我都会先跑一个随机策略自检。脚本很简单python -c from env_Drones import DronesEnv; env DronesEnv(); obs env.reset(); [env.step([env.action_space.sample() for _ in range(env.n_agents)]) for _ in range(50)]; print(random rollout ok)如果随机策略都能稳定跑完 50 步不出异常说明环境接口层面没问题。之后再做 reward 范围检查记录 100 个回合的累计 reward确认没有 NaN、没有绝对值爆炸。若 reward 一直为 0回去检查 done 条件是否在第一步就被触发若某个 agent 的 reward 明显大于其他 agent要先确认是不是 reward 分配逻辑写错。最后把每步的 rendered frame 存成 gif仓库里那些 gif 就是用类似方法合成的这一步能直观判断任务是否可学。我还会加一个“单步断言”在 reset 之后连续调用 step 两次对比前后 obs 是否有变化。如果两次 step 返回一模一样的 obs说明环境状态没有更新多半是 actions 没有被真正应用。这种小检查在新增地图或修改移动逻辑时能快速暴露低级错误。拿到完整的轨迹后把每个 agent 的累计 reward 画成曲线如果曲线在某个步数突然下降往往不是算法问题而是环境给了大额惩罚这时候回看 PDF 里的奖励设计说明最有效。本文还有配套的精品资源点击获取