RuView ADR-018 实践指南:ESP32 CSI 传感节点的二进制帧协议与四层开发落地路径
【免费下载链接】RuViewπ RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.项目地址: https://gitcode.com/GitHub_Trending/wi/RuView
本文以 RuView 仓库中 ADR-018(ESP32 开发实现路径) 为骨架,讲解如何把"ESP32 CSI 传感器网格"从架构设想推进为可运行的固件 + 聚合器 + 信号桥接 + Python 接入链路的完整实现方法。读者读完可掌握 ADR-018 二进制 CSI 帧格式的逐字节布局、50 Hz 限速与 ENOMEM 退避两大稳定化手段,以及"无需硬件即可逐层测试"的 Rust/Python 实现策略与源码证据。
ADR-018 本身定位明确:ADR-012 回答了"构建什么(what)与为什么(why)"(ADR-012-esp32-csi-sensor-mesh.md),而本 ADR 回答"如何构建(how)"——给出具体的开发顺序、现有代码中的集成点,以及在硬件到位之前每一层如何先行验证。仓库中的 v2/crates/wifi-densepose-hardware 与 firmware/esp32-csi-node 已将本 ADR 的大部分设想实现并进一步演进,下文会在每层对照真实源码给出印证。
二进制帧格式:固件与解析器之间唯一契约
ADR-018 的核心是一份固件侧 C 代码写、主机侧 Rust 解析器读的固定二进制帧格式。整个开发栈的第一件事就是"锁定字节"——固件必须逐字节按此布局序列化,解析器才能在任何硬件到手之前被充分测试。
| 偏移 | 大小(字节) | 字段 | 说明 |
|---|---|---|---|
| 0 | 4 | Magic0xC5110001 | 小端(LE)魔数,用于流同步与误码识别 |
| 4 | 1 | Node ID | 0–255,单节点唯一标识 |
| 5 | 1 | 天线数量 | 单天线 ESP32 为 1 |
| 6 | 2 | 子载波数量 | LE u16 |
| 8 | 4 | 频率 Hz | LE u32,如 2.4 GHz ch1 = 2412(MHz 值直接填入) |
| 12 | 4 | 序列号 | LE u32,聚合器据此计算丢包 |
| 16 | 1 | RSSI | i8,单位 dBm |
| 17 | 1 | 噪声底 | i8,单位 dBm |
| 18 | 2 | 保留(零) | 见下方关于 ADR-110 演进的说明 |
| 20 | N×2 | I/Q 数据对 | 每子载波 (i8, i8),按天线重复 |
帧总长 = 20 + n_antennas × n_subcarriers × 2 字节。以 3 天线 × 56 子载波计算:20 + 3 × 56 × 2 = 356 字节/帧。
实际解析器实现位于 v2/crates/wifi-densepose-hardware/src/esp32_parser.rs,其模块文档与 ADR-018 二进制格式一一对应,并声明了无模拟保证(No-Mock Guarantee):该解析器要么成功解析真实字节,要么返回具体的ParseError,绝不生成合成数据。解析逻辑的关键校验点:
- 魔数校验:
magic != ESP32_CSI_MAGIC时返回ParseError::InvalidMagic; - 天线数校验:
n_antennas == 0 || n_antennas > MAX_ANTENNAS(4)拒绝非法帧; - 子载波边界检查:超过
MAX_SUBCARRIERS即拒绝(当前实现上限为 256,对应 80 MHz 带宽的解析上限);ADR-018 原文描述解析器会边界检查n_subcarriers(≤512),实际代码按 PPDU 类型与子载波数联合推导带宽,阈值随演进收紧,属于后文"演进"部分要交代的差异; - 完整帧长度预检:
data.len() < HEADER_SIZE + iq_byte_count时返回InsufficientData; - 流式重同步:
parse_stream()(esp32_parser.rs)可在连续字节流中以魔数搜索方式重同步,容忍 UDP 乱序与粘包。
I/Q 载荷以"天线优先"存储:先是 ant0 的全部子载波 (I, Q) 对,再是 ant1……。解析时每对 (i, q) 被转换为带符号 i16 子载波数据,并附带一个派生子载波索引index(基于半幅换算,负数表示负频段),最终构成SubcarrierData。build_test_frame()/build_test_frame_with_he()(esp32_parser.rs)测试辅助函数从 56 子载波 HT 帧、HE-SU/HE-MU/HE-TB PPDU、多天线等维度构造样本帧,为聚合器与桥接层测试复用同一套"造帧"模式提供了基础。
演进:字节 18–19 从保留位变为 PPDU 类型与标志
需要特别指出:ADR-018 定稿时字节 18–19 为"保留(零)",但仓库当前实现显示该区域已被 ADR-110 扩展为PPDU 类型(0=HT/legacy,1=HE-SU,2=HE-MU,3=HE-TB)与标志字节(bit0 bw40、bit2 STBC、bit3 LDPC、bit4 15.4-sync),见 esp32_parser.rs。关键兼容设计是:旧固件这两字节发零,解析后等同PpduType::HtLegacy+ 空标志,完全向后兼容。这意味着如果你的固件实现了本 ADR 的原始布局,解析器仍可正常处理;而新固件打开CONFIG_CSI_FRAME_HE_TAGGING后可携带 HE 帧类型信息,使带宽推导从"子载波数启发式"升级为"HE-LTF 4 倍加密音调网格 + 显式 bw40 标志"。
另一个同源演进是同端口多包类型复用:ESP32 固件把 ADR-039 边缘生命体征(magic0xC5110002)、ADR-069 特征向量、ADR-081 紧凑特征状态等多种包混排在同一个 UDP 端口上。esp32_parser.rs 定义了从0xC5110002到0xC5110007的兄弟 magic 常量与ruview_sibling_packet_name()分类函数;解析器遇到这些"同端口但非 CSI"的包会返回ParseError::NonCsiPacket,让上层能路由或跳过,而不是误报"数据损坏"。
现状盘点:哪些已实现,哪些待开发
ADR-018 用一张表清晰划分了已实现与未实现边界。截至本文写作时,对照仓库实际状态:
| 组件 | 位置 | 状态(ADR 撰写时 / 当前仓库) |
|---|---|---|
| 二进制帧解析器 | wifi-densepose-hardware/src/esp32_parser.rs(现位于 v2/crates/wifi-densepose-hardware/src/esp32_parser.rs) | 已完成,parse_frame()/parse_stream(),测试 7+ 条且持续扩充 |
| 帧类型 | csi_frame.rs→ v2/crates/wifi-densepose-hardware/src/csi_frame.rs | 已完成,含CsiFrame、CsiMetadata、SubcarrierData、to_amplitude_phase() |
| 解析错误类型 | error.rs→ v2/crates/wifi-densepose-hardware/src/error.rs | 已完成,ParseError枚举 6+ 变体(InvalidMagic、InvalidAntennaCount、InvalidSubcarrierCount、InsufficientData、ByteError、NonCsiPacket 等) |
| 信号处理流水线 | wifi-densepose-signalcrate | 已完成(Hampel、Fresnel、BVP、Doppler、频谱图) |
| Python CSI 提取器 | archive/v1/src/hardware/csi_extractor.py | ADR 撰写时为 stub,当前已演进为异步实现,见 Layer 4 说明 |
| ESP-IDF C 固件 | firmware/esp32-csi-node | ADR 撰写时未实现,当前已完整落地并远超最初范围 |
| UDP 聚合器 | v2/crates/wifi-densepose-hardware/src/aggregator/mod.rs | 已实现并附带 5+ 单元测试 |
| CsiFrame → CsiData 桥接 | v2/crates/wifi-densepose-hardware/src/bridge.rs | 已实现并附带 4 条单元测试 |
Layer 1:ESP-IDF 固件,回调里的逐字节序列化
固件工程位于 firmware/esp32-csi-node,主目录main/下有 csi_collector.c(CSI 采集与序列化)、stream_sender.c(UDP 发送与退避)等模块。ADR-018 给出了核心思路:ESP-IDF 的 CSI 回调里不做事后加工,直接把wifi_csi_info_t序列化为 20 字节头部 + 原始 I/Q 载荷,再经 UDP 交给主机。
CSI 回调 → 帧序列化的关键代码
ADR-018 给出的csi_collector.c回调模板(当前仓库实现进一步扩展了通道跳变、NDP 注入等能力,核心序列化逻辑一致):
static void csi_data_callback(void *ctx, wifi_csi_info_t *info) { if (!info || !info->buf) return; uint8_t frame[FRAME_MAX_BYTES]; uint32_t magic = 0xC5110001; memcpy(frame + 0, &magic, 4); frame[4] = g_node_id; frame[5] = info->rx_ctrl.ant; // 天线索引 uint16_t n_sub = info->len / 2; memcpy(frame + 6, &n_sub, 2); uint32_t freq_mhz = g_channel_freq_mhz; memcpy(frame + 8, &freq_mhz, 4); memcpy(frame + 12, &g_seq_num, 4); frame[16] = (int8_t)info->rx_ctrl.rssi; frame[17] = (int8_t)info->rx_ctrl.noise_floor; frame[18] = 0; frame[19] = 0; memcpy(frame + 20, info->buf, info->len); // I/Q 原样拷贝 stream_sender_write(frame, 20 + info->len); g_seq_num++; }真实源码中 csi_collector.c 的注释确认了字节布局,并且比 ADR 设想走得更远:
- 序列化前有
#ifndef CONFIG_ESP_WIFI_CSI_ENABLED的编译期守卫——若 sdkconfig 未开启 CSI 则直接编译报错,避免"固件能编译但运行时报wifi:CSI not enabled in menuconfig!"的困惑(ADR-057 的教训); - 本地 NVS 配置的防御性拷贝(
s_node_id、MAC 过滤配置),因为 CSI 回调以 100–500 Hz 频率触发,若在回调里读取可能被 WiFi 驱动初始化破坏的全局配置,会引发 Core 0 LoadProhibited panic——这正是仓库注释记录的真实事故(约 2400 次回调后 panic)。
两个稳定化机制:50 Hz 限速与 ENOMEM 退避
ADR-018 明确"不做片上 FFT",理由有两个:原始 I/Q 在 ESP32 采样率(56 子载波约 100 Hz ≈ 35 KB/s/节点)下比特征更便宜;SOTA 信号处理流水线(Hampel/Fresnel/BVP/Doppler,源自 ADR-014)应统一跑在 Rust 聚合器侧。因此固件侧真实工程最要紧的是两个保护机制:
50 Hz 发送限速:promiscuous 模式下 CSI 回调可达 100–500 次/秒。若全部
sendto(),lwIP pbuf 池会被快速耗尽。csi_collector.c 用CSI_MIN_SEND_INTERVAL_US (20*1000)控制发送节拍,并加了一道更早的处理门限CSI_MIN_PROCESS_INTERVAL_US——在序列化/UDP 之前就把过量回调丢弃,把有效回调率压到约 50 Hz,为 WiFi FIQ 留出余量(这是对 Core 0cache_ll_l1_resume_icachepanic 的实测修复)。ENOMEM 指数退避:
sendto()返回ENOMEM(errno 12)时,所有发送抑制一段时间让 lwIP 回收缓冲。stream_sender.c 当前实现比 ADR 原文的固定 100 ms 更健壮:退避从 100 ms 起按连续失败次数指数翻倍(100→200→400→…,上限 2000 ms),首次成功发送后重置——仓库注释明确记录,固定 100 ms 退避在持续缓冲压力下会让节点反复失败,指数退避才能真正排干缓冲。
sdkconfig 与 Kconfig 配置项
ADR-018 规定固件必须开启的配置:
CONFIG_ESP_WIFI_CSI_ENABLED=y CONFIG_LWIP_SO_RCVBUF=y CONFIG_FREERTOS_HZ=1000仓库实测中,仅这三项不够。当前 sdkconfig.defaults 还加入了(注释记载了 2026-06-08 在 S3/C6 上的实测依据):
CONFIG_IDF_TARGET="esp32s3" CONFIG_LWIP_UDP_RECVMBOX_SIZE=32 CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=64 CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=64 CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 CONFIG_ESP_WIFI_EXTRA_IRAM_OPT=y CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=8192stock IDF v5.4 默认值(UDP recv mbox=6、TCPIP mbox=32、WiFi 动态 TX 缓冲=32)在 CSI promiscuous 模式下太小,会导致约 90% 帧被丢弃;上述调大约以 3 KB 额外堆为代价把相关缓冲池提高约四倍。注意注释同时诚实记录:缓冲调大消除不了feature_state emit的残留 ENOMEM,那是 WiFi 无线电 TX 空口时间在 CSI promiscuous RX 下的饱和,最终要靠降低特征上报节奏解决。
节点级参数由 Kconfig.projbuild 定义,idf.py menuconfig下位于 "CSI Node Configuration":
| 配置项 | 默认值 | 范围 | 含义 |
|---|---|---|---|
CSI_NODE_ID | 1 | 0–255 | 节点 ID,写入帧字节 4 |
CSI_TARGET_IP | 192.168.1.100 | — | 聚合器主机 IP |
CSI_TARGET_PORT | 5005 | 1024–65535 | 聚合器 UDP 端口 |
CSI_WIFI_SSID | wifi-densepose | — | 连接/嗅探的 SSID |
CSI_WIFI_CHANNEL | 6 | 1–13 | 侦听信道 |
CSI_SELF_PING_HZ | 50 | 10–50 | 主动 ICMP 探测频率,用于在安静网络上产生稳定 OFDM CSI 源 |
其中CSI_SELF_PING_HZ上限被刻意限定在 50 Hz:50 Hz 是实测的 WiFi 回调安全天花板,更高频率会触发 WiFi ISR 与包缓冲故障。
构建工具链
ADR-018 建议 ESP-IDF v5.2+(锁定版本)并用espressif/idf:v5.2Docker 镜像做可复现 CI。当前固件 CMakeLists 顶部标注"Requires ESP-IDF v5.4+"(CMakeLists.txt),README 徽章亦显示 ESP-IDF v5.4,说明固件需求随 ESP32-C6/Wi-Fi 6 等新特性的加入而上调——按当前仓库实际为准。
Layer 2:UDP 聚合器——多节点收帧、丢包计数与容忍解析
聚合器是wifi-densepose-hardwarecrate 内的新模块,入口为可独立运行的Esp32Aggregator。ADR-018 的原型代码与真实实现 aggregator/mod.rs 几乎一致:
pub struct Esp32Aggregator { socket: UdpSocket, nodes: HashMap<u8, NodeState>, // 以帧头 node_id 为键 tx: SyncSender<CsiFrame>, // 出站到桥接层 } struct NodeState { last_seq: u32, drop_count: u64, last_recv: Instant, }真实实现把last_recv: Instant拆成了frames_received/frames_dropped计数器,并用wrapping_add+saturating_sub处理序列号回绕;run()是一个阻塞接收循环,逐包调用handle_packet()(该方法公开以便单测直接喂包)。逐包处理逻辑完全贯彻 ADR-018 的两条铁律:
- 坏包绝不崩溃:
Esp32CsiParser::parse_frame()返回Err时静默丢弃并继续; - 管道满时丢帧不阻塞:
tx.try_send(frame)失败即放弃该帧(聚合器不是瓶颈制造者)。
丢包统计直接利用帧头序列号:update()中期望值 =last_sequence.wrapping_add(1),实际值与期望值的差累加进frames_dropped,对外通过drops_for_node(node_id)暴露。配置由AggregatorConfig承载(默认0.0.0.0:5005、通道容量 1024)。
聚合器在真实代码中同样无需硬件即可测试。mod tests中的build_test_packet()用与解析器测试相同的"造帧"模式逐字段构造 ADR-018 帧,覆盖了:
- 有效帧接收与元数据保真(node_id、sequence、子载波数);
- 序列号间隙统计(seq 0 后接 seq 5,
drops_for_node(1) == 4); - 垃圾包容错(不 panic、不产帧、节点数为 0);
- 多节点共存(node 1 与 node 2 同时追踪);
- 回环 UDP 全链路:真实
sendto→ 本机 socket →recv_from→handle_packet→ 通道出帧。
Layer 3:CsiFrame → CsiData 桥接层
桥接层把硬件级CsiFrame(I/Q 对)转换为流水线就绪的CsiData(幅度/相位向量),衔接 SOTA 信号处理流水线。ADR-018 给出的中间结构体(CsiData:timestamp_unix_ms、node_id、n_antennas、n_subcarriers、amplitude、phase、rssi_dbm、noise_floor_dbm、channel_freq_mhz)在真实实现 bridge.rs 中原样落地,并补充了sequence字段和便捷方法snr_db()(RSSI − 噪声底,以 dB 为单位)。
impl From<CsiFrame> for CsiData的核心换算:
amplitude[i] = √(Iᵢ² + Qᵢ²),长度 = n_antennas × n_subcarriers,天线优先排布;phase[i] = atan2(Qᵢ, Iᵢ);- 使用
Vec<f64>而非 ndarray,保持零额外依赖。
桥接测试与 ADR-018 的描述一一对应:bridge.rs 中test_bridge_from_known_iq用 (I=3, Q=4)→幅度 5.0、(I=0, Q=10)→幅度 10.0 的已知帧验证到 f64 精度;test_bridge_multi_antenna验证 2 天线 × 3 子载波的 6 长度幅度/相位;test_bridge_snr_computation验证 RSSI=−45、噪声底=−90 时 SNR=45;test_bridge_preserves_metadata验证元数据(node_id、频率、序列号、RSSI)在转换中完整保留。
Layer 4:Python_read_raw_data()的真实 UDP 实现
在 Rust 流水线完全接管前,Python 侧需要一个真实的 CSI 来源。ADR-018 给出的方案是替换 archive/v1/src/hardware/csi_extractor.py 中抛NotImplementedError的_read_raw_data()桩,改为 UDP socket 读取器——它直接从聚合器接收"ESP32 格式(magic0xC5110001)"的二进制帧:
import socket as _socket def _read_raw_data(self) -> bytes: """Read one raw CSI frame from the UDP aggregator.""" if not hasattr(self, '_udp_socket'): host = self.config.get('aggregator_host', '127.0.0.1') port = int(self.config.get('aggregator_port', 5005)) sock = _socket.socket(_socket.AF_INET, _socket.SOCK_DGRAM) sock.bind((host, port)) sock.settimeout(1.0) self._udp_socket = sock try: data, _ = self._udp_socket.recvfrom(4096) return data except _socket.timeout: raise CSIExtractionError( "No CSI data received within timeout — " "is the ESP32 aggregator running?" )几点值得展开:
- 连接参数(聚合器地址/端口)用环境变量或配置项驱动,默认
127.0.0.1:5005,与 Rust 聚合器默认端口一致; - 超时 1 秒后抛出
CSIExtractionError而非静默返回占位数据——这与仓库既有测试纪律一致:宁可失败也不要假数据(csi_extractor.py 中CSIExtractionError的 docstring 明确写着 "raised instead of silently returning random/placeholder data"); - 测试方式:单测中用后台线程起 mock UDP server(pytest +
socket.socket),集成阶段连真实聚合器。
注意演进:当前 archive/v1/src/hardware/csi_extractor.py 已是一份约 660 行的异步重构版本,_read_raw_data变为async def,并配套ESP32CSIParser、CSIData数据类与CSIParser协议。若你按 ADR-018 阅读,这是该层"已从 ADR 方案继续前进"的体现;同时由于 v1 目录已进入归档状态(见 ADR-187),新读者应关注 v2 crate 的 Rust 流水线,Python 层更多承担过渡与交叉验证角色。
开发序列:无硬件先行,三阶段验收
ADR-018 把落地节奏切成三个相互独立、逐级收敛的阶段:
Phase 1(固件 + 聚合器,无需流水线集成): 1. 编写 firmware/esp32-csi-node/ C 工程(ESP-IDF v5.x) 2. 烧录到一块 ESP32-S3-DevKitC 3. 用 Wireshark 在笔记本 UDP 端口确认二进制帧到达 4. 编写聚合器 crate + 回环测试 Phase 2(桥接 + Python 桩替换): 5. 实现 CsiFrame → CsiData 桥接 6. 用 UDP socket 替换 Python _read_raw_data() 7. 用合成帧跑通 Python 流水线对回环聚合器的端到端联调 Phase 3(真实硬件集成): 8. Python 流水线对接实时 ESP32 帧 9. 采集 10 秒真实 CSI 数据包(firmware/esp32-csi-node/proof/) 10. 验证数据包哈希(ADR-011 模式) 11. 将 ADR-012 与本 ADR 标记为 Accepted每阶段都有明确的"完成判据":Phase 1 的可观测信号是 Wireshark 里按 ADR-018 布局解码的帧;Phase 2 的判据是合成帧端到端走通;Phase 3 的判据是真实帧 + 哈希可验证的证据包,从而满足 ADR-011 的"去模拟/真实性证明"要求。
四层"无硬件测试"矩阵
ADR-018 声称全部四层在购买任何 ESP32 之前都可测试,仓库测试也证实了这一点:
| 层 | 测试方法 | 仓库证据 |
|---|---|---|
| 固件二进制格式 | Rustbuild_test_frame()造帧,与手工计算的参考帧逐字节比对 | esp32_parser.rs 的测试模块 |
| 聚合器 | 回环 UDP:向 127.0.0.1 发合成帧,聚合器接收并转发到通道 | aggregator/mod.rs 的test_aggregator_loopback_udp等 |
| 桥接 | assert!(amplitude[0] ≈ √(I₀²+Q₀²))到 f64 精度 | bridge.rs 的test_bridge_from_known_iq |
| Python UDP 读取器 | pytest 中后台线程起 mock UDP server | archive/v1/src/hardware/csi_extractor.py |
此外,真实的回环 UDP 测试揭示了 UDP 语义的一个细节:聚合器端必须通过send_socket.send_to()+recv_socket.recv_from()完成真实 socket 往返,并等待约 50 ms 让包到达——这与直接调用handle_packet()的单元测试互补,前者验证协议栈行为,后者验证纯解析逻辑。
后果评估与后续演进
正向收益
- 分层可测性:每层都能在硬件到位前独立验收,风险被隔离在单层内;
- 零新增外部依赖:Rust 与 Python 都用标准库 UDP socket,固件只用 ESP-IDF 与组件生态;
- 桩代码消除:替换掉 Python 硬件层最后两个
NotImplementedError桩(csi_extractor.py与router_interface.py),以真实数据驱动的代码替代; - 真实性证明:Phase 3 产生哈希可验证的 CSI 证据包,满足 ADR-011 对硬件来源数据的要求;
- 信号 crate 复用:ADR-014 的 SOTA 信号处理(Hampel/Fresnel/BVP/Doppler/频谱图)经桥接层转换后无需改动即可处理真实 ESP32 帧。
代价与限制
- 固件依赖 ESP-IDF 工具链:2+ GB 安装体积,CI 必须用官方 Docker 镜像或跳过固件编译(真实仓库的 CI 已采用 Docker Build 方式,见 firmware/esp32-csi-node/README.md 的 CI 徽章);
- 原始 I/Q 带宽成本:100 Hz × 3 天线 × 56 子载波 ≈ 35 KB/s/节点;6 节点 ≈ 210 KB/s,只适合 LAN 不适合 WAN——这也是 ADR-018 主张"特征提取放主机"反而把原始流出的原因之一;
- 单天线现实:多数 ESP32-S3-DevKitC 仅一枚板载天线,多天线数据需要 U.FL 外接天线或专门的多射频板。
暂缓事项
- 多节点时钟漂移补偿:ADR-012 规划的是特征级融合,本 ADR 的聚合器只按节点转发原始
CsiFrame,漂移补偿留给未来的FeatureFuser层; - 固件 CI:GitHub Actions 中编译固件需要 ESP-IDF Docker 镜像,CI 集成推迟到 Phase 3 硬件验证之后。
与仓库现状的对照:一份"Proposed" ADR 如何长成了完整的固件
ADR-018 状态为 Proposed(2026-02-28),但从仓库看,它的四层设想已被全部落地并超越:csi_collector.c增加了通道跳变(ADR-029)、主动 ping 造流(CSI_SELF_PING_HZ)、边缘处理与 WASM 运行时;esp32_parser.rs增加了 HE PPDU 标记、兄弟包识别与带宽推导;固件 README 显示 CSI 流式上传"以 20 packets/s 硬件接收底线承载 ADR-018 二进制格式"。可以从源码结构推断:ADR-018 与其姐妹 ADR(ADR-110 的字节 18–19 复用、ADR-081 的紧凑特征状态同端口复用)属于同一连续演进线上的协议层决策,而 ADR-018 帧格式本身(20 字节头部 + I/Q)至今仍是这条 UDP 链路的基石。
与其他 ADR 的交互矩阵
| ADR | 交互点 |
|---|---|
| ADR-011 | Phase 3 产出真实 CSI 证据包,满足去模拟要求 |
| ADR-012 | 本 ADR 实现其架构的开发路径(帧格式、固件目录、聚合器设计) |
| ADR-014 | 桥接层之后 SOTA 信号处理可直接复用 |
| ADR-008 | 聚合器处理多节点;分布式共识属于后续关注点 |
结语:把"字节契约"当作开发的锚点
RuView 的 ADR-018 之所以值得作为落地范本研读,在于它把一个易踩坑的异构系统问题(C 固件 ↔ Rust 解析器 ↔ 信号流水线 ↔ Python 接入)收敛为一个20 字节固定头部 + I/Q 载荷的二进制契约,再用四层独立可测的结构让整条链路在硬件缺位时依然能被完整验证。无论你后续把固件演进到 HE 标记(ADR-110)还是把多包类型复用到同一端口,ADR-018 确立的"解析器只认字节、造帧辅助函数贯穿各层测试、坏包静默容忍"三条纪律都仍然适用——这也是从本仓库源码(esp32_parser.rs、aggregator/mod.rs、bridge.rs 与 firmware/esp32-csi-node)中可逐条追溯、可引用的最稳定资产。
【免费下载链接】RuViewπ RuView turns commodity WiFi signals into real-time spatial intelligence, vital sign monitoring, and presence detection — all without a single pixel of video.项目地址: https://gitcode.com/GitHub_Trending/wi/RuView
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考