简介基于多智能体深度强化学习的车联网频谱共享与资源分配优化源码包面向车联网通信、深度学习与强化学习领域的研究人员和开发者。项目针对车辆高移动性导致信道快速变化、集中式资源管理受限的问题将V2V链路复用V2I链路频谱建模为多智能体深度强化学习任务实现基于分布式执行的MADDPG算法通过集中训练Critic网络、分布式执行各智能体动作并设计相应奖励函数与训练机制提升V2I总容量与V2V传输可靠性。zip压缩包共20个文件以13个Python源码文件为核心辅以6个pyc缓存和1份使用说明整体仅82KB轻量易部署。源码中除MADDPG外还包含DDPG、MADQN及随机策略等对比实现并提供环境建模、经验回放、模型训练等模块便于读者横向对比不同算法在车联网场景下的表现。已有199人学习下载适合作为课程设计、论文复现及算法研究的参考。1. 车联网通信资源分配优化这份多智能体深度强化学习源码到底能直接跑出什么做车联网通信资源分配优化的人基本都会卡在同一个问题上V2V链路要去复用V2I链路已经占用的频谱而车辆一进入高速运动信道每隔几十毫秒就会变一次集中式调度根本追不上这种变化。以前我调这类仿真最怕的不是算法复杂而是环境、智能体、奖励全粘在一起改一个参数就要把整套流程重新读一遍。这份Python源码包把多智能体深度强化学习的三个基线都放进了同一个车联网通信场景里MADDPG、MADQN和Random策略每个算法目录下都配一份Environment_marl.py环境文件训练入口和解说文齐全。它特别适合复现论文实验、做毕业设计也适合把频谱分配这块逻辑抠出来改到自己的任务上省掉从零搭环境的大段时间。2. 拆开源码包四个子目录的分工与MADDPG在连续动作空间里的选型理由2.1 目录结构先看清SAMADDPG、MADQN、MADDPG、Random各管什么下载解压之后你会看到四个代码目录外加一份使用说明.txt。第一次看这套代码别急着打开训练脚本先按下面这张表把文件职责分清目录/文件关键文件在实验里的角色MADDPG/maddpg.py, model_agent_maddpg.py, replay_buffer.py, segment_tree.py主算法分布式执行、集中训练的多智能体DDPGMADQN/madqn.py, replay_memory.py多智能体DQN基线动作空间被离散化Random/random.py随机策略基线用来证明学习策略确实比瞎试强SAMADDPG/DDPG_method.py, Environment_marl.py从命名看是MADDPG的改进版具体差异以使用说明.txt为准使用说明.txt运行说明记录了入口脚本和基本环境依赖每个算法目录下都有Environment_marl.py这是有意的设计环境接口保持一致算法文件只负责把状态映射成动作这样你要对比MADDPG和MADQN不需要改环境代码只需要在各自的入口脚本里跑。MADDPG里还带了replay_buffer.py和segment_tree.py说明经验回放用的是带优先级的存储结构而不是普通的list回放。代码组织方式在资源分配仿真里很常见也很招人喜欢环境与算法解耦。你后面想换自己的信道模型只要保留环境返回的state、action、reward接口训练代码一行都不用动。我一般会先打开Environment_marl.py确认里面返回的状态维度再去看算法文件否则很容易在“环境是给单一智能体还是多个智能体”这个问题上翻车。看一下文件树也很快find . -type f -name *.py | sort这条命令会把所有Python文件按路径列出来。你应看到Environment_marl.py在每个子目录里重复出现这是正常的不用怀疑文件放错了不同算法对环境的调用方式不同环境部分复制一份更保险。参数说明find是Linux和macOS下的命令Windows下可以用dir /s /b *.py效果等同。执行完后先把使用说明.txt里提到的入口脚本名单和这里对一遍项目跑不通多半是入口找错了。2.2 连续动作空间为什么选MADDPG而不是DQN车联网频谱共享里的动作是发射功率。功率是一个连续量你可以把功率离散成几个档位然后用DQN但离散化会带来两个问题一是档位太粗功率控制精度不够V2V链路一次性冲到最大功率会造成严重干扰二是档位太多DQN的动作输出维度爆炸训练样本利用率更差。MADDPG的思路是每个智能体维护一个Actor网络输出连续动作再加一个Critic网络汇总所有智能体的动作来评价。训练时Critic能看到全局信息——其他V2V链路的动作和状态这正好补上了V2V链路互相干扰这个关键信息执行时每个智能体只用自己的局部观察跑Actor不依赖中心节点。这在车联网里特别重要因为基站不可能在每一毫秒都收集到所有车辆精确的信道状态分布式执行才能跟上信道变化。工程上MADDPG里最常翻车的点是确定性策略的探索问题。DDPG类的算法会用随机噪声来探索很多照着论文实现的版本忘记在action上加噪声结果模型从头到尾都在同一个功率值附近打转。这套源码把replay_buffer和segment_tree单独拿出来说明至少在经验回放上做了准备。一个简化版的集中式Critic更新逻辑大致长这样# 伪代码说明MADDPG中Critic为何能看到全局动作 def update_critic(memory): # memory.sample() 返回一批经验每个智能体的obs, action和公共reward obs_all, action_all, reward, next_obs_all, done memory.sample() # target actor 根据每个智能体的next_obs生成next_action next_action_all [target_actor[i](next_obs_all[i]) for i in range(n_agents)] # target critic 用全局状态和全局动作计算目标Q值 target_q reward gamma * target_critic(next_obs_all, next_action_all) * (1 - done) # critic网络的输出也要基于全局状态和全局动作 current_q critic(obs_all, action_all) # 最小化 TD errorbackward更新critic参数 loss mse_loss(current_q, target_q) optimizer_critic.zero_grad() loss.backward() optimizer_critic.step()这里的核心就在critic(obs_all, action_all)这一行训练阶段把所有智能体的动作都塞给Critic这样每个智能体都能学会“我提高功率会怎么影响其他链路”而不是只看到自己那一点点奖励。代码逻辑说明伪代码里特意用target_actor和target_critic是MADDPG和DDPG系列维护的目标网络它们不参与实际动作生成只用来计算稳定的更新目标。参数说明gamma一般取0.9到0.99车联网环境状态变化快取太低模型会很近视取太高训练初期的探索噪声会被长期奖励放大容易震荡。2.3 公共奖励函数与集中式Critic源码里的协作信号车联网资源分配里V2V链路要保证传输可靠性V2I链路要尽量提高总容量两个目标本质是打架的V2V功率大一点V2I被干扰就多一点。MADDPG这类多智能体算法能在这两件事之间找到折中靠的是奖励函数设计成“公共奖励”。摘要描述里说“各个智能体与环境交互均获得一个共同的奖励”这就是把V2I总容量和V2V传输成功率合成一个标量。这种做法的好处是每个智能体不只需要自己成功还要其他链路也成功避免了自私策略。公共奖励在实现时通常会写成类似下面的形式def compute_reward(v2i_capacity, v2v_reliability, w10.6, w20.4): # 目标V2I容量尽量高V2V可靠性尽量接近1 reward w1 * v2i_capacity - w2 * abs(v2v_reliability - 1.0) return reward代码逻辑说明v2i_capacity是所有V2I链路容量之和v2v_reliability是当前时隙V2V传输成功率的估计。减去abs(v2v_reliability - 1.0)表示离满可靠性越远惩罚越大。参数说明权重w1和w2是你在调参时最值得动的地方如果训练出来V2I容量很高但V2V可靠性很低说明w2给得太小反之如果所有智能体都不敢发射功率说明w2太大会让智能体为保可靠性牺牲效率。源码里不一定叫w1和w2但思路是一致的改完奖励函数后一定记得要看训练曲线的量级奖励量级差两个数量级会导致policy梯度把Critic信号冲掉。这套“公共奖励集中式Critic分布式Actor”的结构是整个项目代码里最值得抠走复用的东西。你在其他通信场景比如无人机基站覆盖、边缘计算卸载里看到的MADDPG源码大概率也是同一套骨架。3. 把仿真跑起来Python环境、训练入口与MADDPG/MADQN怎么对比3.1 Python环境与依赖安装先让项目跑起来再说调参拿到这套代码的第一件事不是读懂算法而是先把环境装好。项目依赖主要是PyTorch这类深度学习框架、numpy科学计算和matplotlib画图工具。如果你的机器没有NVIDIA显卡装CPU版PyTorch就够了这类几十个智能体的小规模仿真在CPU上也能跑只是训练时间会慢一些。安装命令常见做法是pip install torch numpy matplotlib如果网络环境允许推荐按官方源再换到国内镜像。装完之后可以用一段验证代码确认环境没问题import torch import numpy as np import matplotlib.pyplot as plt print(torch version:, torch.__version__) print(numpy version:, np.__version__)代码逻辑说明这三行只是把依赖import进来如果没报错说明torch、numpy、matplotlib在当前的Python环境里已经可用。参数说明torch版本建议2.x1.x其实也能跑但如果你后面要改网络结构2.x的API更完整numpy版本如果装得太新偶尔会和老代码里np.float之类的写法冲突报错时先看是AttributeError还是ImportError前者多半是numpy新版改接口后者才是缺包。3.2 训练入口与输出从maddpg、madqn到random.py解压后你会看到MADDPG、MADQN、Random、SAMADDPG四个目录每个目录下都有一个主脚本。先跑Random是最稳的因为随机策略不涉及训练网络它给整个实验提供一个“下界”如果Random跑出来的性能已经和你后来训练的MADDPG差不多说明环境或者奖励设计可能有问题算法再高级也学不出东西。在MADDPG目录下运行训练cd MADDPG python maddpg.py在MADQN目录下运行cd MADQN python madqn.py运行过程中终端通常会打印每个episode的平均奖励和当前的学习率、epsilon值。如果项目没有把训练过程可视化你在代码里搜plt或者print关键字就能找到结果输出位置。常见做法是训练结束后保存一个result.png里面画了V2I平均容量和V2V传输成功率的曲线。一个简单的画曲线脚本可以在训练结束后独立执行import matplotlib.pyplot as plt # 假设训练过程中把每轮的奖励保存在列表 episode_rewards 中 episode_rewards [10, 12, 15, 18, 20, 19, 21, 25] plt.plot(range(len(episode_rewards)), episode_rewards) plt.xlabel(episode) plt.ylabel(average reward) plt.title(MADDPG training curve) plt.grid(True) plt.savefig(result.png, dpi150)代码逻辑说明把每个episode的奖励画出来是最直接的收敛性判断方法——曲线整体往上走说明策略在改进原地不动说明探索或奖励设计有问题上蹿下跳说明学习率或批次大小有问题。参数说明dpi150只影响输出图片清晰度不影响训练结果改这个参数主要是为了论文插图好看。如果你发现项目本身不输出训练数据那就得在训练循环里手动加一行print(fepisode {ep}, reward {reward:.2f})再把输出重定向到日志文件里。3.3 用Random基线检验算法收益曲线对比的正确姿势很多刚接触这类源码的人跑完MADDPG看到奖励曲线上升就直接拿去写论文这是不够的。你要把Random也算一遍然后让两条曲线出现在同一张图里。Random策略在车联网场景里的作用不是“对比”这么简单它是在帮你验证环境是不是有病如果random.py跑出来结果也很高说明接收端的SINR计算或者奖励里有漏洞导致乱发功率也能拿到高奖励。对比的时候要注意MADDPG训练过程有探索噪声前期可能还不如Random真正比较要看训练结束后关闭噪声的评估阶段。评估时通常固定随机种子、固定车辆移动轨迹跑若干episode取平均这样才能排除运气的成分。我在第一次跑这类代码时把MADDPG的reward曲线平滑到Random下面第一反应是代码写错了后来才发现是忘了把训练噪声去掉评估的时候还在探索。这是最常见的翻车点之一后面专门有一章讲这类排查。3.4 训练耗时要心里有数episode数与设备的关系车联网环境里一个episode通常模拟几十到上百个时隙每个时隙都要更新信道和计算SINR所以训练时间和你设的episode总数强相关。如果你用的是CPU几百个episode可能要跑几十分钟到几个小时不要指望一次调参就能快速收敛。常见做法是先设成一个很小的episode数比如50个把整条训练流程跑通确认没有报错后再加到500甚至2000个。如果你发现每轮训练都要三分钟以上可以考虑减少每个episode的时隙数先把算法的趋势验证出来。4. 参数怎么调信道模型、奖励权重和训练机制里真正影响收敛的开关4.1 环境参数表第一次修改重点盯哪些量车联网仿真的环境参数基本都集中在Environment_marl.py的开头部分。最常见的参数如下参数名常见默认范围影响n_V2V / n_V2I4~10 条链路智能体数量直接影响训练复杂度和状态维度freq2~6 GHz路径损耗和衰落的计算基准vehicle_speed20~60 m/s信道相干时间速度越高信道变化越快power_max10~30 dBm动作空间上限决定干扰强度noise_power-114 ~ -90 dBmSINR计算时的底噪参数没改对奖励量级会漂time_slots100~2000训练总时隙太少模型学不完太多浪费时间参数说明n_V2V和n_V2I是最先要动的。源码里的默认值可能是为了复现某一篇论文设置的你换成“10条V2V8条V2I”之后Actor网络的输入维度会变如果Environment_marl.py没有自动适配维度你就会在首次前向传播时收到一个维度不匹配的报错。解决方法是先看环境返回的state的shape再去改model_agent_maddpg.py里的网络输入维度。vehicle_speed建议按你用的道路场景来设仿真目的不是把速度调到最大而是让信道相关时间符合真实车流否则训练出来的功率策略换一条路就失效。4.2 奖励函数改法权重、量级与可靠性惩罚第2章给过公共奖励的伪代码这里说实际操作。拿到代码后搜reward关键字通常能找到类似v2i_capacity和v2v_ber的变量。奖励设计有三个经验原则你照着改大概率能避开坑。第一个原则是量级匹配。V2I容量算出来可能是几百MbpsV2V可靠性是个0到1的小数如果你直接把它们相加小数分量会被大数吞掉智能体只优化大数。常见做法是对容量做归一化或取log让两个目标的量级落到同一个区间。第二个原则是惩罚要放在关键约束上。V2V链路的传输可靠性往往有硬指标比如要大于90%。奖励函数里可以用一个阶跃或分段函数只有低于阈值时才给惩罚高于阈值就不额外奖这样智能体不会去追求100%而过度牺牲V2I容量。第三个原则是不要频繁改奖励。多智能体场景下奖励函数的每个权重变化都等效于改变整个博弈的规则一旦改完就得重训不存在微调后接着训的说法。我见过有人把w2从0.4调到0.5之后直接加载原模型继续训练结果曲线直接起飞表现一片混乱这不是权重大小的问题是经验回放里的旧数据和新的奖励目标已经不一致了。4.3 训练机制里的两个旋钮学习率与经验回放优先级MADDPG里有几个超参数比算法本身更影响结果。第一个是Actor和Critic的学习率很多实现里Critic学习率比Actor高一个数量级因为Critic要拟合的Q值函数变化更快。如果你发现训练过程loss在初期就爆炸先把学习率降到1e-4以下。第二个是经验回放的batch大小和优先级。这个项目里出现了segment_tree.py说明MADDPG版本使用的是按优先级采样的经验回放。使用优先级回放时alpha参数控制优先级采样的强度alpha太大智能体永远在学那些最难的经验导致样本分布失衡alpha太小优先级回放退化成均匀采样。常见的alpha范围是0.4到0.6在训练后期再逐步退火到0。调整超参数的位置一般在maddpg.py、model_agent_maddpg.py的开头直接搜learning_rate、batch_size、alpha这些变量名。改的时候一次只动一个参数动了之后至少跑完一个完整实验再看效果否则多个变量一起动出问题了你根本不知道是哪一步改坏的。5. 避坑排查跑不通、不收敛、曲线震荡的几类典型问题实录5.1 报错ModuleNotFoundError子目录之间文件路径没配对现象在MADDPG目录下运行maddpg.py提示ModuleNotFoundError: No module named Environment_marl。原因maddpg.py和环境文件不在同一层目录或者运行入口的sys.path没有把当前目录加进去。解决先看报错行所在文件的import语句把Environment_marl.py移动到和主脚本同一级目录或者在脚本开头加两行import sys sys.path.append(.) # 把当前目录加进模块搜索路径这段代码是最常用的后悔药没加这两行时代码会在import阶段挂掉加上之后模块就能被找到。参数说明.指的是当前工作目录如果你用IDE运行且工作目录不是项目根目录改成..或者环境文件的绝对路径。Windows下还要注意文件名不要带中文字符Python的import对中文路径支持不好容易莫名其妙报编码错误。5.2 训练不收敛奖励一直在一个值附近抖动或者缓慢下降现象训练了500个episode平均奖励始终在初始值附近没有上升趋势。原因常见的有三种一是探索噪声太小Actor找到当前高奖励动作后就不敢尝试别的功率二是奖励函数里有两个目标V2V可靠性已经顶到1但V2I容量被压得极低整体奖励卡在中间三是Critic的target网络更新太慢Q值估计滞后。解决先增强探索把动作噪声的标准差调到动作范围的0.1倍以上让智能体在早期一定要见过各种功率档位。再看reward曲线里V2I和V2V的分量如果只有V2I在涨、V2V在跌就是权重失衡回4.2改惩罚。最后把soft update的tau从0.01调到0.05加快目标网络追赶速度。改完之后再训练一轮不要图省事直接加载旧模型噪声参数变了旧经验回放里的数据也要作废。5.3 曲线震荡前几个episode冲高然后突然崩盘现象奖励在前20个episode冲到很高紧接着大幅下跌之后再也不回来。原因也是多智能体强化学习的经典坑——经验回放里存的是旧策略产生的高奖励样本新策略一更新这些旧样本的奖励分布已经和当前策略不匹配Critic被旧数据带偏。解决优先检查batch size和回放缓冲区大小batch size太小更新步数多但每次用到的样本太少Critic方差很大回放缓冲太大旧样本在缓冲区里呆的时间太长。常见的折中是batch size取256到512回放缓冲区容量设置成每次训练能覆盖最近几十个episode即可不要贪大把几千个episode的全存下来。另一个办法是给奖励曲线做滑动平均再判断趋势。单个episode的随机性很大特别是在信道快速变化的车联网场景看曲线要看100个episode的均值。不要因为单轮奖励掉下来就急着调参那大概率只是信道波动。5.4 复现论文图表时对不上随机种子和评估协议不一致现象把默认参数跑出来的数值和论文里的表对不上V2I容量差出10%以上。原因强化学习实验高度依赖随机种子车辆位置、信道衰落、经验采样全都有随机性如果论文没有明确seed差出10%很常见另一个原因是评估时是否关闭了探索噪声训练中的Actor输出动作加上噪声之后性能会低很多。解决复现时先固定随机种子。在训练脚本开头设置import numpy as np import torch import random np.random.seed(42) random.seed(42) torch.manual_seed(42)参数说明固定同一个seed多次运行的结果仍有微小差异因为PyTorch的并行计算和显卡算子不保证逐bit复现但波动会小很多。要得到可写进论文的评估值不要拿训练过程中的reward曲线说事单独写一个测试循环加载训练好的模型关闭噪声跑50个以上episode取平均。我自己的经验是评估时把每条V2V链路的成功率和V2I容量分别统计比只看合成奖励更容易定位模型偏向了哪个目标。6. 进阶验证迁移到自己的V2V场景前先做这三个检查6.1 检查动作边界与功率约束是否一致把这份代码用到你自己的场景时第一件事是确认Actor输出的动作经过tanh之后处于[-1,1]再被映射到发射功率范围。如果映射公式写的是power (action 1) * power_max / 2那当收益允许时模型最高只可能给到power_max不会越界。你真正要检查的是环境里的干扰计算用的功率和动作映射后的功率是不是同一个值很多改动场景的人在这里换了单位导致SINR算出来全不对。6.2 检查状态输入是否包含足够的信道信息如果你的自建场景里车辆运动模型变化了比如从高速路变成城区低速Environment_marl.py里返回的state如果还只包含旧场景的信道增益算法就感知不到变化。常见做法是往state里加上本链路当前的SINR、相邻链路的干扰水平以及车辆相对速度。加状态维度的同时要改model_agent_maddpg.py的网络输入层否则会报维度错。这里我一般会先打印一次环境的state和action的shape拿纸记下来再动手改网络结构。6.3 固定随机种子重复三次训练检验稳定性最后一件事也是最容易被跳过的事固定种子只跑一次不算数。我自己的习惯是同一个实验配置换三个种子各跑一遍如果三条训练曲线收敛到差不多的水平说明算法结果可信如果种子之间差出20%那说明探索、网络初始化或经验回放还不够稳这时候去调超参数才是有效的不要拿一次运行的结果去和别人论文里的最佳值比。那次我迁移这套MADDPG代码到无人机辅助通信场景前前后后反复训练了十几次发现最影响结果的不是网络结构而是随机种子和探索噪声的配合从那以后我每次迁移必做三种子重复实验。希望这些踩坑经验能帮到你。本文还有配套的精品资源点击获取