RuView:用WiFi信号看穿墙壁——基于CSI的空间感知技术深度解析

RuView:用WiFi信号"看穿墙壁"------基于CSI的空间感知技术深度解析

项目地址:github.com/ruvnet/RuVi...

你有没有想过,你家里那个落满灰尘的WiFi路由器,其实一直在用无线电波"描绘"整个房间的三维结构?

不是科幻。2023年卡内基梅隆大学(CMU)的DensePose From WiFi研究已经证明:WiFi信号可以像摄像头一样识别人体姿态。而现在,一个叫RuView的开源项目把这项技术从论文搬到了现实------用9美元一块的ESP32芯片,配合Rust写的信号处理管线,实现了穿墙生命体征监测、人体姿态估计和空间占用检测。

没有摄像头,没有可穿戴设备,没有云端。纯粹靠物理。

1. WiFi信号里藏着什么信息?

1.1 CSI:信道状态信息

WiFi路由器持续向外发射射频信号。当这些信号遇到人体、家具、墙壁时,会发生反射、衍射和散射。接收端可以通过一种叫做**信道状态信息(Channel State Information, CSI)**的数据来量化这些变化。

CSI本质上是一个复数矩阵,包含每个OFDM子载波的振幅相位

ini 复制代码
CSI = [H(f₁, t₁), H(f₂, t₁), ..., H(fₙ, t₁)]
      [H(f₁, t₂), H(f₂, t₂), ..., H(fₙ, t₂)]
      ...

其中 H(f, t) 是频率为 f 的子载波在时刻 t 的频率响应。一个典型的WiFi信道有52-56个子载波,每个子载波携带独立的多径信息。

为什么OFDM子载波这么重要?因为不同频率的信号对空间扰动的敏感度不同。低频子载波穿透力强但空间分辨率低,高频子载波容易被遮挡但分辨率高。把所有子载波的信息综合起来,就能得到一个高维度的空间感知向量。

RuView使用ESP32-S3芯片作为CSI采集节点。ESP32的WiFi驱动被修改后可以输出每个接收到的帧的CSI数据,采样率可达100Hz以上。每个CSI帧包含52个子载波的复数值,单帧数据量约416字节。

1.2 为什么人体运动能被检测到?

人体主要由水组成,水的介电常数远高于空气(约80 vs 1)。当一个人在WiFi信号传播路径上移动时,他会:

  • 改变多径传播路径:人体反射和吸收射频能量
  • 引入相位偏移:人体的存在改变了信号的传播距离
  • 造成振幅衰减:人体吸收部分射频能量

即使是静止不动的人,胸腔随呼吸的起伏(约5mm幅度)也会造成可检测的相位变化。心跳引起的体表微振动(<1mm)同样能被高灵敏度CSI系统捕捉到。

这里有一个直觉上的类比:想象你在一个黑暗的房间里,有人在房间里走动。你看不到他,但你能感觉到空气的流动变化------WiFi感知就像用无线电波"感觉"空气流动。

1.3 多径效应与空间指纹

在室内环境中,WiFi信号不会只走直线。它会从墙壁、地板、天花板、家具上反射,形成多条到达路径。这就是多径效应(Multipath Effect)

每条路径有不同的长度,因此有不同的相位延迟。接收端收到的信号是所有路径的叠加:

ini 复制代码
H = Σᵢ aᵢ · e^(-j2πfτᵢ)

其中 aᵢ 是第i条路径的衰减系数,τᵢ 是第i条路径的传播延迟。当人体移动时,会新增或消除某些传播路径,导致叠加结果发生显著变化。

RuView利用这个特性来建立射频指纹(RF Fingerprint)。通过长时间采集某个空间的CSI数据,可以建立该空间的"射频地图"------家具的位置、房间的形状都会编码在CSI的统计特征中。当环境发生变化(如家具被移动),射频指纹也会随之改变,系统就能检测到这种变化。

2. 信号处理管线

2.1 带通滤波提取生命体征

RuView的核心信号处理思路非常直接:不同频段的CSI变化对应不同的物理现象。

