协作认知无线电Q-Learning频谱分配仿真:原理、代码与复现

发布时间:2026/10/25 12:48:27
协作认知无线电Q-Learning频谱分配仿真:原理、代码与复现 简介针对深度强化学习中的Q学习算法在协作认知无线电网络中的应用这份资源面向通信工程与机器学习交叉方向的研究者尤其适合希望复现动态频谱分配、协作感知与干扰管理实验的中高级学习者。压缩包内共十四个文件以十二个脚本文件为主辅以一段操作录像和一个说明文本整体仅约823KB轻量且便于按模块研读。目前已有三百零四人学习或下载。内容包括状态观测、状态集合构建、通信速率设置、干扰计算、Q值更新与信道选择等核心算法模块整理为多个可调用函数使用者只需运行主程序即可走通完整流程切勿直接执行子函数附带的录像演示了在较新版本环境下的正确操作步骤、当前文件夹路径设置与排错要点可对照完成实验并理解算法迭代细节。对于希望快速掌握强化学习在认知无线电中落地实现的学习者这一资源结构清晰、可直接运行便于快速参照与二次开发。1. 深度强化学习Q-Learning在协作认知无线电网络中的应用一个能直接复现的仿真工程认知无线电网络里最棘手的问题不是感知频谱空洞而是当主用户PU和次用户SU共享同一段频谱时系统必须实时决定“哪个次用户在哪个信道上接入、以什么功率发射”。这个决策空间随用户数和信道数指数膨胀信道增益又随时在变传统查表法和固定优先级策略根本追不上环境变化。这套深度强化学习Q-Learning在协作认知无线电网络中的仿真工程把动态频谱接入建模成马尔可夫决策过程用Q表学习最优分配策略代码覆盖状态观测、状态集构建、SINR更新、Q表决策全链路还带一份操作演示视频。运行环境是Matlab 2021a或更高版本主脚本是Runme.m。正在做认知无线电方向仿真、需要跑通基线算法或准备论文对比实验的研究生和工程师跟着录像动手半小时就能跑出第一轮分配结果。2. 从频谱接入到Q表更新为什么协作认知无线电选Q-Learning2.1 协作认知无线电的问题定义PU、SU与频谱空洞先把网络角色对齐。主用户PU是频谱的授权持有者次用户SU只能在不对PU造成有害干扰的前提下机会式接入。所谓“协作”指的是SU主动帮助PU转发数据或参与协作感知改善PU收到的SINR作为交换PU允许SU在满足速率约束的前提下共享频谱。这个协作关系一旦成立系统的核心优化问题就变成SU以什么策略选择信道和功率才能在满足PU所需传输速率的同时把自身接入造成的畸变和失真压到最低。工程文件里set_reqired_trans_rate.m负责定义这个速率需求update_pu_sinr_level.m负责在每次动作后刷新PU的SINR水平get_distortion.m则计算SU接入给链路带来的失真代价。这三者构成了环境侧的反馈回路动作影响SINRSINR变化带来新的失真代价代价又驱动下一次决策。这个回路本质上是动态的、多步的正好落入强化学习的建模范围。2.2 状态、动作、代价三元组如何映射到代码Q-Learning的第一步是把物理网络状态抽象成离散状态、动作和即时代价。本工程的状态向量由两部分拼接而成信道增益矩阵G的展开值加上当前PU的SINR水平。由于信道增益是连续量直接进Q表会维度爆炸create_state_set.m会预先枚举或采样一组代表性的状态组合observe_state.m再把实时观测到的连续状态向量投影到状态集里最接近的那一行。这个离散化过程我在代码里见过很多种写法有的是等间隔量化有的用聚类中心这个工程用的是最近邻匹配function state_idx observe_state(G, pu_sinr_level, state_set) % 将连续网络观测映射到离散状态索引 % G: 信道增益矩阵行对应SU列对应信道 % pu_sinr_level: 当前主用户信干噪比水平标量 % state_set: 预先生成的状态集每行是一个候选状态 state_vec [G(:) , pu_sinr_level]; % 拼成完整状态向量 state_idx findnearest(state_set, state_vec); % 最近邻匹配 end这里要注意state_vec的拼接顺序必须和create_state_set.m生成状态集时的列顺序完全一致否则最近邻匹配会错位。findnearest.m内部用欧氏距离找最近状态如果状态集中元素非常多这段会成为性能瓶颈我一般会建议改成先对状态集做归一化、再用向量化距离计算能省掉一层循环。动作空间则是信道分配选项。工程里q_learning_allocation2.m在每个状态下从Q表选动作决策函数叫argmin_Q.m而不是常见的argmax_Q.m这说明Q表里存的是期望累计代价而不是期望累计奖励。值越小代表这个动作越好。这个差异是理解整套代码的钥匙如果按教科书的“奖励最大化”逻辑去读会发现学习方向完全是反的。2.3 为什么不用凸优化而用Q-Learning有读者会问频谱分配不是可以用凸优化或博弈论做吗确实可以但前提是知道精确的信道模型和实时CSI。协作认知无线电里SU对PU链路的干扰信道是动态变化的随机数发生器randomize_G.m和randomize_G2.m的存在就是模拟这种时变信道。凸优化在这种非平稳环境下每时隙都要重新求解计算量和建模误差都不可控。Q-Learning是无模型的时序差分方法它不假设信道服从什么分布只靠反复试错累积代价经验。每轮迭代用贝尔曼方程把当前动作的即时代价和下一步最优代价加权合并逐步逼近真实的最优策略。对离散动作空间的问题Q-Learning实现简单、收敛行为可解释相比策略梯度类深度强化学习算法不需要调神经网络结构在Matlab里用纯m函数就能跑这也是这个工程选择它的直接原因。3. 代码包拆解Runme.m与12个m文件的调用关系和参数细节3.1 先看文件清单建立整体调用地图拿到压缩包先别急着双击运行把文件分类看一遍。整个工程共12个m文件、1个操作录像和1个FPGA衔接说明txt。文件角色主要职责Runme.m主脚本初始化参数、构建状态集、启动Q学习循环、输出分配结果observe_state.m状态观测把信道增益与PU SINR映射为离散状态索引create_state_set.m状态集构建生成所有代表性状态组合q_learning_allocation2.m学习主流程迭代更新Q表并执行动作选择argmin_Q.m决策函数从Q表取累计代价最小的动作update_pu_sinr_level.m环境更新根据动作刷新PU的SINR水平get_distortion.m代价计算计算SU接入造成的链路失真set_reqired_trans_rate.m约束设定定义PU与SU的所需传输速率randomize_G.m / randomize_G2.m信道随机化生成和扰动信道增益矩阵find_idx.m / findnearest.m工具函数索引查找与最近邻匹配3.2 Runme.m主流程从初始化到出图的完整顺序Runme.m是整个工程的入口。摘要里特意强调“不要直接运行子函数”原因就在于子函数依赖主脚本工作区里的变量单独运行会报“未定义函数或变量”。我按这类频谱分配工程最常见的组织方式梳理出主流程先清空工作区、固定随机种子、设置网络规模和Q学习超参数然后依次调用create_state_set构建状态集、初始化Q表、进入q_learning_allocation2叠加学习轮次最后输出收敛后的分配结果和代价曲线。% Runme.m 主脚本结构示意 clear; clc; rng(42); % 固定随机种子保证可复现 params.n_su 4; % 次用户数量 params.n_ch 4; % 可用信道数量 params.alpha 0.1; % Q学习率 params.gamma 0.9; % 折扣因子 params.epsilon 0.2; % 探索率 params.n_episodes 500; % 学习轮数 G randomize_G(params); % 初始化信道增益矩阵 state_set create_state_set(params); Q zeros(size(state_set, 1), params.n_actions); [Q, cost_history] q_learning_allocation2(Q, state_set, G, params);固定随机种子这一步很多人会忽略但对于复现论文结果极其重要。randomize_G里有随机数生成epsilon-greedy探索也依赖随机数不固定种子的话同一套参数两次运行的结果差异会很大后面第5章会专门展开讲。alpha、gamma、epsilon这三个值强烈建议先跑默认的再逐步调。3.3 决策侧argmin_Q与q_learning_allocation2的更新规则q_learning_allocation2.m里每个学习回合重新随机化信道矩阵模拟信道时变然后在每个时隙里执行“观测状态→选动作→算代价→更新Q表→转移状态”五步。function [Q, cost_history] q_learning_allocation2(Q, state_set, G, params) % Q-Learning频谱分配主流程 % 返回训练完成后的Q表和每轮平均代价历史 n_actions params.n_actions; for ep 1:params.n_episodes G randomize_G2(G, params); % 每轮重新扰动信道 state observe_state(G, 0, state_set); episode_cost zeros(1, params.n_steps); for t 1:params.n_steps if rand() params.epsilon action randi(n_actions); % 探索随机动作 else action argmin_Q(Q, state, n_actions); % 利用代价最小 end pu_sinr update_pu_sinr_level(action, G, params); distortion get_distortion(G, action, params); next_state observe_state(G, pu_sinr, state_set); % 贝尔曼更新目标值 即时代价 折扣因子 * 下状态最小代价 target distortion params.gamma * min(Q(next_state, :)); Q(state, action) (1 - params.alpha) * Q(state, action) ... params.alpha * target; state next_state; episode_cost(t) distortion; end cost_history(ep) mean(episode_cost); end end核心更新一行用了min而不是max这是整套代码的命门。min(Q(next_state, :))取的是下一状态所有动作里累计代价最小的那个意味着智能体在规划未来时总是假定自己会选最优动作。如果误写成max算法会去学“最差情况”分配结果会完全发散。argmin_Q的内部实现一般是先对第state行做全局搜索找到最小值对应的列索引若同时存在多个等值动作取第一个即可这种近似不影响收敛。3.4 环境侧SINR更新、失真计算与速率约束环境侧函数决定了Q-Learning收到的反馈是否合理。update_pu_sinr_level.m的输入是当前动作和信道增益输出是PU更新后的SINR内部逻辑通常是先把SU接入带来的干扰加到PU的噪声项里再和PU的目标SINR门限做比较。get_distortion.m则把“实际速率低于需求速率的程度”映射成代价。这两个函数合起来构成了即时代价的核心来源。function distortion get_distortion(G, action, params) % 计算当前动作下的链路失真代价 % 失真定义实际传输速率低于所需速率时的惩罚量 req_rate set_reqired_trans_rate(params); % 读取速率需求 achieved compute_achieved_rate(G, action); % 根据信道增益估算可达速率 if achieved req_rate distortion 0; % 满足需求零代价 else distortion (req_rate - achieved) / req_rate; % 缺额占比作为代价 end end这个代价函数的设计思路值得学习满足需求时代价为0不满足时代价随缺口线性增长。要注意set_reqired_trans_rate.m如果返回的是一个向量而params里没指定SU下标get_distortion里直接做标量比较会报维度错误这是子函数独立调试时最容易撞上的坑。GPU加速在Matlab R2021a里对纯m函数意义不大真正耗时在最近邻匹配那步第6章会给出优化思路。4. 在Matlab 2021a上跑通Runme.m环境、步骤与结果判读4.1 环境检查版本、路径与工作区三件事运行前的环境检查决定了能否一次跑通。Matlab版本要求是2021a或更高低版本在函数语法上大概率不兼容比如某些用到arguments块或隐式扩展的写法在R2020b会直接红字报错。路径检查是第二优先级Matlab左侧的当前文件夹窗口必须切到工程根目录否则调用子函数时搜索路径里找不到对应m文件报错信息往往是“未定义函数或变量observe_state”。第三件事是确认工作区干净最好先执行clear避免之前调试残留的变量把params结构体覆盖掉。4.2 从Runme.m开始跑通全流程操作录像里演示的顺序和我实际复现的路径一致解压压缩包到纯英文路径不要带中文和空格→ 打开Matlab 2021a → 左侧目录切到工程根目录 → 打开Runme.m → 直接点击运行。录像里那几个关键节点值得对照着暂停尤其是第一次运行控制台打印状态集维度的地方。cd /d D:\Projects\CRN_Qlearning matlab -nosplash -nodesktop -r Runme命令行方式适合批量实验但日常调试还是推荐在Matlab GUI里跑。运行过程中控制台会逐轮输出当前Episode和平均代价。q_learning_allocation2内部如果写了进度显示你会看到代价先震荡后缓慢下降。整个500轮训练在普通台式机上大概几分钟量级具体看状态集大小和SU数量。如果你看到代价从头到尾纹丝不动优先检查Q表是否被意外初始化成全零且epsilon恒定导致只做随机探索这个后面第5章专门说。4.3 结果输出与收敛性判读训练结束后Runme.m会绘制收敛曲线和最终分配结果图。判读时有一个重要原则不要只看最后一轮代价要看曲线的整体形态。正常的训练曲线应该是前期快速下降、中期震荡收窄、后期趋于平缓。如果曲线呈锯齿状且幅度不减小常见原因是epsilon没有衰减智能体永远在探索无法收敛到最优策略。如果曲线下降后又反弹要怀疑gamma过大导致的价值传播不稳定。% 绘制平均代价收敛曲线 figure(Color, w); plot(1:length(cost_history), cost_history, LineWidth, 1.5); xlabel(Episode); ylabel(Average Distortion); title(Q-Learning 收敛曲线); grid on;这张图可以直接进论文的baseline对比图。我一般会同时记录学习结束后的最终分配矩阵对比状态集里不同初始条件下SU选择的信道是否稳定。如果同一初始状态多次复现给出的分配动作一致说明策略已经收敛到确定性策略。4.4 关键超参数的调整顺序alpha、gamma、epsilon三者的调整顺序有讲究。先调alpha把学习率从0.1逐步降到0.05、0.01观察收敛曲线是否提前稳定再调gamma从0.9升到0.95会增强远期代价的考量但gamma太接近1容易产生价值振荡epsilon相对独立作用是平衡探索与利用固定探索率的工程我从没见过多数会做线性衰减或指数衰减。调整时每次只动一个变量否则干扰混淆严重很难定位是哪个参数的影响。5. 避坑清单路径、版本与四个高频运行错误5.1 双击Runme.m报“未定义函数或变量”现象从Matlab编辑器直接点运行Runme.m控制台立刻报“未定义函数或变量observe_state”或类似的子函数找不到错误。原因Matlab当前文件夹不在工程根目录。编辑器会运行脚本但函数搜索路径只包含当前文件夹和addpath添加的路径子函数m文件不在其中就一概找不到。解决看Matlab工具条下方的“当前文件夹”窗口手动切到解压后的工程目录。如果还报错检查是不是加过addpath覆盖了同名函数执行path看看搜索顺序里有没有其他目录的observe_state.m干扰。从那以后我拿到任何Matlab工程的第一件事就是先用pwd确认位置再点运行。5.2 单独运行子函数报维度错误现象想“调试一下”q_learning_allocation2.m单独点运行报矩阵维度不匹配或者提示Q未定义。原因子函数只是被主脚本调用的模块它依赖主脚本构造的params、state_set、G这些变量。单独运行时这些变量不在工作区函数入口参数不满足调用约定必然报错。解决始终从Runme.m进入整个执行流程。想调试某个子函数就在Runme.m里调用它的那一行设置断点然后点击“运行”进入调试模式工作区里能看到所有上下文变量这才是正确的调试姿势。操作录像里演示的也是这个流程。5.3 同一参数跑两次结果差异很大现象不修改任何参数连续运行两次Runme.m画出来的收敛曲线形态差不多但最终平均代价和分配矩阵有明显差异。原因整套代码里存在三类随机源randomize_G/G2初始化信道是随机的epsilon-greedy探索是随机的如果环境反馈里还混了噪声方差那也是随机的。强化学习在线交互本身方差就大不固定随机种子等于在野外做不可控实验。解决主脚本里加rng(42)或者rng(default)固定全局随机流。做论文对比实验时每种参数组合至少跑5次取均值±标准差不要拿单次结果当结论。这个坑算得上强化学习复现的最常见翻车点。5.4 训练很久但Q表基本没变现象控制台一直在打印Episode但cost_history稳定在某条水平线上Q表更新量趋近于零最终分配结果看起来像随机选的。原因状态离散化粒度太粗多个完全不同的物理状态被映射到同一个状态索引Q表感知不到环境差异或者学习率alpha太小更新权重几乎为零学习被“冻住”了。解决先验证状态集覆盖范围。把create_state_set生成的状态集画出来检查是否覆盖了observe_state在训练中实际观测到的向量区间。增加状态集密度能解决大部分“白学”问题但要注意状态集变大后Q表尺寸同步膨胀训练时长增加这是复杂度换准确度。alpha低于0.01就可以排除是学习率冻住了通常0.05到0.1之间比较合理。6. 进阶验证与FPGA衔接确认Q表收敛后再改参数6.1 画Q变化量曲线判断是否真正收敛平均代价曲线只能看到外在表现想确认Q表本身是否还在地动山摇最直接的办法是记录相邻两次迭代的Q差值的最大绝对值。随着训练推进这个差值应当单调递减接近机器精度说明Q表已经完全稳定。把这个量加进训练记录里比只看代价曲线更早暴露不收敛问题。dq max(abs(Q - Q_prev), [], all);6.2 从仿真到FPGA的衔接思路工程里那个fpgamatlab.txt文件点出了一个重要方向Matlab仿真验证Q表策略后真正上线往往要落成FPGA决策模块。我的习惯是把训练收敛后的Q表用save(Q_table.mat,Q)导出再转成coe或mif文件FPGA里用Block RAM存Q表决策时根据状态索引查表取argmin这样把在线学习变成在线查表规避了FPGA上实现强化学习更新的复杂逻辑。查表决策是纯组合逻辑时延可控适合对实时性敏感的频谱接入场景。6.3 多用户场景的维度控制技巧最后分享一个扩展经验。把SU数量从4个增加到8个时状态向量里G(:)的长度会翻倍状态集会指数膨胀训练时长完全不可控。我一般先把信道增益量化成低比特等级再用状态索引组合或者只保留对分配结果影响最大的前几条主路径增益进状态向量牺牲一点精度换取可训练性。深度强化学习算法在这里可以做降维替代但要引入网络训练复杂度高一个量级。先用Q-Learning把线性规模场景吃透再考虑往深度网络迁移是性价比最高的路线。这套工程的可贵之处在于完整性和可运行性它把协作认知无线电中感、传、算、策的全流程都落到了具体m文件里。从那以后我每次拿到这类Matlab仿真包都强制先跑通主脚本再看子函数评估算法效果前先固定随机种子确认收敛前绝不改超参。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询