☰
用GNU Radio和B210搭建FM收音机:完整信号处理链路解析
2026/10/4 6:07:50 网站建设 项目流程

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_downloader

B210的固件镜像包含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设为1,Decimation设为5,含义是把输入信号每隔5个样本抽取1个,采样率从2M降到400kHz。Type参数选择Complex,因为IQ数据流是复数的。

Rational Resampler的Type参数特别容易搞混,因为GNU Radio里复数(Complex)、浮点(Float)、整数(Int)数据类型的区分是初学者最容易出错的点。UHD源输出的是Complex,FM解调器接收的也是Complex,所以这里用Complex。如果选错类型,流图连接时会显示红点,表示数据类型不匹配,运行时也会报错,出现这种情况非常正常,习惯了就好。

重采样之后接WBFM Receive模块。这个模块的参数中,Quadrature Rate设为400k,Audio Decimation设为1,因为我们在模块外已经做了重采样,模块内就不需要再做抽取了。这里如果把Audio Decimation设成默认值2,输出采样率就会变成200k,虽然也能工作,但逻辑上有点绕,而且后续音频重采样会麻烦一些。

WBFM Receive输出的是Float类型的单声道音频流,采样率是400kHz。我用另一个Rational Resampler把它降到48kHz,参数上Interpolation设为3,Decimation设为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设为Float,Default Value设为100M,Start设为88M,Stop设为108M,Step设为100k,GUI 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界面上出现大量红色报错,提示"O"(Overrun)和"U"(Underrun)。

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控件的ID

4.5 一个容易被忽略的坑:信道带宽与频谱中心频率的匹配

很多人调台时习惯于在FM频段随意扫动频率滑竿,听到哪个台就停在哪,但有时会碰到一个奇怪的现象:明明某个频点听起来应该是电台的位置,频谱上也确实有一个明显的信号突起,但解调出来的声音却是噪音或者严重失真。

这个问题的根源在于FM广播信号的频谱结构。合法的FM广播信道带宽是200kHz,相邻信道间隔也是200kHz,电台信号中心频率一般位于88.1、88.3、88.5这样的频点上。如果你把中心频率设在了两个电台之间的空隙处,比如88.2MHz,那么频谱上你看到左右两侧可能各有一个电台的部分信号,但本振信号并没有对准任何一个电台的完整频谱,解调出来自然是乱七八糟的。解决方法是微调中心频率,让频谱上那个信号的峰值正好落在显示窗口中间,一般步进设成100kHz,很快就能对准。

5. 功能扩展与进阶方向

5.1 从单声道到RDS信息解码

FM收音机跑通并调优之后,如果你还想进一步玩出点名堂,第一个推荐扩展的方向是RDS(Radio 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_format="fc32", 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版本以及可能影响系统行为的系统依赖包版本。这看起来很麻烦,但当你过几个月回头优化代码时,就会发现如果没有这些记录,你连"为什么当时能跑现在跑不了"都排查不出来。开发环境的一致性管理,往往比代码本身更能决定一个项目的长期可用性。

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

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

立即咨询