
用B210跑GNU Radio的朋友十有八九都见过这么一条报错Error: RuntimeError: Expected FPGA compatibility number 24, but got 22:或者是The FPGA image on your device is not compatible with this version of UHD.第一次碰到这个我是真的愣了一下明明上次还在跑怎么突然就翻脸了。其实这个问题的定位很明确UHD驱动和板子上的FPGA固件版本对不上号。这篇文章就专门讲清楚这个版本冲突是怎么来的、怎么确认、以及怎么干净利落地解决给正在被这个报错卡住的朋友一条可以直接照做的路子。1. 先搞清楚问题本质FPGA固件版本冲突是怎么来的1.1 现象描述你看到的报错长什么样USRP B210在GNU Radio环境下工作实际上是三层配合uhd驱动库 → USRP硬件 → GNU Radio的UHD源模块。任何一层版本不匹配都会以报错的形式告诉你。常见的报错形态有这么几类RuntimeError: Expected FPGA compatibility number 24, but got 22这是最常见的说明驱动想要的固件版本号和板子里实际烧录的版本号不一致。RuntimeError: The FPGA image on your device is not compatible with this version of UHD.这种说法更直白兼容性检查直接没通过。AssertionError: Expected component UHD-FPGA...这种在调用GNU Radio流图时出现本质上是UHD源端初始化失败。有些朋友会问报错里的数字代表什么UHD的固件和驱动程序之间有一套**兼容性编号compatibility number**机制固件在出厂时烧录了一个初始版本UHD各版本对固件有各自期望的兼容性编号。只有当期望值和实际值一致时驱动才愿意继续跟硬件通信。数字对不上不管你的GNU Radio流程图画得多漂亮启动的那一瞬间就会被拦下来。1.2 冲突的根源UHD版本与固件版本不匹配版本冲突的原因总结起来逃不出这几类第一系统里UHD库被升级了。你用apt或pip升级过uhd、gnuradio驱动代码换新它期望的固件兼容性编号跟着变但B210板子上的FPGA固件还是出厂那个老版本。这是最常见的情况。第二镜像目录指向了错误版本。UHD运行时通过环境变量UHD_IMAGES_DIR查找usrp_b210_fpga.bin如果你之前手动下载过多个版本或者换过环境变量UHD可能抓到了一个不匹配的镜像。第三板子之前被其他工具刷过固件。比如做过其他实验、刷过自定义FPGA镜像后来又没刷回标准版本再回到GNU Radio环境下就会报冲突。第四UHD本身版本过旧而B210固件已经被更新过。这种情况相对少但确实存在尤其是用过uhd_image_loader更新完固件又回退了驱动版本。1.3 为什么不建议跳过这个错误刚接触SDR的初学者碰到这个报错第一反应往往是搜索怎么跳过版本检查。我强烈建议不要走这条路。原因有三点兼容性检查的目的是防止驱动和固件之间出现行为不一致。跳过检查后可能表面上能跑但采样率、频率校准、天线开关控制这些底层行为都可能在你看不到的地方出错。后续排查数据异常时版本冲突会变成一个巨大的干扰项你会浪费大量时间在一个本来可以快速解决的问题上。重新烧录固件其实并不复杂半小时内就能搞定成本远低于带病运行。正确的思路是让UHD和固件版本重新对齐。下面就从诊断和实操两个层面展开。2. 诊断与准备动手前先确认这几件事2.1 查看UHD版本和固件版本在动手改任何东西之前先确认当前状态。打开终端依次执行uhd_find_devices这个命令会列出系统能找到的所有USRP设备。如果B210连接正常你会看到类似下面的输出-- UHD Device 0 Device Address: serial: 31B1234 name: B210 product: B210 board_id: 0x050a接着查看UHD库的版本uhd_config_info --version比如输出UHD 4.5.0.0就说明你的驱动是4.5.x系列。再查看B210当前的固件信息需要从一个Python环境跑一小段代码python3 -c import uhd usrp uhd.usrp.MultiUSRP() print(usrp.get_pp_string()) get_pp_string()输出里会包含FPGA版本和固件版本信息注意看类似这样的字段Device: B210 Mboard: B210 FPGA Version: 22 Firmware Version: 7这里的FPGA Version就是板子上实际的固件兼容性编号。把UHD期望的版本和板子实际的版本放在一起对照问题就一目了然了。以最典型的场景为例uhd_config_info显示UHD 4.5.0.0get_pp_string()显示FPGA Version 22而UHD 4.5.0.0期望的FPGA兼容性编号是24那冲突确认无疑。2.2 确认镜像文件的存放位置UHD查找镜像文件遵循一个明确的顺序首先看环境变量UHD_IMAGES_DIR如果没有设置再看编译时默认路径通常是/usr/share/uhd/images。检查环境变量是否设置了echo $UHD_IMAGES_DIR再查看标准镜像目录下都有什么文件ls -l /usr/share/uhd/images/重点确认这些文件是否存在usrp_b210_fpga.binusrp_b210_bootloader.bin如果文件不存在或者你看看文件大小几乎为零那就是镜像缺失后面烧录步骤会失败。假如目录里只有一套镜像而报错说期望24得到22通常不是文件缺失而是文件版本和当前UHD期望不一致。2.3 备份当前固件重刷固件前备份不是必须的但如果你手头的板子处于特殊状态或者你后面可能要做对照实验备份一下更稳妥。操作很简单用uhd_image_loader就能把当前固件读出来uhd_image_loader --argstypeb200 --read-all --out-fileb210_backup.bin这一步会在当前目录生成一个完整的固件镜像备份。不过坦率说B210的标准固件在官网都能重新下载所以备份更多是心理安慰。真正值得你花时间的是搞清楚自己需要哪个版本的镜像。3. 实操三种解决思路与完整步骤3.1 方法一用uhd_image_loader重新烧录匹配固件这是最直接、也最推荐的方法。核心思路就是让UHD自己下载匹配当前驱动版本的固件然后烧到板子上。如果你的系统已经安装了UHD那么下载镜像和烧录工具通常都已经有了。先用下面的命令下载匹配当前UHD版本的镜像uhd_images_downloader这个命令会从官方源下载一套与当前UHD版本配套的镜像文件放到系统默认的镜像目录。执行成功后重新检查一下镜像目录ls -l /usr/share/uhd/images/usrp_b210_fpga.bin确认文件存在后开始烧录。断电重启一下B210确保USB连接稳定最好是直连USB 3.0口不要经过集线器。然后执行uhd_image_loader --argstypeb200如果板子连接正常会看到类似这样的输出Unit: B210 FPGA Image: /usr/share/uhd/images/usrp_b210_fpga.bin Erasing FPGA image... Writing FPGA image... Programming FPGA image... successful.出现successful字样就说明烧录完成。断电重新插拔一次USB线然后再次运行uhd_find_devices确认设备正常枚举。接着再用Python脚本查看一次FPGA版本import uhd usrp uhd.usrp.MultiUSRP() print(usrp.get_pp_string())如果此时FPGA Version已经变成24和UHD期望值一致那问题就解决了重新打开GNU Radio Companion跑你的流程版本冲突的报错不会再出现。3.2 方法二手动指定镜像目录和版本这种方法适合两种情况一是你有多套镜像文件放在自定义目录二是你不想重新烧录板子上的固件想直接让UHD加载指定版本镜像。先说明一条B210的重要特性B210的FPGA配置是每次上电时从主机加载的不像某些USRP设备是固化在板载Flash里。这意味着如果你有一份匹配的镜像文件只要让UHD启动时加载这份文件就行甚至不需要写板载Flash。这也是为什么很多人换了镜像目录就解决了问题原理就在这里。具体操作是把下载好的镜像文件放到一个固定目录比如~/uhd_images/然后设置环境变量export UHD_IMAGES_DIR~/uhd_images再运行你的GNU Radio程序看看报错是否消失。注意这种方法有一个关键点环境变量只对当前终端有效。如果你在脚本里调用GNU Radio或者通过Python调用UHD需要确保环境变量传到了那个进程里。最稳妥的方式是在启动脚本的头部显式加上export UHD_IMAGES_DIR$HOME/uhd_images或者干脆把UHD的镜像文件直接软链到默认目录sudo ln -s ~/uhd_images/usrp_b210_fpga.bin /usr/share/uhd/images/usrp_b210_fpga.bin这样UHD走默认路径就能找到正确版本。这个方法还有个隐含好处如果你需要做不同版本的对照实验可以准备多套镜像目录启动时切换环境变量就可以了不用反复刷板子。灵活性比方法一好很多。3.3 方法三降级或升级UHD库有时候你的镜像文件没问题但UHD驱动版本和GNU Radio要求的UHD API版本互相打架比如GNU Radio某个发行版绑定的是UHD 3.15版本但系统里装的是UHD 4.5版本。这时候直接换驱动版本也是解决办法。先说降级。如果你用的是pip安装的PyUHDpip install uhd3.15.0.0注意这样只升级了Python层面的UHD库底层的C UHD库还是要单独处理。实际上GNU Radio教程里安装UHD最通用的方式还是通过系统包管理器Ubuntu/Debian系sudo apt install libuhd-dev libuhd4.5.0 uhd-host如果需要指定版本先查一下仓库里有哪些可选版本apt-cache policy libuhd-dev根据输出选择你需要的版本再安装。如果你是源码编译安装的UHD直接删除原编译目录切换git分支重新编译git clone https://github.com/EttusResearch/uhd.git cd uhd git checkout v3.15.0.0 mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease ../ make -j4 sudo make install sudo ldconfig编译安装之前记得先卸载旧版本避免两个版本的库在系统里互相干扰。需要特别提醒的是不要只升级UHD而不处理GNU Radio的依赖关系。GNU Radio的gr-uhd模块编译时绑定的是特定版本的UHD开发头文件UHD升级后gr-uhd模块可能要重新编译否则运行时会报符号找不到之类的错误。3.4 方法选择建议三种方法没有绝对的好坏关键看你的场景场景推荐方法理由环境刚升级过UHD固件还停留在出厂版本方法一直接烧匹配固件一劳永逸不想动板子只是临时跑一个项目方法二通过环境变量切换镜像不需要持久化改动UHD/GNU Radio版本本身不兼容方法三从根源上拉齐版本关系板子多人共用别人改过镜像方法一重新烧录后可恢复统一状态我个人的经验是绝大多数情况下方法一就够了先把固件烧成和驱动匹配的版本后面就不会再折腾了。方法二适合做实验的时候临时救急方法三更多是解决整个软件栈都乱掉了的极端情况。如果你刚入坑不要想太多直接用方法一干净利落。4. 常见问题与排查技巧实录4.1 No device found 检查顺序很多朋友在烧录前会先遇到设备枚举失败的问题。B210连上电脑uhd_find_devices却返回No devices found这时候按下面顺序排查第一USB口是否真的识别了。执行lsusb | grep -i ettu正常情况会看到类似ID 2500:0020 Ettus Research LLC的输出。如果没有说明USB枚举层面就失败了换个USB口试试首选直连主板的USB 3.0口其次是USB 2.0口但要拔掉其他USB设备减少供电竞争。第二供电是否充足。B210对电源的要求很高很多No device found其实是供电不足导致的。USB 3.0口一般可以满足但是笔记本的USB口经常供电不足最好使用带独立供电的USB Hub或者使用B210原装电源适配器。第三权限问题。Linux下普通用户经常没有权限访问USB设备。临时测试可以加sudosudo uhd_find_devices长期解决需要配置udev规则安装UHD时一般已经带了规则文件检查一下是否在正确位置ls /etc/udev/rules.d/ | grep uhd如果没看到手动创建规则echo SUBSYSTEMusb, ATTR{idVendor}2500, MODE0664, GROUPdialout | sudo tee /etc/udev/rules.d/90-usrp.rules sudo udevadm control --reload-rules sudo udevadm trigger然后把自己加入dialout组重新登录后生效sudo usermod -aG dialout $USER这三个问题排查完绝大多数枚举不到设备的场景都能解决。4.2 烧录过程中断了怎么办uhd_image_loader烧录过程中如果usb线被碰掉、电脑断电、或者软件崩溃会出现什么情况这是很多人的噩梦但实际上B210的设计已经考虑到了这一点。B210的板载引导程序独立于FPGA镜像存在只要引导程序没坏设备就能重新枚举。烧录过程中断后重新插上USB线执行uhd_find_devices如果还能发现设备直接重新执行uhd_image_loader重新烧录即可。如果uhd_find_devices显示设备但状态异常尝试强制恢复模式。B210上有一个小的按钮按住按钮的同时插入USB线保持几秒钟再松开设备会进入烧录恢复模式。在这种模式下重新执行uhd_image_loader --argstypeb200,blank_eepromtrue不过一般情况下用不到blank_eepromtrue这个参数只有板子上的EEPROM信息也损坏了才需要。一般用户走到这一层的情况极少。经验之谈烧录固件时保持USB线稳定别乱动板子耐心等待它提示完成。整个过程通常一两分钟内结束真的没必要来回摇晃。4.3 版本匹配速查表与避坑建议为了减少查来查去的麻烦我整理了一份常见UHD版本和B210 FPGA固件兼容性编号的对应关系供参考。UHD版本系列B210期望FPGA兼容性编号备注UHD 3.14.x20较老版本UHD 3.15.x22广泛使用的稳定版本UHD 4.0.x22部分早期4.x仍是22UHD 4.1.x23过渡版本UHD 4.3.x24较新的主流版本UHD 4.4.x/4.5.x24最新版本常见编号这个表来自我在多个环境实测和自己的记录不代表官方文档里一定有对应描述。如果你的UHD版本期望的数字不在上表里以uhd_images_downloader下载的实际镜像为准那个镜像文件本身就包含了正确的兼容性编号信息。避坑建议给几条实在的装UHD和GNU Radio优先用系统发行版的包管理器不要混装。我自己踩过最大的坑就是系统里同时有apt装的UHD和pip装的PyUHD两者版本不一致导致GNU Radio调用时出现莫名其妙的问题。后来干脆把pip版本卸载统一用apt装世界清净了。换过UHD版本后最好重新生成一次GNU Radio的配置文件。gnuradio-config-info有时候显示的还是旧版本的UHD信息不碍事但容易误导你判断版本关系。不要随便用usrp_b210_fpga.bin的旧镜像覆盖新镜像。尤其从网盘或别人那里拷贝的镜像文件来源不可控烧录前先检查文件大小是否在正常范围。正常B210的FPGA镜像大约在几百KB到1MB不等如果文件只有几KB那多半是下载损坏。检查镜像文件的校验值。如果从官方源下载官网会提供md5或sha256校验值下载完后花十秒钟比对一下避免因文件损坏导致烧录后设备行为怪异。常见的镜像校验命令md5sum /usr/share/uhd/images/usrp_b210_fpga.bin和官网给的校验值对照一致再烧。4.4 烧录完成后的验证步骤固件烧好、环境变量配好、版本对齐了别急着开心先做一套完整的验证确保GNU Radio环境下真正能跑起来。第一步用uhd_find_devices确认设备能被发现。第二步跑一个简单的Python测试流图看能否正常收发import uhd import numpy as np usrp uhd.usrp.MultiUSRP() usrp.set_rx_freq(uhd.types.tune_request(2.4e9)) usrp.set_rx_gain(20) usrp.set_rx_rate(1e6) streamer usrp.get_rx_stream(uhd.usrp.StreamArgs(fc32, sc16)) samps np.zeros(10000, dtypenp.complex64) metadata uhd.types.RXMetadata() streamer.recv(samps, metadata, 1.0) print(metadata.error_code)如果输出的是NONE说明采样正常。如果报TIMEOUT或其他错误码说明固件虽然匹配但其他参数配置可能有问题可以先恢复到默认参数再测试。第三步打开GNU Radio Companion拖一个UHD源和一个QT GUI的频谱显示连起来跑一下。这次如果还能看到波形动态变化说明整个链路没有问题版本冲突的困扰彻底完结。4.5 关于多人共用板子与团队协作的经验如果你是在实验室或者团队里和其他人共用B210版本冲突的概率会倍增。因为不同人可能用不同的依赖管理工具、不同的Python环境甚至不同的GNU Radio发行版。我的建议是在团队内部固化一套环境约定指定统一的UHD版本和GNU Radio版本在环境说明文档里写清楚。使用Anaconda或者虚拟环境隔离不同项目的依赖而不是在系统层面反复折腾。每次更新驱动后主动执行一次uhd_images_downloader并重新加载镜像避免有人带着旧固件的板子接入新环境。记录板子的序列号和当前固件版本贴在工作台上下次有人接入时一眼就能看到状态。这些虽然看起来是管理层面的杂事但真的能帮你省下大量排查为什么我这边能跑你那边不能跑的时间。最后说一个我个人的小习惯每次解决完固件版本冲突后我会顺手在终端历史里记一下当前UHD版本和FPGA版本。做法也很简单就是执行一次uhd_config_info --version并把输出追加到一个记录文件里uhd_config_info --version ~/usrp_versions.log python3 -c import uhd; uuhd.usrp.MultiUSRP(); print(FPGA:, u.get_pp_string()) | grep -i fpga ~/usrp_versions.log这个办法虽然原始但在下一次出问题时能快速确认你上次能跑通的环境长什么样。很多人重复踩版本坑就是因为没有记录每次都要从头摸一遍。花十秒钟记一笔长远看很值。