呼吸检测:呼吸频率在0.1-0.5 Hz(6-30 BPM)。对CSI振幅信号施加0.1-0.5 Hz带通滤波器,然后用过零检测计算频率:

rust 复制代码
// RuView中的呼吸检测核心逻辑(简化版)
fn detect_breathing(csi_amplitude: &[f64], sample_rate: f64) -> f64 {
    // 1. 带通滤波:0.1-0.5 Hz
    let filtered = bandpass_filter(csi_amplitude, 0.1, 0.5, sample_rate);
    
    // 2. 过零检测
    let zero_crossings = count_zero_crossings(&filtered);
    
    // 3. 计算BPM
    let duration = csi_amplitude.len() as f64 / sample_rate;
    let frequency = zero_crossings as f64 / (2.0 * duration);
    frequency * 60.0  // 转换为BPM
}

fn bandpass_filter(signal: &[f64], low_hz: f64, high_hz: f64, sample_rate: f64) -> Vec<f64> {
    // 二阶Butterworth带通滤波器
    let nyquist = sample_rate / 2.0;
    let low_normalized = low_hz / nyquist;
    let high_normalized = high_hz / nyquist;
    
    // 计算滤波器系数
    let (b, a) = butterworth_coefficients(2, low_normalized, high_normalized);
    
    // 应用IIR滤波
    apply_iir_filter(signal, &b, &a)
}

心率检测:心率在0.8-2.0 Hz(40-120 BPM),使用同样的过零检测方法,只是滤波器通带不同。

这种基于频域分离的方法简单但有效。呼吸和心跳在频域上天然分离,不需要复杂的盲源分离算法。

实际测试中发现一个问题:当人坐着不动时,呼吸信号很稳定,但心率信号容易被身体的微小移动干扰。解决方法是在心率检测前加一个**自适应噪声消除(ANC)**步骤------用另一个子载波的信号作为参考噪声,通过LMS算法消除体动干扰。

2.2 Fresnel区模型与穿墙检测

WiFi信号的空间传播可以用Fresnel区来建模。第一Fresnel区是一个以发射端和接收端为焦点的椭球体。当人体进入某个Fresnel区时,会对该区域内的信号产生最大影响。

python 复制代码
# Fresnel区边界计算
import numpy as np

def fresnel_zone_radius(d1, d2, wavelength, n=1):
    """
    计算第n Fresnel区在指定点的半径
    
    Args:
        d1: 到发射端的距离
        d2: 到接收端的距离
        wavelength: 信号波长
        n: Fresnel区序号
    """
    total_distance = d1 + d2
    return np.sqrt(n * wavelength * d1 * d2 / total_distance)

# 2.4 GHz WiFi的波长约为0.125m
wavelength = 0.125  # meters

# 计算在房间中心处的第一Fresnel区半径
d1, d2 = 3.0, 3.0  # 发射端和接收端各距中心3米
radius = fresnel_zone_radius(d1, d2, wavelength, n=1)
print(f"第一Fresnel区半径: {radius:.2f}m")  # ≈ 0.43m

RuView利用这个模型实现了穿墙检测。当多个CSI节点部署在不同位置时,每个节点的Fresnel区交叉覆盖,人体在任何位置都会扰动至少一个节点的信号。

穿墙检测的物理原理是:WiFi信号(尤其是2.4GHz)可以穿透大多数非金属墙壁。信号穿过墙壁时会有衰减(通常5-15dB),但仍有足够的能量在墙的另一侧形成Fresnel区。当有人在墙的另一侧走动时,信号的多径结构会发生变化,这种变化可以被检测到。

2.3 去噪与异常值处理

原始CSI数据噪声很大------环境中的其他WiFi网络、蓝牙设备、微波炉都会造成干扰。RuView采用了多级去噪策略:

