2026/10/4 6:07:55

用GNU Radio和B210搭建FM收音机:完整信号处理链路解析

用GNU Radio和B210搭建FM收音机:完整信号处理链路解析 1. 项目概述与核心价值1.1 为什么选GNU Radio加B210做FM收音机很多刚接触软件无线电的朋友第一次听说GNU Radio和USRP B210第一反应往往是这么贵的设备做收音机是不是有点大材小用。以前我也这么想但真正把整套流程跑通之后我的看法完全变了。用B210配GNU Radio做FM收音机根本目的不是听广播而是把无线电信号从天线到扬声器的完整数字信号处理链路彻底搞清楚。FM广播信号本身大家天天都在听波段固定、调制方式成熟、信号强度足够是做SDR入门验证的最佳载体。GNU Radio是这个领域的常青树它用图形化的模块连接方式把数字信号处理流程直观地呈现出来从信号源到滤波器、解调器、音频输出每个环节都是一个模块你连接它们的过程就是搭建接收机的过程。B210则是Ettus公司非常经典的USRP设备用一块AD9361射频前端芯片覆盖了70MHz到6GHz的频率范围既能发射也能接收玩FM广播所在的88到108MHz频段属于完全在它的舒适区里。1.2 这个项目能帮你解决什么问题先说几个最直观的收获。第一你会真正理解接收机链路中各个模块存在的必要性。比如为什么采样率很高的IQ信号要先做低通滤波再抽取为什么FM解调之后要再过一遍低通滤波器这些问题在课本里看十遍都不如自己在GNU Radio里把某个模块断开、听一下效果来得深刻。第二你会掌握GNU Radio这套工具链的使用方式包括流图怎么搭、参数怎么调、运行时的动态调参怎么实现这些技能后续做任何SDR项目都用得上。第三你会熟悉USRP B210从驱动安装、设备识别到射频参数配置的完整流程为后面做更复杂的收发系统打基础。我见过不少人在这一步卡住有的卡在环境安装上有的卡在GNU Radio版本和驱动不匹配上还有的卡在流图能跑但没声音这种莫名其妙的问题上。这个项目最适合的人群是有一定通信原理基础、想动手实践但还没找到合适切入点的人。你不需要是FPGA专家也不需要精通Linux内核只要会基本的命令行操作能够耐心按步骤走基本都能把这个系统跑起来。当然如果你是零基础建议先补一下IQ信号、采样率、混频器这些基本概念否则有些步骤你会知其然不知其所以然。提示FM收音机只是手段理解软件无线电的完整链路才是目的。所以别急着跳过中间环节每个模块都上手调一调、断开听一听收获会大得多。2. 系统架构与关键模块解析2.1 GNU Radio流图中的核心信号链路整个FM接收机的信号链路可以用一句话描述B210把天线收到的射频信号下变频为基带IQ数据流送入GNU Radio流图后经过抽取滤波、FM解调、音频重采样最终输出给声卡播放。这句话听起来简单但拆开来看每个环节都有讲究。B210工作在接收模式下AD9361芯片先把88到108MHz范围内的射频信号混频到基带输出两个正交分量也就是I路和Q路信号。GNU Radio里的UHD源模块负责从USRP硬件读取这些采样数据。这里有一个关键点UHD源模块输出的采样率就是我们设定的采样率默认情况下是复数数据流Complex Float32因为IQ两路数据合并在一起才能表示完整的频带信息。实际使用中我会把采样率设置在2MHz左右在这个采样率下B210足以覆盖整个FM广播频段的一部分后续通过调整中心频率就可以搜索不同电台。从UHD源模块出来之后信号进入的是抽取和滤波链路。这可能是整个流图里最容易被忽略但其实最关键的部分。B210输出的2MHz采样率的复数信号直接送给FM解调器是有问题的一来运算量太大二来FM广播信号本身只占大约200kHz带宽用2MHz去处理完全是浪费。所以我先通过一个低通滤波器把感兴趣的频段切出来然后进行抽取把采样率降到几百kHz的量级。抽取环节我用的是Rational Resampler模块它可以把信号按任意有理数比例重新采样。我习惯把2MHz的信号按5比1抽取到400kHz这样既保证了FM信号那200kHz带宽的完整性又大大减轻了后续模块的计算压力。这一步做完信号才真正适合送入FM解调器。2.2 FM解调与音频重采样的完整链路GNU Radio里内置了WBFM Receive模块这个模块就是专门解调宽带FM广播信号的。它内部做的事情可以拆成三步先用正交解调把FM信号变为基带然后经过一个低通滤波器去除高频噪声最后通过一个去加重滤波器Deemphasis来还原发射端预加重所提升的高频分量。之所以要这样做是因为FM广播在发射端会对高频分量做提升以便抵抗传输过程中的噪声接收端必须做反向处理才能还原原始音频的频响特性否则你听到的声音会觉得尖锐刺耳。WBFM Receive模块要求输入的信号采样率有一个推荐范围模块里默认的值是2.4MHz输入、480kHz输出。如果直接用它会自动做重采样但这会白白消耗计算资源。我通常在它前面先做一次Rational Resampler把采样率降到合适范围再把WBFM Receive模块内部的输入采样率参数设置成匹配的值这样整体效率是最高的。FM解调器输出的是单声道音频信号采样率通常在240kHz左右这个采样率对音频播放来说太高了。声卡常见的工作采样率是48kHz或44.1kHz所以还需要再做一次重采样。我用一个Rational Resampler把240kHz按5比1降到48kHz然后接上Audio Sink模块输出到声卡即可。2.3 为什么发射端和接收端各环节的采样率设计如此重要很多人第一次跑通流图后会觉得反正能出声参数随便填就行。这种想法在你刚入门FM收音机时危害不大但当你后面去做OFDM或者其他更复杂的系统时采样率不匹配会直接导致系统完全无法工作。FM收音机是一个非常好的学习载体因为它的信号带宽、音频带宽都是固定已知的你可以逐一验证每一个模块的参数设置是否合理。整个链路的采样率设计逻辑是这样的射频信号被B210采样后以复数IQ数据的形式输出采样率越高能覆盖的频谱范围越宽但数据量也越大FM广播单信道占用约200kHz带宽所以处理端只需要保留这200kHz的数据就足够了解调后的基带音频信号最高频率大约在15kHz左右按照奈奎斯特定理采样率只需要30kHz以上就能保留全部信息但考虑到音频输出和滤波器的过渡带做到48kHz是最稳妥的。每降低一次采样率都是在对信号做一次精炼去掉不需要的频谱成分保留有用的信息这对后续处理效率和系统稳定性都有直接好处。3. 实操过程与核心环节实现3.1 环境准备与驱动安装注意事项先说明一下我这里使用的环境是Ubuntu 20.04 LTS和GNU Radio 3.8版本。UHD驱动版本用的是3.15.0。这些信息仅供参考你可以根据自己的系统版本安装对应的最新版本但要注意GNU Radio和UHD的版本兼容关系版本差异过大时UHD源模块可能无法正常识别硬件。安装UHD驱动这一步很多人会踩坑。它有两种安装方式一种是直接用apt安装另一种是从源码编译。个人强烈建议直接用系统包管理器安装省时省力sudo apt-get update sudo apt-get install uhd-host libuhd-dev安装完驱动后需要用uhd_images_downloader命令下载B210的固件镜像uhd_images_downloaderB210的固件镜像包含FPGA镜像和DSP固件两个部分第一次上电时主机检测到设备会自动加载对应镜像这个过程只需要一次。下载完固件后可以用uhd_find_devices命令确认设备是否被正确识别uhd_find_devices如果命令输出中包含USRP B210的信息那么恭喜你驱动层面已经通了。如果设备没有被识别可以运行dmesg看看系统日志常见的是USB线缆问题或者USB接口供电不足。B210最重要的是使用USB 3.0连接USB 2.0接口虽然偶尔也能识别但数据带宽完全不够用采样率稍微设高点就会出现数据丢包。GNU Radio的安装相对简单用apt安装即可sudo apt-get install gnuradio装好之后在终端输入gnuradio-companion就能打开图形化界面也就是我们常说的GRC。GRC界面左侧是模块库中间是画布右侧是模块属性面板整体操作逻辑和很多可视化编程工具类似。3.2 搭建FM接收流图的分步详解打开GRC后先添加UHD: USRP Source模块。这个模块是整个流图的数据源头配置它时需要格外注意几个核心参数。Samp Rate采样率设置为2M也就是每秒200万个样本。Ch0: Center Freq中心频率设置为100M这个值可以根据你所在城市能收到的FM电台频点来定比如我在的城市有个特别清晰的电台在101.8MHz那我就会把中心频率设成101.8M这样信号正好落在采样频带的中间位置。Ch0: Gain Value增益设置为30dB左右先跑通流程后面再细调。注意Gain Value这个参数在不同版本的GNU Radio中可能显示为Ch0: RF Gain或直接叫Gain本质上都是调节射频前端的放大倍数。UHD源模块输出的信号是一个复数数据流接下来接上Rational Resampler。这个模块需要设置两个参数Interpolation设为1Decimation设为5含义是把输入信号每隔5个样本抽取1个采样率从2M降到400kHz。Type参数选择Complex因为IQ数据流是复数的。Rational Resampler的Type参数特别容易搞混因为GNU Radio里复数Complex、浮点Float、整数Int数据类型的区分是初学者最容易出错的点。UHD源输出的是ComplexFM解调器接收的也是Complex所以这里用Complex。如果选错类型流图连接时会显示红点表示数据类型不匹配运行时也会报错出现这种情况非常正常习惯了就好。重采样之后接WBFM Receive模块。这个模块的参数中Quadrature Rate设为400kAudio Decimation设为1因为我们在模块外已经做了重采样模块内就不需要再做抽取了。这里如果把Audio Decimation设成默认值2输出采样率就会变成200k虽然也能工作但逻辑上有点绕而且后续音频重采样会麻烦一些。WBFM Receive输出的是Float类型的单声道音频流采样率是400kHz。我用另一个Rational Resampler把它降到48kHz参数上Interpolation设为3Decimation设为25。为什么是3比25呢因为400kHz乘3等于1.2MHz再除以25正好是48kHz。如果你用的是44.1kHz采样率那对应比例就是49比400但用48kHz更省事声卡支持得也最好。最后接上Audio Sink模块Device Name保持默认值让系统自动选择默认声卡就行。Sink模块还有一个重要参数是OK To Block默认勾选即可它的作用是当声卡来不及消费数据时让上游模块暂停防止音频卡顿或撕裂。连接好所有模块后点击GRC上方的Generate按钮生成Python代码然后运行。如果一切正常你的扬声器应该就能听到电台的声音了。3.3 通过图形界面实时调台与效果优化第一次跑通听到声音兴奋劲儿过去之后就该进入调优阶段了。最常用的调台方法是在GRC运行界面上用WX GUI Slider或者QT GUI Range组件动态调整UHD源的中心频率。我的做法是在流图中多添加一个QT GUI Range控件把这个控件的输出连接到一个变量上然后让UHD源模块的Center Freq参数引用这个变量。这样流图运行后界面上会有一个频率滑竿实时拖动就能扫台体验非常直观。具体操作是从模块库的Widgets分类中拖入一个QT GUI Range在属性里把Type设为FloatDefault Value设为100MStart设为88MStop设为108MStep设为100kGUI Hint设为你喜欢的位置。然后在UHD源模块的Center Freq参数栏直接输入这个Range控件的ID名称运行时就会自动关联。调台时注意观察信号强弱增益参数要跟着频率微调。FM广播信号强度差异很大你所在城市的主台信号可能很强20dB增益就够了而偏远地区的小台可能需要40dB以上才能听清楚。判断增益是否合适的方法很简单声音清晰没有杂音就是合适出现明显的背景嘶嘶声或者失真说明增益太高了。此外WBFM Receive模块里还有一个Audio Gain参数默认值约0.65这个参数控制的是解调后音频的放大倍数。我实测下来如果信号源本身音量偏低把Audio Gain调到0.9左右会有明显改善但调到1.0以上就容易出现削波失真所以这个值要谨慎调整。4. 常见问题与排查技巧实录4.1 设备无法识别或数据丢包问题现象运行GRC流图时UHD源模块报错提示找不到USRP设备。这种问题八成出在三个地方。第一USB线缆用的不是USB 3.0的线或者插在了USB 2.0接口上。B210的数据带宽要求比较高USB 2.0虽然能识别设备但跑起来采样率稍高就会疯狂丢包甚至直接断开。第二UHD驱动版本和固件版本不匹配解决方法是运行uhd_images_downloader重新下载固件确保固件和驱动配套。第三设备电源问题B210是通过USB供电的有些电脑的USB供电能力不足会导致设备间歇性掉线。排查顺序建议是先用lsusb命令查看USB设备列表确认B210是否出现在列表中再用uhd_find_devices确认驱动能否看到设备最后用UHD自带的测试工具uhd_usrp_probe查看设备详细信息。如果UHD能识别但GRC流图运行时报错那问题多半出在GRC的UHD源模块参数配置上。现象流图运行后音频断断续续或者GRC界面上出现大量红色报错提示OOverrun和UUnderrun。Overrun表示USRP硬件缓冲区溢出数据没来得及被主机读取就被覆盖了。这通常是因为主机处理速度跟不上数据产生速度。解决办法有三个方向降低采样率比如从2M降到1.92M减少其他模块的计算量或者关闭电脑上占用大量CPU的程序。Underrun是声卡缓冲区空转说明音频数据没能及时送到声卡一般也是主机性能问题。这里有个小技巧把Audio Sink模块的Buffer Size参数适当调大一些能有效缓解Underrun。4.2 有信号但没声音的排查路径现象流图正常运行看起来也没报错但扬声器里一点声音都没有。这个问题的排查路径要从后往前追。先检查Audio Sink模块的配置确保Device Name没填错如果是多声卡系统可能要显式指定设备名称。然后在Audio Sink前面加一个QT GUI Time Sink看看音频波形在可视化界面上是否有输出。如果波形是一条直线说明前面某个环节没有信号传过来如果波形正常那问题就出在声卡配置或系统音频设备上。如果确认音频数据没问题但就是没声音那么问题可能出在FM解调之前。最常见的坑是WBFM Receive模块的输入采样率设置与实际输入信号采样率不匹配。假如Rational Resampler输出的采样率是400k但WBFM Receive的Quadrature Rate参数还停留在默认值2.4M那么解调出来的音频就会被拉伸变形甚至完全听不到人声。这种问题在图形界面上不容易看出来但通过QT GUI Frequency Sink观察解调前后的频谱就能发现异常。我的习惯是在关键节点都加上可视化模块UHD源之后加一个Frequency Sink看频谱确认B210-接收到信号且频谱上有对应电台的波形突起FM解调之后加一个Time Sink看音频波形音频重采样后再加一个Time Sink确认最终输出。这样做的好处是能快速定位问题发生在哪一段调试效率提升不是一点点。4.3 信号太弱或噪声太大的调优策略如果你所在的位置离FM发射塔比较远或者是在室内接收信号弱是常态。这种情况下不要一味调高增益因为增益放大信号的同时也会放大噪声。我实测的经验是当天线端能收到比较干净的信号时20到30dB增益是最合适的如果信号微弱先将增益调到40dB以上听到声音后再逐步往下微调找到信号刚好清晰、噪声还不刺耳的那个最佳点。天线的作用往往被初学者忽略。B210的包装盒里配备了两根简单的天线在室内用原配天线能收几个强台就不错了。如果想把灵敏度做起来建议把天线拿到窗边或者干脆接一根室外天线。FM广播用的还是垂直极化波天线尽量垂直摆放。一个小技巧是把天线吸在金属物体上或者贴在窗框上往往能明显提升接收效果虽然原理上不算正规做法但实践中很管用。注意B210本身支持全双工收发可同时进行。虽然这个项目里只用了接收功能但如果你后续想扩展做FM发射实验务必确认你所在地区对无线电发射的规定不要随意占用广播频段发射信号这既是对他人的尊重也能避免给自己惹麻烦。4.4 常见问题速查表为了方便读者快速定位问题我把日常使用中遇到过的典型问题和对应解决办法整理成了一张表按出现频率排序现象可能原因解决方法设备无法识别USB线缆不是3.0或供电不足更换USB 3.0线缆换接口检查电源UHD报固件镜像错误固件镜像未下载或版本不匹配运行uhd_images_downloader重新下载运行时报O/U错误主机处理性能不足或采样率过高降低采样率关闭其他进程加大缓冲区有频谱但没声音WBFM模块参数配置错误检查Quadrature Rate与输入采样率是否一致声音刺耳去加重参数或增益设置不当检查WBFM模块参数降低Audio Gain声音里有连续嗡嗡声天线离干扰源太近或增益过高挪动天线位置适当降低增益频率滑竿不起作用变量关联设置错误确认UHD源模块Center Freq引用了Range控件的ID4.5 一个容易被忽略的坑信道带宽与频谱中心频率的匹配很多人调台时习惯于在FM频段随意扫动频率滑竿听到哪个台就停在哪但有时会碰到一个奇怪的现象明明某个频点听起来应该是电台的位置频谱上也确实有一个明显的信号突起但解调出来的声音却是噪音或者严重失真。这个问题的根源在于FM广播信号的频谱结构。合法的FM广播信道带宽是200kHz相邻信道间隔也是200kHz电台信号中心频率一般位于88.1、88.3、88.5这样的频点上。如果你把中心频率设在了两个电台之间的空隙处比如88.2MHz那么频谱上你看到左右两侧可能各有一个电台的部分信号但本振信号并没有对准任何一个电台的完整频谱解调出来自然是乱七八糟的。解决方法是微调中心频率让频谱上那个信号的峰值正好落在显示窗口中间一般步进设成100kHz很快就能对准。5. 功能扩展与进阶方向5.1 从单声道到RDS信息解码FM收音机跑通并调优之后如果你还想进一步玩出点名堂第一个推荐扩展的方向是RDSRadio Data System解码。FM广播在传输音频的同时还在副载波上调制了一些数据信息包括电台名称、节目类型、交通信息等。GNU Radio社区里有gr-rds这个外置模块专门用来解调RDS数据。安装好之后把WBFM Receive输出的音频信号额外分一路给RDS解码器运行流图就能实时看到你正在收听电台的名称和正在播放的歌曲信息。这个功能在外面开车时用手机搜台听起来很实用自己动手实现一遍对数字调制和时分复用的理解会更深一层。5.2 用Python脚本调用USRP实现自动化扫台GRC图形化界面适合做原型验证和教学演示但你如果想把FM接收变成一个自动化工具比如定时扫描某个频段内所有电台并录音或者把IQ数据保存下来做离线分析那就得会用Python脚本直接调用GNU Radio的模块。GRC生成的Python代码本身就给你做了一个很好的示范——你可以在它基础上改参数也可以从头写一个简洁版本from gnuradio import gr, blocks, analog, audio, uhd class fm_receiver(gr.top_block): def __init__(self): gr.top_block.__init__(self) self.usrp_source uhd.usrp_source( ,.join((, )), uhd.stream_args(cpu_formatfc32, channels[0]), ) self.usrp_source.set_samp_rate(2e6) self.usrp_source.set_center_freq(100e6, 0) self.usrp_source.set_gain(30, 0) self.resampler1 blocks.rational_resampler_ccc(1, 5) self.wbfm_rx analog.wbfm_rx(400e3, 1) self.resampler2 blocks.rational_resampler_fff(3, 25) self.audio_sink audio.sink(48000, , True) self.connect(self.usrp_source, self.resampler1) self.connect(self.resampler1, self.wbfm_rx) self.connect(self.wbfm_rx, self.resampler2) self.connect(self.resampler2, self.audio_sink) def main(): fm fm_receiver() fm.start() input(Press Enter to quit...\n) fm.stop() fm.wait() if __name__ __main__: main()这段代码虽然简陋但结构完整做的事情和GRC流图完全一致。你可以在中间加一个定时器实现自动扫台睡眠几秒就换一个频率把录制的音频数据写到文件里这样一个全自动的FM电台监控器就出来了。写Python脚本的好处是代码一切都在掌控之中后续想复用某个环节做别的项目直接改脚本就行不用再拖一大堆图形模块了。5.3 从FM收音机到其他SDR项目的迁移路径FM收音机这个项目最有价值的点是它把整个SDR接收链路完整走了一遍射频前端配置、采样率规划、抽取滤波、正交解调、音频输出。这套经验可以无缝迁移到其他项目上。比如想接收航空管制频段的AM信号只需要把中心频率调到118MHz到137MHz之间把WBFM Receive换成NBFM Receive或者直接用解调模块链路的其他部分几乎不用改。想接收短波频段就换模块和参数即可。想解码数字对讲机信号B210的输出数据同样接入解码流程。唯一要提醒的一点是不同应用对硬件资源的需求差异很大。FM收音机只用了B210很小的数据带宽采样率2MHz对USB 3.0来说非常轻松。但如果你后面做20MHz甚至更高采样率的应用比如频谱监测或者宽带信号采集主机CPU占用率会明显上升内存占用也会翻倍到时候优化计算效率和采用更高效的数据类型比如8位整数代替32位浮点就是你需要考虑的新问题了。6. 写在最后一次从零到一的SDR实践心得做这个FM收音机的项目我最大的体会是软件无线电的最大门槛根本不是硬件和代码而是你对信号处理链路有没有一个全局的把握。B210本身是一块通透的板子GNU Radio也把每个模块的输入输出关系摆得明明白白真正难的是理解为什么每一个模块放在那个位置、为什么要设置那样的参数。整套系统跑通之后我建议至少再拆掉几个模块重新观察把低通滤波器拆了听听效果去掉抽取直接解调看看CPU占用率的变化把中心频率故意偏置几十kHz听听声音的扭曲程度。这些实验做一遍比你背十遍通信原理都管用。这也是我为什么一直在强调FM收音机是SDR入门最好的实践项目。说回B210这块板子它的市场价格并不便宜比起几十块钱的RTL-SDR接收棒来说确实贵了不少。但两者定位完全不同RTL-SDR只能接收带宽只支持到2.4MHz左右B210不仅能收还能发带宽最高可到56MHz可用的频率范围从70MHz横跨到6GHz。如果你想往更深的方向走比如做无源雷达实验、做跳频通信测试甚至只是简单地想让自己的接收系统在极端弱信号条件下还能稳定工作B210的投入是值得的。当然如果你只是想听听收音机买个RTL-SDR绰绰有余完全没必要上B210这一点大家根据自己实际情况来选就好。最后分享一个我个人的小习惯每次做完一个SDR项目我都会把当时的软件版本一份份记录下来包括操作系统版本、UHD版本、GNU Radio版本以及可能影响系统行为的系统依赖包版本。这看起来很麻烦但当你过几个月回头优化代码时就会发现如果没有这些记录你连为什么当时能跑现在跑不了都排查不出来。开发环境的一致性管理往往比代码本身更能决定一个项目的长期可用性。