做无线感知的人,十有八九都听过这句话:WiFi 不仅能传数据,还能当雷达用。但真正去复现相关论文时你会发现,绝大多数方案要么依赖多链路设备,要么需要密集的指纹标定,能做到“单条WiFi链路 + 无设备携带 + 连续坐标输出”的屈指可数。Widar2.0 这篇论文的标题本身就把这三个关键词摆到了台面上:Passive Human Tracking with a Single Wi-Fi Link。我读完第一遍时的感受是:思路不复杂,但每个环节都踩在信号处理的关键点上。
这篇读 Paper 笔记,我想把它讲透。单链路为什么难,CSI 里到底藏着什么信息,BVP 这个速度-位置联合估计是怎么把多普勒频移变成坐标的,以及复现过程中最容易翻车的几个工程细节。适合刚接触无线感知方向的研究生,也适合打算把手势识别、跌倒检测、人员看护这类能力往智能家居里塞的开发者。我的建议是:不管你是准备跑实验还是想落地产品,先把这篇论文的物理模型吃透,再看代码,顺序反了很容易被一堆参数绕晕。
1. 这篇论文到底在解决什么问题
1.1 什么是单链路被动人体追踪
被动人体追踪,用大白话说就是:人身上不挂任何设备,系统只靠环境里的无线信号变化,判断这个人现在站在哪、往哪走。这和手机定位、手环定位有本质区别——被动追踪不需要目标主动配合,也不需要目标携带信标。典型场景是老人夜间起床跌倒检测、室内入侵感知、智能照明跟随,这些场景里人是“零负担”的,体验上比任何可穿戴设备都自然。
那“单链路”又是什么?链路指的是一个发射机和一个接收机之间的无线信道。所谓单链路,就是整个系统里只有一台发射设备(比如一个普通无线路由器)和一台接收设备(比如一台带无线网卡的笔记本),数据也只在这一个收发对之间采集。没有多个 AP 协同,没有天线阵列,硬件上几乎零改动。这在实际部署里吸引力极大:成本低、安装简单、不需要对房间做专门改造。
但问题也随之而来。单链路意味着空间采样维度极少,传统的到达角、到达时间、双曲线定位等方法都需要多个收发端或者较大天线阵列才能提供足够约束。所以 Widar2.0 最核心的贡献不是提出一个新的滤波公式,而是证明了:即使只有一个链路,只要把多普勒信息用到位,人体跟踪依然是可解的。
1.2 为什么此前没人做出来
在 Widar2.0 之前,学术界在 WiFi 人体感知上的主流路线其实已经不少了。手势识别用 CSI 幅度波动,步态识别用多普勒特征,室内定位用 RSSI 指纹或者 CSI 指纹。但这些方向大多在回答“这个人做了什么动作”或者“这个人大概在哪个区域”,要连续输出“他现在具体在哪个坐标”,精度通常不够。
原因是多方面叠加的。第一,WiFi 信号带宽有限,距离分辨率很低,不可能像 FMCW 雷达那样直接测目标距离。第二,室内多径严重,直接拿 CSI 幅度做特征,很容易被环境中的静态反射淹没。第三,单链路条件下,目标相对发射机和接收机的运动信息只有一维的投影,从一维观测恢复二维坐标,数学上是个典型的欠定问题。
Widar2.0 的破局点在于换了一个观测对象——不再盯着 CSI 的幅度变化,而是用高灵敏度的多普勒频移,并且在反演阶段没有试图单帧求解,而是把多帧观测通过速度-位置联合估计框架(BVP)融合起来。多径在这里不再是干扰,反而成了额外约束的信息源。这个思路在当年属于比较超前的,现在看依然是单链路人体追踪绕不开的参考方案。
1.3 技术定位与贡献总览
从论文整体来看,Widar2.0 的完整流程可以概括为:采集 CSI → 相位校准 → 短时傅里叶变换生成多普勒谱 → 提取多普勒谱峰 → 构建速度-位置联合估计问题 → 求解目标位置 → 平滑输出轨迹。
这个流程里有几个关键决策值得注意。
- 用 CSI 而不是 RSSI,保证了多普勒提取的灵敏度。
- 用多普勒频移而不是直接测距,规避了 WiFi 带宽对距离分辨率的限制。
- 用离线轻量标定而不是密集指纹,提升了环境迁移能力。
- 用 BVP 联合估计而不是单帧定位,解决了单链路欠定问题。
| 方案类型 | 设备要求 | 目标是否携带设备 | 空间标定成本 | 输出精度 |
|---|---|---|---|---|
| 主动雷达 | 专用毫米波雷达 | 否 | 低 | 厘米级 |
| 多链路 WiFi 指纹定位 | 多个 AP + 采集端 | 否 | 高,需逐点采集 | 米级 |
| Widar2.0 单链路多普勒 | 一个 Tx + 一个 Rx | 否 | 低,只需少量位置标定 | 亚米级到米级 |
表格里最后一行就是 Widar2.0 的定位。它没有去和专用雷达比精度,而是在“普通 WiFi 设备 + 无佩戴 + 低成本标定”这个约束组合下,把精度做到了可用的程度。这个取舍非常务实,也是我觉得这篇论文最值得学习的地方。
2. 底层物理量:CSI 才是 WiFi 感知的灵魂
2.1 RSSI 为什么做不了这件事
很多第一次接触 WiFi 感知的人会问:直接用网卡上报的 RSSI 不行吗?答案是不行,而且不是精度问题,是信息量问题。RSSI 是接收端把整个信道里所有路径的能量叠加之后,再取平均值得到的一个功率值。它只回答“信号强不强”,完全不含“信号是怎么复合出来的”这些细节。
打个比方,RSSI 像是全班考试的平均分,平均分高了你只知道整体不错,但不知道谁擅长数学谁擅长语文,更不知道哪个学生今天缺考。而 CSI 是每个学生的各科详细成绩单,每个子载波都有独立的幅度和相位,能看出多径干涉的“频谱花纹”。人体移动时,反射径长度发生变化,会在不同子载波上引起不同程度的相位旋转和幅度涨落,这些微观变化只有 CSI 才捕捉得到。
所以 Widar2.0 的一开始就是从 CSI 下手。实验里如果你手上只有 RSSI 数据,就算后端算法再强,也无法提取出多普勒频移的微量波动——这个方向直接就被 RSSI 的信息瓶颈卡死了。
2.2 CSI 里到底装了什么信息
WiFi 的 OFDM 系统把一个信道划分成若干子载波,每个子载波都可以看作一个独立的窄带信道。CSI 就是对这些子载波信道的复增益估计,通常表示为幅度和相位两部分。以常见的 20 MHz 带宽为例,有几十个子载波;40 MHz 带宽则上百个。每个子载波上的复增益,实际上是发射端、接收端、目标、环境共同作用的结果。
由于人体运动速度很慢,由人体反射引起的 CSI 变化频率也很低,通常在 0.1 Hz 到 30 Hz 这个范围。而无线网卡的帧采样率可以做到几百到一千赫兹,完全覆盖这个动态范围。这意味着 CSI 中其实包含了远低于通信系统“有效载荷”频率的低频运动信息,只是平时这些信息作为干扰被通信系统滤掉了。
Widar2.0 做的事情,本质上就是把这些平时被当成噪声的低频成分提取出来,再用物理模型解释它们。这一步很关键:不是拿到 CSI 就直接上深度学习,而是先明确“信号里有什么物理量”,再设计对应的提取算法。论文把重点放在多普勒频移而不是 CSI 幅度上,就是因为多普勒与人体运动速度之间有一个非常干净的物理对应关系,幅度特征在这种量级下反而容易非线性失真。
2.3 多普勒频移是怎么从 CSI 里冒出来的
多普勒频移这个现象大家都熟悉:火车靠近时汽笛声变尖,远离时变低沉。无线信号也一样。当人体移动导致反射路径长度持续变化时,接收信号的相位会持续旋转,等效频率发生偏移。对一条反射路径来说,瞬时频率偏移量与路径长度变化率成正比,公式写出来就是:
f_d = -(1/λ)(v · u_T + v · u_R)
其中 λ 是 WiFi 信号波长,v 是人体运动速度矢量,u_T 是从目标指向发射机的单位向量,u_R 是从目标指向接收机的单位向量。λ在 2.4 GHz 频段大约 0.125 米,人体以每秒 1 米的速度行走时,多普勒频移大约 8 Hz,听起来很低频,但完全在 CSI 的时间分辨率之内。
从信号处理角度看,多普勒频移是相位的微分量。所以采集到的 CSI 必须保留准确的相位信息,而且相位必须足够稳定,否则微小的频移会被噪声淹没。这也是后面所有工程细节里最麻烦的一点:WiFi 收发两端时钟不同步,相位本身存在恒定漂移,如果不对 CSI 相位做校准,多普勒谱直接是糊的。
3. 核心算法拆解:BVP 速度-位置联合估计
3.1 信号处理前端:从 CSI 到干净的多普勒谱
先说前端,因为后面所有位置解算都建立在多普勒谱的质量上。我的经验是,至少一半的复现问题出在这一步,而不是算法本身。
标准前端流程一般分四步。
第一步,采集原始 CSI。需要拿到每个子载波在每个包时刻的复数值,通常用支持 CSI 导出的开源工具完成。第二步,做相位校准。由于发射机和接收机的本地振荡器不同步,CSI 相位里会混入一个线性递增的漂移项。常用的做法是对一段时间内的相位做线性拟合,再把残差当作有效相位。第三步,按子载波把 CSI 序列重排为时间序列,对每个子载波做短时傅里叶变换(STFT),得到“多普勒频移-时间”的二维图谱。第四步,在多普勒谱上做峰值提取。人体反射对应的多普勒峰通常是最强且连续的一段,用峰值追踪算法可以把它从背景杂波中分离出来。
STFT 的窗长选择是个取舍。窗长越长,频率分辨率越高,但时间分辨率越低。在 2.4 GHz 频段,0.5 秒的窗大约对应 2 Hz 的频率分辨率,折算成人步行速度大概能分辨每秒 0.25 米的变化,基本满足连续追踪的需求。如果再强调实时性,可以用 0.3 秒左右,然后配合后端的平滑滤波。
3.2 多普勒观测与空间几何的约束关系
现在问题进入核心:有了多普勒频移,怎么变成位置?
把前面那个公式翻过来看。给定一个候选位置 P 和一个候选速度向量 v,我们可以根据房间几何算出 u_T 和 u_R,再算出预测的多普勒频移。实际观测到的多普勒频移是从信号里提取出来的。于是定位问题变成一个逆问题:找一条位置-速度轨迹,使得每个时刻的预测多普勒都尽可能等于观测多普勒。
难点在于,单个时刻的多普勒观测只是一个标量,而未知量是二维位置加二维速度,对应四个自由度。一个标量方程显然没法唯一确定四个未知量,解空间是一个有冗余的超曲面。这就是为什么单帧定位做不了。
但把时间维度引入后,情况完全不一样。目标的运动不是独立的,相邻时刻的位置变化由速度决定,速度本身又有运动学约束。多帧观测串联起来,等于是用连续时间上的多个标量观测去约束一条位置-速度轨迹。多普勒方程在这个过程里就像锁链一样,一环扣一环,把欠定问题逐步收紧成一个可解的优化问题。这就是 Widar2.0 里 BVP(Body-Velocity-Profile)的核心思想:不是把目标当作瞬时点,而是当作一条在速度-位置空间里连续演化的轨迹来估计。
3.3 位置解算里的工程技巧
数学上,BVP 的求解可以写成最小化预测多普勒与观测量之间残差的优化问题,同时加入两个先验:位置变化必须与速度向量自洽,速度大小必须落在人体运动合理区间内,比如 0 到 3 米每秒。
实际求解时直接做全局搜索代价很高,论文采用的做法是先做一个解析初始化,再用迭代优化精修。解析初始化相当于先找出一个大概的候选区域,缩小优化范围;迭代阶段再用牛顿法或梯度法在初始值附近收敛。这种“先粗后细”的思想在工程上很常见,但它特别依赖前端多普勒谱的干净程度。
我在实际复现中发现,单纯看多普勒谱峰值还不行。因为人体各个部位反射强度不同,躯干反射最强,手臂摆动也会产生额外的多普勒分量。峰值追踪如果不加约束,经常会跳到手臂或者腿对应的旁瓣上。后来我加了一个速度连续性的逻辑:当前帧追踪到的峰值位置,必须离上一帧峰值位置足够近,否则认为是错误峰。这个技巧很朴素,但对定位稳定性的提升非常明显。
3.4 为什么不用指纹也能定位
传统指纹定位的做法是:把房间划分成网格,在每个网格位置采集大量 WiFi 特征,构建特征-位置映射数据库,在线阶段用当前特征查数据库。问题在于采集工作量极大,而且环境一变(家具移动、人员桌位变化)数据库就失效,需要重新采集。
Widar2.0 之所以能避开指纹,是因为它内部有一个参数化的物理模型。模型参数只有房间几何、收发端位置、反射面关系这些有限项,离线阶段只需要在房间里走几条路径、站几个已知位置,把模型中的少量偏差项校准出来即可。这比逐点采集省了一个数量级的功夫,而且模型的可解释性更强,换环境后的迁移成本也低。
这里我想多说一句:指纹方案并不是完全没用,在某些强多径、非视距严重的场景下,指纹反而比物理模型更鲁棒。但指纹方案天生不适合从一个房间搬到另一个房间,而 Widar2.0 的模型迁移只需要重新做一轮轻量标定。对于要做产品的人来说,这个差别往往决定了成本上限。
4. 离线标定与在线解算:工程系统怎么搭
4.1 离线阶段要标定什么
Widar2.0 的离线标定不需要你在房间网格上站几百个点。实测下来,标定流程大概是这样:确定发射机和接收机的固定位置,记录它们的坐标;在房间里选几个有代表性的路径,让人按已知轨迹走动;同时采集 CSI,提取多普勒谱,把已知轨迹真值代入,用于拟合模型中的偏差参数。
这些标定动作本质上是在解决两个问题:一是消除系统性的相位和非理想因素,二是校准信号传播模型中的反射系数和路径损耗。由于模型参数数量有限,几段标定轨迹通常就够用了,整个过程大概十几分钟。
对比之下,我在其他多链路方案上做过逐点指纹采集,一个 20 平方米的房间要采几百个点,每点还要停留若干秒,一搞就是大半天。只要环境变一次,就是一次灾难。所以 Widar2.0 这种“轻量标定 + 物理模型”的路线,工程友好度是明显占优的。
4.2 在线阶段的数据流
在线阶段的处理流程比离线更简洁。
- 持续从无线网卡读取 CSI。
- 对相位做同样的校准,避免长期漂移。
- 滑动窗口计算多普勒谱。
- 峰值追踪得到目标的多普勒频率序列。
- 将频率序列送入 BVP 求解器,得到位置坐标。
- 用卡尔曼滤波器平滑轨迹,输出最终坐标。
这里有个参数值得重点提:CSI 的包采样率。理论上越高越好,因为人体动作在快走时多普勒频率可能到 15 Hz 往上,为了不混叠,需要至少 30 Hz 的采样率。但实际采集时我通常设到 100 Hz 以上,一方面是为了留足余量,另一方面是后续 STFT 需要多个子载波联合平均,数据多一点能显著提高谱质量。缺点是数据量变大,实时处理时要注意队列积压。实测中 100 Hz 的 CSI 数据,跑一个简单的多普勒谱估计加 BVP 迭代,普通 CPU 上仍然能实时运行,性能压力并不大。
4.3 环境泛化到底能做到什么程度
很多人关心:换一间房间要不要重新训练?Widar2.0 的做法决定了它比指纹方案泛化能力强,但也不是完全不用管。因为 BVP 模型里仍然需要知道发射机、接收机的坐标,以及环境中主要反射面的位置。换一个房间,只要重新量一下设备位置和几个反射面坐标,然后走一小段标定轨迹,就能继续使用。
所以准确说,Widar2.0 的泛化逻辑是“参数重置”,不是“零配置”。这在实际部署中已经非常理想了,毕竟没有任何感知方案可以完全不考虑环境变化。论文里展示的实验大多是在空旷室内和普通办公室环境下做的,我在类似环境复现时,精度基本能达到“知道人在房间哪个区域、往哪边移动”的粒度;在杂物很多、金属家具密集的环境下,精度会下降得明显一些,这时需要增加离线标定的轨迹数量来补偿。
5. 复现中踩过的坑与排查实录
5.1 相位漂移:多普勒谱最大的敌人
复现 Widar2.0 最痛苦的部分不是算法,而是相位漂移。WiFi 收发两端的时钟频率不可能完全一致,导致 CSI 相位里混入了一个随时间线性增长的漂移项。如果不处理,直接对 CSI 相位做 STFT,你会发现谱图上出现一条稳定斜向上的亮线,根本分不清真实多普勒。
解决办法分两层:一是对每个子载波的相位序列做线性拟合,把整体漂移减去;二是在 STFT 之前做子载波间的相位校正,因为不同子载波的漂移可能不同。我踩过的坑是,只做了整体线性拟合,没针对子载波逐一遍历,结果低频段还好、高频子载波上残留漂移依然明显。后来把“逐子载波去漂移”加进预处理流程,多普勒谱一下子就干净了。这条经验在几乎所有基于 CSI 相位的工作里都通用。
5.2 多普勒模糊与人和物分辨
多普勒模糊是个原理性问题:同一时刻,房间不同位置的两个人,如果相对发射机接收机的径向速度相同,会产生几乎一样的多普勒频率。单纯依赖多普勒是无法分辨这种模糊的。Widar2.0 能缓解这个问题的原因是,它追踪的不是单帧孤立的速度,而是连续轨迹;不同位置的轨迹在速度空间里的演化方式不同,配合位置连续性,大部分模糊都能被排除。
但在实际场景里,如果房间里有一个移动的吊扇、宠物或者另一个静止的人,多普勒谱会出现额外的峰。峰值追踪一旦追踪错对象,定位就会跳到完全不相关的区域。我的做法是在追踪阶段维护多个候选峰,让 BVP 求解器对每个候选都解一次,再根据轨迹连续性选择最合理的一个。计算量稍微变大,但鲁棒性提升明显。
另外,人的躯干反射能量比四肢强得多,所以多普勒谱里通常有一个主峰对应躯干速度,几个较弱峰对应四肢。实验时尽量让人正常行走而不是原地挥手,否则追踪极易被四肢峰值带偏。
5.3 追踪跳变、静止检测和边缘失效
跳变是定位系统常见病。现象是轨迹上一帧还在房间左侧,下一秒跳到右侧,再下一秒又跳回来。原因往往是多普勒谱出现误峰,或者 BVP 优化落入了错误的局部极小值。缓解手段除了前面说的多候选追踪,还可以在优化目标里加大位置平滑项的权重,让轨迹不能突变。卡尔曼滤波器虽然不能解决数据层面的错误,但能把突变成滤波后的缓变,看起来更自然,也不会造成执行器误动作。
静止检测则是另一个容易被忽视的问题。目标完全静止时,多普勒频移为零,谱峰落在零频附近,和静态多径混在一起。所以系统里需要单独设计一个判断:如果连续若干帧的多普勒能量都集中在零频,就认为目标静止,保持上一帧位置输出。这个逻辑看着简单,但没有它,目标一旦停下,定位结果会随机漂。
边缘失效发生在目标接近房间边界或者被大型遮挡物挡住的时候。此时反射路径消失,多普勒峰可能突然断裂。我的应对是给峰值追踪算法加一个“保持窗口”:如果峰丢失不超过几百毫秒,用上一帧速度外推位置;超过这个时间才宣布丢失。这样短暂失效不会把轨迹切成几段。
5.4 一个小而高效的处理流程代码骨架
这里给一个从 CSI 相位序列估计多普勒谱的伪代码骨架,实际工程里在这个基础上加滤波和峰值追踪就能形成完整前端。
# 伪代码:从 CSI 相位序列估算多普勒谱 # csi_data: shape = (子载波数, 时间帧数) all_spectra = [] for sub in range(csi_data.shape[0]): # 1. 提取相位并解卷绕 phase = unwrap(angle(csi_data[sub, :])) # 2. 线性拟合去除收发时钟造成的相位漂移 coeffs = polyfit(t_axis, phase, deg=1) phase_corrected = phase - polyval(coeffs, t_axis) # 3. 短时傅里叶变换得到多普勒谱 freq_axis, time_axis, spectrum = stft( phase_corrected, window_length=0.5, # 秒 overlap=0.5, # 50% 重叠 sampling_rate=100 # CSI 帧率 Hz ) all_spectra.append(abs(spectrum) ** 2) # 4. 对所有子载波的谱平均,增强信噪比 doppler_map = mean(all_spectra, axis=0)代码里最关键的是第二步。很多人刚开始会把 unwrap 误写成直接取 angle,结果相位在 -π 和 π 之间跳变,STFT 后全是伪峰。另一个常见问题是对整段长时间数据只做一次线性拟合,但 WiFi 时钟漂移并不是稳定常数,它会随温度缓慢变化。工程上更稳妥的做法是把数据切成长度适中的块,比如 10 秒一段,逐段做线性拟合,效果会好很多。
6. 常见问题速查与排查经验
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| 多普勒谱糊成一片,看不到清晰峰 | 相位漂移未完全校正 | 逐子载波线性去漂移,分块拟合 |
| 谱峰存在但不连续,跳动明显 | 峰值追踪误跟到旁瓣或四肢峰 | 加速度连续性约束,多候选追踪 |
| 目标静止时坐标随机漂 | 零频特征与静态多径混叠 | 加静止检测,静止时保持上一帧输出 |
| 目标走到房间角落时轨迹断裂 | 反射路径被遮挡,峰丢失 | 加保持窗口,用上一帧速度外推 |
| 运动方向经常判断反 | 多普勒符号受相位解卷绕错误影响 | 检查 unwrap 是否正确,确认采样速率 |
| 换房间后误差明显增大 | 模型参数未更新 | 重新标定设备坐标和反射面参数 |
| 实时处理时数据越积越多 | STFT 窗长过大或采样率过高 | 降低窗长到 0.3 秒,适当降采样率 |
排查时我的习惯是先把前端多普勒谱可视化,确认谱图干净再往后走。如果谱图质量不行,后端算法再努力也白搭。这算是整个复现里性价比最高的一步。
根据我个人经验,有几个细节想再强调一下。第一个是不要一上来就追求高精度厘米级,Widar2.0 的定位粒度更适合描述为“房间内区域级连续追踪”,能知道人在哪个区域、什么时候开始移动、往哪个方向移动,这已经能支撑很多智能场景。第二个是实验环境的干扰比想象中多,隔壁办公室的 WiFi 信号、微波炉、手机蓝牙都会在频谱上留下痕迹,采集时尽量选 WiFi 信道拥堵程度低、时段安静的场景。第三个是算法参数别照搬论文,论文里的参数是在他们的实验环境下调出来的,到了你的房间,至少要把目标速度范围、STFT 窗长、平滑权重重新过一遍。
最后再分享一个小技巧:调试 BVP 时,把预测多普勒和观测多普勒画在同一张图上,如果两条曲线形状整体吻合,只是有固定偏移,那是模型标定误差;如果形态对不上,那是峰值追踪或者相位处理的问题。这个区分能帮你快速定位到底是哪个环节出了问题,省下大量盲目调参的时间。