rust 复制代码
// CSI数据预处理管线
fn preprocess_csi(raw_csi: Vec<CsiFrame>) -> Vec<CsiFrame> {
    // 1. 相位展开(Phase Unwrapping)
    let unwrapped = unwrap_phase(&raw_csi);
    
    // 2. 移除线性相位偏移(去除频率偏移引起的相位漂移)
    let calibrated = remove_linear_phase(&unwrapped);
    
    // 3. Hampel滤波器去除异常值
    let filtered = hampel_filter(&calibrated, window_size=5, threshold=3.0);
    
    // 4. 移动平均平滑
    let smoothed = moving_average(&filtered, window_size=3);
    
    smoothed
}

fn hampel_filter(signal: &[f64], window_size: usize, threshold: f64) -> Vec<f64> {
    let mut result = signal.to_vec();
    for i in window_size..signal.len() - window_size {
        let window = &signal[i - window_size..=i + window_size];
        let median = median(window);
        let mad = 1.4826 * median_absolute_deviation(window, median);
        
        if (signal[i] - median).abs() > threshold * mad {
            result[i] = median;  // 用中位数替换异常值
        }
    }
    result
}

Hampel滤波器比简单的均值滤波更鲁棒------它用中位数和MAD(中位绝对偏差)来检测异常值,对脉冲干扰有很好的抵抗力。

相位展开是另一个关键步骤。WiFi CSI的相位值被限制在-π, π范围内,当实际相位变化超过这个范围时会出现跳变。相位展开算法通过检测并补偿这些跳变,恢复出连续的相位曲线。这对后续的频率分析至关重要。

3. 姿态估计:从WiFi到人体关键点

3.1 WiFlow架构

RuView的姿态估计模块基于CMU的DensePose From WiFi研究,使用了名为WiFlow的架构。核心思路是:

  1. 输入:10个CSI传感器的信号(振幅+相位)
  2. 特征提取:1D卷积网络提取时频特征
  3. 关键点回归:输出17个COCO标准人体关键点的坐标
python 复制代码
import torch
import torch.nn as nn

class WiFlowEncoder(nn.Module):
    """CSI特征提取网络"""
    def __init__(self, num_sensors=10, num_subcarriers=52):
        super().__init__()
        # 每个传感器输入: [振幅, 相位] × 子载波数
        self.input_dim = num_sensors * 2 * num_subcarriers
        
        self.conv_blocks = nn.Sequential(
            nn.Conv1d(1, 64, kernel_size=7, padding=3),
            nn.BatchNorm1d(64),
            nn.ReLU(),
            nn.MaxPool1d(2),
            
            nn.Conv1d(64, 128, kernel_size=5, padding=2),
            nn.BatchNorm1d(128),
            nn.ReLU(),
            nn.MaxPool1d(2),
            
            nn.Conv1d(128, 256, kernel_size=3, padding=1),
            nn.BatchNorm1d(256),
            nn.ReLU(),
            nn.AdaptiveAvgPool1d(16),
        )
        
        self.fc = nn.Linear(256 * 16, 17 * 2)  # 17个关键点, 每个(x, y)
    
    def forward(self, csi_input):
        # csi_input: [batch, num_sensors * 2 * num_subcarriers, time_steps]
        x = self.conv_blocks(csi_input)
        x = x.flatten(1)
        keypoints = self.fc(x)
        return keypoints.view(-1, 17, 2)  # [batch, 17, 2]

这个网络结构并不复杂------3层1D卷积加一个全连接层。关键在于输入数据的构造:把10个传感器的振幅和相位数据拼接成一个高维向量,让网络自己学习如何融合多传感器信息。

3.2 无摄像头训练:代理标签方法

一个关键问题是:没有摄像头怎么训练?

RuView采用了一种**代理标签(Proxy Labels)**方法:

  1. 部署阶段:在目标空间同时放置CSI传感器和摄像头
  2. 数据采集:摄像头提取DensePose标注作为ground truth
  3. 模型训练:用CSI数据训练模型预测摄像头的结果
  4. 实际使用:移除摄像头,模型仅依赖CSI工作

这意味着训练需要一次性的摄像头辅助,但部署后完全不需要摄像头。目前PCK@20(20%阈值内的正确关键点比例)约为2.5%,看起来很低,但这是一个无摄像头系统------在完全黑暗、有遮挡的环境下也能工作。

