ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

WiFi穿墙感知实战:从CSI原理到RuView开源项目解析

WiFi穿墙感知实战:从CSI原理到RuView开源项目解析 如果我说一台普通的路由器加上一块WiFi网卡就能感知到隔壁房间有没有人在走动、呼吸频率是否正常甚至能画出一张粗糙的“墙后热力图”你会不会觉得这是电影里的黑科技实际上这种基于WiFi射频感知的技术已经存在很多年了而最近我一直在折腾的RuView开源项目就是把这个方向浓缩成一套可以自己动手搭建的设备方案。RuView本质上不是我们平时挂在嘴边的“WiFi网络工具”它更像是把WiFi信号当成一部低功耗雷达来用。这篇文章我就站在一个做了几周实验的从业者角度把RuView的项目思路、硬件取舍、软件链路和落地时绕不开的坑一次讲清楚。如果你对嵌入式开发、信号处理或者智能家居传感感兴趣这篇解析应该能帮你少走不少弯路。1. 先搞清楚RuView到底是做什么的1.1 这不是“破译WiFi密码”而是把WiFi当雷达用每次聊到WiFi感知类项目总有人下意识想到那些“蹭网”、“破译密码”的东西。必须先说清楚RuView跟那些完全沾不上边它研究的是WiFi信号在物理空间中传播以后产生的变化。我们的手机、路由器和网卡之所以能通信靠的是电磁波在空气里飞行这些电磁波遇到墙体会反射、穿透遇到人也会反射、散射甚至人的呼吸都会让信号产生微小的起伏。在常规通信里这些多径反射是干扰是做通信的人想尽办法要去消除的东西。但在RuView这类项目眼里这些干扰恰恰是“眼睛”——因为你不需要去读懂WiFi里面承载的数据包只需要分析信号自身的振幅、相位、延迟变化就能反推出空间里发生了什么。WiFi的波长在12厘米左右2.4GHz频段虽然比可见光大得多但已经足以感知人体这种级别的目标。所以RuView做的事情更像是“无源雷达”不发探测脉冲而是利用环境中本来就存在的WiFi信号给空间做一次扫描。当然只看数字“穿墙看壁”这四个字确实容易让人往隐私方向想。但正经做这个方向的人讨论的都是老人跌倒检测、家庭安防、救援搜索这类场景绝对不是拿去窥探别人房间。合理的应用边界我放到后面单独讲技术上我们先把它当成一套射频感知系统来拆。1.2 项目核心能力与应用场景从能力上来说RuView可以分成三个层级。最基础的是人体存在检测也就是判断墙后面或者房间里有没有人往上一个层级是活动识别包括走路、静止、跌倒、呼吸节律这些动作再往上才是真正意义上的“穿墙成像”把目标在房间里的大概位置画成一张二维热力图。这三个层级的难度和所需硬件复杂度完全不在一个量级。我自己在实验环境里跑下来前两个层级用一块Intel 5300网卡加三根天线就能做到不错的效果。在隔着一道24厘米砖墙的情况下检测一个人在墙后走动这种任务准确率可以达到实用级别。呼吸监测稍微难一点因为胸腔起伏带来的信号变化极其微弱需要做相位校准和长时间的滤波。至于成像那就需要天线阵列或者软件无线电平台来配合普通网卡能做的只是一些非常粗糙的“虚拟孔径”成像实验分辨率远达不到监控摄像头的水平。应用场景上目前比较成熟的方向有这么几类智能家居的无人自动关灯、全屋人在感应养老场景的跌倒检测和呼吸异常报警还有灾后救援时判断废墟下是否有生命迹象。另外一个很有意思的方向是“隐私友好的存在感应”比如卧室里不想装摄像头但又想知道有没有人活动WiFi感知就很合适。它不采集图像只输出概率和位置坐标从隐私角度比摄像头温和得多。1.3 为什么非得折腾开源项目市面上其实已经有一些商用WiFi感知芯片了比如某些路由器内置的“WiFi雷达”功能。但这些方案都是黑盒给开发者留出的空间很小。RuView这一类开源项目最大的价值是把整个链路从固件补丁、数据采集、信号处理到可视化全部摊开给你看。对于搞嵌入式或者信号处理的人来说这套东西的“学习性价比”非常高。你不需要花几万块买测试设备只需要一块二手网卡、一根带外部天线的路由器、一台能装Linux的旧电脑就能亲手触摸到CSI信道状态信息这个非常有名的概念。而且开源社区积累了很多代码从Linux内核驱动补丁到Python分析脚本都有照着跑一遍比看十篇论文都管用。2. 穿墙感知的核心原理从CSI到“图像”2.1 WiFi信号如何携带墙后信息要理解RuView绕不开一个术语CSI中文叫信道状态信息。我们把WiFi信号想象成一条细细的激光束但实际上无线信号在室内传播时会被墙壁、家具、人体无数次反射最后到达接收端的其实是无数条“多径分量”叠加在一起的混合信号。普通设备只会给出一个RSSI也就是信号强度数值这个数值就像你把所有颜色的光混在一起只报一个“亮度”信息量极其有限。而CSI更进一步WiFi使用的是OFDM调制技术一个信道里同时传输几十个不同频率的子载波。接收端能够针对每一个子载波测出它对应的振幅衰减和相位偏移等于把“混合白光”重新拆成了“光谱”。当墙体后面有人走过时人体的反射路径长度会变化导致不同子载波的振幅和相位发生规律性扰动。RuView要做的就是把这组高维复数数据拿来分析找到扰动背后的物理含义。还有一个特别重要的物理现象是“微多普勒效应”。人体呼吸时胸腔表面会做毫米到厘米级别的周期运动这种缓慢位移会让部分反射波的频率产生微小偏移。虽然人移动速度不快但是子载波的相位对位移极其敏感只要距离变化达到波长的一小部分相位就会产生肉眼可见的变化。这也是为什么WiFi感知能做呼吸监测的原因。2.2 从CSI到活动检测的完整处理链路拿到原始CSI数据以后直接看是看不出什么东西的必须经过一套固定的处理流程。第一步是“清洗数据”把网卡驱动采集到的复数矩阵按照子载波索引排列并去除因为设备时钟不同步带来的相位噪声第二步是“去噪”用带通滤波或者SVD降维把环境中的静态反射分量去掉第三步是“特征提取”比如计算短时能量、振幅方差、多普勒频移第四步才是分类或者决策。我举个例子检测墙后有没有人走动。假设你和路由器之间隔着一堵墙当墙后没人时接收端的CSI振幅图像是一条相对平稳的曲线。一旦有人走动人体的反射路径不断变化多个子载波振幅会同时出现剧烈波动。这时候你只需要设定一个滑动窗口比如200毫秒计算窗口内振幅的方差和均值比当这个指标超过阈值就判定有人活动。这也是RuView最简化的核心逻辑。活动识别就更复杂一点。不同动作走路、坐下、跌倒产生的微多普勒特征不一样走路会有规律的周期性峰值跌倒则是短时间内出现一个巨大脉冲然后迅速消失。这些特征可以直接输入机器学习分类器随机森林、SVM甚至小型的CNN效果会远好于简单的阈值判断。2.3 穿墙成像的数学思路为什么比检测难很多成像之所以难是因为检测只需要回答“有没有变化”成像却需要回答“变化发生在哪个位置”。WiFi信号在墙壁里的反射非常复杂普通墙壁会让信号衰减一半甚至更多再加上室内环境的多径干扰想用三角定位的思路直接解算出目标坐标误差会非常大。主流方案是借鉴雷达技术里的“合成孔径”思想让天线在空间上移动或者使用多根天线组成阵列在不同位置采集反射信号然后通过后向投影算法把每一对发射-接收天线的测量值映射到空间网格上相位对齐的位置会增强其余位置互相抵消。最终得到一张二维热力图亮斑就对应目标可能存在的位置。如果严格用RuView这类低成本网卡做成像你会发现精度受限于天线数量和物理孔径。三根天线只能组成一个很小的线性阵列角度分辨率非常有限。想要达到“数米外分辨出一个人站在桌子左侧还是右侧”的程度往往需要额外加装机械滑轨或者多个同步采集节点。这也是我认为这个项目最适合做人体感知而不是军事情报产品的原因。3. 复现RuView需要准备什么硬件3.1 经典方案Intel 5300网卡加Linux CSI Tool如果要在网上搜“WiFi CSI 开源项目”八成结果都会指向那套经典的平台Intel 5300无线网卡、一台老旧台式机或笔记本、Linux系统搭配linux-80211n-csitool补丁。这套方案从2011年左右就开始流行技术资料最多坑也基本都被踩平了。Intel 5300是一块支持3×3 MIMO的802.11n网卡二手价格几十块钱非常便宜。它的驱动被研究者打过补丁可以把每个接收包的CSI原始数据从网卡固件里读出来。你需要特别注意不是所有Intel网卡都能做这个事目前资料最全的依然是这块“老古董”。如果你手头只有集成在主板上的WiFi模块基本就可以放弃了插槽和固件限制会让采集工作非常痛苦。整体硬件清单大概是这样的一张Intel 5300网卡最好带三根外置天线一台能装Ubuntu 14.04或16.04的电脑虚拟机不行因为要加载内核模块一台普通的WiFi路由器作为发射端还需要一根网线用来连接开发机和路由器保证网络连接稳定避免无线链路干扰实验。为什么强调外置天线因为内置天线在笔记本机壳里相位特性不确定后期做校准会很头痛。3.2 替代方案Atheros、ESP32和软件无线电我理解很多人不想为了一个项目去翻十年前的老电脑所以这里也整理了几个替代方案。Atheros网卡也是一个流派对应的CSI工具是Atheros CSI Tool主要基于AR9580和AR9380芯片。它比Intel方案的优势在于能拿到更完整的带宽和天线配置而且部分版本支持5GHz频段下的采集。不过它的安装过程同样涉及内核补丁依赖关系比较老对新手并不友好。乐鑫ESP32系列则是近几年的新选择。ESP32本身集成了WiFi收发器有一些固件项目能够提取到比较粗略的信道信息比如ESP32-CSI-Tool。这条路的优势是便宜、低功耗、体积小适合做分布式节点劣势是提取的CSI要么子载波数量少要么精度达不到Intel方案的水平更适合做轻量级存在检测而不是精细的呼吸监测或成像。如果你预算多一点还可以直接上软件无线电设备HackRF、USRP或PlutoSDR。它们不依赖普通网卡而是自己完成WiFi信号的采样和解调可以非常灵活地拿到整个信道响应。效果当然最好但成本容易超过几千元而且对射频知识的要求高很多我个人不建议零基础的同学一上来就碰这个。我做一个简单对比表格供参考方案核心设备成本CSI精度上手难度适合场景Intel 5300 CSI Tool二手Intel 5300网卡低较高中学习研究、呼吸/活动检测Atheros CSIToolAR9380/AR9580网卡低较高中高5GHz频段实验ESP32 CSI固件ESP32开发板极低较低低分布式存在感应软件无线电HackRF、USRP等高高高成像算法验证、定制波形3.3 天线阵列搭建要点如果你想玩到“成像”这一步天线阵列是关键。不管用哪种网卡天线之间都必须保持稳定、已知的间距。对于2.4GHz频段半个波长大概是6.25厘米对于5GHz频段则大概是3.1厘米。理论上天线间距在半波长以内可以避免栅瓣实际工程中我们通常会稍微缩一点比如5厘米左右。搭建阵列时有一个非常容易犯的错误用金属支架固定天线。金属对射频信号有强烈的反射和屏蔽会把你的测量结果搞得一团糟。最好使用尼龙柱、塑料支架或者3D打印件。另外每根射频线缆的状态也会影响相位一致性如果你发现同一天线在不同时间的CSI相位跳变特别大大概率是线缆接触不良。还要提醒一下普通WiFi网卡的天线接口和天线之间是通过“天线线缆扣式连接器”连的这种结构本身就会引入相位误差。如果你只做检测不做成像这个误差影响不大一旦要做波束成形或者图像重构就必须先做“相位校零”也就是把一根标准天线放在固定位置记录系统固有相位偏移然后在后续处理里减掉。4. 软件环境搭建与数据采集实操4.1 内核补丁与驱动安装步骤这部分是复现过程中劝退最多人的地方。先把步骤写在这里再解释每一步的目的。第一步准备一台没有太多生产数据的老电脑装Ubuntu 14.04或者16.04。为什么不用新版本因为linux-80211n-csitool补丁是配合老内核开发的我在Ubuntu 20.04上试过编译时报错一堆最后花了一天时间改代码不如直接准备老系统。第二步下载linux-80211n-csitool源码包并找到对应的内核版本标签。你需要先安装编译内核模块所需的工具链包括build-essential、linux-headers-generic和git。然后按补丁说明把源文件覆盖到内核源码目录里重新编译无线网卡驱动模块。第三步加载编译好的驱动模块。这一步有个小坑系统自带的内核模块名为iwlwifi你编译出来的模块也叫这个很容易覆盖冲突。我建议先备份原模块然后确认加载的模块来自你编译的目录不要用modprobe -r乱卸载系统模块。第四步把无线网卡设置成监控模式。一般的WiFi网卡默认是station模式它只会处理发给自己的包但我们的CSI采集工具需要接收所有信道上的无线帧。命令大致是sudo iw dev wlan0 set type monitor之后再设置采样信道。每次实验前最好先做一次快速扫描确认当前环境里没有其他WiFi网络在抢占同一个信道。这些步骤没有哪一步是能跳过的。跳过内核补丁驱动根本不会回传CSI跳过监控模式采集到的数据包数量会少得可怜跳过信道扫描你以为采集的是实验信号实际上可能是隔壁房间的微波炉干扰。4.2 用脚本抓取CSI数据当驱动装好以后你会拿到linux-80211n-csitool-supplementary这个辅助工具包里面包含两个关键工具一个用来生成CSI测量参数另一个用来从内核接收数据并写入文件。最简单的操作是在发射端路由器上开一个UDP包发送脚本让接收端不停收到数据包然后在接收端执行类似这样的命令sudo ./log_to_file csi_data.dat这个命令会把后续所有收到的CSI记录到文件里。默认记录格式是二进制的每一条包含了很多元信息比如包序号、帧控制字段、天线数、子载波数然后是每个子载波的复数CSI值。想快速看数据是否正常可以用Python写个小脚本读取二进制里的CSI数组转成振幅并画图。这里给出一个非常简化的Minimal解析思路import numpy as np # 假设你从原始文件里读出了csi_buf长度为 2 2*30*3 # 其中2字节是保留字段之后是30个子载波 * 3根天线 * 复数(2字节实/虚) csi_buf np.frombuffer(raw_chunk, dtypenp.int16) csi_complex csi_buf.reshape(-1, 2).view(np.complex64).flatten() amplitude np.abs(csi_complex) print(amplitude.shape) # (90,) 即 30*3这段代码不去纠结文件格式细节重点是想说明最终算法拿到的就是一组表示“频率-空间-时间”变化的复数。如果你画出来的振幅曲线是一条平滑直线说明环境里没有动态目标如果有明显的突起或周期性波动说明你有东西可以检测了。4.3 验证数据可用的两个小指标在开始跑模型之前先花十分钟检查数据质量能避免后面所有结果都不可信。第一个指标是“空环境稳定性”保证周围没人、路由器固定、无移动物体采集三分钟数据计算CSI振幅的方差。如果方差非常低说明系统工作正常如果方差高得离谱优先检查是否有其他无线设备在抢信道、网卡天线是否松了。第二个指标是“线性相位性”。CSI相位会因为收发两端时钟不同步而呈现一个线性的斜率我们通常做一个线性拟合把相位还原成符合物理规律的形式。如果你发现相位在子载波之间毫无规律地乱跳说明硬件通路有问题这种情况下后面计算多普勒特征基本是白算。5. 核心算法和代码思路走读5.1 从原始CSI到运动特征的一个Python例子我这里写一个最常用的运动检测核心逻辑不是完整工程代码但能把RuView背后的信号处理思路讲明白。假设你已经解析出了CSI振幅矩阵矩阵形状是(子载波数, 时间帧数)比如30个子载波每两秒采集100帧。import numpy as np def motion_score(csi_amp, window20, threshold1.5): # csi_amp shape: (30, N) # 每个子载波独立归一化 norm_amp (csi_amp - csi_amp.mean(axis1, keepdimsTrue)) / (csi_amp.std(axis1, keepdimsTrue) 1e-6) # 滑动窗口内的总体波动 scores [] for i in range(0, csi_amp.shape[1] - window, window): seg norm_amp[:, i:iwindow] seg_var np.mean(np.var(seg, axis1)) scores.append(seg_var) # 高于阈值的窗口视为有活动 scores np.array(scores) events scores threshold return scores, events这段代码的核心逻辑是全子载波的平均方差。为什么不用单一子载波因为墙面反射、人体移动对每个子载波的影响差别很大有些子载波可能刚好落在“零点”上变化不明显。取所有子载波的平均能提升稳定性。阈值threshold1.5是经验值如果你发现误报很多就往上调漏报多就往下调。5.2 活动识别模型怎么训练有了运动特征以后下一步是区分“走动”“站立”“跌倒”“呼吸”这些不同活动。我在实际项目中采用的做法是收集有标签数据然后离线训练一个随机森林分类器。每个样本的输入特征是固定长度的时间窗口比如2秒内统计每个子载波振幅的均值、方差、最大值、最小值、过零率再加上CSI相位的标准差。随机森林的好处是稳健、不用做太多特征工程而且训练速度很快。我准备大约2000个样本三类动作走动、静止、呼吸各占三分之一在树数量100时分类准确率在93%左右。如果你用深度学习那需要的数据量就不是几千条能解决的了我不建议在入门阶段直接上LSTM或CNN。另外特别提醒活动识别模型非常依赖实验位置。同一个模型在客厅有效换到仓库里可能就废了因为多径环境变了。Robust做法是每换一个场景都重新采集一段背景数据做一类“场景自适应”的均值校准。5.3 实时热力图的简化实现思路热力图这部分我给的代码会比较抽象因为它本身就是整个项目里最复杂的模块。核心流程是先把你关心的区域划分成一个网格比如每20厘米一个点然后在多个采集时刻利用网卡的天线阵列或者移动天线在不同空间位置得到CSI测量值。接着对每个网格点计算“该位置是否存在反射目标”的置信分数。一个接近工程可用的做法是“空域滤波”也就是用每个天线的相位差来形成一组“方向观测量”然后直接叠加。伪代码如下def beamform(csi_mat, angle_grid, antenna_positions, wavelength): # csi_mat: (num_antenna, num_subcarrier, num_time) # antenna_positions: 每个天线的坐标 heatmap np.zeros((len(angle_grid), csi_mat.shape[2])) for i, angle in enumerate(angle_grid): steering_vector np.exp(-2j * np.pi * np.sin(angle) * antenna_positions / wavelength) weights np.conj(steering_vector) beam_output np.abs(np.dot(weights, csi_mat[:, 0, :])) # 取某个子载波 heatmap[i, :] beam_output return heatmap这不是严格意义上的穿墙成像但如果你把天线阵列沿墙摆放它就是一个最基础的波束扫描图。想要更高分辨率可以用后向投影算法把所有天线的时域信号做逆投影叠加。这部分需要你熟悉傅里叶变换和波动方程基础没有捷径可走。6. 复现过程中我踩过的坑与排查表6.1 编译内核模块失败这是所有第一次做这个项目的人都会遇到的问题。我自己的经历是在Ubuntu 18.04上编译编译器太新老补丁里的部分C代码无法通过。解决办法不是硬刚代码而是换成14.04或16.04的虚拟机注意虚拟机不行因为网卡直通很麻烦。最好是找一台旧笔记本装Linux。还有一种失败是因为内核版本和补丁版本不匹配。建议先查一下现有内核的版本号再去找对应tag的CSITool源码。如果实在找不到最后的手段是手动修改源文件把已经被内核移除的API调用替换成新版API。不过这个工作量很大对驱动开发不熟的人不建议尝试。6.2 数据噪声大、误报频繁使用中遇到的信号噪声来源很杂。排除周围WiFi干扰之后最常见的是人体的位置恰好落在无线信号“死角”上。我在试验时发现同一个房间里人站在窗边和站在墙角时CSI振幅波动差了接近两倍。这不是网卡的问题而是墙面反射结构导致的衰落。解决方法是不要只盯着一根天线看尽量使用空间分集也就是把三根天线的特征都参与判断。此外可以在软件里加一个简单的带通滤波器把0.1Hz到5Hz之外的信号变化去掉。这个频段覆盖了呼吸约0.2-0.4Hz和走动约1-2Hz又能滤掉大多数家电引起的慢漂移。6.3 从单房间扩展到多房间如果只是单房间检测一台路由器加一个接收端足够。但要想做整屋覆盖我建议把系统拆成多个采集节点每个节点使用ESP32这样的低功耗设备只上传时间戳和CSI统计量由中心服务器做融合判断。注意所有节点必须进行时间同步最简单的方法是使用网络对时协议或者在一根网线上同时广播同步包。多节点部署还有一个更大的坑相邻节点之间可能互相干扰。因为CSI测量要求信道干净如果两个节点同时用同一个信道数据里会混入互测信号。解决方案是把不同房间的采集节点分配到不重叠的信道或者让发射端采用时分的方式轮流跟每个接收端通信。6.4 一些越早知道越好的合规建议作为把这个技术当兴趣研究的开发者我必须专门留出一段来谈边界。RuView以及所有WiFi感知类项目检测的都是“空间中的无线电波状态”这本身没有攻击性但如果你把它用在未经允许的公共或他人场所就可能触犯隐私和治安相关的法律。我自己的原则是只在自有设备、自有房屋、或者明确征得同意的实验室里做实验。不采集可以关联到个人身份的楼层信息不做持续跟踪。如果需要做住宅内的“存在感应”提前告知同住人并且只保留活动等级或位置坐标不保存原始CSI数据。不把这类技术用于绕过门禁、窥探邻居、非法监控。研发本身是一件很酷的事但技术被错误使用以后带来的麻烦也是真实的。把精力放在合法、正向的场景比如帮助老人防跌倒、节约家庭能源这个方向才走得长远。最后再分享一点我个人做完这个项目后的体会整套系统里最耗时间的不是编译驱动也不是训练模型而是调数据质量。CSI这种东西看一眼曲线你觉得信号不错一上算法就全是毛刺。你需要反复对比在不同房间、不同天线布局、不同人体位置下的表现慢慢形成一种“手感”。一旦你能稳定地复现出墙后人走动的检测那种成就感比跑通一个水平很高的图像模型还要爽。如果你现在正准备入这个坑我的建议是别急着买昂贵设备先用手头能借到的Intel 5300配一台旧电脑跑通最小系统把CSI数据读出来画一画这个项目你就已经成功一半了。
返回列表