☰
机器学习频谱感知实战:从特征工程到在线推理的认知无线电链路
2026/10/5 6:25:00 网站建设 项目流程

简介:这份资源面向计算机、电子信息工程、数学等专业的大学生及科研入门者,围绕认知无线电中的频谱感知任务,提供一套基于机器学习的完整MATLAB仿真方案,可用于课程设计、期末大作业与毕业设计。压缩包共22个文件、约14.78MB,包含8个csv数据集、5个ipynb算法笔记、2个py脚本、2份pdf报告与2个md说明文档,覆盖训练测试样本、信噪比标签及SVM、KNN、决策树等算法的实现与对比。代码采用参数化编程,参数可灵活调整,注释清晰,并附赠可直接运行的案例数据,便于快速复现与二次修改。资源还整理了会议论文与实验报告,帮助读者理解能量检测、协作检测及机器学习分类在频谱感知中的建模思路。目前已有52人学习,适合希望从理论到仿真完整掌握频谱感知流程的读者参考。

1. 频谱感知遇上机器学习:认知无线电里最该先跑通的一环

认知无线电要解决的核心矛盾很直白:频谱资源被静态分配占满,但很多授权频段在时间和空间上大量空闲。次级用户想用这些空隙,第一步不是发射,而是先判断"这段频谱现在到底有没有人用"——这就是频谱感知。传统做法靠能量检测、循环平稳检测这类固定门限方法,在低信噪比、噪声功率漂移、多径衰落的场景下,门限一偏就翻车,虚警和漏检同时上来。把机器学习引进来,本质是用数据驱动的方式替代人工设定门限,让感知器自己从信号特征里学出"有主用户"和"只有噪声"的边界。这篇笔记面向做认知无线电、无线信号检测、课程设计选题的工程师和学生,从特征工程讲到模型训练再到在线推理,把一条能复现的链路走完,顺带把参数怎么设、坑在哪讲清楚。

2. 为什么能量检测在低信噪比下会崩:从假设检验到特征可分性

2.1 能量检测的数学边界

能量检测的判决规则很简单:把接收信号在一段时间内的采样能量累加,和门限比较。设接收信号为 $y(n)$,在 $H_0$(只有噪声)和 $H_1$(主用户信号加噪声)两种假设下,检验统计量为

$$T = \frac{1}{N}\sum_{n=1}^{N}|y(n)|^2$$

门限由目标虚警概率 $P_{fa}$ 反推。问题出在:当信噪比低于 -10 dB 时,$H_0$ 和 $H_1$ 下的能量分布严重重叠,要压住虚警就得抬高门限,漏检率立刻飙升。更麻烦的是噪声不确定性——实际接收机前端噪声功率会随温度、增益漂移,标定时的门限过一会儿就不准了。这就是所谓的 SNR wall,能量检测在墙以下无论怎么加采样点都救不回来。

机器学习切入的点在于:不再只看能量这一个标量,而是从信号里提取多维特征,让两类样本在高维空间里重新变得可分。哪怕单看能量分不开,循环谱特征、高阶累积量、协方差矩阵结构这些维度上,主用户信号和噪声的差异依然存在。

2.2 特征工程:把信号变成模型能吃的向量

常见做法是提取以下几类特征,拼成一个特征向量喂给分类器:

特征类别具体特征对噪声的鲁棒性计算量
能量类平均功率、能量方差低 SNR 下差极低
循环平稳类循环谱密度、循环频率峰值强,能抗噪声不确定性高
高阶累积量$C_{40}$、$C_{41}$、$C_{42}$对高斯噪声天然免疫中
协方差类协方差矩阵特征值分布、最大最小特征值比中高中
时频类小波系数、STFT 能量熵中中高

高阶累积量之所以对高斯噪声免疫,是因为高斯过程的三阶及以上累积量恒为零,主用户调制信号(如 QPSK、OFDM)则非零。协方差类特征利用的是多天线或过采样带来的信号相关性,噪声在理想情况下不相关,主用户信号有相关性,最大最小特征值比(MME)就是经典做法。

我一般会先用能量 + 高阶累积量 + 协方差特征值比这三类拼一个 8 到 12 维的向量,维度不高,训练快,在 -15 dB 以上基本能拉开差距。循环谱特征虽然强,但计算量大,在线推理时如果采样率不高、实时性要求紧,可以先不上。

2.3 从假设检验到分类问题的映射

把频谱感知建模成二分类问题:$H_0$ 是负类(只有噪声),$H_1$ 是正类(主用户存在)。评价指标不能只看准确率,因为实际场景里 $H_0$ 样本远多于 $H_1$,类别不平衡。真正该盯的是检测概率 $P_d$ 和虚警概率 $P_{fa}$,以及它们的权衡曲线(ROC)。工程上通常先固定 $P_{fa}$ 到一个可接受的值(比如 0.1),再看 $P_d$ 能到多少。这一点和能量检测的 Neyman-Pearson 准则是一致的,只是现在判决面由模型学出来,而不是人工设。

