☰
MADDPG编队控制实战:多智能体强化学习从建模到调参
2026/10/11 21:55:31 网站建设 项目流程

简介:一套基于MADDPG的多智能体编队控制学习工程包,面向深度强化学习初学者和无人机、自动驾驶领域的编队控制研究者,旨在解决多智能体在动态环境中形成并保持特定队形、避碰及协同决策等问题。压缩包共18个文件,以Python源码为主(13个py文件),涵盖MADDPG算法主体、DDPGAgent、Actor-Critic网络、经验回放缓冲区、参数配置以及训练与测试脚本,另含4个txt状态文件和1个pyc文件,包体仅11KB。这些模块清晰展示了从环境搭建、智能体策略更新到结果评估的完整流程,模块间分工明确,便于直接运行和二次开发;调整params.py中的学习率、探索参数或控制器逻辑,即可验证不同场景下的编队效果。目前已有1787人学习下载。通过该系统化代码,读者能够深入理解MADDPG的多智能体协作机制与深度强化学习的落地实现,也可作为课程设计、毕业设计或相关科研工作的有力参照。

1. 编队控制为什么押注 MADDPG:一个能自己协商的决策框架

编队控制在机器人集群、无人车编组和无人机协同里一直是个硬需求,传统做法要么依赖集中式规划器做全局路径分配,要么靠人工势场这类反应式算法维持队形。前者在智能体数量增加时计算量爆炸,后者在动态障碍物和通信延迟面前经常翻车。深度强化学习里的 MADDPG(Multi-Agent Deep Deterministic Policy Gradient)能解决这个问题:它把每个编队成员当成独立决策的智能体,通过集中训练、分布执行的框架,让所有成员在只拿到局部观测的情况下学出协同编队策略。这篇笔记面向想用 MADDPG 做编队控制的开发者,从环境建模、算法改造到训练调参,给出可以直接落地的方案和参数。

2. 把编队问题翻译成 MARL 建模:状态空间、动作空间与奖励塑形

2.1 为什么编队控制适合用 MADDPG 而不是单智能体 PPO

编队控制的本质是多智能体协同问题,队形保持要求每个成员不仅知道自己该去哪,还要知道队友在哪、相对位置关系如何。单智能体强化学习把其他成员当作环境的一部分来处理,这在智能体数量固定、交互规律简单的场景下勉强可用,但一旦编队规模变化或者成员角色互换,策略就会失效。MADDPG 采用集中式训练分布式执行(CTDE)的范式,训练时评论家网络能看到所有智能体的状态和动作,演员网络只依靠自身观测做决策,正好匹配编队控制的实际部署约束。

另一个关键点是 MADDPG 天然支持连续动作空间。编队控制里每个成员通常输出速度和转向角这类连续量,如果用 DQN 系列算法需要做动作离散化,精度和训练效率都会打折。DDPG 家族算法解决连续控制问题是成熟方案,MADDPG 又通过多智能体评论家解决了 DDPG 在竞争或合作环境下环境非平稳性的问题,这两个特性叠加让它在编队控制里成为首选。

2.2 状态空间设计的三个层次:自车状态、队友观测与目标信息

状态空间设计直接决定训练能不能收敛。我在做编队控制时把状态分成三个层次:第一层是自车运动学信息,包括位置、速度、航向角;第二层是队友的相对位置和相对速度,这部分信息是编队保持的核心输入,让每个智能体知道自己在队形中的位置;第三层是目标和环境信息,包括目标点位置、障碍物距离。

一个常见误区是把绝对坐标直接塞进状态空间。绝对坐标会让智能体过拟合到特定起点,编队起点一换就失效。正确做法是用相对坐标:自车与队形参考点的偏移量、自车与每个队友的相对位置关系。这样状态空间在任意全局位置下都有相同的分布结构,迁移性要好得多。我一般把状态维度控制在 15 到 30 之间,太多冗余特征反而让策略收敛变慢。

