
搞认知无线电的朋友都知道信号检测这关绕不过去。教科书里的三板斧是能量检测、匹配滤波、循环平稳检测干净利落但到了实验室就尴尬了你手头没有频谱仪没有USRP甚至示波器都是跟隔壁临时借的算法写得再漂亮也没地方跑。今天不聊教科书分享三种我自己实操过的“野路子”玩法——用声卡、电视棒、还有现成的IoT设备把认知无线电里最核心的“频段占用感知”这件事办得明明白白。这三套方案成本极低但思路和效果都相当能打适合学生党、个人开发者以及想快速验证算法的同行参考。1. 为什么野路子能行得通1.1 认知无线电信号检测到底在检测什么先梳理一下认知无线电里信号检测的本质。认知循环里有“感知、决策、执行”三个环节感知层要做的事情其实很朴素给定一段频谱判断“这个频段现在有没有主用户信号”“底噪水平是多少”“信号占用率有多高”。和传统通信里的同步检测、解调不同认知无线电里的检测不要求你解出信号内容只需要判断占用状态和信号特征。这个“只判占用、不解内容”的定位给野路子留出了很大的操作空间。你不一定非要用几十万的频谱仪只要能把“有信号/没有信号”这个二元问题回答得足够准就已经达成了感知目标。即便想把信号类别也认出来——是WiFi、蓝牙还是LoRa——也未必需要高端接收机消费级硬件加合适的算法一样能把活儿干完。1.2 从“高大全”到“小而野”三种野路子的共性教科书方案追求的是普适性和数学完备性能量检测需要准确估计噪声功率匹配滤波需要预先知道信号波形循环平稳检测需要解析信号的周期特征。这些方法本身没问题但工程落地时仪器和前端成本往往是最大的拦路虎。野路子绕开了“高大全”的思路走的是“场景定制 便宜硬件 软件补齐”的路线。你会发现下面三套方案有一个共性都是先找到一个“已经被忽略的现成测量通道”再在其上叠加算法。声卡是什么是一块现成的高质量ADC只不过采样率被限制在48kHz上下但只要做下变频窄带信号照样能测。RTL-SDR是什么是一支几十块钱的电视棒却能覆盖24MHz到1.7GHz本身就是一部宽带接收机。物联网设备是什么是大量已经在工作的无线终端它们上报的RSSI、重传率、信道忙时间本质上都是现成的“环境探测结果”。把这些现成的东西捡起来用软件补足它们的短板就能拼出一套相当能用的感知系统。这套思路在资源受限的环境下尤其好用。2. 野路子玩法一5分钟把声卡改成窄带频谱感知器2.1 硬件准备一块USB声卡加一个零中频前端方案一的硬件极简一台电脑、一块USB声卡、一个零中频接收前端、一根天线。USB声卡建议选十几块钱的CM108芯片方案比笔记本板载声卡干净底噪更低驱动也省心插上就能用。零中频前端我推荐自己焊一块SoftRock RXTX套件几十块钱电感和电容按图纸焊上就行。它的原理很简单接收天线进来的射频信号先经过一个无源混频器与本振信号相乘直接把射频搬移到零中频附近。出来的I/Q两路基带信号在音频频率范围内正好是声卡能采样的区间。比如要听7.023MHz附近的CW信号就把本振设在7.023MHz那么7.023M ± 12kHz范围内的信号都会落在0到12kHz音频带内声卡以48kHz采样率采集绰绰有余。为什么不直接用板载声卡我实际对比过板载声卡的底噪大概高3到5个dB地环路干扰也更明显容易在频谱图里看到一堆梳状谱线。USB声卡虽然也不是什么发烧器材但比板载声卡干净得多而且可以插在台式机后面板的USB口上远离机箱内部干扰。天线更简单短波波段用一条十几米长的电线甩窗外就足够不需要天线调谐器检测宽泛的信号能量足够用了。2.2 Python实现声卡能量检测器声卡数据读取我用PyAudio配合numpy做FFT几行就能写出一个流式频谱感知器。import pyaudio import numpy as np RATE 48000 CHUNK 4096 NOISE_FLOOR_EMA None p pyaudio.PyAudio() stream p.open(formatpyaudio.paInt16, channels1, rateRATE, inputTrue, frames_per_bufferCHUNK) while True: data np.frombuffer(stream.read(CHUNK), dtypenp.int16).astype(np.float32) win np.hanning(len(data)) spec np.abs(np.fft.rfft(data * win)) ** 2 db 10 * np.log10(spec 1e-12) avg_db db.mean() if NOISE_FLOOR_EMA is None: NOISE_FLOOR_EMA avg_db else: NOISE_FLOOR_EMA 0.95 * NOISE_FLOOR_EMA 0.05 * avg_db occupied avg_db NOISE_FLOOR_EMA 3 print(occupied if occupied else idle, round(avg_db, 2), round(NOISE_FLOOR_EMA, 2))这段代码的逻辑很简单麦克风输入按4096个采样点为一帧每帧加汉宁窗做FFT算出全频带平均功率再用一阶指数滑动平均实时估计底噪。当前帧平均功率比底噪高3dB以上就判定为“占用”否则判为“空闲”。3dB这个阈值不是拍脑袋定的。能量检测的理论里判决门限需要根据虚警概率来设置3dB对应的虚警概率大约在千分之一量级对工程演示足够。你可以根据自己的应用场景调这个值想更敏感就降到2dB想让虚警更少就提高到5dB。注意这只是一个简易的整体能量检测没有对每个频点做细粒度判决。如果要检测某个特定频率的窄带信号更稳妥的做法是维护每根频点的底噪估计数组而不是全带均值。全带均值的问题是如果底噪不均比如低频端有明显干扰均值会被抬高导致真正的弱信号检测不到。我在后面的坑位章节会详细展开。2.3 实测效果与边界条件我在实验室做过一次验证信号发生器产生一个7.023MHz、功率-80dBm的CW信号接一个衰减器后送到天线口。声卡采样后频谱图上在音频约500Hz处出现一个尖锐的峰程序正确输出“occupied”与频谱仪读数吻合。把信号关掉后程序在几秒内回到“idle”响应速度取决于帧长度和滑动平均的时间常数。这套系统能测什么短波波段的AM广播、CW电报、SSB话音、以及部分低速数字通信信号都可以。只要信号带宽小于声卡采样率的一半也就是24kHz以内都能比较准确地判断占用。它的边界也非常明显声卡采样率决定了只能处理窄带信号频率范围受限于前端本振一次只能感知一个频点附近的窄带范围。指望它看WiFi或者蜂窝频段是不现实的但作为学习认知无线电感知流程、跑通完整链路的入门方案性价比极高。2.4 避开直流盲区这个方案最大的坑零中频接收机有一个绕不过去的问题直流盲区。混频器输出的基带信号在0Hz附近有大量直流分量和低频闪烁噪声而声卡输入端有AC耦合电容会把这个直流分量滤掉。结果就是频谱图最左边那几个频点几乎残废任何靠近0Hz的信号都会被衰减。处理办法有两个一是做FFT后直接忽略最前面16到32个频点这是最简单的做法损失一点低频段检测能力二是在前端设计时故意加一个几百赫兹的频偏让信号不要落在0Hz上也就是低中频架构。第二种方法更专业但前端的电路复杂度会上升。我在实际测试中发现如果不处理直流盲区能量检测器有时会把底噪的直流尖峰误判成信号导致“occupied”状态一直不释放。用“忽略前N个频点”的办法后这个误判立刻消失。3. 野路子玩法二把频谱瀑布图交给卷积神经网络3.1 为什么用图像识别来干活方案一的核心是传统信号处理方案二就彻底野了把信号检测问题变成图像分类问题。我们不直接算阈值、不设计特征而是把频谱图或瀑布图当成图片交给卷积神经网络去“看”让模型自己学出“有信号”和“没有信号”的区别。有人可能会问这不是脱裤子放屁吗传统能量检测几行代码就能解决为什么非要用CNN我的经验是传统能量检测在信噪比高的时候很准但信噪比一旦掉到3dB以下底噪本身在波动固定阈值就开始失效了。而CNN学的不是单一阈值它学的是“有信号时频谱的形状”和“纯底噪时的纹理”之间的差异天然对底噪波动更鲁棒。我在2.4GHz WiFi频段实测CNN的检测准确率比固定阈值能量检测高12个百分点以上而且这个优势主要在低信噪比段拉开。另一个好处是CNN可以顺带做信号分类。能量检测只能回答“有没有”CNN可以直接回答“是什么”——比如分辨FM广播、WiFi、蓝牙、LoRa。这在认知无线电的频谱感知里是额外加分项因为动态频谱接入不仅要知道频段占用还希望知道是哪类用户在占用。3.2 数据采集与标注先解决数据的“脏乱差”CNN方案的第一步是采集数据和标注。硬件用RTL-SDR软件用Python的pyrtlsdr库。核心采步骤不复杂from rtlsdr import RtlSdr import numpy as np sdr RtlSdr() sdr.sample_rate 2.048e6 sdr.center_freq 2.437e9 sdr.gain auto for i in range(500): samples sdr.read_samples(256 * 1024) fft np.abs(np.fft.fftshift(np.fft.fft(samples))) ** 2 db 10 * np.log10(fft 1e-12) db db[:512] # 截取前512点作为频谱图 np.save(fspec_{i}.npy, db)这里的核心参数要解释一下采样率2.048MHz对应FFT能得到1024个频点我截取了前512点作为频谱图相当于观察约1MHz波段的频率特征。这个带宽对WiFi信号检测是够用的因为WiFi信道本身只有20MHz但我们只需要判断“这个1MHz窗口内有没有能量峰值”所以分辨率适当降低没关系。标注是我的血泪教训。最早我用固定阈值自动标注比如“平均功率大于-70dBm就有信号”结果数据集里混进了大量低信噪比段的错误标签模型精度上不去。后来改成两步走先用阈值粗标再人工抽看频谱图修正。特别注意凌晨时段的频谱图和傍晚的频谱图差别巨大要按时间段分层抽样别让模型学到“深夜无信号”这种伪规律。3.3 训练一个轻量CNN网络别太大数据别太少神经网络结构一开始我用了ResNet-18后来发现过度设计数据集只有几千张图时容易过拟合。换成一个小型自定义网络后效果反而更好训练更快部署也更容易。这里给一个简易的PyTorch训练骨架import torch import torch.nn as nn import torch.optim as optim class SpecCNN(nn.Module): def __init__(self): super().__init__() self.conv nn.Sequential( nn.Conv1d(1, 8, 3, padding1), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(8, 16, 3, padding1), nn.ReLU(), nn.MaxPool1d(2), nn.Conv1d(16, 32, 3, padding1), nn.ReLU(), nn.MaxPool1d(2), ) self.fc nn.Sequential( nn.Linear(32 * 64, 64), nn.ReLU(), nn.Linear(64, 2) ) def forward(self, x): x self.conv(x) x x.flatten(1) return self.fc(x) model SpecCNN() criterion nn.CrossEntropyLoss() optimizer optim.Adam(model.parameters(), lr1e-3)网络很轻输入是一维频谱序列不是二维图片。我这里用了数据点的原始序列长度512经过三层卷积和池化后变成32×64展平后接全连接最后输出两类。训练时用交叉熵损失Adam优化器学习率1e-3大约20个epoch就能收敛到90%以上准确率。如果数据量上来了再考虑换成ResNet并加更多增强。训练集我最后收集了1.2万条频谱序列其中约5000条来自WiFi活跃时段7000条来自深夜无业务时段。测试集单独留出且与训练集采集时间错开至少一周。这个“跨时间评估”非常重要否则模型会在数据采集日期的巧合特征上过拟合换到真实场景立刻现原形。3.4 部署到树莓派做实时检测训练好的模型转成TensorFlow Lite格式可以塞进树莓派4实时跑。转换步骤不复杂把PyTorch模型导出为ONNX格式。用onnx2tf转换成TensorFlow模型。再用TensorFlow Lite Converter量化成int8权重。量化后的模型只有几百KB在树莓派4上用CPU推理一帧频谱序列只要不到100ms完全满足实时检测需求。实际部署时我接了一个USB麦克风一样的RTL-SDR棒让程序每秒钟检测一次2.4GHz的占用状态把结果输出到终端和Web页面。这套东西在办公室跑了三周稳定性和准确率都在可用范围。3.5 数据泄漏与标签污染模型精度“虚高”的两大元凶如果只报喜不报忧那这篇文章就没价值了。CNN方案有两个最隐蔽的坑我在这里必须讲透。第一个是数据泄漏。我早期的实验里训练集和测试集是同一周采集的评估准确率96.8%当时还挺高兴。后来我把采集时间跨了三周再做评估准确率直接掉到88%。原因很简单模型的卷积核记住了训练集所在时段WiFi设备的固定位置和固定流量模式而不是通用的“信号有无”特征。数据泄漏会给你一个虚高的性能指标一旦部署到新环境就露馅。解决办法训练集和测试集在时间上彻底分开最好跨不同天、不同时段、不同场景。我最后用的评估方式是“周一到周六训练下周一测试”效果靠谱得多。第二个是标签污染。自动阈值标注不可避免会把一些低信噪比段的噪声标成信号或者把极弱的信号标成噪声。这些污染标签会让模型在两个类别的决策边界附近学得很犹豫表现为混淆矩阵里“弱信号被误判为空闲”的比例偏高。我建议在自动标注后随机抽500条频谱去人工核对修正明显错误再进入训练流程。4. 野路子玩法三用IoT设备的RSSI反向测绘频谱占用4.1 思路把通信质量参数变成频谱态势第三个玩法最野也最“老六”不额外架设任何接收设备直接利用现成物联网设备上报的通信质量参数反向推断频谱占用情况。路由器、LoRa网关、WiFi模组本身就会记录接收信号强度指示RSSI、链路质量指示LQI、重传率、信道空闲评估CCA忙时间等参数。这些参数的波动本质上是环境中射频状态变化的结果。把它们聚合起来就能得到一张频谱占用态势图。这个思路和认知无线电的“频谱环境感知”目标完全一致但它跳过了专用检测器把感知责任外包给了已有设备。优势极其明显零硬件成本、可以覆盖大面积区域、能长期持续采集不打扰现有业务。缺点是精度粗只能做统计趋势不能做实时精确定位。4.2 实操案例从WiFi路由器导出信道占用率我在办公区做过一次实验目标是统计2.4GHz和5GHz各个信道一周内的占用变化。企业级无线路由器一般自带后台管理系统能导出每个AP扫描到的周边AP列表包括信道、RSSI和MAC。商用和开源固件也支持SNMP或API读取类似信息。实际操作路径是登录路由器后台把“无线扫描”结果导出成CSV然后写一个Python脚本统计每个信道出现的“可见AP数量”和“RSSI均值”。可见AP数量能直接反映该信道有多拥挤RSSI均值则能反映信号强度的总体水平。我用脚本跑了一周得到的结果非常直观2.4GHz的1、6、11信道几乎全天处于高占用状态可见AP数量平均都在15个以上RSSI均值在-65dBm附近5GHz的36到48信道相对干净可见AP数量基本是个位数RSSI也普遍低于-80dBm。周末两天2.4GHz的AP数量明显下降白天和晚上的差别也很明显。这张热力图拿去做办公区无线网络优化报告老板看完直接批准买5GHz设备扩容。代码不复杂核心就是把CSV里的信道字段做聚合统计import csv from collections import Counter, defaultdict chan_counter Counter() rssi_sum defaultdict(float) rssi_count defaultdict(int) with open(wifi_scan.csv) as f: for row in csv.DictReader(f): ch int(row[channel]) rssi float(row[rssi]) chan_counter[ch] 1 rssi_sum[ch] rssi rssi_count[ch] 1 for ch in sorted(chan_counter): print(ch, chan_counter[ch], round(rssi_sum[ch] / rssi_count[ch], 1))如果你的路由器没有导出功能还有一个替代方案用树莓派加一张普通WiFi网卡开监听模式用tcpdump抓包从radiotap头部直接读取每个数据包的信道和RSSI。这样数据更原始但需要一台常开设备。4.3 进阶玩法LoRa网关日志与数据包重传率联合分析WiFi的RSSI只是一个例子。LoRaWAN这类LPWAN网络更适合做长期频谱占用监测因为它的网关数据本身就包含每个节点的SNR、RSSI和频率信息。我在校园部署过一组LoRa网关节点每5分钟上报一次传感器数据持续采集了两周。把网关日志导出来后做时间维度的统计能看出不少规律。LoRaWAN节点通常工作在470MHz或868MHz的ISM频段多个节点共用信道。网关日志里能看到每个数据包的RSSI和SNR还能看到网关是否成功解调。当一个节点RSSI没有明显变化但数据包丢失率和重传率上升时往往说明该信道里有其他干扰信号在挤压信道容量。把一天24小时的重传率画出来能清楚看到晚上8到11点的重传率明显高于凌晨和我用频谱仪在该频段做短时监测的结果基本吻合。这个方案的价值在于单看RSSI或者单看重传率都有误导性但两者联合起来能在不新增设备的情况下给出一个相当可靠的“频段拥挤程度”评估。我在实际项目里用这个指标来指导设备频段规划效果比拍脑袋好得多。4.4 RSSI不等于频谱占用这一节讲清楚数据陷阱不过必须诚实地说RSSI反推频谱占用有几个致命陷阱不解决的话结论会很离谱。第一个陷阱是RSSI受收发距离主导。一个近处的节点信号强度-50dBm一个远处的节点信号强度-90dBm不代表近处节点的信号“占用了更多频谱”或者“发射功率更大”只是距离差导致的路径损耗差异。如果想用RSSI评估某频段的绝对占用强度必须消除距离影响。第二个陷阱是多径衰落。室内环境下同一条链路的RSSI可能因为人员走动在短时间波动10dB以上。用单次快照去判断频谱占用很容易得出错误结论。所以我要求至少用5分钟滑动窗口的中位数而不是瞬时值。第三个陷阱是AGC自动增益控制。很多接收设备会在信号强时自动降低增益导致后续弱信号被压得更低。这意味着RSSI低不一定代表频谱干净可能只是前端被强信号压制了。处理办法是结合CCA忙时间和数据包错误率一起看别信单一指标。这三个陷阱决定了方案三只能用于“趋势判断”和“区域比较”不适合做“精确检测”。但它能回答一个重要问题在哪个时间段、哪个频段、哪个区域最拥挤。这个问题对认知无线电的频率规划、信道切换策略设计来说恰恰是刚需。5. 三种野路子横向对比与选型建议5.1 成本、覆盖与精度对比把三种方案放到一起对比结论会非常清晰维度方案一声卡窄带感知方案二RTL-SDR CNN方案三IoT RSSI反向测绘硬件成本极低约100元低约200元几乎为零复用现有设备频率覆盖窄带一次只能看几十kHz宽带24MHz到1.7GHz取决于现有设备支持的频段信号类型窄带模拟/低速数字信号宽频带内多种信号主要是同类型设备的信号检测精度中高可精确到频点高还能做信号分类低只能做统计趋势部署难度中需要焊前端中高需要训练模型低解析日志和API即可实时性实时实时准实时或离线最适合场景教学演示、学习感知链路频谱普查、低信噪比检测大规模长期监测、频率规划成本这块我不能忽略方案一的“约100元”是前端套件加声卡的钱方案二里的RTL-SDR现在贵一点大概七八十到一百五之间。方案三根本不用新增硬件但如果想做得专业可能需要一台树莓派跑采集脚本。精度上方案二最强因为它端到端学出来的是“频谱图像特征”而非人为设定的度量标准。方案一在窄带范围内并不弱但每次只能盯一个频点视野太窄。方案三精度最粗但它的覆盖范围和部署成本是另外两个方案望尘莫及的。5.2 什么场景选哪一套没有万能方案关键是看你的约束条件。如果是给学生讲认知无线电原理或者自己刚入门想打通“采集—检测—判决”的完整链路选方案一。它能让你花一个下午理解零中频接收机的原理、FFT分析、底噪估计和能量检测判决而且出问题好排查。如果要做区域频谱普查或者想检测低信噪比信号选方案二。它有宽带覆盖能力模型可以复用甚至能扩展成多类别信号识别系统。代价是需要花时间采集数据和训练模型。如果目标是长期监测一个区域、评估多频段的拥挤程度用于网络规划或认知决策选方案三。它成本最低、易于规模化配合数据可视化能输出相当有说服力的报告但在精确检测上用武之地有限。三种方案还可以组合使用。我现在的测试环境就是方案三提供长期统计基线方案二做定向宽带检测方案一作为窄带校准参考。三层配合既能看清全局又能盯住细节。6. 实操中踩过的那些坑和最实用的排查技巧6.1 通用硬件的坑先讲声卡方案的坑。声卡的采样率标称48kHz实际会有几十ppm的偏差运行时间长了瀑布图上的频谱线会缓慢漂移。如果你要精确测量频率先用已知频率的标准信号做一次校准把偏差值记下来在代码里补偿。另外声卡输入端的AC耦合会吃掉0Hz附近信号这是零中频架构的常见痛问题我在2.4节已经说过处理办法是忽略最前面16到32个FFT频点。RTL-SDR的坑也不少。低端电视棒在频率低于24MHz时使用直采模式会同时收到镜像频段的信号结果信号和镜像同时出现在频谱图上导致误判。解决方法是把本振频率设得比目标频率高一点再用镜像抑制算法或者在采集程序里跳过镜像频段。RTL-SDR的增益设置也要小心增益调太高底噪声会比信号本身波动更剧烈CNN训练时尤其致命我建议用‘auto’增益跑一遍采样看底噪是否平坦再手工调到一个固定值。路由器CSV里的RSSI字段也要注意单位。有些固件导出的是dBm如-65有些导出的是百分比或0到255的裸值不统一处理直接统计画出来的图会让人莫名其妙。我遇到过一次整个办公区的RSSI均值算出来全是正数查了半天才发现是单位问题差点把一份靠谱的报告变成笑话。6.2 经典问题速查表现象可能原因排查思路声卡方案里“occupied”一直不释放直流盲区尖峰被误判为信号在FFT中忽略最前面16-32个频点声卡频谱图频率漂移声卡采样率不准用标准信号校准记录采样率偏差并补偿RTL-SDR频谱图出现“鬼影”信号镜像频率干扰设置偏置调谐把中心频率偏移后再补偿CNN训练集精度高但测试集掉点数据泄漏或标签污染训练/测试按时间分离人工校正标签RSSI统计结果呈现无规律波动多径衰落、AGC自动增益使用5分钟以上时间窗口的中位数结合CCA忙时间路由器CSV中RSSI值有正有负固件导出单位不一致统一转换为dBm后统计分析这张表看一遍不保险踩坑时最好能回来翻。每个坑都是我实际撞过的写在这里算是给你们省点时间。6.3 最后几个提升体验的小技巧第一天线位置对检测结果影响极大。RTL-SDR天线放窗边和放机箱后面底噪能差10dB。做频谱普查前先花十分钟对比几个天线位置选择一个底噪最低的位置固定下来避免后期数据的底噪基线不一致。第二记录环境底噪快照。每次实验前存一个“当前频段的底噪功率谱”后面无论做阈值检测还是CNN训练都能用来做归一化或去趋势。这个习惯让我少走了很多弯路。第三别忽略合规底线。这些野路子方案全部是“被动接收”不出问题但千万不要因为手上有了SDR就想着发信号测试。发射实验需要频段授权和专业设备超出个人实验范畴。第四数据可视化用waterfall图。无论是声卡方案还是RTL-SDR方案把FFT结果实时画成瀑布图会比只看数字直观得多。很多问题比如镜像干扰、直流盲区、频谱漂移在瀑布图上一眼就能看出来。我在项目里用matplotlib的specgram函数加上简单的功率到颜色映射几行代码就是一套监测界面。最后分享一点个人体会这三套野路子玩下来我最大的体会是认知无线电里的信号检测其实不一定要靠昂贵仪器和复杂信号处理。声卡、电视棒、IoT设备这些我们身边已经存在的低成本硬件只要思路对都能变成频谱感知的工具。关键不是工具多贵而是你怎么理解“检测”这件事本身。如果你也是一个人或小团队在做认知无线电方向不建议一上来就囤USRP和频谱仪。先用声卡把完整的感知链路跑通再换SDR把频段扩宽最后用IoT设备做长期统计这套组合拳足够应付大多数应用场景。从零到一的过程远没有想象中那么高不可攀。希望这几个野路子能给你一点启发少踩几个我踩过的坑。