简介:本资源是一个面向人工智能与机器人方向研究者、高校师生及强化学习实践者的多无人机协同围捕仿真项目,聚焦于解决多智能体在动态环境中协同决策与目标围捕的核心挑战。项目基于MADDPG算法,在自定义Gymnasium仿真环境上训练3架无人机智能体协同围捕5个移动目标,完整覆盖环境建模、策略网络(Actor/Critic)设计、经验回放与分布式训练全流程,并通过PyTorch实现全部深度神经网络模块。压缩包共53个文件,含9个核心Python源码(如sim_env.py、maddpg.py、networks.py)、25个预训练模型权重文件(按agent编号与target/non-target区分)、CSV训练日志、README说明文档、LICENSE及附赠的Word技术说明与PNG系统示意图,整体大小为3.79MB。已有149人学习下载,资源结构清晰、模块解耦合理,提供可直接运行的训练/评估入口(main.py与main_evaluate.py),配套缓冲区管理、数学工具封装与分数历史记录等实用组件,便于复现、调试与二次开发。
1. 项目概述与核心价值
最近在复现和优化一个多无人机协同围捕的项目,核心是基于MADDPG算法,在自定义的Gymnasium环境中训练多个无人机智能体,让它们学会协同决策,高效地围捕一个动态目标。这个项目听起来很酷,但真正动手做起来,从环境搭建、算法实现到训练调参,每一步都充满了挑战。如果你也对多智能体强化学习(MARL)或者无人机集群控制感兴趣,想找一个能跑通、能理解、还能自己动手改的实战项目,那这个项目绝对值得你花时间研究。它不仅仅是一个算法实现,更是一个完整的仿真系统,涵盖了从环境交互、智能体设计到分布式训练的全流程。
为什么说这个项目有代表性?首先,多无人机协同围捕是一个典型的合作式多智能体任务,智能体之间既有竞争(都想靠近目标)又需要合作(形成包围圈),这比单智能体任务复杂得多。其次,MADDPG算法是多智能体深度强化学习领域的一个里程碑式的工作,它巧妙地解决了非平稳环境、信用分配等经典难题。最后,整个项目基于PyTorch和Gymnasium这两个目前最主流、最灵活的框架,这意味着代码结构清晰,易于扩展和移植到其他任务上。无论是为了发论文、做比赛,还是纯粹想深入理解MARL,这个项目都能给你提供一个扎实的起点和丰富的实践经验。
2. 项目整体架构与设计思路
2.1 核心问题定义:多无人机协同围捕
我们先明确要解决什么问题。想象一个二维或三维的仿真空间,里面有一个高速移动的“逃逸者”(目标),和多个由我们控制的“追捕者”(无人机)。追捕者的目标是在最短时间内,通过协同运动,对逃逸者形成合围,并将其限制在一个很小的区域内。这里的关键是“协同”。单个无人机再快,也很难单独抓住一个灵活的目-标,必须依靠团队配合,有人拦截,有人驱赶,有人封堵退路。
这个任务抽象成强化学习问题,包含几个要素:
- 环境 (Environment):我们自定义的Gymnasium环境。它定义了状态空间(所有无人机和目标的位置、速度等)、动作空间(每个无人机的控制指令,如加速度、角速度)、状态转移动力学(物理引擎模拟移动)、奖励函数(引导智能体学习围捕行为)以及终止条件(成功围捕或超时)。
- 智能体 (Agent):每个无人机对应一个智能体。它们共享相同的策略网络结构,但在训练和执行的输入不同。
- 算法 (Algorithm):我们采用MADDPG。其核心思想是“集中式训练,分布式执行”。在训练时,每个智能体的Critic网络可以观察到全局状态(所有智能体的动作和状态信息),从而能更好地评估联合动作的价值;但在执行时,每个智能体只根据自己的局部观测做出决策,这符合实际分布式系统的要求。
2.2 技术栈选型与理由
为什么选择PyTorch、Gymnasium和MADDPG这个组合?
- PyTorch:作为深度学习框架,其动态图特性非常适合强化学习这种需要频繁交互、调试的研究场景。我们可以像写普通Python代码一样构建网络,在前向传播中插入打印语句调试,非常直观。此外,PyTorch的自动求导和丰富的优化器让我们能专注于算法逻辑本身。
- Gymnasium:作为OpenAI Gym的官方分支和继承者,它提供了强化学习环境的标准接口(
reset,step,render等),拥有庞大的社区和丰富的第三方环境。自定义环境只需继承gym.Env类并实现几个关键方法,就能无缝接入各种主流算法库,兼容性极佳。 - MADDPG算法:对于连续动作空间的多智能体合作-竞争任务,MADDPG是经过大量验证的有效方法。相比传统的DDPG直接应用到多智能体场景(会因环境非平稳而失效),MADDPG通过让Critic使用额外信息(其他智能体动作)来稳定训练。相比更复杂的算法如QMIX(适用于离散动作),MADDPG在连续控制上更自然,且代码结构相对清晰,易于理解和修改。
注意:这个项目对算力有一定要求。虽然仿真环境本身不重,但MADDPG训练需要大量样本,训练周期较长。建议使用带有GPU的机器进行训练,可以显著缩短时间。CPU也能跑,但需要耐心。
2.3 项目文件结构解析
一个组织良好的项目结构是成功的一半。典型的项目目录可能如下:
multi_uav_pursuit/ ├── envs/ # 环境定义 │ ├── __init__.py │ └── pursuit_env.py # 核心:自定义围捕环境 ├── maddpg/ # 算法实现 │ ├── __init__.py │ ├── actor.py # 演员网络 (策略网络) │ ├── critic.py # 评论家网络 (价值网络) │ ├── maddpg_agent.py # 单个智能体类,包含Actor和Critic │ └── replay_buffer.py # 经验回放池 ├── configs/ # 配置文件 │ └── pursuit_config.yaml # 超参数配置:学习率、折扣因子等 ├── models/ # 保存训练好的模型 │ └── checkpoint_10000.pth ├── logs/ # 训练日志,用于TensorBoard可视化 │ └── events.out.tfevents.xxx ├── train.py # 主训练脚本 ├── eval.py # 模型评估脚本 └── requirements.txt # 项目依赖包列表这种结构将环境、算法、配置、模型分离,符合“高内聚、低耦合”的原则,方便我们单独调试环境或修改算法。
3. 核心模块深度解析与实现
3.1 自定义Gymnasium环境构建
这是项目的基石。我们需要在pursuit_env.py中创建一个继承自gym.Env的类。
3.1.1 定义观测空间和动作空间
对于连续控制,我们通常使用Box空间。
import gymnasium as gym import numpy as np class MultiUAVPursuitEnv(gym.Env): def __init__(self, num_pursuers=3, world_size=100.0): super().__init__() self.num_pursuers = num_pursuers self.world_size = world_size # 动作空间:每个追捕者的二维加速度 (ax, ay),范围[-1, 1] # 假设有3个追捕者,动作空间形状就是 (3, 2) self.action_space = gym.spaces.Box( low=-1.0, high=1.0, shape=(num_pursuers, 2), dtype=np.float32 ) # 状态空间(对Critic):包含所有追捕者和逃逸者的位置、速度。 # 例如:3个追捕者(位置xy, 速度xy) + 1个逃逸者(位置xy, 速度xy) => 状态维度 (3*4 + 1*4) = 16 state_dim = num_pursuers * 4 + 4 # 4: px, py, vx, vy self.state_space = gym.spaces.Box( low=-np.inf, high=np.inf, shape=(state_dim,), dtype=np.float32 ) # 观测空间(对Actor):每个追捕者只能看到局部信息,例如自身和逃逸者的相对位置、速度。 # 这里简化,让每个追捕者看到逃逸者的相对位置和自身速度。 obs_dim_per_agent = 4 # 2 (相对位置) + 2 (自身速度) self.observation_space = gym.spaces.Box( low=-np.inf, high=np.inf, shape=(obs_dim_per_agent,), dtype=np.float32 )这里的关键区别是state_space和observation_space。state是全局的,用于训练时的Critic网络;obs是局部的,用于每个智能体的Actor网络和执行。
3.1.2 设计奖励函数
奖励函数是指引智能体学习的“指挥棒”。设计好坏直接决定最终效果。一个有效的围捕奖励可以包含以下几部分:
- 距离奖励:鼓励追捕者靠近逃逸者。例如,
reward_distance = -alpha * distance,距离越近,惩罚越小(奖励越大)。 - 围捕成功奖励:当所有追捕者与逃逸者的距离都小于某个阈值(形成包围圈)时,给予一个大的正奖励(如+100),并结束本轮。
- 时间惩罚:每一步给予一个小的负奖励(如-0.1),鼓励快速完成任务。
- 碰撞惩罚(可选):如果追捕者之间发生碰撞,给予负奖励,避免智能体挤在一起。
- 协同奖励(进阶):可以设计奖励来鼓励分散包围,例如,奖励追捕者与逃逸者连线的角度分布更加均匀。
在step函数中,我们需要计算每个智能体的奖励。MADDPG通常需要为每个智能体提供独立的奖励。一个简单的实现可以是所有追捕者共享基于全局状态的团队奖励,也可以为每个追捕者设计包含个人贡献的奖励。
3.1.3 实现状态转移动力学
在step函数中,我们需要根据智能体的动作(加速度)更新所有实体的状态。这通常涉及简单的物理模拟:
def step(self, actions): # actions: shape (num_pursuers, 2) dt = 0.1 # 时间步长 # 1. 更新追捕者状态 for i in range(self.num_pursuers): # 根据加速度更新速度 (假设质量=1) self.pursuer_vel[i] += actions[i] * dt # 限制最大速度 speed = np.linalg.norm(self.pursuer_vel[i]) if speed > self.max_speed: self.pursuer_vel[i] = self.pursuer_vel[i] / speed * self.max_speed # 更新位置 self.pursuer_pos[i] += self.pursuer_vel[i] * dt # 边界处理(反弹或穿越) self.pursuer_pos[i] = np.clip(self.pursuer_pos[i], -self.world_size, self.world_size) # 2. 更新逃逸者状态(简单规则:远离最近的追捕者) # ... 逃逸者策略实现 ... # 3. 计算观测、状态、奖励、是否结束 obs = self._get_obs() # 获取每个智能体的局部观测 state = self._get_state() # 获取全局状态 rewards = self._get_reward() # 计算每个智能体的奖励 done = self._check_done() # 检查是否围捕成功或超时 # 4. 可选的额外信息 info = {} return obs, state, rewards, done, info_get_obs方法为每个追捕者计算其局部观测,例如[target_rel_x, target_rel_y, self_vx, self_vy]。_get_state方法将所有实体的位置和速度拼接成一个一维向量。
实操心得:物理模型的复杂度需要权衡。过于简单(如直接位置更新)可能使问题太简单,学不到真正动力学;过于复杂(如完整的无人机动力学模型)会大幅增加仿真计算量,拖慢训练。对于算法验证,一个带速度、加速度和简单阻尼的二阶积分模型通常是个不错的起点。边界处理也很重要,直接穿越边界可能会让智能体“作弊”,反弹规则更符合物理直觉。
3.2 MADDPG算法原理与PyTorch实现
MADDPG的核心是每个智能体拥有两套网络:Actor(策略)和Critic(价值),并且都有对应的目标网络用于稳定训练。
3.2.1 Actor网络与Critic网络设计
Actor网络 (
actor.py):输入是智能体的局部观测obs_i,输出是该智能体的连续动作action_i。通常是一个多层感知机(MLP),最后一层用tanh激活函数将输出限制在动作范围内(如[-1, 1])。import torch.nn as nn import torch.nn.functional as F class Actor(nn.Module): def __init__(self, obs_dim, action_dim, hidden_dim=256): super().__init__() self.fc1 = nn.Linear(obs_dim, hidden_dim) self.fc2 = nn.Linear(hidden_dim, hidden_dim) self.fc3 = nn.Linear(hidden_dim, action_dim) def forward(self, obs): x = F.relu(self.fc1(obs)) x = F.relu(self.fc2(x)) # 使用tanh将输出映射到[-1, 1] action = torch.tanh(self.fc3(x)) return actionCritic网络 (
critic.py):输入是所有智能体的动作和全局状态state,输出是对应智能体在该状态-联合动作下的Q值估计。这是MADDPG与DDPG的关键区别——Critic能看到其他智能体的动作。class Critic(nn.Module): def __init__(self, state_dim, total_action_dim, hidden_dim=256): # total_action_dim = num_agents * action_dim_per_agent super().__init__() input_dim = state_dim + total_action_dim self.fc1 = nn.Linear(input_dim, hidden_dim) self.fc2 = nn.Linear(hidden_dim, hidden_dim) self.fc3 = nn.Linear(hidden_dim, 1) # 输出单个Q值 def forward(self, state, actions): # actions: 所有智能体动作拼接起来的向量 x = torch.cat([state, actions], dim=-1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) q_value = self.fc3(x) return q_value
3.2.2 智能体类封装 (maddpg_agent.py)
每个智能体类需要管理自己的Actor、Critic以及它们对应的目标网络,并实现动作选择、网络更新等方法。
class MADDPGAgent: def __init__(self, obs_dim, state_dim, action_dim, agent_id, args): self.obs_dim = obs_dim self.state_dim = state_dim self.action_dim = action_dim self.id = agent_id # 网络 self.actor = Actor(obs_dim, action_dim).to(device) self.actor_target = Actor(obs_dim, action_dim).to(device) self.actor_optimizer = torch.optim.Adam(self.actor.parameters(), lr=args.lr_actor) # Critic的输入维度:全局状态 + 所有智能体的动作 total_action_dim = args.num_agents * action_dim self.critic = Critic(state_dim, total_action_dim).to(device) self.critic_target = Critic(state_dim, total_action_dim).to(device) self.critic_optimizer = torch.optim.Adam(self.critic.parameters(), lr=args.lr_critic) # 硬更新目标网络(或后续使用软更新) self.hard_update(self.actor_target, self.actor) self.hard_update(self.critic_target, self.critic) def select_action(self, obs, noise=None): """根据观测选择动作,训练时可添加探索噪声""" obs = torch.FloatTensor(obs).unsqueeze(0).to(device) action = self.actor(obs).cpu().data.numpy().flatten() if noise is not None: action += noise return np.clip(action, -1.0, 1.0) def update(self, agents_replay_buffer, agent_list): """核心更新函数,从回放池采样并更新网络""" # 1. 从共享的经验回放池采样一个批次的数据 obs_batch, state_batch, action_batch, reward_batch, next_obs_batch, next_state_batch, done_batch = agents_replay_buffer.sample(args.batch_size) # 2. 转换为Tensor obs_batch = torch.FloatTensor(obs_batch).to(device) # [batch, num_agents, obs_dim] state_batch = torch.FloatTensor(state_batch).to(device) # ... 其他数据转换 ... # 3. 计算Critic损失 # 3.1 计算目标Q值: r + gamma * Q_target(s', a'_1, ..., a'_n),其中a'_i由目标Actor网络根据next_obs生成 next_actions = [] for i, agent in enumerate(agent_list): next_obs_i = next_obs_batch[:, i, :] next_action_i = agent.actor_target(next_obs_i).detach() # 注意detach next_actions.append(next_action_i) next_actions = torch.cat(next_actions, dim=1) # 拼接所有智能体的下一个动作 next_state_value = self.critic_target(next_state_batch, next_actions).detach() target_q = reward_batch[:, self.id].unsqueeze(1) + args.gamma * next_state_value * (1 - done_batch[:, self.id].unsqueeze(1)) # 3.2 计算当前Q值 current_q = self.critic(state_batch, action_batch) # 3.3 Critic损失:MSE critic_loss = F.mse_loss(current_q, target_q) self.critic_optimizer.zero_grad() critic_loss.backward() # 可选:梯度裁剪,防止爆炸 torch.nn.utils.clip_grad_norm_(self.critic.parameters(), args.max_grad_norm) self.critic_optimizer.step() # 4. 计算Actor损失 # 4.1 重新计算当前状态下,智能体i的动作(其他智能体动作固定为采样数据中的动作) # 目的是优化Actor,使得Critic给出的Q值最大 current_actions = [] for i, agent in enumerate(agent_list): if i == self.id: # 对于当前智能体,使用Actor网络重新生成动作(需要梯度) new_action_i = agent.actor(obs_batch[:, i, :]) else: # 对于其他智能体,使用采样数据中的动作(不需要梯度) new_action_i = action_batch[:, i*action_dim:(i+1)*action_dim].detach() current_actions.append(new_action_i) current_actions = torch.cat(current_actions, dim=1) # 4.2 Actor损失:-Q(s, a_1, ..., a_i(new), ..., a_n),即最大化Q值 actor_loss = -self.critic(state_batch, current_actions).mean() self.actor_optimizer.zero_grad() actor_loss.backward() torch.nn.utils.clip_grad_norm_(self.actor.parameters(), args.max_grad_norm) self.actor_optimizer.step() def hard_update(self, target, source): """硬更新目标网络参数""" for target_param, param in zip(target.parameters(), source.parameters()): target_param.data.copy_(param.data)这是一个高度简化的核心流程。实际实现中,还需要考虑软更新(soft_update,每次用微小比例更新目标网络,训练更稳定)、经验回放池的共享与采样策略、探索噪声(如OU噪声)的添加与衰减等细节。
3.2.3 共享经验回放池
多智能体场景下,一个常用的技巧是使用一个共享的经验回放池,存储格式为(obs_all, state, action_all, reward_all, next_obs_all, next_state, done_all)。这样每个智能体在更新自己的Critic时,都能从所有智能体的交互经验中学习,提高了数据利用率,也帮助Critic更好地理解联合动作的价值。
4. 训练流程与核心调参技巧
4.1 主训练循环 (train.py)
训练脚本的骨架如下:
def train(): # 1. 初始化环境 env = MultiUAVPursuitEnv(num_pursuers=args.num_agents) # 2. 初始化智能体列表 agents = [MADDPGAgent(...) for i in range(args.num_agents)] # 3. 初始化共享经验回放池 replay_buffer = ReplayBuffer(args.buffer_size) total_steps = 0 for episode in range(args.max_episodes): obs, state = env.reset() episode_reward = 0 for step in range(args.max_episode_len): # 4. 每个智能体根据当前观测选择动作(添加探索噪声) actions = [] for i, agent in enumerate(agents): action = agent.select_action(obs[i], noise_process[i]) actions.append(action) actions = np.array(actions) # shape (num_agents, action_dim) # 5. 环境执行一步 next_obs, next_state, rewards, done, _ = env.step(actions) # 6. 将经验存入回放池 replay_buffer.push(obs, state, actions.flatten(), rewards, next_obs, next_state, done) obs = next_obs state = next_state episode_reward += np.sum(rewards) # 累计团队总奖励 total_steps += 1 # 7. 如果回放池数据足够,开始训练 if len(replay_buffer) > args.batch_size: for agent in agents: agent.update(replay_buffer, agents) # 每个智能体独立更新 # 8. 定期软更新目标网络 if total_steps % args.target_update_interval == 0: for agent in agents: agent.soft_update() if done: break # 9. 记录日志,保存模型 if episode % args.log_interval == 0: print(f"Episode {episode}, Total Reward: {episode_reward:.2f}, Steps: {step}") # 使用TensorBoard记录奖励、损失等 # writer.add_scalar('Reward/Episode', episode_reward, episode) if episode % args.save_interval == 0: save_models(agents, episode)4.2 超参数调优经验谈
MADDPG的训练对超参数比较敏感。以下是一些关键参数和我的调参经验:
| 参数 | 典型范围/值 | 作用与影响 | 调参心得 |
|---|---|---|---|
| 学习率 (lr_actor, lr_critic) | 1e-4 到 1e-3 | Actor和Critic网络的更新步长。 | Critic的学习率通常比Actor稍大(如1e-3 vs 5e-4),因为Critic需要更快地收敛来提供准确的Q值估计。如果训练不稳定(奖励剧烈震荡),尝试同时降低两者。 |
| 折扣因子 (gamma) | 0.95 到 0.99 | 衡量未来奖励的重要性。 | 对于围捕这种有明确终止状态的任务,可以设高一些(0.98-0.99),鼓励智能体考虑长远收益(完成围捕)。 |
| 软更新系数 (tau) | 0.005 到 0.01 | 控制目标网络更新速度。θ_target = τ * θ + (1-τ) * θ_target | 这是一个非常关键的稳定训练的参数。值越小(如0.005),目标网络更新越慢,训练越稳定但可能收敛慢;值越大(如0.05)则反之。通常从0.01开始尝试。 |
| 回放池大小 (buffer_size) | 1e5 到 1e6 | 存储经验的数量。 | 越大越好,但受内存限制。对于多智能体,由于样本复杂度高,建议至少1e6。确保在开始更新前,池中有足够多样本(如1e4)。 |
| 批次大小 (batch_size) | 256 到 1024 | 每次更新从回放池采样的样本数。 | 较大的批次(如512)通常能提供更稳定的梯度估计,但会增加计算量。GPU内存允许的情况下,建议使用较大的批次。 |
| 探索噪声 | OU噪声参数:theta~0.15, sigma~0.2 | 为动作添加随机性,促进探索。 | 初期需要较大的探索(sigma可设0.3),随着训练进行,可以线性衰减sigma。OU噪声比高斯噪声在惯性系统上探索效率更高。 |
| 网络隐藏层维度 | 128, 256, 512 | 神经网络中间层的神经元数量。 | 任务越复杂,需要越大的网络容量。对于我们的围捕任务,两层256的MLP通常足够。可以先从128开始,如果学习能力不足再增加。 |
踩坑记录:我最开始训练时,奖励一直不增长,在零附近徘徊。排查后发现是奖励函数设计不合理。我最初只给了到达目标的大奖励,但中间步骤没有奖励(稀疏奖励),导致智能体根本探索不到成功状态。后来加入了基于距离的稠密奖励(每一步都根据与目标的距离给予惩罚),学习立刻就有了进展。奖励函数的形状(reward shaping)是多智能体强化学习成功的关键,有时甚至比算法本身更重要。
5. 实战调试与性能优化
5.1 训练过程监控与可视化
“黑箱”训练是痛苦的。必须要有有效的监控手段。
- TensorBoard:记录每个episode的总奖励、每个智能体的平均奖励、Critic和Actor的损失值。观察奖励曲线是否上升、损失是否收敛。如果奖励曲线剧烈抖动,可能是学习率太高或批次大小太小。
- 定期渲染测试:每隔一定训练代数,用当前策略运行一个episode并渲染出来,直观地看智能体的行为。你会发现训练初期智能体像无头苍蝇,后期逐渐学会包抄和拦截。这是最直接的验证方式。
- 关键指标监控:
- 平均围捕时间:成功围捕所需的平均步数,越短越好。
- 成功率:在测试的N个episode中,成功围捕的比例。
- 智能体间平均距离:避免智能体挤成一团,这个距离应保持在一个合理范围。
5.2 常见训练问题与排查
| 问题现象 | 可能原因 | 排查与解决思路 |
|---|---|---|
| 奖励不上升,一直很低 | 1. 奖励函数设计问题(稀疏奖励)。 2. 探索不足,智能体没找到好策略。 3. 网络结构太简单或学习率太低。 | 1.检查奖励函数:加入稠密奖励引导。可以先用一个简单的规则控制器(如直接飞向目标)在环境中跑,看看奖励是多少,作为一个基准。 2.增加探索:增大噪声的sigma,或使用熵正则化等探索鼓励方法。 3.调整网络和超参:尝试增大网络隐藏层,适当提高学习率。 |
| 奖励曲线剧烈震荡 | 1. 学习率过高。 2. 批次大小太小。 3. 目标网络更新太快(tau太大)。 | 1.降低学习率,特别是Actor的学习率。 2.增大批次大小。 3.减小tau(如从0.01调到0.005),让目标网络更稳定。 |
| 训练后期性能突然崩溃 | 1. 过拟合或策略坍塌。 2. 探索噪声衰减过快,策略陷入局部最优。 3. 经验回放池中旧数据过多。 | 1.减缓噪声衰减,甚至保留一个最小噪声。 2. 使用优先级经验回放,让算法更关注那些TD误差大的、还没学好的经验。 3. 定期保存模型快照,如果崩溃可以回滚到之前的版本。 |
| 智能体学会“偷懒”或奇怪行为 | 奖励函数存在漏洞,被智能体找到“捷径”。 | 仔细审查奖励函数。例如,如果只奖励靠近目标,智能体可能学会一直跟在目标后面但永不包围。需要增加对“包围态势”的奖励,比如奖励智能体分布在目标的不同方向。 |
5.3 性能优化技巧
- 向量化操作:在环境
step函数和网络前向传播中,尽量使用NumPy/PyTorch的向量化操作,避免Python循环,可以极大提升仿真和训练速度。 - GPU加速:确保PyTorch安装了CUDA版本,并将网络和张量放到GPU上(
.to(device))。对于大规模网络和批次,GPU加速效果显著。 - 并行环境采样:如果CPU核心多,可以使用
SubprocVecEnv等工具创建多个环境实例并行采样数据,能大幅提升数据收集效率,这是加速强化学习训练的常用手段。 - 梯度裁剪:在Critic和Actor的优化器step之前,使用
torch.nn.utils.clip_grad_norm_对梯度进行裁剪,能有效防止训练因梯度爆炸而崩溃。
6. 项目扩展与进阶思考
这个基础项目可以作为一个平台,向多个方向扩展:
环境复杂度升级:
- 从2D到3D:将环境扩展到三维空间,动作空间变为三维加速度,这更贴近真实无人机。
- 加入障碍物:在场景中设置静态或动态障碍物,智能体需要学会避障的同时进行围捕。
- 更复杂的逃逸者策略:让逃逸者也使用一个预训练的策略或者基于规则的更智能策略(如强化学习对手),增加挑战性。
算法升级:
- 尝试其他MARL算法:如MATD3(MADDPG的改进版,解决了过估计问题)、MAPPO(基于策略梯度的多智能体算法)等,在这个环境上对比效果。
- 引入通信机制:让智能体之间可以传递简单的消息,学习何时通信、通信什么,实现更高级的协同。
- 分层强化学习:高层策略决定战术(如“包抄”、“驱赶”),底层策略执行具体动作。
从仿真到现实(Sim2Real):
- 动力学模型精细化:接入更真实的无人机动力学模型(如PX4的软件在环仿真)。
- 加入传感器噪声:在观测中加入高斯噪声,提高策略的鲁棒性。
- 域随机化:在训练时随机化环境的一些参数(如无人机质量、风阻系数),使得训练出的策略能适应现实中的参数变化。
这个项目最吸引我的地方在于,它像一座桥梁,连接了强化学习的理论算法和具挑战性的多智能体协同控制问题。当你看到最初乱撞的无人机们,经过数万轮训练后,能像猎犬一样默契地分工、拦截、合围时,那种成就感是无与伦比的。过程中你会深刻理解信用分配、环境非平稳性、探索-利用权衡这些MARL核心概念。我建议你在跑通基础版本后,一定要尝试修改奖励函数,你会发现,智能体行为的变化直接反映了你设计的“价值观”,这或许是强化学习最富哲学趣味的一点。
本文还有配套的精品资源,点击获取