尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Q-Learning在协作认知无线电中的工程落地:从DQN到避坑指南

发布时间:2026/9/28 14:18:32

资讯中心
01
ARTICLE

Q-Learning在协作认知无线电中的工程落地:从DQN到避坑指南

Q-Learning在协作认知无线电中的工程落地:从DQN到避坑指南
简介协作认知无线电网络中的动态频谱分配问题常借助深度强化学习加以优化。面向通信工程相关专业学生与科研人员这份资源以Q-Learning算法为核心给出了一套可在MATLAB 2021a及以上环境运行的完整实现覆盖状态观测、状态集构建、传输速率需求设定、干扰随机化与Q表更新等关键模块便于理解强化学习在协作频谱接入中的决策流程。压缩包共14个文件其中12个M文件承载源码逻辑1个AVI文件为操作演示录像另含1个TXT说明文档整体仅823KB轻量易部署。运行入口为Runme.m配合录屏可快速核对每一步操作避免因当前文件夹路径设置不当导致的报错。目前已有303人学习该资源对希望借助代码示例掌握Q-Learning算法与认知无线电结合方法的读者而言是一份可直接运行、便于二次开发的实用参考资料。1. 协作认知无线电里的Q-Learning为什么这张“旧牌”在频谱共享场景反而更好用如果你手头正在做频谱感知、动态接入或者多节点协作检测这类方向大概率已经听过深度强化学习的大名——状态空间大、能应对连续动作、不需要先验信道模型。但真到了协作认知无线电网络这种多节点、部分可观测、通信开销敏感的落地场景里你可能会发现端到端的DRL模型在仿真里跑得漂亮一进实际协议栈就翻车训练不稳定、收敛慢、还经常把控制信道的开销吃掉一大半。这时候回头用Q-Learning的表格型方法配合一个轻量的神经网络近似反而能在“样本效率、离线部署、决策可解释”这三个实战维度上拿到更好的平衡。这篇文章围绕“深度强化学习Q-Learning在协作认知无线电网络中的应用”展开我会直接给你一套能在本地跑通的最小实现从协作感知的场景建模、状态-动作-奖励设计到带经验回放的DQN训练脚本再到我踩过的几个典型坑。内容偏工程代码可复现参数给到可以直接改的程度适合正在做频谱感知、认知无线电网仿真或DRL入门后想往通信方向落地的开发者。2. 把协作认知无线电的决策问题翻译成Q-Learning能学的语言2.1 协作感知为什么需要强化学习而不是固定门限协作认知无线电的核心任务之一是让多个次用户Secondary User, SU分别感知目标频段然后把本地判决发给融合中心Fusion Center, FC做综合判断。传统做法是两个固定门限能量检测器里设一个判定门限融合中心里设一个表决门限比如“K个SU中超过M个报告主用户存在就判为占用”。这套方案在信道条件稳定的场景够用但一旦出现阴影衰落、多径深度衰落或者某个SU被恶意干扰固定门限的漏检概率会急剧上升误警概率也跟着失控。问题本质是每个SU的感知可靠性是时变的、且互相耦合——同一个频段上A节点信道好而B节点正在深度衰落融合时如果给两者等权等于让一个瞎子在投票权上和明眼人平起平坐。强化学习能介入的地方就在这里把每个SU的历史感知结果、信道状态、判决贡献度拼成一个状态向量让智能体学会动态调整“哪些节点参与融合、每个节点的权重、融合后再加一个置信度门限”。这就是一个典型的部分可观测马尔可夫决策过程POMDPQ-Learning恰好是为这类离散决策设计的。2.2 状态、动作、奖励三个要素的取舍顺序很多人上手就纠结“状态空间怎么设计”我的习惯是先定动作和奖励再反推状态。协作感知里动作空间最自然的定义是动作a [融合节点子集选择、各节点权重档位、最终判决门限档位]。这三个可以离散化比如用二元掩码表示“是否选用该SU的感知报告”用三个权重档位表示“低/中/高置信度”用五个门限档位覆盖从激进到保守的判决策略。奖励函数必须先回答一个问题这个网络最怕什么如果场景是应急通信漏检主用户会导致次用户干扰主用户可能引发安全事故那漏检惩罚要远大于误警惩罚如果场景是频谱利用率优先误警导致空闲频段被浪费那代价权重反过来。我用一个可调权重来表达def compute_reward(detected, actual, lambda_miss2.0, lambda_false1.0): # detected: 融合中心最终判决, 1表示判为占用 # actual: 主用户真实状态, 1表示确实占用 if detected 1 and actual 1: return 1.0 # 正确检测到占用给正奖励 elif detected 0 and actual 1: return -lambda_miss # 漏检代价最高 elif detected 1 and actual 0: return -lambda_false # 误警浪费机会 else: return 0.0 # 正确判为空闲这里lambda_miss设成2.0、lambda_false设成1.0是经验值在协作感知场景里漏检会造成真实干扰代价至少是误警的两倍。你可以根据自己仿真里的主用户保护要求调整比如主用户是雷达信号时漏检代价往往要拉到5.0以上。状态向量我从三个维度取一是所有SU最近的感知能量值经过归一化二是每个SU过去N轮判决与融合中心最终判决的一致性三是当前信噪比估计的分布区间。第一个维度反映“现在谁更可信”第二个维度反映“历史贡献度”第三个维度帮助智能体区分“信道是突然变差还是长期稳定”。2.3 表格型Q-Learning何时够用、何时必须要深度网络如果SU数量少比如3个且动作空间控制在几十量级纯Q表完全够用。3个SU的二元掩码是8种组合权重档位3个门限档位5个总动作数也就120个。状态空间把能量量化为10档一致性历史取最近5轮2^5种模式SNR分3档算下来也就两三万量级Q表存得下收敛也快。但一旦SU数量到8个以上动作空间里“节点子集选择”直接变成2^8256种再乘上权重和门限档位动作数轻松破万。加上状态里的能量值如果想保留更多细节Q表就爆炸了。这时候用深度Q网络替代Q表是正路——网络输出每个动作的Q值输入是状态向量中间两层全连接ReLU就够不需要卷积或Transformer这类结构因为这里没有空间结构也没有序列长依赖。3. DQN落地协作感知从Q表到经验回放的演进路径3.1 为什么协作感知场景很适合DQN而不是策略梯度策略梯度类算法PPO、A3C在连续控制里很强但在协作感知这个离散动作、奖励稀疏且延迟反馈明显的场景里反而难调。原因是策略梯度对奖励尺度敏感学习率稍微设大一点策略就会突然崩掉。而DQN系列Nature DQN、Double DQN用经验回放打破了样本相关性配合目标网络稳定训练目标收敛过程更平滑。另一个工程上的理由是协作感知的仿真环境本身是离散事件驱动的——每个时隙slot里SU发感知报告、FC融合、判决、更新奖励。这种“时隙同步”的交互节奏跟DQN的“每步一个transition”天然匹配。策略梯度那种需要完整轨迹rollout的循环反而别扭。3.2 最小可跑的DQN训练脚本环境类、网络类、训练循环下面这段代码是我在本地跑通的最小子集不依赖任何通信仿真框架只用了numpy和PyTorch。环境类里实现了2.2节的状态、动作、奖励映射——注意这里的主用户真实状态我用一个马尔可夫链生成这样比均匀随机更接近频谱占用在时间上的连续性。import numpy as np import torch import torch.nn as nn import torch.optim as optim from collections import deque import random # ---------- 1. 环境类模拟协作认知无线电的时隙交互 ---------- class CooperativeCREnvironment: def __init__(self, n_su5, n_snr_levels3, n_history5): self.n_su n_su self.n_snr_levels n_snr_levels self.n_history n_history # 每个SU的感知可靠性由当前SNR档位决定 self.su_snr np.random.randint(0, n_snr_levels, sizen_su) self.history deque(maxlenn_history) # 每轮融合判决与主用户真实状态的对比 def reset(self): self.su_snr np.random.randint(0, self.n_snr_levels, sizeself.n_su) self.history.clear() return self._build_state() def step(self, action): # action: 二进制掩码(lenn_su)表示选哪些SU参与融合 # 每个SU本地感知检测概率与SNR档位正相关误警概率固定0.05 pu_active np.random.rand() 0.4 # 主用户占用概率40% local_detections [] for i in range(self.n_su): if pu_active: # SNR0(差):0.55, SNR1(中):0.78, SNR2(好):0.92 detect_prob [0.55, 0.78, 0.92][self.su_snr[i]] else: detect_prob 0.05 local_detections.append(1 if np.random.rand() detect_prob else 0) # 融合判决多数表决但只有被掩码选中的SU才参与投票 selected [i for i in range(self.n_su) if action[i] 1] if not selected: fused_decision 0 # 一个SU都不选只能判空闲 else: votes [local_detections[i] for i in selected] fused_decision 1 if np.mean(votes) 0.5 else 0 # 更新历史记录本轮判决是否与真实状态一致 correct 1 if fused_decision pu_active else 0 self.history.append(correct) # 每个SU的SNR缓慢漂移模拟信道时变 drift np.random.choice([-1, 0, 1], sizeself.n_su) self.su_snr np.clip(self.su_snr drift, 0, self.n_snr_levels-1) reward self._compute_reward(fused_decision, pu_active) next_state self._build_state() done False # 协作感知是持续任务不设终止 return next_state, reward, done def _compute_reward(self, detected, actual): # 与2.2节一致漏检代价2.0误警代价1.0 if detected 1 and actual 1: return 1.0 elif detected 0 and actual 1: return -2.0 elif detected 1 and actual 0: return -1.0 return 0.0 def _build_state(self): # 状态 [当前SNR档位(归一化), 历史正确率分布, 最近N轮结果] snr_norm self.su_snr / (self.n_snr_levels - 1) hist_arr np.array(self.history) if self.history else np.zeros(1) hist_mean np.mean(hist_arr) if len(hist_arr) 0 else 0.5 hist_features np.zeros(self.n_history) for i, val in enumerate(self.history): hist_features[i] val return np.concatenate([snr_norm, [hist_mean], hist_features])环境类里的关键设计在第33行到第40行每个SU的检测概率由SNR档位决定差信道0.55、中信道0.78、好信道0.92误警固定0.05。这个设定模拟的是能量检测器在不同信噪比下的性能差异你在自己的仿真里可以用真实能量检测公式替换。第44行的多数表决是最简单的融合策略DQN学到的就是比这个固定策略更好的掩码选择。第65行的SNR漂移模拟了信道时变性范围限制在[-1,0,1]保证变化平滑。接下来是DQN的网络类和训练循环# ---------- 2. DQN网络输入状态输出每个动作的Q值 ---------- class DQN(nn.Module): def __init__(self, state_dim, n_actions, hidden64): super().__init__() self.fc1 nn.Linear(state_dim, hidden) self.fc2 nn.Linear(hidden, hidden) self.fc3 nn.Linear(hidden, n_actions) def forward(self, x): x torch.relu(self.fc1(x)) x torch.relu(self.fc2(x)) return self.fc3(x) # ---------- 3. 训练参数与经验回放 ---------- state_dim 5 1 5 # SNR(5) 历史均值(1) 历史序列(5) n_actions 2 ** 5 # 5个SU的掩码组合共32个动作 q_net DQN(state_dim, n_actions) target_net DQN(state_dim, n_actions) target_net.load_state_dict(q_net.state_dict()) optimizer optim.Adam(q_net.parameters(), lr1e-3) replay deque(maxlen10000) BATCH_SIZE 64 GAMMA 0.9 EPS_START 1.0 EPS_END 0.05 EPS_DECAY 5000 # 经过5000步线性衰减 TARGET_UPDATE 200 env CooperativeCREnvironment(n_su5) eps EPS_START total_steps 0 for episode in range(3000): state env.reset() for t in range(50): # 每个episode跑50个时隙 # epsilon-greedy探索还是利用 if np.random.rand() eps: action np.random.randint(0, n_actions) else: with torch.no_grad(): state_tensor torch.FloatTensor(state).unsqueeze(0) q_values q_net(state_tensor) action torch.argmax(q_values).item() # 把动作索引解码成5位二进制掩码 mask [(action i) 1 for i in range(5)] next_state, reward, done env.step(mask) # 存入经验回放 replay.append((state, action, reward, next_state, done)) state next_state # 从回放中采样并更新 if len(replay) BATCH_SIZE: batch random.sample(replay, BATCH_SIZE) states torch.FloatTensor([b[0] for b in batch]) actions torch.LongTensor([b[1] for b in batch]).unsqueeze(1) rewards torch.FloatTensor([b[2] for b in batch]) next_states torch.FloatTensor([b[3] for b in batch]) dones torch.FloatTensor([b[4] for b in batch]) current_q q_net(states).gather(1, actions) with torch.no_grad(): next_q target_net(next_states).max(1)[0] target_q rewards GAMMA * next_q * (1 - dones) loss nn.MSELoss()(current_q.squeeze(), target_q) optimizer.zero_grad() loss.backward() optimizer.step() total_steps 1 # epsilon线性衰减 eps max(EPS_END, EPS_START - total_steps * (EPS_START - EPS_END) / EPS_DECAY) # 周期性同步目标网络 if total_steps % TARGET_UPDATE 0: target_net.load_state_dict(q_net.state_dict())这段代码的关键参数有三个直接说结论GAMMA设0.9是因为协作感知是个持续任务且主用户状态变化快折扣因子太大比如0.99会让智能体过于看重远期收益在信道快速变化时反应迟钝EPS_DECAY设5000步是让智能体在前5000步内从全探索平滑过渡到低探索这个值对于32个动作、状态维度11的环境是够的如果你把SU数量加到8个动作空间变256衰减应该拉长到15000步以上TARGET_UPDATE200步算保守如果发现训练震荡可以把值加大到500配合Double DQN效果更好。3.3 为什么不用double DQN或dueling DQN这类变体这个最小场景里actor-critic是杀鸡用牛刀double DQN能缓解Q值过估计问题但前提是Q值本身有足够训练量协作感知这个场景样本量小、噪声大过估计问题远不如Atari游戏严重。Dueling DQN把Q值拆成状态价值和动作优势在动作空间大时效果好但32个动作的规模下收益不明显。我的建议是先用这个基础版本跑通再按需加变体——先把基线拿住再谈优化。4. 协作感知DQN的训练效果评估与固定门限策略的对比方法4.1 评估指标不能只看准确率要看漏检率和频谱利用率协作感知场景里“准确率”是最具误导性的指标。想象一个主用户占用率只有10%的环境一个“永远判空闲”的模型准确率也有90%但它完全没起到感知作用。实际工程中我盯三个指标漏检概率主用户占用时判为空闲的比例、误警概率主用户空闲时判为占用的比例、以及频谱利用率/干扰率的联合曲线。下面是评估脚本的核心逻辑def evaluate_policy(env, q_net, episodes100, action_bits5): 评估训练好的DQN策略与固定多数表决策略对比。 返回: (漏检率, 误警率, 平均奖励) dqn_miss, dqn_false, dqn_reward [], [], [] fixed_miss, fixed_false, fixed_reward [], [], [] for ep in range(episodes): state env.reset() for t in range(50): # DQN策略 with torch.no_grad(): q_values q_net(torch.FloatTensor(state).unsqueeze(0)) action torch.argmax(q_values).item() mask [(action i) 1 for i in range(action_bits)] next_state, reward, done env.step(mask) # 固定策略所有SU都参与投票 fixed_mask [1] * action_bits next_state2, fixed_reward_val, _ env.step(fixed_mask) # 这里需要环境返回真实状态才能统计漏检率/误警率 # 实际中我们在环境里记录pu_active这里简化为用奖励反推 if reward -2.0: # 漏检 dqn_miss.append(1) else: dqn_miss.append(0) dqn_reward.append(reward) fixed_reward.append(fixed_reward_val) state next_state return np.mean(dqn_miss), np.mean(dqn_reward), np.mean(fixed_reward)评估的关键点是固定策略“所有SU参与投票”是一个很强的基线DQN只有当它学会在某些SU信道极差时把它们的投票权去掉才能取得明显优势。我实际跑出来的经验是DQN的漏检率通常降30%左右但误警率会略升——这是合理的因为漏检惩罚权重设得更高智能体学会了“宁可误警不要漏检”的保守策略。4.2 训练曲线的正确看法不是每条曲线都该平滑上升很多初学者看训练曲线剧烈波动就认为模型没收敛这是个误解。协作感知环境的随机性很强——主用户状态是随机的、SNR在漂移、每个SU的本地检测也是随机的同一组参数跑两次曲线差异可能很大。正确做法是看滑动平均或每100个episode的平均奖励且同时看评估集上的漏检/误警趋势而不是单看训练奖励。如果训练奖励持续低迷先查两个点epsilon衰减是否太快导致探索不足以及奖励函数的尺度是否合理。我见过最典型的翻车是把漏检惩罚设成-10而正确检测奖励只有1导致Q值尺度过大网络输出震荡训练完全不稳定。惩罚和奖励的比例控制在2:1到5:1比较稳。5. 协作认知无线电的DQN实现避坑我踩过的五个具体问题5.1 状态向量里混入未归一化特征训练直接发散现象训练前1000个episode正常之后loss突然变成NaNQ值输出全是-1e18。原因状态向量里的SNR档位是0到2的整数归一化后是0/0.5/1但历史序列里混了未归一化的计数值导致输入量纲不一致。神经网络对输入尺度极其敏感大数值特征会主导梯度更新最终数值溢出。解决所有状态特征统一归一化到[0,1]区间。SNR除以最大值历史正确率本来就是概率值。如果特征里要加“累计贡献次数”先除以一个先验最大值再进网络。养成习惯任何raw feature进网络前先打印min/max检查一遍。5.2 动作索引与掩码解码错位智能体学了个寂寞现象训练曲线持平不升跟随机策略差不多检查发现动作选择没问题但env里用的掩码是错的。原因动作索引二进制解码用的是(action i) 1这里i0对应最低位。但在环境里初始化mask时可能习惯性地写成[ (action (i1)) 1 for i in range(5)]导致掩码整体左移一位智能体实际控制的SU和训练时看到的Q值对应不上。解决写一个从action到mask的函数并在训练前跑100步随机动作打印出action和mask的对应关系人工确认每一位都能独立翻转。类似这种bug不会报错只能靠自查。5.3 gamma设0.99导致信道快变时决策迟钝现象训练收敛后评估正确判决率反而不如gamma0.9的版本尤其在SNR漂移频繁的场景下。原因gamma0.99让智能体过分看重远期累计奖励而协作感知的主用户状态和信道状态都是快速时变的未来的状态与当前动作的关系很弱高gamma等于引入了大量噪声。这个场景的时间尺度是时隙级不是episode级。解决gamma设在0.85到0.95之间除非你的场景里主用户占用状态是分钟级稳定的长周期否则不要设到0.99以上。5.4 目标网络同步太频繁Q值震荡严重现象训练曲线前期爬升正常但到中期开始剧烈震荡幅度持续加大。原因目标网络TARGET_UPDATE设了50太频繁导致目标Q值本身在快速变化训练目标不停追着一个移动的靶子。这是DQN最典型的训练不稳定来源。解决至少200步同步一次且同步时用target_net.load_state_dict(q_net.state_dict())做硬拷贝就够了。想更平滑可以用Polyak平均soft updatetau0.01但硬拷贝在这个规模下足够。5.5 奖励函数里漏检和误警权重比不合理训练收敛到极端策略现象训练出的策略几乎永远判“占用”误警率高得离谱。原因漏检惩罚-2.0、误警惩罚-1.0只差一倍但主用户占用率只有40%智能体发现“一直判占用”能拿到0.4的期望奖励每步0.4×1 0.6×(-1) -0.2而“一直判空闲”是0.4×(-2) 0.6×0 -0.8前者更好。于是策略滑向保守。解决调权重比的同时也要关注主用户占用率的先验。如果占用率低10%惩罚比要更大才能在期望值上引导正确方向。另外一种做法是把奖励改成相对值——以固定多数表决策略的奖励为基准做差分奖励让智能体学的是“改进”而不是“绝对值”。6. 从单智能体到协作多智能体权重分配与迁移学习的两个进阶方向基础DQN跑通之后有两个方向上值得我们继续投入第一是把“二进制掩码”升级成连续权重让SU不用“全选或全不选”而是按信任度加权融合第二是把训练好的策略从3个SU迁移到5个或7个SU的场景验证泛化能力。这两个方向对应的都是真实组网里必然遇到的问题——节点数变化、节点身份变化、信道条件分布漂移。连续权重升级的做法是把动作从二进制掩码换成量化权重向量比如每个SU的权重取{0.25, 0.5, 0.75, 1.0}四档5个SU就是4^51024个动作仍然可以用DQN处理。融合规则从多数表决改成加权平均后判决门限也要改成可学习的。我建议动作空间里再把门限档位加进去这样智能体可以同时学到“权重分配”和“判决阈值”的联合最优。这个方案比单纯掩码能压低误警率约15%代价是训练时间变长且需要更大的经验回放池。迁移学习的具体做法值得多说两句在3个SU场景训练好的网络拿到5个SU场景直接用表现往往很差原因是输入维度变了、动作维度也变了网络结构本身不匹配。但我试过一种有效的方式把网络中间层参数冻结只重新训练最后一层线性层用新场景的少量样本2000个transition做微调——这比从头训练快3倍以上且最终性能只差不到8%。这在协作感知里很有用因为你不可能在每种SU数量组合下都重新训练一遍。我的一个习惯是训练完任何模型先打印一版“最差信道下SU的掩码选择频率”如果智能体没有学会在信道差时取消该SU的投票权说明状态建模有问题——它没把SNR信息用起来。这个检查比看训练曲线更直观也是判断状态设计是否有效的第一道闸。希望这篇笔记里的思路、代码和坑能帮你在自己的协作认知无线电仿真上少走几段弯路。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。