# 编队控制环境的单步状态构建示例 def get_state(agent_id, fleet_positions, dt): # agent_id: 当前智能体编号 # fleet_positions: 所有智能体位置列表, shape: (n_agents, 4) my_pos = fleet_positions[agent_id][:2] my_vel = fleet_positions[agent_id][2:] formation_offset = formation_pattern[agent_id] # 目标队形下的相对偏移 # 相对位置信息: 以自车为中心的坐标系表示队友位置 state = [my_pos[0], my_pos[1], my_vel[0], my_vel[1]] for j, pos in enumerate(fleet_positions): if j != agent_id: rel_pos = pos[:2] - my_pos # 只保留相对位置和相对速度, 避免绝对坐标 state.extend([rel_pos[0], rel_pos[1], pos[2]-my_vel[0], pos[3]-my_vel[1]]) # 队形参考点误差: 当前相对目标队形位置的误差 ref_pos = target_pos + formation_offset error = ref_pos - my_pos state.extend([error[0], error[1]]) return np.array(state, dtype=np.float32)

这段代码的核心逻辑是把绝对坐标换算成相对量。第 5 行到第 7 行组装自车速度信息,第 9 行到第 14 行遍历所有队友计算相对位置和相对速度,第 16 行加入队形误差。注意最后一项 error 是相对目标编队位置的偏移,这个信号是奖励函数的直接对应项,有很强的引导作用。在实现时要把 state 统一成 numpy float32 格式,避免后续网络输入类型不匹配的问题。

2.3 动作空间设计:连续控制下的输出限幅与安全约束

编队控制的动作输出一般是两个维度:线速度和角速度。这里有个容易翻车的点:如果直接让策略网络输出原始控制量,训练初期可能会出现疯狂震荡的输出现象,因为网络在探索阶段不知道什么动作是安全的。我的做法是在环境侧做动作限幅,把网络输出映射到物理可行的区间。比如线速度限制在 -1.0 到 2.0 m/s,角速度限制在 -1.5 到 1.5 rad/s,超出范围的输出直接裁剪。

更稳妥的做法是把动作输出经过一个平滑滤波器,避免相邻时间步的剧烈变化。实际部署时,电机或舵机都有响应频率上限,如果相邻控制指令跳变太大,执行器会跟不上,表现为编队抖动甚至发散。我通常在环境里加一阶惯性环节对动作做平滑处理,虽然会引入一点延迟,但稳定性大幅提升。

动作空间还有一层安全约束要考虑:智能体之间的碰撞避免。最简单的方式是在环境物理引擎层面做硬约束,两个智能体距离小于安全半径时直接报警并给予惩罚。也有更软的做法,在动作输出后叠加一个斥力场修正量,让智能体自动远离队友。不过我不推荐在环境里做太多软修正,因为会掩盖策略本身的缺陷,训练出来的策略离开这个修正就失效了。

2.4 奖励塑形:编队误差、到达奖励与碰撞惩罚的权重博弈

奖励函数是编队控制训练的另一个胜负手。一个完整的编队奖励通常包含三部分:编队保持奖励、目标到达奖励和碰撞惩罚。编队保持奖励一般设计为队形误差的负函数,误差越小奖励越高,常见形式是-alpha * formation_error,其中 formation_error 是当前队形和期望队形的位置偏差总和。目标到达奖励采用稀疏奖励,智能体进入目标点一定范围内给正向奖励,这个部分引导智能体最终完成编队移动任务。

权重分配是实际调试中最玄学的部分。我一开始给碰撞惩罚设了很大的负值,结果智能体全部停在原地不动,因为任何移动都可能撞到队友。后来把碰撞惩罚从 -10 降到 -2,让智能体在避开碰撞的前提下探索移动,策略才慢慢学出来。实践经验是:碰撞惩罚的绝对值不能超过到达奖励的 2 到 3 倍,否则智能体会过度保守。编队保持奖励的系数也要控制好,太大会让智能体只顾保持队形而忽略目标移动,训练后期队形成了但编队整体不动。

