资讯动态

车联网资源分配中的多智能体深度强化学习建模与算法对比

发布时间:2026/9/23 3:02:44 来源:尧图企业网站定制
简介这是一套基于多智能体深度强化学习实现的车联网通信资源分配优化项目源码采用Python编写面向计算机、通信等相关专业学生可用于毕业设计、期末课程设计或大作业参考。包内包含环境建模、DDPG、MADDPG、MADQN等算法实现以及经验回放缓冲区、随机策略对照和详细使用说明目录模块划分清晰便于理解多智能体强化学习在实时通信资源调度中的应用流程。资源包总计20个文件以Python脚本为主体含13个py源码文件与6个pyc预编译模块另附1个txt使用说明整体仅84KB轻量易部署解压后即可着手运行调试。该项目为个人毕设成果评审得分达97分代码经过严格调试确保可直接运行目前已有188人学习下载对希望快速上手车联网资源分配优化课题的同学具有较高参考价值。1. 车联网资源分配为什么直接用多智能体深度强化学习把车联网频谱分配写成凸优化问题很容易难在每次调度周期只有几十到几百毫秒拓扑一变上一轮算出的最优解立刻失效。而多智能体深度强化学习把每个V2V链路当作独立智能体训练时集中评判、执行时只跑一遍策略网络计算量就是几次矩阵乘法。这套基于Python的毕设源码正是按这个思路搭出的完整可运行基线环境建模、三种多智能体强化学习算法、随机策略对照都在包内评审分97分。它解决的不只是车联网通信资源分配优化如何建模而是把MAC层的调度问题翻译成状态、动作、奖励的完整方法。适合想快速复现课程设计、期末大作业或毕业设计的计算机/通信方向同学也适合想回补多智能体工程实现的开发人员。2. 从 Environment_marl.py 看车联网场景建模解压后最容易忽略的是Environment_marl.py在 SAMADDPG、MADDPG、Random 三个目录里各出现一次。这不是文件复制错了而是刻意让三种控制器共用同一个环境接口训练时才能公平对比。车联网通信资源分配优化的核心问题就是在一个不断变化的车辆拓扑下决定每个V2V链路用哪个子信道、多大功率发送数据环境代码写的质量直接决定算法能不能收敛。2.1 V2V/V2I 干扰模型与信道计算车联网通信里最基本的单元是V2V链路和V2I链路。V2I负责车辆和路侧单元之间的宽带连接V2V负责车辆之间的低时延直连。为了提升频谱利用率一般让V2V复用V2I的上行资源于是同一个资源块上可能同时存在多个发射机互相干扰就出现了。环境里最核心的计算就是信道增益它决定了速率和干扰矩阵。import numpy as np def channel_gain(distance, shadow_std8.0, path_loss_exp3.5): # 对数距离路径损耗参考距离1m处损耗35dB path_loss_db 35 10 * path_loss_exp * np.log10(max(distance, 1.0)) shadow_db np.random.normal(0, shadow_std) # 城市环境用瑞利衰落近似小尺度信道波动 rayleigh np.random.rayleigh(1.0) return 10 ** (-(path_loss_db shadow_db) / 20) * rayleigh这段代码对应Environment_marl.py里计算信道矩阵的函数。path_loss_exp是对数距离路径损耗指数城市车联网环境通常取 2.5 到 4取值越大干扰随距离衰减越快问题越容易解。shadow_std8代表城市峡谷里的阴影效应瑞利衰落每个调度时隙都应该重新采样阴影衰落则可以在一次 episode 内保持不变。调参时可以先固定小尺度衰落只开大阴影标准差观察奖励曲线波动幅度是否明显变大如果波动过大说明环境随机性太高需要增大经验回放容量或 batch size。参数常见取值对训练的影响子信道数 n_RB4~10决定 MADQN 动作空间大小V2V/V2I 数量4~8智能体数量增加MADDPG 计算量增长明显路径损耗指数2.5~4越大干扰越小问题越简单阴影衰落标准差4~10dB越大收敛越慢需要更大回放池发射功率上限10~23dBm影响 SINR 动态范围时延约束50~200ms决定延迟惩罚权重这些参数不是装饰调它们会直接改变强化学习任务的难度。如果毕设里想体现改进效果建议固定环境参数只换算法这样控制变量最清晰。2.2 状态空间、动作空间与奖励函数设计环境建模的第二步是把真实调度问题映射到强化学习四元组。常见做法是每个 V2V 链路作为一个 agent它的状态观测包括当前链路在 V2I 信道上的增益、周围干扰功率、发射队列中待发送的数据量、发送端与接收端的相对距离和速度。动作在不同算法里不一样DDPG/MADDPG 直接输出连续动作比如连续功率值和 RB 选择概率MADQN 则把动作定义为 RB 索引或 RB 和量化功率的组合。奖励函数需要同时反映吞吐量和时延不能只给一个速率值。def compute_reward(self, rate, demand, delay, delay_threshold0.1): # 用供给需求比描述该链路本轮是否满足业务量 serve_ratio min(rate / max(demand, 1e-6), 1.0) timeout 1.0 if delay delay_threshold else 0.0 return 2.0 * serve_ratio - 1.0 - timeout在Environment_marl.py里compute_reward通常是环境类的方法。这里把 serve_ratio 从 [0,1] 映射到 [-1,1]再减掉超时惩罚 1.0奖励范围在 [-2,1] 之间。这样设计比直接用净速率稳定原因是车辆业务通常以数据包到达率 demand 为目标超过需求的部分对用户体验贡献不大。做单体演示时可以只给平均奖励做 MADDPG 时最好让 critic 看到所有 agent 奖励的加权和而不是只看个体奖励否则收敛后容易出现个别链路长期饿死的情况。2.3 多智能体环境接口reset/step 怎么同时兼容三种算法这份代码里三个目录都放Environment_marl.py就是为了保持接口一致。OpenAI Gym 风格的 reset/step 足够用reset 返回所有 agent 的初始观测step 接收所有 agent 的动作并返回下一观测、奖励和是否结束。动作的 shape 统一为[n_agents, action_dim]即使 MADQN 内部用的是离散动作也要在环境入口转成同一维度的标识或独热向量这样上层算法切换时环境不用改。class EnvironmentMARL: def __init__(self, n_v2v4, n_v2i4, n_rb4, max_steps300): self.n_v2v n_v2v self.n_v2i n_v2i self.n_rb n_rb self.max_steps max_steps self.step_count 0 def reset(self): self.step_count 0 self._init_vehicle_positions() return self._get_obs() def step(self, actions): # actions: [n_agents, action_dim] self._update_positions() rates, delays self._calculate_rates(actions) rewards [self.compute_reward(r, d, delay_i) for r, d, delay_i in zip(rates, demands, delays)] self.step_count 1 done self.step_count self.max_steps return self._get_obs(), rewards, done, {}这段接口代码重点关注两个约定一是动作必须是 n_agents 行二是每个 agent 返回独立 reward 而不是求和后的标量。多智能体训练中如果某个 agent 的 reward 始终为 0优先检查 step 里是不是把多个 agent 的速率混在一起求平均了。第四步返回的 info 字典建议放当前 SINR、时延这些 debug 信息之后画曲线、做消融实验都不用再改环境。3. DDPG、MADDPG、MADQN 三种算法同环境实现对比同一个Environment_marl.py配上不同控制器效果差异很大。这个压缩包同时提供 SAMADDPG、MADDPG、MADQN 和 Random 四个目录正好用来观察不同算法在车联网资源分配场景下的表现。尤其值得关注的是三份环境代码相同算法却分成了连续动作和离散动作两条路线这种对比在论文里非常容易形成结构性结论。3.1 连续动作和离散动作的适配差异车联网资源分配的动作结构其实是混合的RB 选择是离散动作发射功率是连续动作。搭建强化学习动作空间时有两条路线。MADQN 用离散索引动作空间大小为n_RB * power_levels实现简单Q 网络直接输出每个动作组合的 Q 值。DDPG/MADDPG 用连续向量动作向量前 n_RB 维是 RB 选择 logits最后一维是功率值。连续动作要先通过 Softmax 或 Gumbel-Softmax 映射成 RB 选择概率再与环境交互。训练前期这个概率分布很容易塌缩到某一个固定 RB 上常见缓解办法是在 actor 输出后加一个 temperature 参数先让概率平滑一点再逐步降低。3.2 DDPG_method.py从单智能体 DDPG 扩展到多链路SAMADDPG 目录下的DDPG_method.py主要负责 DDPG 控制器实现。与标准单智能体 DDPG 不同多链路场景里每个 agent 可以独立维护 actor 和 critic也可以共享一套网络参数。从文件组织看该目录下没有单独的 agent 类文件使用了集中式的replay_memory.py更像是在 DDPG 之上做参数共享的多智能体扩展。它比 MADDPG 少了一个全局 critic训练开销小但对环境非平稳性的抵抗也弱。def update(self, replay_buffer, batch_size, gamma0.99, tau0.005): obs, act, rew, next_obs, done replay_buffer.sample(batch_size) with torch.no_grad(): next_act self.target_actor(next_obs) target_q self.target_critic(next_obs, next_act) y rew gamma * (1 - done) * target_q current_q self.critic(obs, act) critic_loss torch.nn.functional.mse_loss(current_q, y) self.critic_optimizer.zero_grad() critic_loss.backward() self.critic_optimizer.step() actor_loss -self.critic(obs, self.actor(obs)).mean() self.actor_optimizer.zero_grad() actor_loss.backward() self.actor_optimizer.step() for tp, p in zip(self.target_actor.parameters(), self.actor.parameters()): tp.data.copy_(tau * p.data (1 - tau) * tp.data) for tp, p in zip(self.target_critic.parameters(), self.critic.parameters()): tp.data.copy_(tau * p.data (1 - tau) * tp.data)这段 update 里最关键的是 critic 不感知其他 agent 动作。当每个 V2V 链路都在同时调整功率和 RB 时环境对单个 agent 来说是非平稳的所以 DDPG 版本容易出现过估计。实际训练中常见现象是前期奖励上升很快后面开始震荡。如果论文需要对比保留这个结果正好说明 MADDPG 的全局 critic 对非平稳性问题有抑制作用。3.3 model_agent_maddpg.py 与优先级经验回放MADDPG 目录单独存在model_agent_maddpg.py这和经典 MADDPG 实现一致。MADDPG 的核心改动是 critic 输入包含所有 agent 的 obs 和 actactor 只用自己的 obs实现集中式训练、分布式执行。这样每个 agent 在更新时知道别人的动作梯度不会被队友的策略变化带偏。# 在model_agent_maddpg.py的critic forward中常见写法 def forward(self, obs_list, act_list): # obs_list: (n_agents, batch, obs_dim) # act_list: (n_agents, batch, act_dim) all_input torch.cat(obs_list act_list, dim-1) x self.fc1(all_input) x torch.relu(x) return self.fc2(x)另外replay_buffer.py、replay_memory.py、segment_tree.py三个文件放在一起说明这版 MADDPG 大概率把优先经验回放接入了训练流程。segment_tree.py实现的是 SumTree支持按优先级二分采样复杂度 O(log N)。在车联网环境中高干扰导致中断的样本占比少但信息量大普通随机回放容易让这些关键样本被淹没优先回放能明显提高中后期收敛速度。使用时注意优先级不能直接用 TD 误差绝对值要加一个很小的 epsilon 正则训练到后期 TD 误差整体变小需要定期衰减优先级否则采样分布会过于集中。3.4 MADQN 的离散动作选择与 epsilon 衰减MADQN 目录下的madqn.py负责给每个 agent 一个 DQN。和 MADDPG 完全不同的思路动作空间完全离散。假设 6 个 RB、3 档功率动作数就是 18Q 网络输入 obs输出 18 个 Q 值。这个方案实现简单不需要 Gumbel-Softmax也不容易出现 critic 过估计。# madqn.py 中 epsilon-greedy 动作选择 import random import numpy as np def select_action(self, obs, epsilon): if np.random.random() epsilon: return random.randint(0, self.n_actions - 1) with torch.no_grad(): q self.q_net(torch.FloatTensor(obs).unsqueeze(0)) return int(q.argmax(dim1).item())MADQN 最需要调的是 epsilon 衰减。在小车环境里常用的线性衰减直接拿来做车联网会塌掉车联网的干扰情况在时间和空间上强相关前面还没探索完后面就过早进入利用阶段。我一般会让 epsilon 在 200 个 episode 内从 0.9 线性降到 0.05之后保持 0.05 做小概率探索这样既保证前期充分试错又给环境动态变化留了余量。3.5 三套算法怎么选对比表格算法文件/目录动作类型训练稳定性计算开销典型用途多智能体 DDPGSAMADDPG/DDPG_method.py连续中低快速验证连续功率控制MADDPGMADDPG/maddpg.py连续高高毕设主线算法改进MADQNMADQN/madqn.py离散中高低子信道选择问题RandomRandom/random.py随机无训练极低作为性能下界选型建议如果主要论述点是 RB 选择用 MADQN 更稳方便画离散动作收敛曲线如果论述点是功率和 RB 联合优化用 MADDPG 做主线拿 DDPG 和 MADQN 做对照即可。不要一开始四个算法全都调一遍环境参数稍微一动组合空间就会爆炸。4. 把训练跑起来目录结构、依赖与随机基线拿到压缩包后不要急着运行先把目录结构对照一遍。这份源码里出现大量同名文件是因为每个算法目录下都保留了一份环境代码改环境时大概率需要同步修改三处。目录结构理清楚了后面的训练和实验才不容易被路径问题干扰。4.1 解压后的目录结构每个文件干什么目录关键文件职责SAMADDPGEnvironment_marl.py, DDPG_method.py, replay_memory.py车联网环境、DDPG 多智能体控制器、经验回放MADQNmadqn.pyDQN/目标网络与 epsilon-greedy 训练MADDPGmaddpg.py, model_agent_maddpg.py, Environment_marl.pyMADDPG 训练主循环、agent 网络、环境MADDPGreplay_buffer.py, replay_memory.py, segment_tree.py经验回放、优先回放采样、SumTreeRandomrandom.py, Environment_marl.py随机策略对比基线与同一环境根目录使用说明.txt运行步骤与依赖说明这里要特别提醒Environment_marl.py在三个目录都存在。如果你要调整一辆车的初始位置分布或数据包到达率最好写一个统一的环境参数文件或者直接用 MADDPG 目录的环境再让其他目录引用它否则很容易出现三份环境不同步、算法对比无效的问题。这是多目录工程里最常见的坑。4.2 环境准备与先跑通 Random 基线conda create -n v2x-rl python3.8 -y conda activate v2x-rl pip install torch1.13.1 numpy1.24.3 matplotlib cd 解压目录/Random python random.py这套环境组合在 Windows 和 Linux 上都能装torch 1.13.1 对应 Python 3.8。如果机器是 Apple Silicon把 torch 换成 2.0 以上即可代码接口基本不变。先跑 Random 基线非常值得random.py会输出随机选择 RB 和功率下的平均链路速率、时延违例率这个结果就是后续所有 RL 算法必须越过的下界。如果 Random 都能跑出不错的效果说明环境干扰建模太弱需要调大 V2V 数量或减小阴影衰落标准差。4.3 训练循环的正确打开方式from Environment_marl import EnvironmentMARL from maddpg import MADDPG env EnvironmentMARL(n_v2v6, n_v2i6, n_rb6, max_steps300) n_agents env.n_v2v maddpg MADDPG(n_agentsn_agents, obs_dimenv.obs_dim, act_dimenv.act_dim, batch_size256) for ep in range(500): obs env.reset() ep_reward 0 for t in range(env.max_steps): acts maddpg.select_actions(obs, noise_scale0.1) next_obs, rewards, done, _ env.step(acts) maddpg.store_transition(obs, acts, rewards, next_obs, done) if maddpg.replay_buffer.size() 256: maddpg.update() obs next_obs ep_reward sum(rewards) / n_agents if ep % 20 0: print(fepisode {ep}, reward {ep_reward:.2f})训练循环里有一个和单智能体强化学习完全不同的地方store_transition存入的是 obs 和 acts 的整个二维数组不是单个 agent 的样本update时也要把 n_agents 个 actor/critic 逐个更新一遍。如果发现 loss 正常下降但每个 episode 奖励不涨检查是否在 step 后忘了把 tuple 转成 numpy array或者 reward 列表和 obs 的行顺序不对应。参数上noise_scale0.1对应动作探索的高斯噪声测试阶段要置 0batch_size256是因为 critic 输入维度包含所有 agent 的动作batch 太小会导致梯度方差大。500 个 episode 对 6 个 agent 的环境通常已经能看到收敛趋势。4.4 训练时最容易踩的三个坑维度不匹配。动作数组不是[n_agents, act_dim]而是[act_dim]时报错往往很靠后训练到一半才爆。建议在 step 开头加一句assert actions.shape[0] self.n_v2v。奖励返回值不统一。有的代码把每个 agent 的 reward 单独返回MADDPG 里可以这样用但 MADQN 如果每个 agent 独立更新把 reward 求和反而会导致 Q 值尺度失控。上层算法必须约定清楚每个 agent 的 reward 是一个长度为 n_agents 的数组。环境静态化。如果车辆位置区域固定网络会直接记住坐标而不是学策略。检查方式很简单训练结束后把初始位置 seed 改掉重新测试如果 sum rate 明显下降说明过拟合到训练拓扑了。生成位置时要在最大车速范围内随机化起点和目的地。5. 从离线训练到在线部署收敛验证与模型导出5.1 在全新随机拓扑上验证可迁移性车联网场景最看重泛化性训练集里的车辆轨迹不能代表真实路口。一个简单的验证方法是修改Environment_marl.py中车辆初始化的随机种子趁模型还在训练时每 50 个 episode 做一次torch.no_grad()评估拉出三条曲线训练平均奖励、测试平均奖励、时延违例率。如果训练奖励在涨而测试奖励不动说明智能体在背位置不是在学习通信策略。此时把车辆起点改为随机生成并限制每个 episode 的起点差异通常能明显缓解。这个操作比无脑加层改网络结构更有说服力也是答辩时能讲清楚的一个点。5.2 把 Actor 网络导出 ONNX 部署到路侧单元# 训练完成后取出第一个智能体的 actor import torch actor maddpg.actors[0] actor.eval() dummy_obs torch.randn(1, env.obs_dim) torch.onnx.export(actor, dummy_obs, v2x_actor.onnx, input_names[obs], output_names[action], opset_version11)ONNX 导出时要注意 opset_version 和 torch 版本对应1.13.1 用 opset 11 没有问题导出后可以用 onnxruntime 验证输出与 PyTorch 一致。路侧单元拿到 ONNX 后一次 CPU 推理能控制在毫秒级而传统优化求解器一次调度通常需要几十到几百毫秒这才是多智能体强化学习在车联网通信资源分配优化里真正能落地的地方。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价