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...

相关推荐
王木风1 小时前
人手一个数据库:拆解 Kimi 背后的 AI 基建新思路
人工智能
PNP Robotics1 小时前
【荣誉时刻】PnP机器人荣获「具身智能跨界融合创新奖」,以硬核实力引领产业融合新范式
人工智能·深度学习·机器学习·机器人
南宫萧幕1 小时前
HEV能量管理策略 Simulink 实战:从零搭建 Rule-based 与 A-ECMS 对比模型及排错指南
人工智能·算法·matlab·simulink·控制
小撒的私房菜1 小时前
Day 5:Agent Loop——整个系列里最关键的一天
人工智能·后端
还没学会摸鱼的钓鱼仔1 小时前
手撕 LangChain Deep Agents 源码(二):System Prompt 的组装——四层叠加背后的潜规则
人工智能
Fleshy数模1 小时前
玩转 LangChain:从 Prompt 模板到多场景 AI 交互实战
人工智能·langchain·llm
华盛AI1 小时前
【键盘驱动的效率平台Raycast介绍】
人工智能
王_teacher1 小时前
LSTM 原理详解手动编写LSTM模型代码
人工智能·llm·nlp·lstm
CV-杨帆1 小时前
YOLO26 检测系统使用教程
人工智能