# 编队奖励计算示例: 三项奖励加权求和 def get_reward(agent_id, fleet_pos, target_pos, action): my_pos = fleet_pos[agent_id][:2] dist = np.linalg.norm(my_pos - target_pos) # 第一项: 目标到达奖励(稀疏) arrive_reward = 10.0 if dist < 0.5 else 0.0 # 第二项: 编队保持奖励(稠密, 用队形误差) formation_error = 0 for j, pos in enumerate(fleet_pos): desired_pos = target_pos + formation_pattern[j] formation_error += np.linalg.norm(pos[:2] - desired_pos) formation_reward = -0.1 * formation_error # 第三项: 碰撞惩罚 collide_penalty = 0.0 for j, pos in enumerate(fleet_pos): if j != agent_id: d = np.linalg.norm(my_pos - pos[:2]) if d < 0.3: collide_penalty = -2.0 break reward = arrive_reward + formation_reward + collide_penalty return reward

这个奖励函数体现了编队控制任务的两个核心矛盾。第一是稠密奖励和稀疏奖励的配合,目标到达給 10 分但只有到达才有,编队误差给连续的小负值,智能体在探索过程中始终有即时信号可以学习。第二是权重博弈:碰撞惩罚 -2.0 与到达奖励 10.0 的比值控制在五倍以内,让智能体愿意冒险移动而不是全军蜷缩。实际调参时可以把这三项奖励分别打印出来看曲线,判断是哪一项在主导策略的行为。

3. FCMADDPG 的算法核心:集中式评论家、门控融合与目标策略平滑

3.1 从 DDPG 到 MADDPG:集中训练分布式执行的原理拆解

MADDPG 的核心思想是在 DDPG 的基础上做了两个关键改动。第一个改动是评论家网络的输入从单个智能体的状态动作扩展为所有智能体的状态动作拼接,这样评论家在训练时可以感知全局信息,对动作价值的评估更准确。第二个改动是每个智能体拥有独立的演员和评论家网络,评论家只服务于对应演员的训练,不像一些共享参数的方法会引入干扰。

环境非平稳性是多智能体强化学习面临的核心难题。在单智能体 DDPG 里,环境转移函数是固定的,经验回放没有问题。但在多智能体环境里,每个智能体的策略都在变化,对其他智能体来说环境转移动态不稳定,直接套用单智能体经验回放会失败。MADDPG 通过让评论家看到所有智能体的动作来缓解这个问题:当某个智能体更新策略时,它能意识到其他智能体的动作改变,从而把环境的非平稳性对价值估计的影响降低。这就是为什么 FCMADDPG 这类变体都保留了集中式评论家的结构——它是整个算法有效性的根基。

3.2 FCMADDPG 的改动点:全连接网络、特征融合与目标网络机制

FCMADDPG 里的 FC 通常指 Full Connected,即所有智能体的状态特征在评论家网络中融合的方式。常见的做法是保留 MADDPG 的多智能体评论家结构,但在特征融合层使用全连接方式处理全局状态拼接后的高维输入。这个改动直接影响价值函数的拟合精度:编队控制任务里智能体数量通常在三到五个,全局状态拼接后的维度在 60 到 150 之间,全连接层能有效捕捉特征间的交叉关系。

训练稳定方面,FCMADDPG 继承了目标网络机制。演员和评论家各维护一套目标网络,通过软更新方式缓慢向在线网络靠近。参数 tau 控制更新速度,我一般取 0.01,这个值的核心作用是防止训练震荡。回放缓冲区也是稳定性的关键一环,编队控制经验数据需要较大的缓冲区容量来保证样本多样性,我设置容量在 100 万条以上,采样批量大小取 256。

3.3 评论家网络的输入拼接:顺序一致性与归一化处理

评论家网络的输入拼接有一个容易踩的坑:智能体顺序必须固定。训练时每次采样的经验数据里,全局状态拼接顺序错了,网络就会学到错误的依赖关系。这个顺序一般在环境初始化时确定,训练全程保持不变。我在实现里专门写了一个断言检查拼接顺序,防止数据预处理环节出问题。

