拓冰建站拓冰建站
首页 / 资讯中心 / 正文

深度强化学习如何解决车联网资源分配:MADDPG与MADQN实战

简介这是一份基于深度强化学习的车联网通信资源分配优化系统毕业设计项目主要面向计算机及相关专业正在准备毕设、课程设计或期末大作业的学生。项目使用Python实现源码附带注释内置MADDPG、MADQN、SAMADDPG、Random等多种算法模块覆盖多智能体环境构建、经验回放、模型训练与对比分析等关键环节适合用于学习车联网场景下通信资源分配问题的建模与求解思路。压缩包共21个文件以.py源码为核心另有.pyc编译缓存、打包的SAMADDPG.zip及项目说明.md整体仅82KB轻量易部署。当前已有961人浏览学习项目经过本地验证可运行附带说明文档能够帮助使用者快速掌握项目结构并在此基础上进行二次开发也可作为毕业设计、课程设计的直接参照。1. 为什么车联网资源分配要交给深度强化学习做车联网V2V/V2I通信仿真的同学应该都有同感频谱资源就那么多车辆还在高速移动基站覆盖范围、信道质量、干扰关系每隔几百毫秒就变一次。传统做法要么靠数学规划建模求解计算量大拓扑一变动又要重算要么靠启发式规则能跑但离最优差得远。这个毕设项目给我的第一印象是它把资源分配问题完整地做成了多智能体强化学习MARL的交互闭环而不是只提供一个孤立的神经网络训练脚本。项目里同时包含了 MADDPG多智能体 DDPG、MADQN多智能体 Q-learning 变体、SAMADDPG 和 Random 四个目录前三个是不同算法策略Random 是随机分配基线。运行时只需要在对应目录下执行python maddpg.py或python madqn.py环境会自动初始化车辆、信道和基站训练完会看到奖励曲线和 V2V 时延达标率的变化。适合正在做毕设或课程设计、需要一套能跑通的 DRL 资源调度代码的人它把「深度强化学习」「车联网通信」「多智能体协作」三个点串在一起直接改参数就能换场景。2. 车联网场景下资源分配问题的建模与算法选型2.1 问题本质时延约束下的频谱复用决策车联网通信资源分配一般描述成在每一帧通常 1ms内若干 V2V 链路的发射端与接收端在高速移动中完成数据包传输系统要决定每条 V2V 链路占用哪个资源块RB、发射功率多大。V2I 链路负责车与路边基础设施的宽带连接V2V 链路传输安全相关消息有严格的端到端时延上限比如 100ms。难点在于多条 V2V 链路可以复用相同频谱但复用会引入同频干扰干扰又反过来影响 QoS 达标率。这是一个典型的非凸、组合优化问题随车辆密度增长解空间指数膨胀。强化学习把这个顺序决策过程建模为马尔可夫决策过程某个时刻环境给出车辆位置、信道增益、队列长度等观测信息智能体输出动作选 RB、选发射功率环境根据动作更新信道状态并返回即时奖励通常是速率、时延、干扰的函数循环往复。传统算法每次求解都要跑一次优化DRL 的做法是把决策策略压进神经网络权重里推理时一次前向传播就出动作延迟极低这也是它在 5G 车联网中被频繁研究的原因。2.2 为什么用 MADDPG 和 MADQN 而不是单智能体 DQN这个项目选型上最值得注意的就是「多智能体」这个前缀。车联网频谱分配里每辆车都可以是独立的决策者共享同一片频谱动作互相影响。如果把它们硬塞进一个 DQN 里把所有车拼成一个超大状态和动作空间维度会膨胀到难以训练另一个更致命的问题是环境非平稳——每辆车同时在学习、在变策略对任何一辆车来说其他车都在「移动靶」。MADDPG 的核心思想「集中训练、分散执行」就是为此设计的训练时 Critic 能看到所有车辆的状态和动作Actor 只用局部观测执行时车辆之间零通信。MADQN 在该项目中走的是价值分解路线适合动作为离散值比如从预定义的 MCS 等级或 RB 集合中选一项的场景。MADDPG 本身面向连续动作适合直接把发射功率当作连续值输出。项目里两套算法并存其实是覆盖了两种最常见的资源分配输出方式离散选 RB 用 MADQN连续调功率用 MADDPG。Random 目录里的random.py不是拿来产生结果的而是给对比实验提供一个性能下界——公布实验数字时评审老师一定会问「比随机分配和传统贪婪算法好多少」你得有这条基线。2.3 状态、动作、奖励怎么设计才能让训练收敛从项目的Environment_marl.py可以看到环境接口是按 OpenAI Gym 风格实现的核心是reset()和step()。状态空间是拼接向量包含 V2I 链路信道增益、各 V2V 链路的干扰信道增益、发射端与接收端的距离、时延余量、数据队列剩余负载。把这些信息原样丢给神经网络是常见的错误做法——量纲差别太大信道增益可能是 1e-4 级别队列长度是几百字节不归一化时 Critic 的 loss 会被大数值维度主导。奖励函数的设计上常见做法是叠加三个项V2I 链路可达速率的对数求吞吐贡献、V2V 链路完成传输的时延达标奖励给正奖励 1、对造成强干扰的动作施加惩罚系数。项目里两种策略的奖励结构是同一个Environment_marl.py计算出来的MADQN 和 MADDPG 只是更新方式不同环境一致才能做横向对比。触发终止条件一般是所有 V2V 数据包都送达、超时或超过最大步数比如一集 100 步。以下代码展示了环境中最核心的信道增益计算和奖励累积逻辑# Environment_marl.py 中简化后的 step 逻辑 def step(self, actions): # actions: 每辆车选择的功率等级或 RB 索引 self.update_positions(self.time_headway) # 更新车辆位置 gains self.compute_gain() # 计算大尺度/小尺度信道增益 interference self.compute_interference(actions, gains) # 复用频谱带来的同频干扰 rate self.compute_rate(gains, interference) # 香农公式计算可达速率 self.V2V_delay self.time_headway # 累计时延 reward self.get_reward(rate) # 奖励包含 V2I 速率和 V2V 时延达标项 done self.check_done() # 所有包送达或超时 next_obs self.get_state() return next_obs, reward, done, {rate: rate, interference: interference}compute_gain()内部做的是路径损耗 阴影衰落 快衰落小尺度衰落的乘法叠加路径损耗指数和阴影衰落方差都来自 3GPP TR 36.885 车联网信道模型参数表。你在改代码时重点关注get_reward()里两个项的权重系数V2I 速率项权重过大智能体会为了吞吐牺牲 V2V 时延指标V2V 时延达标项过大功率会被推到最大值干扰跟着恶化。项目里默认配置在多数仿真参数下是均衡的但换了车辆密度或数据包到达率这两个权重基本一定要重新调。3. 源码结构拆解环境实现、回放池与三种算法的工程落点3.1 目录结构与运行入口解压后整个项目按算法分成四个平级目录正好对应一个完整的对比实验设计。MADQN 目录里有madqn.py和replay_memory.pyMADDPG 目录文件最多除了模型定义model_agent_maddpg.py、算法maddpg.py还多了replay_buffer.py和segment_tree.py这是为优先经验回放Prioritized Experience Replay准备的SAMADDPG 目录下是DDPG_method.py和环境文件单独跑一个单智能体 DDPG 版本用来验证集中式 Critic 的收益。每个目录都自带一份Environment_marl.py方便独立运行不必担心修改一个环境影响其他算法对比。运行之前先确认 Python 版本和依赖。项目在 Python 3.7 环境下验证过__pycache__里留存了 cpython-37 的字节码文件代码本身只需要 numpy 和 PyTorch不需要安装 SUMO 或 NS-3 这类重量级仿真器——所有信道计算和车辆移动模型都在环境文件里内置了。装好依赖后进任意目录执行python maddpg.py即可训练。日志里会打印每 100 个 episode 的平均奖励、V2V 时延达标率、V2I 平均吞吐三项指标这三项正好对应论文里通常要放的三张收敛曲线。3.2 经验回放池为什么要单独写一个 segment_treereplay_memory.py是普通的 FIFO 回放池存放(state, action, reward, next_state, done)五元组容量默认几万条。普通回放池均匀采样的效率在稀疏奖励场景下不高——车联网里大部分时刻 V2V 传输都在正常进行奖励差异不大真正有价值的是那些「高干扰导致时延超限」的样本。MADDPG 目录里多出的segment_tree.py实现了 Segment Tree 数据结构支持 O(log n) 复杂度的优先级更新和按优先级采样。每次 TD-error 较大的样本优先级升高下次被采样到的概率更大训练早期信道变化剧烈时这个机制让收敛速度快不少。用优先回放后要注意两个连带问题一是优先级高的样本被反复采样网络会过拟合到那几个极端样本训练后期需要把优先级指数alpha从 1.0 按衰减策略调低到 0.4~0.5二是采样分布偏了样本均值不再是无偏估计需要用重要性采样权重修正replay_buffer.py里返回权重参数的公式就是(1/N * 1/P(i))^betabeta 随训练进度从 0.4 升到 1.0。3.3 MADDPG 模型文件中 Actor-Critic 的状态拼接model_agent_maddpg.py里定义了每个智能体的 Actor 和 Critic 网络。Actor 输入只有自己的局部观测obs输出动作Critic 输入是所有智能体状态和动作的拼接向量torch.cat([o1, o2, ..., oN, a1, a2, ..., aN], dim-1)输出单个 Q 值。这个拼接动作本身在代码中很直观但它引出一个额外的工程问题车数量一变网络输入维度就得跟着变改dim拼接维度和第一层全连接in_features时很容易出错。项目里车辆的默认数量是固定的如果你要改车辆数需要同步修改maddpg.py里创建智能体传参部分的obs_dim和n_action。每辆车两个网络Actor/Critic再加上各自的目标网络四辆车就有 16 个网络。训练时只更新 online 网络目标网络用软更新tau0.01。这里的小技巧是检查目标网络参数是否真的在缓慢变化——打印损耗值和polyak系数就能判断是没连对图还是环境本身有问题。3.4 SAMADDPG单智能体 DDPG 当作对比实验SAMADDPG 目录下的DDPG_method.py与 MADDPG 形成对比把多辆车的观测全部拼成一个超长状态向量动作也是所有车的动作拼在一起的连续向量训练一个中心化的 DDPG。这种做法的上限是「全局最优」——它掌握的信息比 MADDPG 的分散执行更多理论上限更高但状态维度是 MADDPG 每辆车独享的 N 倍训练难度剧增样本效率下滑。这个目录存在的意义不是说明 SAMADDPG 比 MADDPG 好或差而是作为验证「集中训练、分散执行」价值的关键对照组如果 SAMADDPG 在平坦场景下都能达到相近的性能说明车联网这个任务本身不需要过强的智能体间协作建模。4. 三种算法实现的核心机制与训练参数对照解读4.1 MADQN 与 DQN 的价值分解逻辑MADQN 的madqn.py在普通 DQN 基础上引入了类似 QMIX 的价值分解思想每个智能体车辆一个独立的 Q 网络输出该车在当前局部观测和动作下的效用值再用一个 mixing network 把所有车辆的动作效用汇聚成全局 Q 值。训练时的目标 Q 值由全局奖励 R gamma * Q_target(next_states, argmax Q_online) 构成。这样做相比于把全体动作拼进一个 Q 网络的朴素 DQN最大的区别在于每辆车的梯度只更新自己的 Q 网络避免了高维动作空间下 Q 值估计的方差爆炸。核心更新代码如下# madqn.py 中 train_step 的核心更新逻辑 for i in range(n_agents): q_eval q_network[i](states[:, i, :]).gather(1, actions[:, i].unsqueeze(1)) # states[:, i, :] 取第 i 辆车的局部观测 # gather 按实际执行的动作索引取对应的 Q 值 q_next q_target_network[i](next_states[:, i, :]).detach() # 目标网络计算下一时刻 Q 值detach 阻断梯度回传 q_target_value reward[:, i] gamma * q_next.max(dim1, keepdimTrue)[0] * (1 - done) # 注意 reward 取的是第 i 辆车的个体奖励分量 loss MSELoss(q_eval, q_target_value) optimizer[i].zero_grad(); loss.backward(); optimizer[i].step()代码里两个参数需要注意gather操作就是 PyTorch 中根据动作索引取 Q 值的最常用实现直接在q_eval的第一维做索引效率比布尔掩码高reward[:, i]用的是每个智能体各自的奖励不是全局标量这对多智能体算法的收敛稳定性非常关键——如果所有车都按同一个全局奖励更新梯度方向不一致很容易震荡。MADQN 适合动作空间为离散选择的场景比如这一个动作必须在 5 个 RB 里选 1 个或者 MCS 调制等级是 0~7 的整数。DQN 输出层神经元数量等于可选动作数用nn.Linear(hidden_dim, n_actions)做成输出层。有一个坑在目标网络更新的频率上——DQN 每训练若干步通常 100~200 步硬拷贝一次权重这个项目里如果沿用 DQN 做法需要确认hard_update的计时点是否以 episode 还是 step 为单位改频率会影响 Q 值估计的平稳性。4.2 MADDPG 的集中式 Critic 与 Actor 梯度MADDPG 的更新分两个阶段。第一阶段每个智能体的 Critic 用 TD 目标更新——TD 目标里的next_q_value要传入其他智能体的 target Actor 生成的 next_actions这就是「集中训练」的核心体现。第二阶段 Actor 的梯度方向来自 Critic 对动作的偏导通过torch.autograd.grad计算。工程实现时把critic_loss.backward()和actor_loss.backward()放在同一个优化器 step 里还是分开 step会显著影响训练稳定性把梯度累加后合并 step 的做法对超参更鲁棒尤其适合调制不熟悉的读者。# maddpg.py 中单智能体更新流程简化 def update(self, batch, agent_id): # batch 从 replay_buffer 采样包含 obs, action, reward, next_obs, done next_actions [] for i in range(self.n_agents): next_action self.target_actor[i](batch[next_obs][:, i, :]).detach() next_actions.append(next_action) next_actions torch.cat(next_actions, dim-1) # 按车辆维度拼接 q_next self.target_critic[agent_id](batch[next_obs], next_actions).detach() q_target batch[reward][:, agent_id] self.gamma * q_next * (1 - batch[done]) q_current self.critic[agent_id](batch[obs], batch[action]) critic_loss nn.MSELoss()(q_current, q_target) self.critic_optimizer[agent_id].zero_grad() critic_loss.backward() torch.nn.utils.clip_grad_norm_(self.critic[agent_id].parameters(), 0.5) self.critic_optimizer[agent_id].step()这段代码里最值得学习的是clip_grad_norm_这个操作。MADDPG 在多智能体训练中 Critic 的梯度范数经常超过 10原因在于 TD 目标里包含其他车辆的 target 网络输出存在「爆炸链」——一辆车目标值变化在梯度里放大到另一辆车上。按经验值梯度裁剪到 0.5~1.0 之间能解决多数训练发散问题。Actor 侧更新时通常给动作加高斯噪声项目里概率默认从 0.1 开始按 episode 衰减到 0.01。评判收敛与否的关键是观察 V2V 链路时延达标率的变化曲线而不是只看奖励数值奖励数值描述的是多种目标的加权求和达标率才是唯一的硬性物理指标。4.3 三种算法超参与迭代量级对照把三种算法的关键超参数整理成对照表方便直接照抄配置做对比实验参数MADQNMADDPGSAMADDPG回放池容量200005000030000batch size128256128学习率Actor/Critic1e-31e-4 / 1e-31e-3折扣因子 gamma0.90.950.95目标网络更新硬更新200步软更新 tau0.01软更新 tau0.01探索策略epsilon 0.9→0.05高斯噪声 σ0.1→0.01高斯噪声 σ0.1→0.01一次验证所需 episode 数约 800~15001500~30001000~2000迭代量级上 MADQN 通常收敛最快因为值函数逼近相对轻量MADDPG 两步更新复杂度高收敛慢但资源分配上限更高实际操作时看前 500 个 episode 的曲线形状最有用——如果 MADDPG 的奖励曲线在前 500 个 episode 完全没有上升趋势优先检查 Critic 的梯度大小其次检查奖励权重是否被 V2I 速率项主导。4.4 训练日志指标与 TensorBoard 可视化改造项目默认用 print 打印训练日志复现时我一般改成 TensorBoard 输出方便对比多组实验。在maddpg.py的训练循环里加一段轻量改造即可from torch.utils.tensorboard import SummaryWriter writer SummaryWriter(./logs/maddpg_v1) for episode in range(total_episodes): ep_reward 0 obs env.reset() while not done: action select_action(obs, noise_scale) next_obs, reward, done, info env.step(action) replay_buffer.push(obs, action, reward, next_obs, done) obs next_obs ep_reward reward writer.add_scalar(reward/train, ep_reward, episode) writer.add_scalar(delay/success_rate, info[v2v_success_rate], episode) writer.add_scalar(throughput/v2i, info[v2i_throughput], episode) if episode % 100 0 and replay_buffer.size() batch_size * 10: for _ in range(10): batch replay_buffer.sample(batch_size) update(batch)关于训练资源的分配环境 step 里计算信道增益时用 numpy 二维数组做矩阵运算性能瓶颈其实在 Python 循环里的一次次compute_interference每辆车 O(N) 遍历查干扰关系。如果想压缩训练时间把compute_interference改成矩阵广播一次算完3000 个 episode 能省下 1/3 的时间。5. 收敛性验证与调参排错的具体技巧先立一个判断标准V2V 时延达标率稳定在 95% 以上、V2I 平均吞吐不再明显增长才算训练真正完成。只盯奖励曲线很容易被误导——奖励函数里 V2I 速率权重稍大一点奖励曲线就能一直涨但 V2V 时延达标率掉到 80% 以下。每个实验跑完要生成对比表四行分别是 Random、MADQN、SAMADDPG、MADDPG 的时延达标率、平均吞吐、平均奖励这张表直接放进论文结果章节。调参时优先动三个位置。第一个是经验回放容量容量小5000会导致样本多样性不够收敛后奖励波动幅度大表现为曲线抖动严重且周期明显容量大到 50000 以上时前期收敛速度会变慢但最终性能更稳。第二个是高斯探索噪声的衰减速度车联网场景车辆密度高时干扰更剧烈噪声衰减太快会让智能体过早进入「确定性策略」状态失去对突变拓扑的适应能力建议把标准差的衰减步数拉长到默认的 1.5 倍再观察若达标率还不行再考虑噪声下限不能归零。第三个是 V2V 奖励项是否已覆盖「提前完成传输」的正反馈——如果奖励只在超时给惩罚智能体学到的策略是「够用就行」不会主动优化余量。常见的失败模式也总结一下。训练 loss 发散的排查顺序先去掉优先回放的alpha设为 0 等价于均匀采样排除采样分布影响再看归一化V2V 时延余量这一维度最大值可能到 1000而信道增益是 1e-3不归一化时 Critic 的梯度会被时延项主导把obs做标准归一化后基本有明显改善。另一种现象是训练后期达标率停在 80%~85% 上不去这通常是功率动作连续值被网络输出到边界后 Actor 更新失去梯度方向导致的「动作裁剪 缩放」——把连续功率映射到离散档位再做 softmax 选择工程上比硬学连续值稳定。对确定要做二次开发的同学建议从环境侧开始改先修改车辆移动模型把基础的直线匀速改成带有加速度的高斯随机运动再观察三种算法对动态性增加的适应表现这个实验在论文里单独成节比更换强化学习算法的说服力更强。最后提一点跑对比实验时各算法的随机种子务必固定np.random.seed(0)和torch.manual_seed(0)都要设否则 1e-4 量级的奖励误差会被误判成算法优劣差异。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门