RuView ESP32 CSI 传感网格:从节点固件到汇聚端的分布式无摄像感知落地指南
本文基于仓库架构决策记录 ADR-012,详解 RuView 如何用单价约 5–15 美元的 ESP32/ESP32-S3 构建可复现的多节点 CSI(信道状态信息)传感网格,打通"物理 WiFi 硬件 → CSI 字节流 → Rust/Python 处理管线"的完整数据通路。读完你将掌握 ESP-IDF CSI 采集三函数、节点固件与 UDP 汇聚端的分工、特征级融合规避跨节点时钟漂移的设计方法,以及从克隆固件、烧录、配置到实机验证的完整落地流程。
一、背景:硬件现实缺口与决策动机
ADR-012 是 RuView(WiFi-DensePose 演进体系)在硬件接入层面的关键架构决策。当时系统的 Rust 与 Python 管线已经实现了真实的信号处理(FFT、相位解缠、多普勒提取、相关特征),但缺少一条从物理 WiFi 硬件到 CSI 字节流再到管线输入的明确路径——csi_extractor.py 与 router_interface.py 仍是返回 np.random.rand() 的占位解析器(该问题在 ADR-011 中被称为"硬件现实缺口")。
要闭合这个缺口,需要选择一个具体、廉价、可复现的硬件平台来产生真实 CSI 数据,并把数据流入现有管线。
候选平台对比:为什么是 ESP32
ADR-012 将 ESP32/ESP32-S3 与两款经典科研网卡做了横向对比:
| 因素 | ESP32/ESP32-S3 | Intel 5300(iwl5300) | Atheros AR9580 |
|---|---|---|---|
| 成本 | 约 5–15 美元/节点 | 约 50–100 美元(二手网卡) | 约 30–60 美元(二手网卡) |
| 可得性 | 大规模量产、现货充足 | 已停产,仅 eBay 有售 | 已停产,仅 eBay 有售 |
| CSI 支持 | 官方 ESP-IDF API | Linux CSI Tool(内核补丁) | Atheros CSI Tool |
| 形态 | 独立 MCU | 需要 PCIe/Mini-PCIe 主机 | 需要 PCIe 主机 |
| 部署方式 | 电池/USB、无线 | 仅限台式机/笔记本 | 仅限台式机/笔记本 |
| 天线配置 | 1–2 TX、1–2 RX | 3 TX、3 RX(MIMO) | 3 TX、3 RX(MIMO) |
| 子载波数 | 52–56(802.11n) | 30(压缩) | 56(完整) |
| 保真度 | 较低(消费级 SoC) | 较高(专用网卡) | 较高(专用网卡) |
结论很明确:ESP32 在"可部署性"上胜出。Intel 5300 与 Atheros 网卡需要特定主板、内核修改与老旧操作系统;而 ESP32 是唯一"陌生人可以从电商买几块板子、下午烧录固件、当晚就能跑起 CSI 网格"的选项。当然,文档也如实指出其保真度低于科研级网卡,这一代价由网格的空间分集来弥补。
二、ESP-IDF 官方 CSI 接口:三个函数打通采集链路
ESP32 的 CSI 采集能力由 Espressif 的 ESP-IDF 官方 API 提供,ADR-012 给出了最核心的三步配置:
// 1. 配置要采集的 CSI 数据
wifi_csi_config_t csi_config = {
.lltf_en = true, // Long Training Field(对 CSI 最有利)
.htltf_en = true, // HT-LTF
.stbc_htltf2_en = true, // STBC HT-LTF2
.ltf_merge_en = true, // 合并 LTFs
.channel_filter_en = false,
.manu_scale = false,
};
esp_wifi_set_csi_config(&csi_config);
// 2. 注册 CSI 数据接收回调
esp_wifi_set_csi_rx_cb(csi_data_callback, NULL);
// 3. 使能 CSI 采集
esp_wifi_set_csi(true);
// 回调收到的内容:
void csi_data_callback(void *ctx, wifi_csi_info_t *info) {
// info->rx_ctrl: RSSI、noise_floor、channel、secondary_channel 等
// info->buf: 原始 CSI 数据(每个子载波一组 I/Q 对)
// info->len: CSI 数据缓冲区长度
// 典型值:112 字节 = 56 个子载波 × 2 (I,Q) × 每项 1 字节
}
在仓库的实际固件中,这套逻辑位于 csi_collector.c:
- 代码以编译期断言强制要求
CONFIG_ESP_WIFI_CSI_ENABLED打开(见 ADR-057 思路),否则直接#error阻止编译,避免"编过了但运行时报CSI not enabled in menuconfig!"这类令人困惑的失败; - 回调以 100–500 Hz 频率触发,为防 lwIP pbuf 耗尽,代码实现了 50 Hz 速率限制(距上次成功
sendto()不足 20 ms 的帧被静默丢弃)与 ENOMEM 退避(sendto()返回 errno 12 时暂停发送 100 ms)两道保护; - 回调内还做了防御性拷贝:把 NVS 配置字段与 MAC 过滤配置复制为模块级静态量,规避
wifi_init_sta()对g_nvs_config的破坏导致 LoadProhibited 崩溃(源码注释记录了80:b5:4e:c1:be:b8设备上的实测案例)。
三、总体架构:从 3–6 个节点到可视化
ADR-012 确立的架构决策是:以 ESP32 CSI 传感网格作为主要硬件接入路径,建立从固件到汇聚端、再到 Rust 管线与可视化的全栈体系。
┌─────────────────────────────────────────────────────────────────────┐
│ ESP32 CSI Sensor Mesh │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ ESP32 │ │ ESP32 │ │ ESP32 │ ... (3-6 nodes) │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ │ │ │ │ │ │ │
│ │ CSI Rx │ │ CSI Rx │ │ CSI Rx │ ← WiFi frames from │
│ │ FFT │ │ FFT │ │ FFT │ consumer router │
│ │ Features │ │ Features │ │ Features │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ │ UDP/TCP stream (WiFi or secondary channel) │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Aggregator │ │
│ │ (Laptop / Raspberry Pi / Seed device) │ │
│ │ 1. Receive CSI streams from all nodes │ │
│ │ 2. Timestamp alignment (per-node) │ │
│ │ 3. Feature-level fusion │ │
│ │ 4. Feed into Rust/Python pipeline │ │
│ │ 5. Serve WebSocket to visualization │ │
│ └──────────────────┬──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ WiFi-DensePose Pipeline │ │
│ │ CsiProcessor → FeatureExtractor → │ │
│ │ MotionDetector → PoseEstimator → │ │
│ │ Three.js Visualization │ │
│ └─────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
三条软件路径的当前落地状态
ADR-012 声明状态为"Accepted — Partially Implemented",其后续执行由 ADR-018 细化。对照当前仓库,各层均已落地到具体源码:
- 固件层 firmware/esp32-csi-node/:完整的 ESP-IDF 工程(当前 README 标注 ESP-IDF v5.4、支持 ESP32-S3 生产目标与 ESP32-C6 研究目标),
main/下由main.c(入口、NVS 初始化、WiFi 初始化)、csi_collector.c(采集 + ADR-018 二进制序列化 + 信道跳频)、nvs_config.c(Kconfig 默认值 + NVS 运行时覆盖)、stream_sender.c(UDP 发送)等模块组成,并有edge_processing.c、wasm_runtime.c、mock_csi.c等后续演进模块; - 解析器与汇聚端 wifi-densepose-hardware crate:
src/esp32_parser.rs实现 ADR-018 二进制帧解析(含 magic 校验、n_subcarriers≤512边界检查、parse_stream()的流重同步),src/aggregator/mod.rs实现 UDP 汇聚二进制,src/csi_frame.rs定义CsiFrame/CsiMetadata/SubcarrierData并实现to_amplitude_phase(),src/bridge.rs完成CsiFrame → CsiData到信号管线的桥接; - Python 侧真实数据入口:ADR-018 中规划以 UDP socket 实现替换 Python 硬件层
NotImplementedError桩(原始 v1 归档位于 archive/v1/src/hardware/csi_extractor.py)。
四、节点固件规范:结构与关键设计决策
ADR-012 规划了固件工程的文件结构(当前仓库已按此落地并大幅演进):
firmware/esp32-csi-node/
├── CMakeLists.txt
├── sdkconfig.defaults # Menuconfig 默认值(CSI 使能)
├── main/
│ ├── main.c # 入口、NVS 配置、WiFi 初始化、CSI 回调
│ ├── csi_collector.c # CSI 采集、混杂模式、ADR-018 序列化
│ ├── nvs_config.c # 来自 NVS 的运行时配置(WiFi 凭据、目标 IP)
│ ├── stream_sender.c # 向汇聚端发送 UDP 流
│ └── Kconfig.projbuild # Menuconfig 选项
└── README.md # 烧录说明(已验证可用)
当前固件 README 将处理管线组织为四层可配置分级(tier),这是对 ADR-012 的重要演进:
| Tier | 名称 | 状态 | 内容 |
|---|---|---|---|
| Tier 0 | 原始 CSI 透传 | Stable(默认) | 将驱动回调中的 CSI 帧按 ADR-018 格式 UDP 上报,约 20 pps/信道,20 字节头 + 每子载波每天线 2 字节 I/Q |
| Tier 1 | 基础 DSP | Stable | 相位解缠、Welford 滑动统计、Top-K 高方差子载波选择、XOR+RLE 差分压缩(约省 70% 带宽) |
| Tier 2 | 完整边缘管线 | Stable | 呼吸/心率带通、存在性检测、跌倒检测、多目标槽计数、1 Hz 32 字节 vitals 包 |
| Tier 3 | WASM 可编程感知 | Alpha | 通过 HTTP 上传签名 RVF 容器中的 WASM 模块,热切换算法无需重新烧录 |
关于"机载特征提取"的设计反转
ADR-012 原文规划了一个约 470 字节/帧的机载特征帧结构(csi_feature_frame_t,含 amplitude[56]、phase[56]、doppler_energy、breathing_band 等),并据此估算带宽:100 Hz × 470 B ≈ 47 KB/s/节点,6 节点约 280 KB/s。
但 ADR-018 明确推翻了这一可选路径(文档自注"contradicting ADR-012's optional feature extraction path"):机载 FFT 不做,固件只负责把原始 I/Q 按 ADR-018 二进制格式流式上传,特征提取交给 Rust 汇聚端复用成熟的 wifi-densepose-signal 管线。理由有两个:
- 原始 I/Q 在 ESP32 采样率下足够便宜(约 100 Hz × 56 子载波 ≈ 35 KB/s/节点,ADR-018 进一步指出 3 天线 56 子载波全速下约 210 KB/s,LAN 内完全可行);
- 让固件保持精简(ADR-012 规划把固件 C 代码压在 200 行以内),把质量敏感的特征工程全部集中在宿主侧。
这套"瘦固件 + 胖汇聚端"的分层在源码中得到印证:固件侧的 csi_collector.c 只负责把 wifi_csi_info_t 转成定长二进制帧并交给 stream_sender,而 DSP 全部位于 Rust crate 与 Tier 1/2 的边缘处理代码中。
固件关键设计决策(ADR-012 原述)
- 机载特征提取:原始 CSI I/Q → 幅值 + 相位 + 频谱带,把每帧带宽从原始约 11 KB 压到约 470 字节(该方案当前已被 ADR-018 的原始 I/Q 透传取代,见上);
- 单调时钟时间戳:每节点用自己的单调时钟,不做跨节点 NTP 同步——时钟漂移交由汇聚端以特征融合而非原始相位融合来消化(见下节);
- UDP 流式传输:低延迟、容忍丢包,缺失帧可接受,顺序靠序列号维持;
- 可配置采样率:10–100 Hz(menuconfig),100 Hz 用于运动检测,10 Hz 对占用检测已足够。
五、汇聚端设计:多节点 UDP 聚合与状态跟踪
ADR-012 为汇聚端定义了核心 Rust 类型(规划于 wifi-densepose-hardware/src/esp32/,当前实际实现在 v2/crates/wifi-densepose-hardware/src/aggregator/mod.rs):
pub struct Esp32Aggregator {
/// UDP 套接字,监听节点数据流
socket: UdpSocket,
/// 每节点状态(最后时间戳、特征缓冲、漂移估计)
nodes: HashMap<u8, NodeState>,
/// 融合特征帧的环形缓冲
fused_buffer: VecDeque<FusedFrame>,
/// 到管线的通道
pipeline_tx: mpsc::Sender<CsiData>,
}
/// 一个时间窗内所有节点的融合帧
pub struct FusedFrame {
/// 时间戳(汇聚端本地单调时钟)
timestamp: Instant,
/// 每节点特征(节点掉线时允许空缺)
node_features: Vec<Option<CsiFeatureFrame>>,
/// 跨节点相关(由汇聚端计算)
cross_node_correlation: Array2<f64>,
/// 融合运动能量(各节点取最大)
fused_motion_energy: f64,
/// 融合呼吸带(相位对齐处做相干求和)
fused_breathing_band: f64,
}
在 ADR-018 的实现版本中,汇聚端主循环更加聚焦:recv_from() 收到 UDP 包后交给 Esp32CsiParser::parse_frame() 解析,以帧头 node_id 维护每节点状态,通过序列号间隙累计 drop_count,随后经 tx.try_send(frame) 转发(管线满则丢帧);解析失败只打日志绝不崩溃。关键特性是:汇聚端纯解析字节缓冲区、无任何 C FFI 或硬件依赖,wifi-densepose-hardware crate README 明确"Parsers either parse real bytes or return explicit ParseError values. There are no synthetic fallbacks"——同一帧字节在任何平台解析结果确定,因此在无硬件时也可用回环 UDP + build_test_frame() 合成帧做完整测试。
时钟漂移处理:特征级融合而非信号级融合
这是 ADR-012 全篇最重要的工程判断。ESP32 晶振漂移约 20–50 ppm,一小时后两个节点可能偏差 72–180 ms,跨节点原始相位对齐在物理上不可行:
信号级融合(对 ESP32 是错的):
对齐各节点的原始 I/Q 采样 → 需要 <1µs 级同步 → 不切实际
特征级融合(对 ESP32 是对的):
每节点:原始 CSI → 幅值 + 相位 + 频谱特征(本地完成)
汇聚端:收集特征 → 相关分析 → 融合决策
无需跨节点相位对齐
具体融合策略:
- 运动能量:各节点取最大(任意节点看到运动即算运动);
- 呼吸带:以 SNR 最高节点为主、其余节点佐证;
- 位置估计:用跨节点幅值比估计位置(无需相位)。
六、按部署规模评估的感知能力(诚实清单)
ADR-012 用一张矩阵明确告知读者:能力上限随节点数上升,但 ESP32 有其物理天花板。
| 能力 | 1 节点 | 3 节点 | 6 节点 | 依据 |
|---|---|---|---|---|
| 存在性检测 | 良好 | 极佳 | 极佳 | 单节点 RSSI 方差 |
| 粗略运动 | 良好 | 极佳 | 极佳 | 多普勒能量 |
| 房间级定位 | 无 | 良好 | 极佳 | 幅值比 |
| 呼吸 | 勉强 | 良好 | 良好 | 0.1–0.5 Hz 频带,依赖摆放 |
| 心跳 | 差 | 差–勉强 | 勉强 | 需理想摆放与低噪声 |
| 多人计数 | 无 | 勉强 | 良好 | 空间分集 |
| 姿态估计 | 无 | 差 | 勉强 | 需模型 + 足够分集 |
诚实评估原文:"ESP32 CSI 保真度低于 Intel 5300 或 Atheros。心跳检测依赖摆放且不可靠;呼吸在摆放得当的前提下可用;运动与存在检测是可靠的。"这种能力边界在后续固件文档中被反复强调——例如 v0.8.8 发行说明(docs/releases/v0.8.8-esp32.md)专门列了一节"What this release does not prove",声明该版本不证明医疗级生命体征、准确的多人计数、身份识别、稠密姿态或穿墙视频等能力。
故障模式与缓解措施
| 故障模式 | 严重度 | 缓解 |
|---|---|---|
| 杂乱房间中多径占主导 | 高 | 网格分集:3+ 节点多角度布置 |
| 人体遮挡节点到路由器的路径 | 中 | 网格中其他节点仍有清晰路径 |
| 时钟漂移破坏跨节点融合 | 中 | 仅特征级融合,不做跨节点相位对齐 |
| 高流量下 UDP 丢包 | 低 | 序列号 + 对 <100 ms 缺口插值 |
| ESP32 WiFi 驱动 CSI bug | 中 | 锁定 ESP-IDF 版本,在已知良好板卡上测试 |
| 节点断电 | 低 | 汇聚端优雅处理缺失节点 |
七、硬件清单与克隆-烧录-运行的最小构建
Starter Kit 物料清单(约 54 美元)
| 物品 | 数量 | 单价 | 小计 |
|---|---|---|---|
| ESP32-S3-DevKitC-1 | 3 | 10 美元 | 30 美元 |
| USB-A 转 USB-C 线 | 3 | 3 美元 | 9 美元 |
| 多口 USB 电源适配器 | 1 | 15 美元 | 15 美元 |
| 消费级 WiFi 路由器 | 1 | 0(复用现有) | 0 |
| 汇聚端(笔记本或树莓派 4) | 1 | 0(复用现有) | 0 |
| 合计 | 54 美元 |
硬件选型上当前仓库 README 有补充约束:推荐 ESP32-S3-DevKitC-1 与 XIAO ESP32-S3 这类全尺寸开发板,并明确警告硬币大小的克隆板(ESP32-S3-Zero、SuperMini 等)在固件持续满负荷运行 WiFi 下可能发热到损坏稳压器——部署时需留气流、避免堆叠密闭、首几分钟要手触测温。另注意大多数 DevKitC 开发板只有单板载天线,多天线数据需要带 U.FL 接口与外部天线的板卡。
Option A:使用预编译二进制(无需工具链)
# 从发布页下载对应芯片/Flash 的预编译包后(当前版本含 bootloader/分区表/OTA 元数据/app 与校验和),
# 用 esptool 烧录(pip install esptool)
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
write_flash --flash_mode dio --flash_size 8MB \
0x0 bootloader.bin \
0x8000 partition-table.bin \
0xf000 ota_data_initial.bin \
0x20000 esp32-csi-node.bin
# 配置 WiFi 凭据(无需重编译)
python firmware/esp32-csi-node/provision.py --port COM7 \
--ssid "YourWiFi" --password "secret" --target-ip 192.168.1.20
# 运行汇聚端
cargo run -p wifi-densepose-hardware --bin aggregator -- --bind 0.0.0.0:5005 --verbose
注意:当前发行包的偏移量为四段(bootloader=0x0、partition-table=0x8000、otadata=0xf000、app(ota_0)=0x20000),其中分区表偏移相较 ADR-012 早期版本的 0x8000 方案已经演进,须以所用固件自带的 partitions_display.csv/partitions_4mb.csv 为准。完整安装包不含 NVS 镜像,因此四偏移安装会保留已有 WiFi 与节点配置。
Option B:Docker 从源码构建(免装 ESP-IDF)
# Step 1: 编辑 WiFi 凭据
vim firmware/esp32-csi-node/sdkconfig.defaults
# Step 2: 用 Docker 构建(仓库根目录执行)
MSYS_NO_PATHCONV=1 docker run --rm \
-v "$(pwd)/firmware/esp32-csi-node:/project" -w /project \
espressif/idf:v5.4 bash -c \
"rm -rf build sdkconfig && idf.py set-target esp32s3 && idf.py build"
# Step 3: 烧录(build/ 下产物)
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
write_flash --flash_mode dio --flash_size 8MB \
0x0 firmware/esp32-csi-node/build/bootloader/bootloader.bin \
0x8000 firmware/esp32-csi-node/build/partition_table/partition-table.bin \
0xf000 firmware/esp32-csi-node/build/ota_data_initial.bin \
0x20000 firmware/esp32-csi-node/build/esp32-csi-node.bin
# Step 4: 运行汇聚端
cargo run -p wifi-densepose-hardware --bin aggregator -- --bind 0.0.0.0:5005 --verbose
固件 README 专门解释了"为什么必须用 Docker":ESP-IDF 在 Windows 的 Git Bash/MSYS2 下不可用(idf.py 检测到 MSYSTEM 环境变量会跳过 main(),即便移除该变量,cmd.exe 子进程注入的 doskey 别名也会破坏 ninja 链接器),因此 Docker 是唯一可靠跨平台构建方式。MSYS_NO_PATHCONV=1 前缀用于防止 Git Bash 把 /project 篡改成 C:/Program Files/Git/project。
对于无显示屏的 DevKitC 板卡,构建时应叠加 sdkconfig.defaults.devkitc overlay(默认构建会编入显示支持,运行时的面板探测在无面板板卡上误报从而禁用 CSI 升级并把吞吐拉到 0)。
以 NVS 运行时配置替代重编译
这是"克隆-烧录-运行"体验的关键。通过 provision.py 把配置写进 ESP32 的 NVS 分区,NVS 值覆盖 Kconfig 默认值,无需为每个新 WiFi 环境重新编译。核心网络配置项:
| NVS Key | 类型 | 默认值 | 说明 |
|---|---|---|---|
ssid |
string | wifi-densepose |
WiFi SSID |
password |
string | (空) | WiFi 密码 |
target_ip |
string | 192.168.1.100 |
汇聚端 IP |
target_port |
u16 | 5005 |
汇聚端 UDP 端口 |
node_id |
u8 | 1 |
节点唯一标识(0–255) |
此外固件已演进出信道跳频/TDM(hop_count、chan_list、dwell_ms、tdm_slot、tdm_nodes,见 ADR-029/ADR-073 思路)、边缘智能(edge_tier 0/1/2、pres_thresh、fall_thresh、vital_win、vital_int、subk_count)、信道覆盖与 MAC 过滤(csi_channel、filter_mac,见 ADR-060)等一整组 NVS 键。provision.py 的 CLI 参数与 CSV 生成逻辑(build_nvs_csv)与 CONFIG_VALUE_CHECKS 一一对应,且采用"默认增量合并"策略:脚本把每个串口先前写入的状态保存在本机用户配置目录,每次调用把新 CLI 值叠加到旧状态上,避免整库擦除造成客户流失配置。
八、二进制帧格式(ADR-018):固件与解析器的契约
由于固件选择透传原始 I/Q,帧格式就成了两端必须严格对齐的线协议契约。ADR-018 与固件 README 同时给出如下布局:
偏移 大小 字段
0 4 Magic: 0xC5110001
4 1 节点 ID
5 1 天线数
6 2 子载波数(LE u16)
8 4 频率 MHz(LE u32)
12 4 序列号(LE u32)
16 1 RSSI(i8)
17 1 噪声底(i8)
18 2 保留
20 N*2 I/Q 对(n_antennas * n_subcarriers * 2 字节)
帧总大小 = 20 + 天线数 × 子载波数 × 2 字节。以 3 天线 56 子载波为例,每帧 356 字节。当前固件在此基础上还扩展了按 magic 区分的包族:0xC5110001(CSI 帧,约 20 Hz)、0xC5110002(vitals 包,1 Hz,32 字节,携带 presence/fall/motion 标志、呼吸率 BPM×100 定点数、心率、RSSI、人数与运动能量)、0xC5110004(WASM 事件输出)。
固件侧序列化见 csi_collector.c(小端写 magic、节点 ID、天线数 = rx_ctrl.ant、n_sub = info->len / 2、频率、自增序列号、RSSI/噪声底,再把 info->buf 原样拼到 20 字节头之后);Rust 侧解析见 wifi-densepose-hardware/src/esp32_parser.rs,验证 magic、对 n_subcarriers≤512 做边界检查、并在 parse_stream() 中按 magic 搜索重同步流。
九、无硬件可测:分层可测试性与真机证据
ADR-018 的四层实现全部设计为"买板之前即可测试":
| 层 | 测试方法 |
|---|---|
| 固件二进制格式 | 在 Rust 中实现 build_test_frame() 辅助函数,输出与手工计算的参考帧逐字节比对 |
| 汇聚端 | 回环 UDP:测试向 127.0.0.1:5005 发送合成帧,汇聚端接收并沿通道转发 |
| 桥接层 | assert_eq!(csi_data.amplitude[0], sqrt(i² + q²)) 到 f64 精度 |
| Python UDP 读取器 | pytest 中后台线程起 mock UDP server |
Proof of Reality(ADR-012 记录的真机实测):用 ESP32-S3-DevKitC-1(CP2102,MAC 3C:0F:02:EC:C2:28)实测 18 秒捕获 693 帧(约 21.6 fps),序列号连续、零帧丢失,存在性检测确认(运动评分 10/10,基于每秒幅值方差),实测帧型覆盖 64 子载波(148 B)、128 子载波(276 B)、192 子载波(404 B),伴随 20 个 Rust 测试 + 6 个 Python 测试通过。
后续固件演进(0.8.8,发行说明)还提供了更新的硬件测量矩阵:2026-08-31 实测 ESP32-C6 节点 4(300.64 秒,原始 CSI 均值 34.92 pps,DSP 时钟 8.00 Hz,稳态传输错误 0)、ESP32-C6 节点 7(36.32 pps,覆盖率 97.40%)、ESP32-S3 节点 1(28.03 pps,覆盖率 100.00%)。这些数据证明的是时序与传输稳定性,而非更好的心率、姿态或计数精度——这是理解该网格当前能力边界的重要标尺。
此外,固件工程还提供 QEMU 模拟测试路径(见 firmware/esp32-csi-node 中 sdkconfig.qemu overlay 与 mock_csi.c):编译期用定时器驱动的合成帧注入器替代真实 WiFi CSI 回调,可覆盖双人呼吸、跌倒、信道扫描、MAC 过滤、环形缓冲溢出、边界 RSSI、零长帧等 10 种场景,并在 CI 中以 14 种 NVS 配置矩阵跑完整 UART 校验——意味着即便没有物理硬件,边缘处理全管线依然可回归。
十、后果与后续 ADR 的衔接
正向后果:
- 54 美元入门套件:接触真实 CSI 数据的最低门槛;
- 硬件全球现货:ESP32 板卡全球充足;
- 真实数据通路:以真实硬件输入替换掉每一个
np.random.rand()占位符; - 证据工件:捕获的 CSI + 期望哈希可证明管线处理的是真实数据(延续 ADR-011 的 Proof of Reality 要求);
- 网格可扩展:加节点加覆盖,无需改软件;
- 特征级融合:绕开了跨节点相位同步这一不可能问题。
负向后果:
- 保真度低于科研级网卡(比 Intel 5300 噪声更大);
- 心跳检测不可靠(微多普勒分辨率不足以稳定测心跳);
- ESP-IDF 有学习曲线(固件开发需要嵌入式 C 知识);
- WiFi 干扰:节点与数据流量共享信道会引入噪声;
- 摆放敏感:呼吸检测需要细心规划节点位置。
与其他 ADR 的关系:ADR-011 由 ESP32 提供真实 CSI 证据包;ADR-008 的网格节点可用简化版 Raft 做配置分发;ADR-003 思路下汇聚端以 RVF 格式存 CSI 特征;ADR-004 思路下 ESP32 网格产生的环境指纹喂给 HNSW 索引。后续实现细节承接自 ADR-018,而固件侧的能力扩展(C6 研究目标、边缘智能分级、WASM 可编程感知、QEMU 测试、信道跳频 TDM、MAC 过滤)则进一步体现在 firmware/esp32-csi-node/README.md 及 ADR-029/039/040/057/060/061 等一系列后续决策中。
结语
ADR-012 的核心价值在于它给出了一条从"想用 WiFi 感知"到"真实节点在跑"的完整决策链:对比平台选型 → 用官方三函数建立采集 → 用瘦固件 + 二进制线协议喂给汇聚端 → 用特征级融合绕开时钟漂移 → 用分层测试做到无硬件可开发。当你需要为 RuView 部署一房间的分布式无摄像感知时,这套"3–6 个 S3 开发板 + 一个汇聚端"的组合,既是仓库中证据最充分的硬件路径,也是验证信号处理算法真实性的最小闭环起点。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00