RuView 源码原理与实现技术过程
面向工程实现的学习手册:从 WiFi CSI 物理基础,一路追踪到 ESP32 固件、Rust DSP、边缘 Vitals、Sensing Server、模型训练与 UI,再给出一个可以真正动手复现的 Mini-RuView 路线。
项目判断:RuView 到底是什么
RuView 将 WiFi 无线信道从“通信链路”变成“空间传感器”。ESP32 读取每个 OFDM 子载波的 Channel State Information(CSI),人体移动、呼吸、姿态变化会改变多径传播路径,使复数信道响应 H(k,t) 的幅度与相位随时间产生可测扰动。
| 能力 | 当前实现方式 | 工程判断 |
|---|---|---|
| CSI 采集与 UDP | ESP32-S3/C6 + ESP-IDF CSI API + ADR-018 二进制帧 | 核心可复现 |
| Presence | 相位方差 + 自适应阈值,也可加载学习模型 | 核心可复现 |
| 呼吸 | 0.1–0.5 Hz 带通 + 零交叉 / Rust vitals pipeline | 实验性,需校准 |
| 心率 | 0.8–2.0 Hz 带通 + 自相关/相干性加权 | 实验性,非医疗测量 |
| Fall / Motion | 相位加速度、运动能量等启发式 | 可运行但依赖环境 |
| 17 点 Pose | Candle / RVF / 训练流水线;仓库标注实时单节点模型仍 first-cut | 研究/开发中 |
| 多节点 / World Model | 多静态融合、图模型、OccWorld、RuField 等 | 高级研究层 |
WiFi CSI 原理:为什么无线信号能“看到”人
2.1 CSI 是什么
OFDM WiFi 会把带宽拆成多个子载波。每个子载波都有一个复数信道响应:
H(k,t) = A(k,t) · exp(j · φ(k,t)) A = √(I² + Q²) # 幅度 φ = atan2(Q, I) # 相位
人体出现后,直达路径、反射路径和绕射路径一起变化。结果不是“看到图像”,而是观察到 一组高维、随时间变化的无线通道特征。
幅度变化
身体移动导致衰落、遮挡、多径叠加改变,适合检测运动强度、粗粒度 presence。
相位变化
对微小路径长度变化更敏感,适合呼吸、微动、速度/多普勒等分析,但需要 unwrap 与去噪。
总体架构:从 ESP32 到浏览器的完整链路
人体改变多径传播路径
ESP-IDF CSI callback → I/Q → ADR-018 UDP;可在 Core 1 做 Edge DSP
解析帧、节点状态、时间戳、幅相转换、硬件归一化
Phase sanitize → feature → motion / presence / breathing / HR
Axum + Tokio:UDP 输入、状态机、模型、REST、WebSocket、MQTT/Matter
实时可视化、自动化、记录、训练、模型加载、语义事件
ESP32 固件:采集层是整个系统的地基
4.1 Raw CSI 帧(ADR-018)
0..3 u32 magic = 0xC5110001 4 u8 node_id 5 u8 n_antennas 6..7 u16 n_subcarriers 8..11 u32 channel/frequency metadata 12..15 u32 sequence 16 i8 RSSI 17 i8 noise floor 18..19 flags / reserved 20..N I/Q pairs
最小实现最重要的不是“复杂算法”,而是让 帧格式、字节序、序列号、采样节奏稳定,否则后面所有信号算法都会被脏数据拖垮。
4.2 为什么固件要双核
CSI callback 位于 WiFi 路径,不能做重计算。RuView 将采集和 DSP 解耦:Core 0 只采集/入环形缓冲区,Core 1 处理 phase、variance、filter。这种结构避免 WiFi 任务因为 DSP 阻塞而丢包。
Core 0: WiFi CSI callback
↓ enqueue
SPSC Ring Buffer
↓
Core 1: phase → stats → filters → events
sendto() 太频繁导致 lwIP pbuf 耗尽。仓库使用速率限制 + ENOMEM backoff。学习实现时务必先做吞吐/丢包监控。Rust 信号处理:CSI 如何变成可用特征
5.1 数据模型
wifi-densepose-signal::CsiData 将原始数据统一成 amplitude[antenna, subcarrier] 与 phase[antenna, subcarrier],然后所有算法围绕同一数据结构运行。
i8 / complex samples
sqrt(I²+Q²), atan2(Q,I)
unwrap → outlier remove → smoothing
Amplitude / Phase / Correlation / Doppler / PSD
motion / presence / activity / pose
| 特征 | 源码 | 意义 |
|---|---|---|
| Amplitude mean / variance / RMS | features.rs | 整体衰落、活动能量 |
| Phase difference / gradient | features.rs | 空间/频域变化 |
| Phase coherence | features.rs | 不同天线/子载波的一致性 |
| PSD | features.rs | 周期性运动、呼吸频段 |
| Doppler | features.rs / ruvsense | 运动速度方向线索 |
| Fresnel / CIR / BVP | fresnel.rs / cir / bvp.rs | 更高级空间与时序建模 |
5.2 相位为什么必须 sanitize
atan2 输出被限制在 [-π, π],真实连续相位跨过边界时会突然跳 2π。如果不 unwrap,速度、呼吸频率、相位加速度都会出现伪峰值。
raw: 2.9, 3.0, 3.1, -3.1, -3.0 unwrap: 2.9, 3.0, 3.1, 3.18, 3.28
Presence / 呼吸 / 心率:最值得先复现的算法
6.1 Presence
最简单可靠的学习版本可以只做“空房校准 + 相位方差”。RuView 的 Edge Pipeline 使用 Welford 在线统计维护均值/方差,并采用约 1200 帧、3σ 的自适应阈值。
# 空房标定 μ0, σ0 = running_stats(phase_variance) # 在线 score = current_variance presence = score > μ0 + 3 * σ0
这不是“识别人”,而是“检测 RF 场相对空房基线是否出现明显扰动”。风扇、门、微波炉、AP 发射功率变化都可能产生误报。
6.2 呼吸
静止人体胸腔周期运动会在 CSI 中形成低频扰动。
residual ↓ bandpass 0.1–0.5 Hz ↓ zero crossing / period ↓ BPM = cycles/min
6.3 心率
频率更高、幅度更弱,因此比呼吸难得多。
residual ↓ bandpass 0.8–2.0 Hz ↓ autocorrelation + phase coherence weight ↓ HR estimate
6.4 Rust vitals crate 四阶段
- CsiVitalPreprocessor:EMA 去除静态分量;
- BreathingExtractor:0.1–0.5 Hz 带通 + 零交叉;
- HeartRateExtractor:0.8–2.0 Hz 带通 + 自相关 + 子载波相干性加权;
- VitalAnomalyDetector:Welford Z-score 做趋势/异常检测。
Sensing Server:系统真正的“中枢”
v2/crates/wifi-densepose-sensing-server/src/main.rs 是单二进制部署面。它用 Axum + Tokio 把底层 CSI、模型、训练、状态和 UI 统一起来。
| 模块 | 职责 |
|---|---|
csi | 输入 CSI 数据源与解析 |
vital_signs | 生命体征检测 |
pose / pose_physics | 姿态模型与物理约束 |
adaptive_classifier | 环境自适应分类 |
training_api / trainer | 记录、训练、进度与模型生成 |
rvf_container / rvf_pipeline | 模型/模块容器与渐进加载 |
multistatic_bridge | 多节点融合 |
field_bridge | 统一 RF field / provenance surface |
main.rs 第一行顺读到最后。更有效的方式是沿数据流追踪:UDP receive → frame parse → AppState → detector/model → broadcast → WebSocket JSON。模型与姿态:为什么不能一上来就做 DensePose
Pose 的目标是学习映射:
CSI time window
↓
Encoder / embedding
↓
spatial-temporal representation
↓
17 keypoints / pose state
但真正难点是 监督数据:需要 WiFi CSI 与摄像头/动作捕捉标签严格时间对齐。单房间、单 AP、单人的训练数据通常会产生很强的域过拟合。
| 层级 | 建议 |
|---|---|
| 阶段 A | 先复现 Presence / Motion,确认采集链路可靠。 |
| 阶段 B | 学习 128-d embedding / contrastive pretraining,先做环境与动作分类。 |
| 阶段 C | 采集同步 Camera + CSI 数据,训练 17-keypoint head。 |
| 阶段 D | 跨房间、跨设备、跨人物 domain adaptation。 |
源码地图:按学习价值排序
推荐阅读顺序
README → user-guide → ADR-018 → firmware README → csi_collector → csi_frame → phase_sanitizer → features → vitals → sensing-server。
不要先读 100+ Cogs、World Model、Matter、Agent、HomeKit 等外围模块。那会稀释对核心 RF sensing pipeline 的理解。
先跑通项目:无硬件与有硬件两条路径
10.1 无硬件:Docker / 模拟数据
docker pull ruvnet/wifi-densepose:latest docker run -p 3000:3000 -p 5005:5005/udp \ -e CSI_SOURCE=simulated \ ruvnet/wifi-densepose:latest
目标不是验证“WiFi 能看人”,而是先理解 UI、API、WebSocket 消息、状态结构和完整软件链。
10.2 Rust 源码运行
git clone https://github.com/ruvnet/RuView.git cd RuView/v2 cargo build --release cargo test --workspace --no-default-features cargo run -p wifi-densepose-sensing-server -- \ --http-port 3000 \ --source simulate
10.3 ESP32-S3 实机
# 1. ESP-IDF 构建 cd firmware/esp32-csi-node idf.py set-target esp32s3 idf.py build # 2. flash idf.py -p /dev/ttyUSB0 flash # 3. 写入 WiFi 与 server IP python provision.py \ --port /dev/ttyUSB0 \ --ssid "YOUR_WIFI" \ --password "YOUR_PASS" \ --target-ip 192.168.1.20 # 4. server cd ../../v2 cargo run -p wifi-densepose-sensing-server -- \ --source esp32 \ --udp-bind 0.0.0.0 \ --udp-allow 192.168.1.0/24
从零实现一个 Mini-RuView:建议的工程过程
定义 20-byte header + I/Q payload,写 sender/receiver,验证 sequence、丢包和 endian。不要碰算法。
写单元测试:I=3,Q=4 → amplitude=5;phase 使用 atan2。然后加入 unwrap。
保存 30–60 秒空房统计,计算 μ + kσ。先实现二分类 presence,而不是 pose。
ESP-IDF CSI callback 将 I/Q 以 UDP 发送。对比 synthetic 与 real 的 parser 行为是否一致。
先呼吸,后心率。每次只加入一个滤波器,并用离线录制数据验证频谱。
将 detector 输出统一为 JSON event,UI 只订阅状态,不直接理解底层 CSI。
11.1 Mini-RuView 最小目录
mini-ruview/
├── firmware/
│ └── esp32/
│ ├── main.c
│ ├── csi.c
│ └── udp.c
├── server/
│ ├── protocol.rs
│ ├── csi.rs
│ ├── phase.rs
│ ├── presence.rs
│ ├── vitals.rs
│ └── main.rs
├── simulator/
│ └── synth_csi.py
├── ui/
│ └── index.html
└── tests/
├── protocol/
└── signals/
11.2 MVP 的核心 Rust 逻辑
// 伪代码
loop {
let packet = udp.recv().await?;
let frame = parse_adr018(packet)?;
let amp = iq_to_amplitude(&frame.iq);
let phase = iq_to_phase(&frame.iq);
let clean = unwrap_and_smooth(phase);
baseline.update_if_calibrating(&clean);
let score = phase_variance(&clean);
let presence = score > baseline.mean + 3.0 * baseline.std;
ws.broadcast(json!({
"type": "sensing_update",
"node": frame.node_id,
"presence": presence,
"score": score
}));
}
学习路线:按 4 周掌握核心技术
| 周 | 重点 | 产出 |
|---|---|---|
| Week 1 | OFDM / CSI / I-Q / amplitude / phase / FFT / 滤波 | Python notebook:合成 CSI + 频谱 + unwrap |
| Week 2 | Rust 数据结构、UDP、ADR-018、signal crate | 自己的 Rust CSI parser + presence detector |
| Week 3 | ESP-IDF CSI、SPSC、UDP、NVS、采样稳定性 | ESP32 → PC 实时 CSI 流 |
| Week 4 | Vitals、Axum、WebSocket、校准和实机评估 | Mini-RuView Dashboard |
调试与验证:RF 项目最容易踩坑的地方
| 现象 | 优先排查 |
|---|---|
| 完全没有 CSI | ESP-IDF CSI enable、promiscuous/management traffic、板型 overlay、WiFi channel |
| UDP 收不到 | 目标 IP、同子网、路由器 WiFi↔LAN 隔离、防火墙、bind 地址 |
| Presence 常亮 | 空房 baseline 不干净、风扇/门/路由器变化、阈值过低 |
| 呼吸 BPM 飘 | 采样率假设错误、窗口太短、subject motion、带通参数 |
| 心率随机 | SNR 不足、位置不佳、没有相干性权重、运动伪影 |
| 模型训练很好、换房间失效 | 典型 domain shift:环境多径被模型记住 |
必须记录的工程指标
CSI callbacks/s UDP sent/s drop rate RSSI / noise floor subcarrier count effective sample rate baseline variance presence score filter latency二次开发方案:从“会跑”升级到“有产品价值”
A. 家庭 Presence
单节点/双节点,重点做校准、抗干扰、状态机、Home Assistant。技术难度最低,最适合产品 MVP。
B. 睡眠呼吸
固定床位 + 位置约束,持续采样,做呼吸趋势与异常,而非追求瞬时心率。
C. 多房间跟踪
每房间多个节点,使用时间同步、节点图与 transition state,难点是标定和跨房间关联。
D. Pose Research
CSI+摄像头同步数据集、对比学习预训练、关键点 head、跨环境 adaptation。投入最大。
推荐的产品化优先级
Presence → Motion → Room Activity → Breathing Trend → Fall Risk → Multi-room → Pose。
这是按“可验证性 / 数据需求 / 泛化难度”排序,而不是按视觉炫酷程度排序。
能力边界与隐私设计
- RF sensing 不是稳定成像。房型、家具、AP、节点朝向都会改变多径场。
- 训练集 benchmark ≠ 现场准确率。必须给出数据集、split、环境与硬件条件。
- 多人的可分离性明显更难。单天线/少节点情况下尤其受限。
- 生命体征需要参考设备验证。不要把实验性 BPM 输出当医疗结论。
- 隐私风险是真实的。无需摄像头不等于无需授权;部署应明确告知、限制采集范围、最小化原始 CSI 保存,并优先本地处理。
关键源码与文档入口
docs/user-guide.md — 安装、运行、数据源与训练入口
firmware/esp32-csi-node — ESP32 CSI 采集与 Edge DSP
ADR-018 — CSI 二进制协议与实现顺序
ADR-039 — ESP32 Edge Intelligence
wifi-densepose-signal — CSI / phase / features
wifi-densepose-vitals — 呼吸与心率
wifi-densepose-sensing-server — 运行时中枢
dd02efe... 附近代码。RuView 迭代非常快,实际运行前建议再次核对 README、firmware release 与相关 ADR。