3. 用 Python 跑通一条频谱感知链路:数据、特征、模型

3.1 生成仿真数据集

没有实测数据时,先用仿真数据把链路跑通。下面这段代码生成 QPSK 主用户信号加高斯白噪声,提取能量、高阶累积量和协方差特征,打标签存成训练集。

import numpy as np from scipy.signal import welch def qpsk_signal(n, fs, fc, snr_db): """生成 QPSK 调制信号,加高斯白噪声""" t = np.arange(n) / fs # 随机 QPSK 符号 symbols = np.random.choice([1+1j, 1-1j, -1+1j, -1-1j], size=n//4) # 上采样 baseband = np.repeat(symbols, 4)[:n] carrier = np.exp(1j * 2 * np.pi * fc * t) signal = np.real(baseband * carrier) # 按 SNR 加噪声 sig_power = np.mean(signal**2) noise_power = sig_power / (10**(snr_db/10)) noise = np.random.randn(n) * np.sqrt(noise_power) return signal + noise def extract_features(x): """提取能量、高阶累积量、协方差特征""" # 能量特征 energy = np.mean(x**2) energy_var = np.var(x**2) # 二阶与四阶累积量(实信号简化版) m2 = np.mean(x**2) m4 = np.mean(x**4) c4 = m4 - 3 * m2**2 # 协方差矩阵特征值比(用滑窗构造虚拟多通道) L = 8 X = np.array([x[i:i+L] for i in range(len(x)-L)]) R = X.T @ X / X.shape[0] eigvals = np.linalg.eigvalsh(R) eig_ratio = eigvals[-1] / (eigvals[0] + 1e-12) return [energy, energy_var, m2, c4, eig_ratio] def build_dataset(n_samples, n, fs, fc, snr_range): X, y = [], [] for _ in range(n_samples): snr = np.random.uniform(*snr_range) # H1: 有主用户 x1 = qpsk_signal(n, fs, fc, snr) X.append(extract_features(x1)); y.append(1) # H0: 只有噪声 x0 = np.random.randn(n) * np.sqrt(np.mean(x1**2) / (10**(snr/10))) X.append(extract_features(x0)); y.append(0) return np.array(X), np.array(y) X, y = build_dataset(2000, 1024, 1e6, 1e5, (-20, 0)) print("特征维度:", X.shape, "正样本比例:", y.mean())

这段代码的逻辑:qpsk_signal生成主用户信号,extract_features把一段采样变成 5 维特征向量,build_dataset在 -20 到 0 dB 范围内随机取 SNR,每个 SNR 下同时生成 $H_1$ 和 $H_0$ 样本。参数上,n=1024是单次感知的采样点数,采样点越多特征越稳但感知时延越大;L=8是构造协方差矩阵的滑窗长度,太小特征值比不稳,太大样本数不够。SNR 范围覆盖到 -20 dB 是为了让模型见到困难样本,实际部署时如果工作 SNR 在 -10 dB 以上,可以收窄范围让模型更专注。

3.2 训练分类器并看 ROC

特征有了,接下来选模型。频谱感知这种中小维度、样本量几千到几万的场景,随机森林和 SVM 是稳妥选择,深度网络反而容易过拟合且推理慢。下面用随机森林训练并画 ROC。