另一个关键点是归一化处理。编队控制里不同特征量纲差异很大:位置坐标可能是几十甚至上百的数值,相对速度只有个位数,目标误差又可能是小数。直接把原始值输入网络会导致梯度被大数值特征主导。我的做法是在环境层面对所有状态做标准化处理,用在线均值方差统计做归一化,而不是固定的 minmax 缩放。在线归一化能适应训练过程中状态分布的变化,经验回放里的数据分布会随着策略提升而改变,固定归一化参数会导致后期训练不稳定。

# 评论家网络输入拼接: 所有智能体状态与动作的统一编排 def build_critic_input(states, actions): # states: list of state arrays, 每个元素是一个智能体的状态 # actions: list of action arrays, 每个元素是一个智能体的动作 assert len(states) == len(actions) state_flat = np.concatenate(states).astype(np.float32) action_flat = np.concatenate(actions).astype(np.float32) # 归一化: 状态和动作分别做标准归一化 state_norm = (state_flat - state_mean) / (state_std + 1e-8) action_norm = (action_flat - action_mean) / (action_std + 1e-8) obs_full = np.concatenate([state_norm, action_norm]) return obs_full

这个拼接函数的核心是维度对齐和归一化。第 5 行的断言确保智能体数量一致,第 7 到 8 行分别把状态和动作展平,第 10 到 11 行做标准化。注意归一化的均值方差参数需要预计算或在线更新,没有经过归一化的原始输入会让评论家网络的价值估计方差特别大,直接影响策略更新的梯度方向。实际训练时我发现,不做归一化的评论家 loss 在训练早期会高出几个数量级。

3.4 演员网络输出层的激活函数选择:双曲正切与动作缩放

演员网络输出层的激活函数决定动作分布的范围。我一般用 tanh 作为输出层激活函数,它把输出压缩到 [-1, 1] 区间,再乘上一个缩放系数就得到实际控制量。tanh 的好处是两端饱和,限制了动作的极端值,训练前期不会出现疯狂输出。

这里有个细节值得注意:tanh 在接近饱和区域时梯度很小,策略更新效率低。如果训练中发现智能体的动作经常落在最大最小值附近,说明缩放系数设得不合理。常见做法是先不加缩放训练一段时间,观察动作的实际分布,再根据分布区间设置缩放系数。我见过有人在输出层直接接线性激活函数,输出范围完全不受控,训练到后期全部散开,编队飞得乱七八糟。

4. 训练一个可用的编队策略:环境搭建、核心超参与收敛性判断

4.1 环境层面的必要条件:多智能体动作同步与终止条件

编队环境的动作同步机制经常被忽视。真实编队中每个智能体的决策频率应当一致,训练环境里的动作执行要保证同步更新:所有智能体同时决定动作,然后同时推进环境状态。如果改成异步更新,不同智能体看到的转移数据时间基准不一致,评论家网络的全局信息就错位了,训练必然出问题。

终止条件的设置也有讲究。一段训练回合应该在什么情况下结束?我的做法是三个终止条件取其一:所有智能体都到达目标点、超过最大时间步、发生碰撞。碰撞检测在训练中承担重要的数据清洗作用,发生碰撞的回合直接终止,避免无效的探索数据污染回放缓冲区。最大时间步我在编队移动任务里设为 200 步,太短策略来不及学出完整路径,太长训练效率低、收尾困难。

4.2 核心超参数初始化:学习率、折扣因子、软更新系数

超参数配置是编队控制训练前期投入最大的环节。我的初始配置如下:演员学习率 1e-4,评论家学习率 1e-3,折扣因子 0.95,软更新系数 0.01,回放缓冲区容量 1e6,批量大小 256。评论家学习率比演员高一个数量级的原因在于评论家承担更重的拟合任务,需要更快的收敛速度。

折扣因子 0.95 在编队控制里比常用的 0.99 更合适。编队控制的回合长度一般不长,200 步封顶,0.95 的折扣让远期奖励衰减快,智能体更注重近期行为,策略收敛速度加快。如果用 0.99,在 200 步的回合里,最后一步的奖励对当前决策的影响仍然很大,会引入不必要的长期规划负担。我之前踩过这个坑,从 0.99 调到 0.95 后训练速度快了接近一倍。

