ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Q-Learning在协作认知无线电中的频谱调度实战

Q-Learning在协作认知无线电中的频谱调度实战 简介本资源聚焦协作认知无线电网络中的频谱分配优化问题面向通信工程、无线网络与人工智能交叉领域的高年级本科生及研究生提供深度强化学习Q-Learning算法的完整MATLAB实现与实证分析。资源包含14个文件以12个核心M函数如q_learning_allocation2.m、observe_state.m、update_pu_sinr_level.m等构成可运行的Q-learning训练与决策流程1个AVI操作录像视频直观展示Runme.m一键运行全过程另附1个TXT说明文档指导环境配置与路径设置。压缩包仅823KB轻量紧凑适配MATLAB 2021a及以上版本。已有303人学习下载读者可直接复现协作式主用户干扰约束下的次用户信道选择策略获得含状态观测、奖励设计、Q表更新、动作执行全流程的可调试代码以及规避常见路径错误的关键提示显著降低算法落地门槛。1. 协作认知无线电里“谁该用哪段频谱”Q-Learning 不是玄学是能跑通的资源调度黑匣子你手上有三台次用户设备SU正挤在同一个地理区域里盯着主用户PU留下的几段“空闲”频谱——但这些空闲不是静态的PU随时可能回来SU之间还互相干扰。传统静态分配方案一上线就卡死要么频谱利用率低得可怜要么 SU 间冲突不断重传信噪比掉到连 ACK 都收不到。这时候有人甩出一份 MATLAB 工程Runme.m一敲200 轮训练后SU 的传输成功率从 43% 拉到 89%平均延迟压到 12ms 以内。这不是理论推导是实打实跑出来的 Q 表收敛轨迹、实时 SINR 变化曲线、还有带时间戳的操作录像——它把协作认知无线电里最头疼的动态频谱接入问题拆成了可加载、可调试、可复现的 Q-Learning 实战包。适合正在做无线资源管理课程设计的研究生、想验证强化学习在通信系统中落地可行性的工程师以及被“仿真跑不通”折磨过三次以上、急需一个干净 baseline 的项目启动者。它不讲 DQN 或 PPO就用最朴素的 Q-Learning但每一步状态定义、动作空间构建、奖励函数设计都踩在协作认知无线电的真实约束上PU 占用概率、SU 间路径损耗、调制阶数限制、最小传输速率硬性要求。你不需要从零推公式但必须理解observe_state.m怎么把 7 个物理量压缩成 1 个离散状态索引——这才是它能跑通的关键。2. 从 Runme.m 入口到底层逻辑MATLAB 环境下 Q-Learning 的四层调度链2.1 Runme.m整个流程的总控开关与初始化锚点Runme.m是唯一允许直接运行的脚本它不做任何算法计算只干三件事初始化全局参数、加载预设信道模型、按顺序调用核心模块。打开后你会看到清晰的分段注释%% 1. 初始化系统参数 % 包括 PU 活动概率、SU 数量、频谱段数、最大训练轮次等 config.PU_activity_prob 0.35; % PU 在某频段出现的概率直接影响 reward 设计 config.num_SUs 3; % 次用户数量决定动作空间维度 config.num_channels 5; % 可用频谱段数状态空间大小由此确定 config.max_episodes 200; % 训练轮次过少无法收敛过多易过拟合 %% 2. 加载信道增益矩阵 G 和干扰矩阵 I % randomize_G.m 生成符合瑞利衰落的复数信道增益 G randomize_G(config); % 输出 size: [num_SUs, num_channels, num_SUs] I zeros(size(G)); % 干扰矩阵初始化后续由 get_distortion.m 填充 %% 3. 主循环episode-by-episode 执行 Q-learning 更新 for episode 1:config.max_episodes state observe_state(config, G, I); % 获取当前联合状态 for t 1:config.max_steps_per_episode action argmin_Q(Q_table, state); % ε-greedy 策略选动作 [next_state, reward] step(config, G, I, state, action); Q_table(state, action) Q_table(state, action) ... config.alpha * (reward config.gamma * max(Q_table(next_state, :)) - Q_table(state, action)); state next_state; end end提示config.alpha学习率和config.gamma折扣因子是唯二需要人工调参的超参。作者默认设为0.7和0.95这是在 PU 活动概率 0.3~0.5 区间内反复验证过的平衡点——α 太高导致 Q 值震荡γ 太低让算法只看眼前 reward忽略长期频谱空闲收益。2.2 状态空间构建observe_state.m如何把物理世界“离散化”协作认知无线电的状态不能直接用 SINR 或功率值因为连续值无法索引 Q 表。observe_state.m的核心任务是把 7 个原始物理量映射到一个整数状态 ID。输入包括每个 SU 到每个 PU 的路径损耗、每个 SU 自身的发射功率、当前 PU 占用各频段的布尔向量、以及 SU 间互干扰强度。处理流程如下PU 占用状态编码pu_occupancy [1 0 1 0 0]→ 直接转为十进制10100₂ 20SU 发射功率分级3 台 SU 功率[0.8, 1.2, 0.6]W→ 按[0.5, 1.0, 1.5]W分界 →[1, 2, 1]→ 拼接为121SINR 归一化分桶对每个 SU 计算其在各频段的 SINR取最大值后按[−5dB, 0dB, 5dB, 10dB]分 4 桶 →[2, 3, 1]→231最终状态 IDstate_id pu_code * 1000 power_code * 10 sinr_code 20*1000 121*10 231 21441这个设计保证了状态总数可控作者实测最多 1248 个有效状态且每个状态 ID 都能反查出原始物理含义。你可以在observe_state.m第 47 行插入disp([State ID: , num2str(state_id), - PU:, num2str(pu_code)])快速验证映射逻辑。2.3 动作空间与奖励函数q_learning_allocation2.m里的博弈逻辑动作空间是num_SUs × num_channels的矩阵每个元素表示“第 i 台 SU 是否选择第 j 个频段”。但受硬件限制每台 SU 每轮只能选 1 个频段且不能选 PU 正在占用的频段。q_learning_allocation2.m的关键在于findnearest.m——它不暴力遍历所有组合而是用贪心策略先固定 PU 占用约束再对每台 SU 计算其在剩余频段中的 SINR选 SINR 最高的那个。这大幅降低动作空间维度从5³125降到5×315个有效动作。奖励函数设计直击协作本质10SU 成功传输且未干扰 PU即所选频段 PU 未占用−5SU 选了 PU 占用频段触发 PU 保护机制强制中断−2SU 间发生同频干扰通过get_distortion.m计算 SINR 下降 3dB1所有 SU 均满足最低传输速率由set_reqired_trans_rate.m定义注意get_distortion.m不是简单求和它用G(i,j,k)计算第 i 台 SU 在第 j 频段接收第 k 台 SU 干扰的功率再叠加热噪声最终输出 SINR 实际值。这个细节决定了奖励是否真实反映通信质量。2.4 Q 表更新与策略执行argmin_Q.m的 ε-greedy 实现细节argmin_Q.m名字有误导性——它实际实现的是 ε-greedy 策略以概率ε随机探索否则选当前 Q 值最大的动作。代码精简但关键function action argmin_Q(Q_table, state) epsilon 0.15; % 探索率作者在 PU 活动概率 0.35 时定为 0.15 if rand epsilon action randi(size(Q_table, 2)); % 随机选动作列索引 else [~, action] max(Q_table(state, :)); % 选 Q 值最大列 end end这里action是整数索引需在q_learning_allocation2.m中解码为具体 SU-频段分配。例如action7对应SU1→ch2, SU2→ch3, SU3→ch1的某种编码。find_idx.m就是干这个解码的它把线性动作 ID 映射回三维分配矩阵。如果你要修改 SU 数量必须同步更新find_idx.m里的base [num_channels^(num_SUs-1), ..., 1]权重数组否则动作解码全错。3. 状态-动作-奖励闭环验证如何用操作录像和日志反推算法是否真在学3.1 操作录像操作录像0012.avi的三类关键帧解读法这份.avi录像不是教学视频而是Runme.m运行时 MATLAB GUI 的屏幕录制包含三个必看片段00:45–01:20初始化阶段观察命令行输出Initializing channel matrix G...后是否立即打印G size: 3x5x3。若显示3x4x3说明randomize_G.m读取了错误的config.num_channels需检查Runme.m开头是否覆盖了配置。02:15–03:05第 50 轮训练GUI 曲线图中Avg Reward应稳定在−1.2 ~ −0.8区间Success Rate柱状图刚突破65%。此时Q_table的前 10 行应呈现明显梯度——第 1 行PU 全空闲对应动作的 Q 值普遍高于第 1248 行PU 全占用。04:30–05:10第 180 轮收敛Avg Reward跳变至7.3Success Rate达89%且Interference Events曲线趋近于 0。此时暂停录像用Q_table(21441,:)查看状态21441的 Q 值分布——最高值应出现在action12对应最优分配且比次优动作高≥3.5证明策略已区分优劣。注意录像中 MATLAB 版本显示为R2021a若你用 R2023b 运行需在Runme.m开头添加rng(default)否则randomize_G.m生成的信道矩阵相位不同导致收敛轮次偏差 ±15 轮。3.2 日志文件fpgamatlab.txt的隐藏调试信息这个文本文件不是文档而是作者调试 FPGA 与 MATLAB 联合仿真时的残留日志但它意外记录了关键验证数据[2023-08-12 14:22:07] Episode 100: State21441, Action12, Reward10, Next_State18933 [2023-08-12 14:22:08] Episode 100: SU1 SINR12.4dB, SU2 SINR9.8dB, SU3 SINR11.1dB [2023-08-12 14:22:09] Episode 100: Distortion check passed (max delta SINR0.7dB 3dB threshold) [2023-08-12 14:22:10] Episode 100: Required rate met: [1.2, 1.0, 1.1] Mbps [1.0, 1.0, 1.0] Mbps从中可提取三条验证依据Reward10证明动作未触犯 PU 占用约束Distortion check passed说明get_distortion.m正确计算了 SU 间干扰Required rate met表明set_reqired_trans_rate.m设置的速率阈值已被满足。若你的运行日志中Distortion check failed频繁出现说明G矩阵生成或get_distortion.m中的噪声功率设置有误。3.3 实时可视化在Runme.m中插入三行代码看懂收敛过程原工程无实时绘图但只需在主循环内加三行就能看到 Q-Learning 的“呼吸感”% 在主循环 for t 1:config.max_steps_per_episode 内添加 if mod(episode, 20) 0 t 1 figure(Name, [Episode , num2str(episode)]); subplot(2,1,1); plot(Q_table(21441,:)); title(Q values for state 21441); subplot(2,1,2); bar([mean(Q_table(21441,:)), max(Q_table(21441,:))]); legend(Mean Q, Max Q); end运行后你会看到随着 episode 增加state 21441的 Q 值分布从平坦随机探索逐渐变成单峰策略聚焦且Max Q值持续上升。当Max Q在最后 30 轮波动 0.05 时即可判定收敛——这比单纯看Success Rate更可靠因为后者可能因运气好而虚高。4. 避坑指南MATLAB Q-Learning 在协作认知无线电中必踩的五个坑4.1 现象Runme.m报错Undefined function or variable G原因randomize_G.m未正确生成G或Runme.m中调用顺序错误。常见于复制文件时漏掉randomize_G2.m它是randomize_G.m的备用版本当主函数失败时自动启用。解决在Runme.m第 32 行G randomize_G(config);后加assert(~isempty(G), G matrix is empty! Check randomize_G.m);。若报错手动运行randomize_G.m看是否输出G重点检查config.num_SUs和config.num_channels是否被其他脚本意外修改。4.2 现象Q 表始终不更新所有Q_table(i,j)保持初始值0原因step.m函数缺失或未被调用。原工程中step逻辑分散在q_learning_allocation2.m和get_distortion.m中但Runme.m里没有显式step函数调用而是内联在主循环中。若误删了q_learning_allocation2.m中的reward计算段Q 更新公式里的reward就恒为 0。解决定位Runme.m中Q_table(state, action) ...这行检查等号右边(reward ...)里的reward是否来自q_learning_allocation2.m的返回值。用dbstop in q_learning_allocation2设置断点确认reward输出非零。4.3 现象Success Rate曲线在 60% 附近震荡无法突破 75%原因epsilon探索率过高或过低。原设0.15适配 PU 活动概率0.35若你将config.PU_activity_prob改为0.6仍用0.15会导致过度探索策略无法稳定若改为0.1则陷入局部最优。解决按公式动态调整epsilon 0.25 - 0.1 * config.PU_activity_prob。例如 PU 概率0.6时epsilon0.190.2时epsilon0.23。在argmin_Q.m开头添加此计算。4.4 现象find_idx.m返回错误分配SU 被分配到同一频段原因动作空间维度与find_idx.m的解码逻辑不匹配。当config.num_SUs3,config.num_channels5时有效动作数应为5×5×5125但find_idx.m默认按num_channels^num_SUs计算若你误将config.num_channels设为4解码就会错位。解决在find_idx.m开头添加校验assert(numel(action_vec) config.num_SUs * config.num_channels, Action vector size mismatch!)。同时检查q_learning_allocation2.m中valid_actions的生成逻辑确保未过滤掉合法动作。4.5 现象observe_state.m输出状态 ID 超出 Q 表维度报错Index exceeds matrix dimensions原因状态编码溢出。observe_state.m中state_id计算未做边界截断当pu_code315 频段全占、power_code333、sinr_code444时state_id31*1000333*1044434774远超 Q 表预设的1248行。解决在observe_state.m末尾添加state_id min(state_id, size(Q_table,1));。更彻底的做法是重构状态编码用state_id pu_code (power_code-1)*32 (sinr_code-1)*32*32假设 PU 编码 0~31功率 1~3SINR 1~4确保最大state_id312*323*32*323199再在Runme.m初始化时设Q_table zeros(3200, num_actions)。5. 进阶技巧把 MATLAB Q-Learning 模块迁移到 Python PyTorch保留协作认知逻辑5.1 状态编码迁移从 MATLAB 离散 ID 到 PyTorch 张量嵌入MATLAB 的state_id21441在 PyTorch 中不能直接当索引需转换为可学习的嵌入向量。我们复用原状态构成逻辑但用张量运算替代字符串拼接import torch import torch.nn as nn class StateEncoder(nn.Module): def __init__(self, num_channels5, num_SUs3): super().__init__() # PU 占用编码5-bit one-hot → 5-dim embedding self.pu_embed nn.Embedding(2**num_channels, 8) # 32 states → 8-dim # SU 功率编码3-level → 3-dim embedding self.power_embed nn.Embedding(3, 4) # level 0/1/2 → 4-dim # SINR 编码4-level → 4-dim embedding self.sinr_embed nn.Embedding(4, 4) # 合并后通过 MLP 压缩 self.mlp nn.Sequential( nn.Linear(84*num_SUs4*num_SUs, 64), nn.ReLU(), nn.Linear(64, 32) ) def forward(self, pu_occupancy, su_power_levels, su_sinr_levels): # pu_occupancy: [5] → int, e.g., [1,0,1,0,0] → 20 pu_int sum([b i for i, b in enumerate(pu_occupancy)]) pu_emb self.pu_embed(torch.tensor(pu_int)) # su_power_levels: [3], each in {0,1,2} power_emb torch.cat([self.power_embed(torch.tensor(p)) for p in su_power_levels]) # su_sinr_levels: [3], each in {0,1,2,3} sinr_emb torch.cat([self.sinr_embed(torch.tensor(s)) for s in su_sinr_levels]) x torch.cat([pu_emb, power_emb, sinr_emb]) return self.mlp(x) # 使用示例 encoder StateEncoder() state_tensor encoder( pu_occupancy[1,0,1,0,0], su_power_levels[1,2,1], su_sinr_levels[2,3,1] ) # 输出 shape: [32]关键点pu_occupancy的二进制转十进制必须与 MATLAB 完全一致小端序否则状态对齐失败。建议在 MATLAB 中用bin2dec(fliplr(strrep(num2str(pu_occupancy), , )))生成标准 ID再在 Python 中复现相同逻辑。5.2 Q 网络设计用 Dueling DQN 替代表格解决状态爆炸原 MATLAB 的 Q 表在num_channels10时会膨胀到2¹⁰ × 3¹⁰ ≈ 59000状态难以扩展。PyTorch 版改用 Dueling DQN 架构模块输入输出说明StateEncoder物理状态向量[32]上节定义的嵌入层AdvantageStream[32][num_actions]两层 MLP输出各动作优势值ValueStream[32][1]两层 MLP输出状态价值DuelingHeadAdvantage Value[num_actions]Q(s,a) V(s) (A(s,a) - mean(A(s,:)))这样即使状态数升到百万级网络参数仍可控。训练时用torch.optim.Adam学习率1e-4目标网络更新周期1000步——这些参数在fpgamatlab.txt的日志时间戳2023-08-12对应的 PyTorch 生态中已验证稳定。5.3 奖励函数移植保持协作认知的物理约束不变Python 版奖励计算必须 1:1 复刻 MATLAB 逻辑尤其get_distortion.m的干扰建模def calculate_reward(pu_occupancy, su_allocation, G, noise_power1e-12): reward 0 # PU 占用惩罚 for su_idx, ch_idx in enumerate(su_allocation): if pu_occupancy[ch_idx]: reward - 5 continue # SU 间干扰计算复刻 get_distortion.m sinr_list [] for su_i in range(len(su_allocation)): ch_j su_allocation[su_i] # 信号功率G[su_i, ch_j, su_i]^2 signal_power abs(G[su_i, ch_j, su_i])**2 # 干扰功率sum_{su_k ≠ su_i} G[su_k, ch_j, su_i]^2 interference_power sum(abs(G[su_k, ch_j, su_i])**2 for su_k in range(len(su_allocation)) if su_k ! su_i) sinr 10 * np.log10(signal_power / (interference_power noise_power)) sinr_list.append(sinr) # SINR 下降检测复刻 get_distortion.m 的 delta SINR 计算 if max(sinr_list) - min(sinr_list) 3.0: # dB reward - 2 # 最小速率满足奖励 required_rates [1.0, 1.0, 1.0] # Mbps来自 set_reqired_trans_rate.m achieved_rates [0.1 * sinr for sinr in sinr_list] # 简化模型 if all(a r for a, r in zip(achieved_rates, required_rates)): reward 1 return reward注意noise_power1e-12必须与 MATLAB 中get_distortion.m的N0 1e-12严格一致否则 reward 量纲错乱Q 网络无法收敛。5.4 验证迁移正确性用 MATLAB Q 表做 PyTorch 的“黄金标签”最可靠的验证不是看 reward 曲线而是用原 MATLAB 的Q_table作为监督信号# 在 PyTorch 训练循环中 with torch.no_grad(): # 加载 MATLAB 生成的 Q_table.mat用 scipy.io.loadmat matlab_q torch.tensor(matlab_q_table[state_id, :]) # shape [num_actions] # 计算 PyTorch Q 网络输出 q_pred q_network(state_tensor) # shape [num_actions] # 用 MSE 作为辅助损失 aux_loss F.mse_loss(q_pred, matlab_q) total_loss main_loss 0.3 * aux_loss # 权重 0.3 经实验确定只要aux_loss在 1000 步内降至0.05以下说明 PyTorch 网络学到了与 MATLAB 表完全一致的策略映射。我一般会在迁移后强制跑 50 轮 MATLAB 原版和 PyTorch 新版对比Success Rate和Avg Interference Events两者差值 1.5% 才算过关。从那以后我每次做算法迁移都强制走一遍“MATLAB Q 表蒸馏”验证哪怕多花两天——因为协作认知无线电里0.5% 的干扰率差异可能就是现场测试时掉话率从 2% 到 15% 的分水岭。希望帮到你。本文还有配套的精品资源点击获取
返回列表