项目README中提到,正在推进ADR-079------使用摄像头ground truth进行训练,目标是将PCK@20提升到35%以上。这个管线已经实现,但数据采集和评估阶段还在进行中。

3.3 脉冲神经网络:更省电的推理方案

RuView的边缘AI模块使用**脉冲神经网络(Spiking Neural Networks, SNN)**进行推理。与传统的连续激活函数不同,SNN使用离散的脉冲信号,更接近生物神经系统的工作方式。

SNN的优势在于:

  • 能耗极低:只有在脉冲发放时才消耗能量,大部分时间是静默的
  • 时序信息天然编码:脉冲的时间本身就携带信息,非常适合处理时序CSI数据
  • 硬件友好:可以在神经形态芯片上高效运行

RuView使用8维特征向量作为SNN的输入,这些特征从原始CSI数据中提取------包括振幅均值、相位方差、频谱能量等统计量。SNN在Cognitum Seed上运行,推理延迟在毫秒级。

4. 边缘计算架构

4.1 ESP32网状网络

RuView的硬件基础是一个ESP32网状网络。每个节点成本约9-15美元,运行Rust编写的固件:

toml 复制代码
# Cargo.toml (ESP32固件)
[package]
name = "ruview-node"
version = "0.1.0"
edition = "2021"

[dependencies]
esp-idf-sys = { version = "0.36", features = ["native"] }
esp-idf-hal = "0.43"
esp-wifi = "0.7"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

[features]
default = ["esp32s3"]
esp32s3 = []

多个ESP32节点组成mesh网络,每个节点负责采集其覆盖区域的CSI数据。节点之间通过ESP-NOW协议低延迟通信(延迟<1ms)。

ESP-NOW是乐鑫开发的轻量级通信协议,工作在WiFi物理层上但不需要建立WiFi连接。它的优势是延迟极低(通常<1ms)、功耗低、支持一对多通信。在RuView中,所有传感器节点将CSI数据通过ESP-NOW发送给主节点,主节点汇总后进行高级处理。

4.2 Cognitum Seed:边缘AI推理

CSI数据的高级处理(姿态估计、活动识别)在Cognitum Seed上运行------这是一个边缘AI计算模块,BOM成本约140美元。它提供:

  • 脉冲神经网络(SNN)推理:比传统DNN更省电
  • 加密见证链:每次测量都用Ed25519签名,防篡改
  • 本地持久化记忆:环境模型存储在本地,不依赖云端
rust 复制代码
// 加密见证链实现
use ed25519_dalek::{Keypair, Signature, Signer, Verifier};

struct WitnessChain {
    keypair: Keypair,
    chain: Vec<WitnessEntry>,
}

struct WitnessEntry {
    timestamp: u64,
    measurement_hash: [u8; 32],
    previous_signature: Signature,
    signature: Signature,
}

impl WitnessChain {
    fn attest(&mut self, measurement: &CsiMeasurement) -> WitnessEntry {
        let hash = sha256(measurement.to_bytes());
        let previous_sig = self.chain.last()
            .map(|e| e.signature)
            .unwrap_or(Signature::new([0; 64]));
        
        let message = [hash.as_slice(), previous_sig.to_bytes().as_slice()].concat();
        let signature = self.keypair.sign(&message);
        
        let entry = WitnessEntry {
            timestamp: current_timestamp(),
            measurement_hash: hash.into(),
            previous_signature: previous_sig,
            signature,
        };
        
        self.chain.push(entry.clone());
        entry
    }
}

这种设计使得每个测量值都有密码学证明。在医疗监测场景中,如果需要向医生证明某次心率数据确实是在某个时间点采集的,见证链可以提供不可伪造的证据。

4.3 系统总架构

把所有组件组合起来,RuView的完整数据流是这样的:

markdown 复制代码
ESP32节点×N → ESP-NOW → 主节点 → CSI原始数据 → 去噪管线
    → 特征提取 → SNN推理 → 活动识别/姿态估计
    → 见证链签名 → 本地存储/WebSocket推送