# 训练主循环: 经验收集与网络更新 for epoch in range(max_epochs): obs = env.reset() done = False while not done: # 每个智能体通过演员网络输出动作, 同时加入探索噪声 actions = [] for agent_id in range(n_agents): action = actor_net(obs[agent_id], eval=False) + np.random.normal(0, 0.1) actions.append(action) next_obs, rewards, done = env.step(actions) # 存储五元组经验 replay_buffer.push(obs, actions, rewards, next_obs, done) obs = next_obs if len(replay_buffer) > batch_size: # 从回放缓冲区采样并更新评论家和演员网络 samples = replay_buffer.sample(batch_size) critic_loss = update_critic(samples) # 最小化 TD error actor_loss = update_actor(samples) # 最大化 critic 输出 soft_update(tau=0.01) # 目标网络软更新

训练主循环的代码体现了 MADDPG 训练的标准流程。第 4 行的探索噪声用的是高斯噪声,标准差 0.1,这个值需要随着训练推进衰减。第 10 行到第 13 行的经验存储是基础保障,第 15 行到第 17 行的更新顺序是固定的:先更新评论家,再更新演员,最后做目标网络软更新。更新顺序不能颠倒,因为演员的梯度依赖评论家的输出,评论家没更新就先更新演员相当于拿着旧标尺量新动作。

4.3 训练曲线怎么读:编队误差下降与策略崩坏的分辨

训练曲线是判断模型好坏的第一依据。编队控制任务里最值得关注的是两个指标:编队误差均值和回合平均奖励。编队误差均值应该呈现缓慢下降趋势,如果在训练早期出现剧烈波动,大概率是探索噪声设太大或奖励权重不合适。

策略崩坏的显著表现是训练曲线先涨后跌。我在一次实验里看到编队误差降到了 1.0 以下,然后断崖式跳到 5.0 以上,再也没恢复。排查发现是回放缓冲区里积累了太多成功经验,网络开始过拟合到少部分高回报样本,加上评论家网络对全局信息的价值估计出现偏差,策略瞬间走向反面。解决方法是增大回放缓冲区容量,并降低评论家学习率。

训练收敛的判断标准不是曲线平滑,而是编队误差是否低于预设的阈值,以及这个状态能否稳定维持多个回合。我一般把验证间隔设为 20 个 epoch,在验证阶段关掉探索噪声,用纯贪婪策略跑 50 个回合取平均编队误差。

5. 避坑指南:编队控制训练里最常见的五个翻车现场

5.1 现象一:训练开始后编队误差居高不下,下降到某个值就停滞不前

这个现象的典型表现是编队误差曲线在某个平台上徘徊,无论怎么加大训练步数都不下降。原因通常是状态空间里缺少关键信息。编队控制里最常见的缺失信息是目标点与队形参考点的相对关系,如果智能体只知道队友位置而不知道当前队形相对目标队的偏差,它无法判断应该往哪个方向调整。

解决方法是检查状态空间是否包含完整的误差信号:自车相对目标队形位置的偏移量、自车相对队友的位置差。我在 2.2 节强调了相对坐标的重要性,这里再补充一点:如果状态变量里混入了与编队无关的噪声特征,也会导致平台期。可以通过在环境里随机丢弃状态特征做对比实验来定位干扰特征。

5.2 现象二:训练中期策略突然崩坏,奖励曲线断崖式下跌

策略崩坏是 MADDPG 系列算法最常见的问题,我在 4.3 节提到过一个案例。深层原因在于评论家网络对全局状态的价值估计产生偏差,导致演员网络被错误的梯度方向引导。复现这个问题的典型条件是:回放缓冲区容量小、评论家学习率设置过高。

解决时先缩小评论家学习率,从 1e-3 降到 3e-4,同时把回放缓冲区容量扩充一倍。如果崩坏仍然发生,检查目标网络软更新系数,把 tau 从 0.01 降到 0.005,降低目标网络漂移速度。我用的一个排查口诀是:崩坏时先调评论家,再调回放,最后调 tau——这三个参数按顺序调整,不要同时改。