from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.metrics import roc_curve, auc import matplotlib.pyplot as plt X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=42, stratify=y) clf = RandomForestClassifier( n_estimators=200, # 树的数量,200 在精度和速度间平衡 max_depth=12, # 限制深度防过拟合 min_samples_leaf=5, # 叶节点最少样本,抑制噪声 class_weight='balanced', # 处理类别不平衡 random_state=42) clf.fit(X_train, y_train) y_score = clf.predict_proba(X_test)[:, 1] fpr, tpr, _ = roc_curve(y_test, y_score) roc_auc = auc(fpr, tpr) print(f"AUC = {roc_auc:.4f}") # 固定虚警率下的检测概率 idx = np.searchsorted(fpr, 0.1) print(f"Pfa=0.1 时 Pd={tpr[idx]:.4f}")

参数说明:n_estimators=200是树的数量,再多收益递减;max_depth=12限制单棵树深度,频谱感知特征维度低,树太深会记住噪声;min_samples_leaf=5让叶节点至少 5 个样本,提升泛化;class_weight='balanced'在正负样本不均衡时自动加权。跑完看 AUC 和固定 $P_{fa}$ 下的 $P_d$,如果 $P_{fa}=0.1$ 时 $P_d$ 不到 0.9,说明特征区分度不够,回去加循环谱或换更高阶累积量。

3.3 在线推理:把模型塞进感知循环

训练完的模型要能在线跑。实际感知节点算力有限,推理延迟要控制在毫秒级。随机森林推理就是若干次比较,比神经网络快得多。下面是一个简化的在线感知循环。

import joblib joblib.dump(clf, "spectrum_sensing_rf.pkl") clf_loaded = joblib.load("spectrum_sensing_rf.pkl") def sense_once(samples): """输入一段采样,输出是否检测到主用户""" feat = np.array(extract_features(samples)).reshape(1, -1) prob = clf_loaded.predict_proba(feat)[0, 1] return prob > 0.5, prob # 模拟在线循环 for _ in range(5): snr = np.random.uniform(-15, -5) samples = qpsk_signal(1024, 1e6, 1e5, snr) detected, prob = sense_once(samples) print(f"SNR={snr:.1f}dB 检测={detected} 置信度={prob:.3f}")

extract_features必须和训练时完全一致,特征顺序、归一化方式都不能变,否则模型输入分布偏移,精度直接崩。prob > 0.5是默认判决门限,实际部署时按目标 $P_{fa}$ 调这个阈值,比如要压虚警就提到 0.7。在线循环里每次感知独立,如果要做时间累积,可以把连续多次的置信度做滑动平均再判决。

4. 避坑与排查:频谱感知落地时最容易翻车的五件事

4.1 训练集和测试集来自同一段噪声,AUC 虚高

现象:离线 AUC 跑到 0.99,一上实测数据 $P_d$ 掉到 0.6。原因:仿真时 $H_0$ 和 $H_1$ 用的是同一组噪声参数,模型学到了噪声的统计指纹而不是信号特征。解决:训练集和测试集的噪声功率、噪声类型(高斯、有色、脉冲)要分开生成,最好留一段完全没参与训练的实测数据做验证。

4.2 特征没做归一化,树模型之外的全翻车

现象:换 SVM 或逻辑回归后精度惨不忍睹。原因:能量特征量级在 $10^{-3}$,协方差特征值比在 $10^2$,量级差几个数量级,距离-based 模型被大特征主导。解决:训练前用StandardScaler做零均值单位方差归一化,归一化参数存下来,在线推理时用同一套参数变换。

4.3 感知周期和主用户帧结构对不上

现象:主用户明明在发射,感知结果却时有时无。原因:感知窗口跨了主用户的静默期或保护间隔,采到的样本一半有信号一半没信号,特征被平均掉。解决:感知窗口要小于主用户最短发射时长,或者做多段感知取多数投票。常见做法是感知时长设为主用户帧长的 1/10 到 1/5。

4.4 类别不平衡导致模型偏向判"无主用户"

现象:准确率 95%,但 $P_d$ 只有 0.5。原因:实际频谱大部分时间空闲,$H_0$ 样本远多于 $H_1$,模型学会全判 $H_0$ 就能拿高准确率。解决:用class_weight='balanced',或者对 $H_1$ 过采样、对 $H_0$ 欠采样,评价指标换成 $P_d$/$P_{fa}$ 而不是准确率。

4.5 模型更新后忘了同步特征提取代码

现象:模型重新训练后精度反而下降。原因:改了特征提取逻辑(比如加了新特征、改了滑窗长度),但在线推理用的还是旧代码,特征对不上。解决:特征提取和模型打包成一个版本化模块,用同一个 commit 部署,推理前做一次特征维度校验。

5. 把感知器做稳的进阶技巧:从单点判决到协作感知

单节点感知在深衰落场景下总有盲区,工程上更常见的是多节点协作。做法是每个感知节点各自提取特征、跑本地模型,输出置信度,融合中心再做一次判决。融合规则可以是硬融合(多数投票)或软融合(置信度加权平均)。软融合在低 SNR 下更稳,因为保留了每个节点的置信度信息。

一个我常用的技巧是:把多个节点的特征向量直接拼接,训练一个融合模型,而不是先本地判决再投票。这样模型能学到节点间的空间相关性——比如某两个节点同时高置信度,可信度比单个节点高置信度更高。代价是融合中心要收集原始特征,通信开销大,适合节点数不多、回传链路带宽够的场景。

验证协作感知有没有效,别只看整体 $P_d$,要分场景看:所有节点 SNR 都高时,协作和单点差不多;部分节点深衰落时,协作能把 $P_d$ 拉回来多少。如果协作后 $P_d$ 提升不到 5 个百分点,说明节点间相关性太强,分集增益有限,不如把资源投到单节点特征优化上。

最后说个我踩过的坑:早期做协作感知时,融合中心用所有节点的特征训练,结果模型严重依赖节点数量,换一个节点数不同的场景就得重训。后来改成每个节点独立模型 + 置信度软融合,扩展性好了很多,加节点不用重训。频谱感知这事,模型不是越复杂越好,特征稳、数据分布对、在线链路一致,比换个花哨的网络结构管用得多。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询