硬件成本分解:
- ESP32-S3 × 3节点: $27
- Cognitum Seed: $140
- 电源/外壳/线材: ~$20
- 总计: ~$187(不到一台入门级摄像头的价格)

5. 实际部署与踩坑

5.1 传感器布局

传感器的放置对检测效果影响巨大。以下是我实际测试中的经验:

css 复制代码
推荐布局(2节点,覆盖20㎡房间):

  [Node A]                    [Node B]
      \                        /
       \    Fresnel Zone 1    /
        \    ___________     /
         \  /           \  /
          \/    人体     \/
          /\             /\
         /  \___________/  \
        /    Fresnel Zone 2  \
       /                      \
  [墙壁]                    [墙壁]

- 两个节点间距 3-6 米
- 高度 1.2-1.5 米(与人体躯干同高)
- 避免金属物体遮挡直射路径

5.2 常见问题与解决

问题1:CSI数据缺失帧

ESP32的WiFi驱动在高负载时会丢帧。解决方法是在采集端做插值:

rust 复制代码
fn interpolate_missing_frames(frames: Vec<Option<CsiFrame>>) -> Vec<CsiFrame> {
    let mut result = Vec::with_capacity(frames.len());
    for i in 0..frames.len() {
        if let Some(frame) = &frames[i] {
            result.push(frame.clone());
        } else {
            // 线性插值:找前后最近的有效帧
            let prev = (0..i).rev().find_map(|j| frames[j].as_ref());
            let next = (i+1..frames.len()).find_map(|j| frames[j].as_ref());
            match (prev, next) {
                (Some(p), Some(n)) => result.push(interpolate(p, n, 0.5)),
                (Some(p), None) => result.push(p.clone()),
                (None, Some(n)) => result.push(n.clone()),
                _ => {} // 两端都无数据,跳过
            }
        }
    }
    result
}

问题2:环境变化导致误报

家具移动、温度变化都会影响CSI基线。RuView用自适应基线校准解决这个问题------每30秒更新一次环境基线,使用指数移动平均:

rust 复制代码
fn update_baseline(current: &CsiFrame, baseline: &mut CsiFrame, alpha: f64) {
    for i in 0..baseline.subcarriers.len() {
        baseline.subcarriers[i] = alpha * current.subcarriers[i] 
                                + (1.0 - alpha) * baseline.subcarriers[i];
    }
}
// alpha = 0.05 时,约10分钟完成一次完整的基线更新

问题3:多WiFi网络干扰

2.4GHz频段拥挤时CSI数据噪声增大。解决方案:

  • 切换到5GHz频段(可用子载波更多,带宽更大)
  • 使用信道跳频(Channel Hopping)避开拥挤信道
  • 增加节点数量提高冗余度

问题4:ESP32发热导致CSI漂移

长时间运行后ESP32芯片温度升高,会导致WiFi射频参数变化,引起CSI数据的系统性偏移。解决方法是在固件中加入温度补偿:

rust 复制代码
fn temperature_compensate(csi: &mut CsiFrame, chip_temp: f32) {
    // 温度偏移系数(经验值,需要针对具体硬件校准)
    let temp_coeff = 0.002;  // 每度偏移0.2%
    let ref_temp = 25.0;     // 参考温度25°C
    
    let correction = 1.0 + temp_coeff * (chip_temp - ref_temp);
    for amp in &mut csi.amplitudes {
        *amp *= correction;
    }
}

6. 性能数据

指标 数值 备注
姿态估计速度 171K embeddings/s M4 Pro处理器
呼吸检测范围 6-30 BPM 过零检测
心率检测范围 40-120 BPM 带通滤波
存在检测延迟 0.012 ms 模型+PIR融合
穿墙深度 最远5米 Fresnel区+多径建模
单节点成本 ~$9 ESP32-S3
完整系统成本 ~$140 ESP32 mesh + Cognitum Seed
姿态估计精度(PCK@20) ~2.5% 无摄像头代理标签
SNN特征维度 8维 脉冲神经网络输入
多频扫描 6个WiFi信道 利用邻居路由器

7. 隐私与伦理

