CSI(Channel State Information,信道状态信息)这个名词,搞无线通信和无线感知方向的同学应该不陌生。最近我在复现几个基于CSI的人体行为识别实验,需要在Linux环境下用Intel 5300网卡采集原始的CSI数据,于是去翻802.11n CSI tool的安装教程。结果发现,网上能搜到的资料要么年代久远、链接失效,要么步骤含糊,照着做根本跑不通。折腾了两天,踩了一堆坑之后,总算把整套环境搭起来了。这篇教程把我实际验证过的安装流程完整记下来,从原理说到命令,再到排查方法,希望能帮后面做CSI方向的同学省下一些时间。
这个工具解决的核心问题,是把普通WiFi网卡变成一套"感知设备"。普通的无线网卡只能给你信号强度(RSSI),而CSI能把每条子载波上的幅度和相位信息都暴露出来,这才有足够的细节去做人的动作识别、室内定位、呼吸检测这类研究。我接下来说的安装流程,是基于Ubuntu 12.04/14.04这类老版本系统设计的,如果你用的是新内核的机器,前面的大部分步骤会直接编译失败,这个先做好心理准备。
1. 为什么CSI tool这么“挑环境”?先搞懂它在改什么
很多人在安装这个工具时卡住,根本原因是没弄明白它改动了系统的哪几层。这不是一个普通的用户态程序,装个依赖就能跑。802.11n CSI tool实际上包含三部分改动:硬件固件、内核驱动模块、用户态采集工具。三者必须配套才能工作。
1.1 它为什么要动网卡固件?
Intel 5300网卡(芯片组代号IWL5300)本身就有计算CSI的能力,但在出厂固件里,这个信息不会被导出到驱动层。David Halperin团队当年的工作,本质上是修改了网卡的微码(microcode),让网卡在每次收到数据包时,自动把各子载波的CSI信息写进内存的特定区域,再通过驱动暴露出来。修改后的固件文件名一般是iwlwifi-5000-2.ucode的变体,安装时要替换掉系统自带的同系列固件。
这里要强调一点:这个修改版固件只对Intel 5300系列有效,5100、6200、6300这些型号都不行。CSI tool读的就是网卡固件计算出的信道矩阵,网卡型号不对,后面的一切都无从谈起。
1.2 内核驱动层的改动是关键
固件导出数据之后,还需要驱动把这部分数据从网卡的DMA缓冲区搬到用户态可见的debugfs接口。原版iwlwifi驱动是没有这个功能的,所以CSI tool项目维护了一批针对不同内核版本的补丁,需要你把补丁打进内核源码里,重新编译iwlwifi模块(可能连带mac80211模块一起编译)。
为什么要编译内核模块而不是直接加载现成的.ko文件?因为内核模块和内核版本是强绑定的,补丁是2012年前后写的,只适配2.6.36到3.2.x这一阶段的内核。新版内核里iwlwifi的代码结构早就重写了,补丁根本打不进去。这也是为什么我建议你优先准备一个老版本的Ubuntu系统,而不是想着在Ubuntu 20.04或者22.04上硬装。
1.3 用户态工具与数据链路
用户态部分相对简单,主要是linux-80211n-csitool-supplementary这个仓库,里面包含了数据采集脚本(log_to_file)和MATLAB解析代码。数据链路是:网卡固件 -> iwlwifi驱动 -> debugfs文件节点 -> 用户态脚本读取。这条链路上任何一个环节没配置对,采集到的数据都会是空的。
2. 安装前必须完成的环境检查与环境准备
这部分我吃了不少亏,所以单独拎出来说。漏掉任何一项,后面都可能白忙一场。
2.1 网卡型号确认:别用lspci和lsusb
我见过有人在论坛里问"为什么我的5300网卡装上没反应",最后发现是USB接口的无线网卡,根本不在支持列表里。CSI tool只支持mini-PCIe或PCIe接口的Intel 5300系列网卡,插在笔记本或者工控机的mini-PCIe插槽上。
确认方法是:
lspci -nn | grep -i network看输出里有没有类似这样的行:
02:00.0 Network controller [0280]: Intel Corporation PRO/Wireless 5300 AGN [8086:4235] (rev ff)关键要看8086这个厂商ID和4235这个设备ID。Intel 5300的常见设备ID是0x4235,如果显示的是其他值(比如0x4232对应的是5100),那这块卡就没法用。有些卖家会拿5100冒充5300,我建议买的时候直接让卖家拍lspci截图。
2.2 系统与内核版本:老版本才是归宿
官方测试环境主要是Ubuntu 12.04 LTS(内核3.2.x)和Ubuntu 12.10(内核3.5.x左右)。我个人的建议是装Ubuntu 12.04.5,然后把内核锁定在3.2.0系列,这个组合的补丁最成熟,社区验证案例最多。
如果你现在的机器是Win10/Win11,想直接用VMware Workstation装Ubuntu 12.04虚拟机,这里给一个我在实践中验证过的关键提示:虚拟机的无线网卡直通问题非常麻烦,VMware默认的虚拟网卡是虚拟设备,CSI tool根本识别不到。需要用USB无线网卡直通的方式把整个USB接口映射进虚拟机,而且Host端要先把这块网卡从系统中卸载(driverctl或者直接拔掉Host驱动),这套操作在VMware里的成功率并不高。更省心的方案是找一台有mini-PCIe插槽的旧笔记本直接装物理机,或者用带PCIe插槽的台式机加转接卡。
2.3 依赖工具安装清单
在Ubuntu 12.04下,先把编译环境准备齐:
sudo apt-get update sudo apt-get install git build-essential linux-source bc sudo apt-get install linux-headers-$(uname -r)注意linux-source这个包,官方源里带的是和发行版匹配的内核源码,下载后一般解压在/usr/src/目录。不要自己另找乱七八糟的内核源码,就用系统自带的。
2.4 关闭Secure Boot和模块签名校验
这个是针对较新硬件的,老机器通常没这个问题。如果你在安装时遇到Required key not available或者模块加载时报签名错误,需要进BIOS把Secure Boot关掉。另外,Ubuntu默认加载模块时只认带签名的模块,自己编译的.ko文件没有签名,可能会被拒绝加载。老版本Ubuntu没这个限制,新版本需要在启动参数里加上module.sig_enforce=0,或者在BIOS里彻底关闭安全启动。
3. 亲测可用的完整安装流程:从源码到模块加载
下面这套命令是我在Ubuntu 12.04.5(内核3.2.0-57)上完整跑通过的。如果你是其他内核版本,步骤一样,但补丁文件名要换成匹配的。
3.1 克隆两个核心仓库
CSI tool的代码分成两部分,缺一不可:
# 核心工具,包含内核补丁 git clone https://github.com/dhalperi/linux-80211n-csitool.git # 辅助工具,包含固件文件和采集脚本 git clone https://github.com/dhalperi/linux-80211n-csitool-supplementary.git这里有个细节:原始GitHub仓库后来因为法律风险被下架过,现在能找到的仓库地址可能和早期的不一样。Clone下来之后,先检查一下目录结构是否完整:
ls linux-80211n-csitool/ ls linux-80211n-csitool/patches/patches/目录下应该有一批补丁文件,命名一般会带内核主版本号,比如针对2.6.36、2.6.38、3.2.x的。选和你系统内核最接近的那个。如果某个补丁文件是空的或者明显不完整,说明这个仓库的版本不对,要换源重新clone。
3.2 把修改版固件装进/lib/firmware
cd linux-80211n-csitool-supplementary ls firmware/这个目录下有一个修改版的5300固件文件。安装方式:
sudo cp firmware/iwlwifi-5000-2.ucode.modified /lib/firmware/iwlwifi-5000-2.ucode注意这条命令的销魂之处:原仓库里修改版固件的文件名往往带着.modified后缀,而系统内核启动时读的固件名是iwlwifi-5000-2.ucode,所以复制时要改名。如果你不清楚内核实际请求的固件名,可以用:
sudo modprobe iwlwifi dmesg | grep iwlwifi看日志里报的是找不到哪个固件文件,就把修改版固件改成那个名字。一上来就乱命名是新手最常见的错误。
3.3 解压内核源码并打入补丁
这一步是整个流程里最容易出问题的地方,一定要耐心。
cd /usr/src sudo tar xjf linux-source-3.2.0.tar.bz2 cd linux-source-3.2.0接下来把当前运行内核的配置复制过来,保证编译出来的模块能和当前内核匹配:
sudo cp /boot/config-$(uname -r) .config sudo make oldconfigmake oldconfig可能会弹出一些交互式问题,一路按回车接受默认值就行。
然后是打补丁。假设你的内核是3.2.0,在patches目录里找到对应的补丁(如果确实只有一个补丁文件,直接用那个):
sudo patch -p1 < /path/to/linux-80211n-csitool/patches/csitool-3.2.patch关键是-p1这个参数,它表示去掉路径的第一层目录。如果你的补丁是从/home/user/linux-80211n-csitool/patches/这个路径下打的,而你现在处于内核源码根目录,那么补丁文件里写的a/net/mac80211/tx.c就会正确作用到本地的net/mac80211/tx.c上。
打完补丁后,确认一下改动是否真的进去了:
grep -n "csi" net/mac80211/tx.c | head -20如果没有任何输出,说明补丁没打进去,多半是路径或者补丁版本不匹配。不要继续往下走,先解决这个点。
3.4 编译mac80211和iwlwifi模块
在内核源码根目录执行:
sudo make modules_prepare这个命令会为模块编译准备好头文件和脚本,是编译单个模块前必须执行的一步。然后:
sudo make M=net/mac80211 sudo make M=drivers/net/wireless/iwlwifi如果你用的内核源码路径里有兼容层的问题,可能编译iwlwifi时会报找不到cfg80211的符号,这时候需要连cfg80211一起编译:
sudo make M=net/wireless编译完成后,对应目录下会生成.ko文件。安装模块:
sudo make M=net/wireless modules_install sudo make M=net/mac80211 modules_install sudo make M=drivers/net/wireless/iwlwifi modules_install模块安装顺序别反,cfg80211是最底层依赖,然后是mac80211,最后才是iwlwifi。
3.5 加载模块并验证固件
先把之前可能加载过的旧模块卸载干净:
sudo modprobe -r iwlwifi sudo modprobe -r mac80211再按顺序加载:
sudo modprobe cfg80211 sudo modprobe mac80211 sudo modprobe iwlwifi然后立刻看内核日志:
dmesg | tail -20正常情况下应该看到iwlwifi成功加载固件的日志,类似:
iwlwifi 0000:02:00.0: loaded firmware version 8.83.5.1 build 33432如果卡在固件加载失败,检查/lib/firmware/下的固件名字和权限,owner必须是root,权限644即可。
4. 验证安装是否成功:从debugfs到实际CSI数据
把模块加载起来还不算完,你必须真真切切地看到CSI数据流出来,才能确认整个链路是通的。
4.1 检查debugfs接口是否存在
CSI tool会在debugfs下创建一个名为linux80211的目录,数据节点就是csi。检查方法:
sudo dmesg | grep -i debugfs ls /sys/kernel/debug/ ls /sys/kernel/debug/linux80211/如果/sys/kernel/debug/linux80211/csi这个文件存在,说明驱动改动生效了。注意debugfs默认挂载在/sys/kernel/debug,有些系统没有自动挂载,需要手动执行:
sudo mount -t debugfs none /sys/kernel/debug4.2 配置无线网络连接
这个工具要求无线网卡处于连接状态(managed模式连接AP)或者monitor模式监听。最简单地测试方法是连上一个AP:
sudo nmcli dev wifi connect "你的SSID" password "你的密码"如果是老系统没有nmcli,用iwconfig配合/etc/network/interfaces配置也行。连上之后,用iwconfig确认网卡已经关联成功:
iwconfig wlan0输出的Link Quality和Bit Rate只要不是0,就能继续。
4.3 采集第一批CSI数据
确认连接后,采集数据有两条路。
第一条路,用辅助仓库自带的脚本:
cd linux-80211n-csitool-supplementary ./log_to_file csi_test.dat脚本跑起来后会持续抓包,Ctrl+C停止,数据落在csi_test.dat里。
第二条路,直接读取debugfs节点:
sudo cat /sys/kernel/debug/linux80211/csi > csi_test.dat这种方式没法控制采集时长,Ctrl+C直接终止即可。我用下来感觉log_to_file更稳定,因为内部做了缓冲区分块读取,不容易丢数据。
4.4 用MATLAB快速验证数据合理性
辅助仓库带了一套MATLAB解析工具,在matlab/目录下。在MATLAB里运行:
csi_trace = read_log_file('csi_test.dat'); csi_entry = csi_trace{1}; csi = get_scaled_csi(csi_entry); abs_csi = abs(csi);如果csi矩阵的维度不是1 x 1 x 30(或者2 x 1 x 30这类多天线形式),说明数据格式不对。正常的5300在20MHz带宽下会输出30组子载波数据。如果维度对,再画个图看看幅度分布是否随子载波平滑变化。这一步通过,你的安装工作才算真正完成。
5. 我踩过的坑与排查链路:遇到问题按这个顺序找原因
这部分是我最想写的。下面每个坑都是我当时花了半小时到两小时不等才定位到的,按严重程度排列。
5.1 编译阶段报错:找不到头文件或符号定义
现象:执行make M=drivers/net/wireless/iwlwifi时报错,说找不到linux/kconfig.h之类。
原因:跳过了make modules_prepare,或者内核源码的.config没配对。
排查顺序:
- 先检查
/usr/src/linux-headers-$(uname -r)目录是否存在,头文件包没装全,模块编译一定失败。 - 回到内核源码根目录执行
make modules_prepare,再重新编译模块。 - 如果还报错,用
make clean清理一次,重新从make oldconfig开始。
我在第一次安装时就是直接编译iwlwifi模块,忘了先modules_prepare,结果报了一系列asm/types.h相关错误,误导我以为是工具链问题。其实从头准备一遍就好了。
5.2 固件加载失败:iwlwifi报“Direct firmware load failed”
现象:dmesg里出现:
iwlwifi ... Direct firmware load for iwlwifi-5000-2.ucode failed with error -2原因分析:/lib/firmware/下没有内核期望的固件文件,或者文件名不对。
处理方式:不要凭记忆猜文件名。执行:
sudo modprobe -r iwlwifi sudo modprobe iwlwifi dmesg | grep -i firmware系统会明确告诉你要的是哪个文件,把这名字和linux-80211n-csitool-supplementary/firmware/下的文件比对。多数情况下,只要把仓库里的iwlwifi-5000-2.ucode.modified复制成日志里报错的那个文件即可。
5.3 网卡能被系统识别,但CSI节点始终不出现
现象:lspci能看到网卡,无线也能连上AP,但/sys/kernel/debug/linux80211/目录不存在。
原因:补丁没真正生效。最常见的情况是补丁打错了文件,或者加载的iwlwifi模块不是刚刚编译的这个。
排查链路:
- 先确认当前加载的iwlwifi模块路径:
cat /sys/module/iwlwifi/taint和lsmod | grep iwlwifi。 - 更直接的办法是看模块编译时间:
modinfo iwlwifi | grep filename,如果路径指向/lib/modules/.../kernel/drivers/net/wireless/iwlwifi/iwlwifi.ko,说明你安装的模块和系统加载的是同一个。 - 还有一个低级错误:编译时Patch是打了,但
make M=...的时候它会重新生成模块,如果补丁修改过的tx.c没被重新编译,那生成的.ko里还是没有CSI代码。我建议在源码目录执行touch net/mac80211/tx.c,再重新编译,强制让它重编这个文件。
5.4 虚拟机上装了老Ubuntu,但无线网卡死活不工作
现象:笔记本或台式机的物理网卡在VMware里看不到。
原因:VMware的无线网卡虚拟化支持很弱,它把物理无线网卡封装成一个虚拟的PCI设备给虚拟机用,但驱动的硬件ID对不上。
建议:别在虚拟机上浪费时间。找一台能插mini-PCIe网卡的旧机器,装物理机。实在要用虚拟机做实验,可以考虑用USB转mini-PCIe的转接板,把5300网卡转成USB设备再USB直通进虚拟机,但这种方式在Host端卸载无线驱动时容易把整个网络搞断,非必要不建议。
5.5 新内核上装老工具的“死路”
我也试过在Ubuntu 20.04(内核5.x)上强上CSI tool。补丁是肯定打不进去的,iwlwifi驱动在这十几年里经历了大规模重写,代码结构和文件组织方式完全变了。即使强行修改代码,新内核还有一系列安全机制会拦截这种操作。结论就是:老工具配老系统,别拿新系统硬扛。如果你手里只有一台新电脑,建议检查一下处理器是否支持虚拟化,然后在虚拟机里装Ubuntu 12.04配USB网卡直通——这是目前唯一比较可行的折中方案。
6. 安装完成后的第一步:采集一份有效数据并确认与解析
当你终于看到了CSI数据,建议趁热打铁,把整个实验链路走通一次。这一步对于后续做实验非常有帮助,也方便你跟同方向的人交流时有个统一的参照。
6.1 不同采样条件下CSI输出形状对照
| 场景 | 天线配置 | CSI矩阵维度 | 说明 |
|---|---|---|---|
| 1根天线发射、1根接收、20MHz | 1x1 | 1x1x30 | 最常见的配置 |
| 2根天线发射、2根接收、20MHz | 2x2 | 2x2x30 | 会有空间分集信息 |
| 3根天线发射、3根接收、20MHz | 3x3 | 3x3x30 | 天线越多,数据量越大 |
| 40MHz带宽 | 任意 | 子载波数56 | 子载波间隔和数量变化 |
我用的是最常见的1x1配置,一开始看到read_log_file返回的数组里每个元素是个结构体,还疑惑为什么不是直接矩阵,后来才明白每个数组元素对应一个数据包,包和包之间的CSI可能有差异,这就是做动作感知时的关键信息。
6.2 与另一个重要细节:CSI数据的幅度计算
MATLAB解析时要注意,get_scaled_csi返回的已经是线性幅度值。如果你看到数值非常小(比如1e-5量级),不要急着怀疑数据错误,先检查是不是把单位搞混了。有些人在这一步会把数据取个20log10转换到dB,这本身没错,但要注意处理的时机——是先做了所有子载波的配准还是直接全局转换,会对后续特征提取造成完全不同的影响。
6.3 一个关于数据格式的实用技巧
log_to_file采集的数据里,每个包除了CSI信息,还带了RSSI、AGC、时间戳等等。如果你想快速写个脚本把CSI部分提取出来,可以先在MATLAB里跑一段:
csi_trace = read_log_file('csi_test.dat'); all_csi = zeros(length(csi_trace), 30); for i = 1:length(csi_trace) csi = get_scaled_csi(csi_trace{i}); all_csi(i, :) = abs(csi(1, 1, :)); end plot(mean(all_csi, 1));这样画出来的平均幅度谱,如果看起来在30个子载波上呈现平滑的包络,说明数据是干净的。如果毛刺非常多,可能是有多径干扰,这时候换个环境(比如离AP近一点)再试。
6.4 我和这个工具打交道的一点体会
说句实话,802.11n CSI tool这套东西,从设计思路上讲,它是把不开放的能力用hack的方式撬开了。它的价值不在于驱动本身写得多么漂亮,而在于给整个无线感知领域提供了一个标准的数据来源。后来出现了很多基于SDR(软件无线电)的CSI获取方案,但直到现在,很多论文里使用的数据采集部分依然在用5300这套老方案,因为它的易用性和数据稳定性经过十多年的检验,依然是同类的天花板。
如果你是想快速出实验结果的,我给的建议是:先把环境装好,确认CSI数据能稳定取向,再设计你的采集方案。这工具虽然老,但支撑学生做一篇硕士论文是完全够用的。接下来的路,就看你自己的idea了。