5.3 现象三:训练结束后只有部分智能体能到达目标点,编队无法整体移动

这个现象非常诡异:编队里的某个智能体表现出色,总能到达目标点,但其他智能体原地打转或者尾随不了。产生这个问题的原因通常是智能体之间的奖励分配不均,或者初始状态不对称导致优势积累。最初几个回合里跑得快的智能体获得了更多到达奖励,评论家对它给出的价值估计更高,策略更新进一步强化了这种行为,形成马太效应。

解决思路是在奖励设计里加入编队整体性指标,比如队形中心点的移动进度作为共享奖励加给所有智能体,让每个智能体都能从编队整体前进中获益。同时检查初始状态的随机化程度,如果初始位置太固定,智能体会过拟合到特定初始队形,换个起点就失效。

5.4 现象四:策略在训练环境里表现良好,换一个起点或队形就完全失效

这个问题指向泛化性不足,根源在于训练环境的随机性不够。编队控制里常见的随机性来源有三个:初始位置随机偏移、目标点位置随机分布、队形参考点的旋转角度随机变化。如果这三个随机性都没加或者加得不够,策略只能学会在固定条件下工作。

我的做法是在环境初始化阶段加入均匀分布的随机扰动:每个智能体的初始位置在基准点附近偏移正负 1 米,目标点在 5 米半径内随机选取,队形每次训练回合随机旋转一个角度。投入产出比最高的还是队形旋转,因为编队控制策略必须对任意朝向都有响应能力。

5.5 现象五:训练过程非常不稳定,loss 曲线充满毛刺

loss 曲线毛刺多、训练过程抖动剧烈,通常是经验回放数据分布不均匀导致的。编队控制任务里的数据天然不平衡:早期多数是碰撞和乱走的数据,后期才是高质量编队轨迹。如果回放缓冲区没有做优先级采样,所有经验被等概率采样,早期低质量样本会持续干扰后期策略提升。

解决方法是改用优先经验回放,或者在缓冲区里对成功回合的数据做额外标记、提高采样权重。更简单的替代方案是加大缓冲区容量,让高质量经验在缓冲区里占更大比例。我在项目里用的是容量 2e6 的缓冲区加上每 5000 步清理一次低质量样本的机制,效果稳定。

6. 用收敛后的模型做编队验证:三个维度与一组实用参数

模型训练收敛只是第一步,能不能在真实场景里用起来才是编队控制项目的最终检验。我习惯从行为、鲁棒性和安全三个方面做验证。

行为层面,关掉探索噪声,让编队跑 100 个随机初始化的回合,统计编队误差的均值和标准差,误差均值低于 0.5 米、标准差小于 0.3 说明编队保持效果可用。鲁棒性层面,在环境里注入通信延迟和传感器噪声,观察编队误差是否出现发散,正常的策略应该能容忍 100 毫秒的通信延迟。安全层面,在目标点附近布置静态障碍物,强制策略走避障路径而不是直接穿越编队前往目标点,看会不会发生碰撞。

我在编队控制项目里最常用的一组验证参数是:随机初始位置偏移正负 1.5 米,目标点距离 8 到 15 米随机,队形旋转角度 0 到 360 度随机,障碍物数量 2 到 4 个。这组参数下的验证结果比固定场景更有说服力,能帮助判断策略是否真的学到了编队协同逻辑而非死记硬背特定路径。

要提醒的是,仿真里验证通过的策略迁移到真机时,最大的坑在于状态观测的真实噪声水平通常远高于仿真设定。如果仿真里传感器噪声标准差设 0.05,真机可能是 0.2,策略很可能直接失效。这个方向目前没有后悔药,唯一的办法是在仿真里把噪声和通信延迟拉到比预期真机值更严苛的水平,给安全余量留足。

编队控制在 MADDPG 框架下已经能有不错的表现,但距离完全可靠还有距离。我自己的教训是:不要把大量精力花在调网络结构上,编队控制在状态空间设计和奖励塑形上的改进空间远大于网络结构的微调,把这两块做扎实,比换任何花哨变体都管用。希望这些经验能帮你在编队控制的学习和部署上少走弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询