WiFi感知最大的优势也是最敏感的点------它不需要摄像头。这意味着:

  • 隐私友好:没有图像数据,不会拍到人脸
  • 穿墙能力:可以检测到隔壁房间的人
  • 无法"关闭":只要WiFi开着就在被感知

RuView通过Ed25519见证链确保数据不被滥用,但穿墙检测能力带来的隐私问题仍然值得讨论。在一些国家,使用这类技术可能需要获得被监测者的同意。

项目本身是MIT开源协议,代码完全透明。用户可以审计数据流向,也可以断开网络让系统完全离线运行。这种设计从架构层面保障了用户对数据的控制权。

8. 论文引用

RuView的技术基础来自以下学术研究:

  1. DensePose From WiFi - Carnegie Mellon University

    • 论文:DensePose From WiFi
    • 核心贡献:证明WiFi CSI可以用于人体姿态估计,提出WiFlow架构
  2. Wi-Fi Sensing: A Survey - IEEE Communications Surveys & Tutorials

    • 综述WiFi感知技术的发展历程和应用场景
  3. PhaseFi: Phase-based Fingerprinting for Indoor Localization - IEEE

    • 基于CSI相位信息的室内定位技术
  4. Ed25519: high-speed high-security signatures - Daniel J. Bernstein

    • RuView见证链使用的签名算法

9. 我的使用感受

说实话,第一次看到这个项目的时候我是怀疑的。WiFi信号真的能检测心率?但实际跑起来之后,确实能检测到------虽然精度比不上专业的胸带式心率计,但作为一个完全无接触的方案,已经很惊人了。

优点:

  • 硬件成本极低,ESP32到处都能买到
  • Rust写的信号处理管线性能很好,内存占用低
  • 文档和架构设计很完善(有ADR、PRD等工程文档)
  • 加密见证链的设计很有远见,为数据可信度提供了保障
  • 项目活跃度很高,Issues和PRs都很活跃

不足:

  • 姿态估计精度目前还比较低(PCK@20 ≈ 2.5%)
  • ESP32的CSI驱动不稳定,偶尔丢帧
  • 需要至少2个节点才能获得可用的空间分辨率
  • 训练阶段仍需摄像头辅助
  • 对于完全不懂信号处理的开发者,入门门槛偏高

适合人群:

  • 对IoT和嵌入式开发感兴趣的工程师
  • 想做智能家居感知系统的开发者
  • 研究无线感知的学术人员
  • 对隐私友好型传感器有需求的产品经理

推荐指数:⭐⭐⭐⭐☆(4/5)

这个项目的技术方向很有价值------用廉价硬件+开源软件实现原本需要昂贵设备才能做到的感知能力。虽然目前还在beta阶段,但已经能看出它在智能家居、养老看护、安防监控等场景的巨大潜力。想象一下:老人在家摔倒了,不需要佩戴任何设备,WiFi系统就能自动检测并报警。这种能力在老龄化社会中价值巨大。

期待后续在姿态估计精度上的提升,以及更多社区贡献者的加入。

项目地址:github.com/ruvnet/RuVi...

相关推荐
IT_陈寒5 小时前
Vue这个坑我跳了两次,原来问题出在这
前端·人工智能·后端
新新技术迷5 小时前
Node给AI接口做SSE代理与鉴权
人工智能
redreamSo6 小时前
大模型是不是到顶了?瓶颈到底在哪
人工智能·openai
Oo9206 小时前
Tool Use 背后的技术逻辑
人工智能
姗姗来迟了6 小时前
Vue3封装AI流式对话组件踩坑实录
人工智能
码上天下7 小时前
用Pinia管理AI多会话状态
人工智能
用户054324329707 小时前
Next.js接大模型流式SSE实操踩坑
人工智能
Assby7 小时前
从 Function Calling 到 MCP:理解 Agent 工具调用的底层通信机制
人工智能·后端
小星AI8 小时前
Claude Code 从入门到精通,一步到位
人工智能
后端小肥肠8 小时前
Codex + Obsidian 做人生副本视频:输入主题文案,直通剪映草稿
人工智能·aigc·agent