heyfinal/wifi-ghost

GitHub: heyfinal/wifi-ghost

基于 ESP32 采集 WiFi CSI 信号实现隔墙人体运动与呼吸检测的开源无线感知系统。

Stars: 0 | Forks: 0

wifi-ghost banner

使用普通 WiFi 隔墙探测人体。
ESP32-WROOM 节点采集信道状态信息 · Python 聚合器实时运行运动 + 呼吸检测。

功能特性 · 工作原理 · 快速开始 · 部署 · 算法 · 鸣谢

## 功能特性 - **隔墙人体探测** — 在没有视线、没有摄像头、没有 PIR 的情况下,可以看到相邻房间的运动。 - **呼吸感知** — 能够在 CSI 振幅中捕捉到 0.2–0.5 Hz 的周期性胸腔起伏调制。*在一次真实的彻夜测试中,隔着一堵石膏板墙成功捕捉到了一名睡眠中成年人的 14–17 BPM 呼吸频率。* - **隐蔽性** — 墙上唯一的东西看起来只是一块普通的 ESP32 开发板(或者最终可以是一个隐藏在接线盒里的 Shelly Plus 1PM)。从外面什么都看不见。 - **无法被视线伪装干扰** — 遮挡、致盲、破坏运动传感器都无济于事,因为它根本没有可以被覆盖的传感器表面。 - **纯本地运行** — 无需云服务、无需蓝牙配对、无需第三方 API。仅通过 UDP 在 ESP32 和 Mac/Pi 之间进行通信。 - **隐蔽告警** — 输出 `csi 03:42:18 master bedroom` 而不是 `MOTION DETECTED ZONE 3`,因此在手机上扫一眼不会暴露任何信息。 ## 工作原理 ``` ┌─────────────────────┐ │ ESP32-TX (beacon) │ ─── broadcasts 20 Hz UDP │ 1× per house │ so RX nodes always have │ Living room │ CSI signal └──────────┬──────────┘ │ 802.11 frames │ (also catches existing │ AP & client traffic) ▼ ┌───────────┐ ┌─────────────────┐ ┌───────────┐ │ ESP32-RX │ │ ESP32-RX │ │ ESP32-RX │ │ NW corner │ ◄────── │ N corner │ ──────► │ E corner │ └─────┬─────┘ └────────┬────────┘ └─────┬─────┘ │ promisc CSI │ promisc CSI │ promisc CSI │ 30–50 Hz/MAC │ 30–50 Hz/MAC │ 30–50 Hz/MAC └──────────┬──────┴────────────┬──────────────┘ │ UDP :4445 binary │ UDP :4446 text/log ▼ ▼ ┌──────────────────────────────────────┐ │ Python aggregator (Mac/Pi) │ │ ┌──────────────┐ ┌──────────────┐ │ │ │ MOTION layer │ │ BREATH layer │ │ │ │ ESPectre- │ │ FFT 0.15– │ │ │ │ style σ_subc │ │ 0.5 Hz peak │ │ │ │ + Hampel + │ │ + SNR > 3 │ │ │ │ adaptive P95 │ │ │ │ │ └──────┬───────┘ └──────┬───────┘ │ │ └──────────┬──────┘ │ │ ▼ │ │ CSV + JSONL + alert │ └──────────────────────────────────────┘ │ ▼ iMessage / webhook / log ``` 每个 RX 节点在混杂模式下启用 `esp_wifi_set_csi_rx_cb`,并通过 UDP 将 64 个 OFDM 子载波的**每包 I/Q 采样**流式传输到聚合器。聚合器在每个 `(rx_node, src_mac)` 流上并行运行两个检测层: | 层级 | 特征 | 窗口 | 阈值 | 捕获内容 | |---|---|---|---|---| | **运动** | 每个数据包的 `std(amp[selected_subcarriers])` → 100 包滑动窗口 → `std(window)` | 100 个数据包(@ 30 Hz 约 2–3 秒) | 自适应:在 10 秒静默校准后设为 `P95(baseline) × 1.10`;3 次命中开启 / 5 次未命中关闭的迟滞机制 | 走动、坐下、手势 —— 任何会干扰双基地路径多径效应的行为 | | **呼吸** | `amp.mean()` 时间序列,重采样至 10 Hz,去趋势,Hann 加窗 FFT | 60 秒 | 0.15–0.5 Hz 频段内出现信噪比 (SNR) > 3 的峰值 | 静止或睡眠中的人 —— 即使没有其他任何物体移动,胸部起伏也会调制 CSI | 一个人可以在短时间内欺骗其中一层(屏住呼吸,完全静止站立),但不可能同时欺骗两层,而且他们尝试的时间越长,就越容易被发现。 ## 快速开始 您至少需要 **2 块 ESP32-WROOM 开发板**(一块作为 TX,一块作为 RX),以及一台连接到同一 WiFi 的 Mac 或 Linux 设备。 ``` # 1. clone git clone https://github.com/heyfinal/wifi-ghost cd wifi-ghost # 2. 配置 WiFi 凭据(所有节点相同) cp firmware/csi_rx/secrets.h.example firmware/csi_rx/secrets.h cp firmware/csi_tx/secrets.h.example firmware/csi_tx/secrets.h # 编辑两者 — 相同的 SSID/PSK # 3. 烧录 TX 节点(即 beacon) ./scripts/flash_wroom.sh /dev/cu.usbserial-XXX tx ESP_TX_LIVING # 4. 烧录一个或多个 RX 节点(即接收器) ./scripts/flash_wroom.sh /dev/cu.usbserial-YYY rx ESP_RX_MASTER # 5. 将每个节点放置在离地约 3-7 英尺处,使用 USB 墙壁充电器供电 # 放置 RX 节点时,使 TX→RX 连线穿过你想要覆盖的房间 # 6. 启动 aggregator(默认 dry-run = 打印警报而不是发送) ./scripts/start_aggregator.sh --debug # 7. 准备就绪后,启用真实警报(如果使用 iMessage,请先设置 CSI_NOTIFY_TARGET) export CSI_NOTIFY_TARGET="+1XXXYYYZZZZ" ./scripts/start_aggregator.sh --live ``` 就是这样。四处走动吧。观察运动 + 呼吸事件的触发。 关于多房间“每个角落都有节点”的设置,请参阅 [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)。 ## 在您家中部署 最重要的一点是:**TX 和每个 RX 之间的双基地连线必须穿过您关注的房间。** 在 TX 和 RX *之间* 走动的人会以一种算法能够以 3-5σ 捕捉到的方式干扰多径效应。在 RX *背后*(远离 TX)走动的人几乎是不可见的。 一个有 6–8 个房间的住宅通常需要: - 位于中心位置(客厅、走廊)的 **1 个 TX 节点** - 位于角落的 **3–4 个 RX 节点**,这样每个房间都位于 TX 和至少一个 RX 之间 利用重叠的双基地基线实现网格覆盖: ``` [RX-NW] [RX-NE] \ / \ [TX-CENTER] / \ / \ / \ / \ / \ / \ / \/ \ [RX-SW] [RX-SE] ``` 如果一个房间位于 **两个或更多** TX↔RX 路径上,它就被冗余覆盖了 —— 即使攻击者以某种方式致盲了一条路径,他们仍然会触发其他路径。 按隐蔽性排列的硬件推荐(最便宜 → 最永久): 1. **ESP32-WROOM 开发板 + USB 电源适配器** — 即您在快速开始中所做的。肉眼可见。 2. **ESP32-WROOM + HLK-PM01 5V AC-DC 模块 + 项目盒,隐藏在墙筋隔层中的空白面板后** — DIY 方案,在 AliExpress 上约 $8/节点,不可见。符合建筑规范/UL 认证方面处于灰色地带;风险自负。 3. **Shelly Plus 1PM UL** — Amazon 上约 $23/个,采用 ESP32-U4WDH,通过 UL 认证,可直接装入您现有插座的接线盒中,支持 OTA 刷入自定义固件。这是最正规的方式。 包含信道选择、典型美式独栋住宅平面图的采样几何结构,以及呼吸与运动阈值调整等详细的部署说明,请参阅 [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md)。 ## 算法细节 本项目最有趣的部分是**检测算法**,而不是固件(固件本质上是带有 UDP 管道的 Espressif esp-csi 示例)。 ### 为什么我们丢弃振幅 **均值** 最直观的方法 —— 对各子载波上的每包振幅求平均值,并根据偏差设置阈值 —— 在廉价的 ESP32 采集数据上*根本行不通*。当人体移动时,各子载波的振幅会独立变化;求平均会将信号淹没在噪声中。我们对此进行了直接测量:在无人的情况下,基线 σ 约为均值的 25%,而当有人*主动穿过双基地路径*时,变化量 δ 仅约为 13%。这完全低于底噪。 ### 为什么我们使用跨子载波的 **std(湍流度)** 当人体穿过双基地连线时,不同的子载波会有不同的反应 —— 有些会衰减,有些会产生相长干涉。**单个数据包中,精选子载波集上的标准差**(“湍流度”)在运动期间会急剧上升,并在安静时保持稳定。然后,我们计算湍流度滑动窗口的**标准差**,以检测湍流度随时间的*变化*(这正是人类引起的变化)。 这与 ESPectre 和 Espressif 的 `esp_radar_motion_dec` 所采用的方法相同。确实有效。 ### 子载波筛选 对于 HT20 捕获,ESP32 会报告 64 个 OFDM 子载波。我们丢弃: - **DC 空** (索引 32) — 无用 - **导频** (索引 7, 21, 43, 57) — 已知信号,无信息量 - **保护频带** (索引 0–10 和 53–63) — 噪声高 ……并使用剩余范围内人工挑选的 12 个子载波子集:`[12, 14, 16, 18, 20, 24, 28, 36, 40, 44, 48, 52]`(与 ESPectre 的固定机器学习配置相同)。 ### Hampel 滤波器 对每包湍流度值应用 7 样本滑动 Hampel 滤波器(基于 5σ MAD),可消除偶发的数据包解码故障,同时不会抹除真实的运动信号。 ### 自适应阈值 固定的 σ 乘数不起作用 —— 每个房屋、每个信道、每天的不同时间段都有不同的底噪。我们改为: 1. 收集系统静默运行前 10 秒的得分值 2. 取该基线的 **95th percentile** (95 百分位数) 3. 乘以 1.10 作为裕量 4. 当当前得分在 **连续 3 个数据包** 超过阈值时触发(运动开启),并在 **连续 5 次未命中** 后解除(运动关闭) 这使得系统能够自动适应您部署它的任何房间、路由器或射频环境。 ### 呼吸检测 独立于运动检测层。对于每个 RX 流,我们保留一个 60 秒的每包平均振幅滑动窗口。每 5 秒: 1. 重采样到均匀的 10 Hz 网格 2. 二次去趋势 3. Hann 窗口 + 实数 FFT 4. 在 **0.15–0.5 Hz 频段**(9–30 次呼吸/分钟)中找到最强峰值 5. 如果相对于频谱其余部分的峰值信噪比 (SNR) > 3.0,则报告该频率 根据经验,当人位于距离 RX 节点约 3 米范围内时,它可以隔着一堵内墙捕捉到成年人 14–18 BPM 的静息呼吸频率。 ## 硬件避坑指南(那些痛苦的经验) **劣质充电器会烧毁 ESP32-CAM 模块。** 在最初的台架测试中烧毁了两个 —— 当 5V 电源实际上达不到 5V 时,板载的 AMS1117 3.3V LDO 就是故障点。请使用额定电流 ≥1 A 的 Anker/Belkin 充电器,而不是无品牌的廉价方块充电器。对于永久性安装,请完全弃用墙壁充电器,改用在项目盒内的 HLK-PM01。 **ESP32-C3 SuperMini 存在已知的 PCB 天线缺陷**,损耗约 6–10 dB。焊接一根 31 毫米的短线作为四分之一波长辐射器大致可以将通信距离增加一倍。当开发板最终位于石膏板墙后或金属接线盒中时,这一点非常重要。 **ESP32-WROOM-32 + Arduino Serial + ESP-IDF WiFi 任务 = UART0 上的竞争条件。** 这就是为什么此固件中的所有日志 + CSI 输出都通过 UDP 传输,而不是 Serial 的原因。`esp_log_level_set("*", ESP_LOG_NONE)` *并不能* 阻止 IDF 在帧传输中途直接向 UART0 写入数据;唯一的解决方法是在 `WiFi.begin()` 之后保持 Serial 静默。 **在使用基于 CH340 的开发板时,921600 的上传波特率会失败**,并提示 `chip stopped responding`。锁定在 460800 以确保刷写可靠。 ## 先前成果与鸣谢 本项目建立在大量学术与开源工作的基础之上。如果您想深入了解: - **[Espressif `esp-csi`](https://github.com/espressif/esp-csi)** — ESP-IDF 上官方的 CSI API 示例 - **[ESPectre](https://espectre.dev)** — 记录最详尽的开源 ESP32 运动检测协议栈;我们原封不动地使用了他们的固定机器学习子载波配置和基于湍流的检测器 - **[StevenMHernandez/ESP32-CSI-Tool](https://github.com/StevenMHernandez/ESP32-CSI-Tool)** — 最初的 ESP32 被动 CSI 捕获项目 - **[esp_radar_motion_dec](https://components.espressif.com/components/espressif/esp_radar_motion_dec)** — Espressif 的 `jitter`/`wander` 参考实现 - **Ma, Zhou, Wang 2019** — *WiFi sensing with channel state information: a survey* (ACM Computing Surveys) - **Adib & Katabi, MIT** — *RF-Capture*, *WiTrack*, *WiSee* (隔墙传感的学术起源) - **PCA-Kalman (EURASIP 2018)** — 基于特征值的存在检测(我们目前未使用此项;对于我们的用例,二元 sigma 即可生效) 如果您发表了使用此代码的研究成果,请在此旁边引用 ESPectre 和 esp-csi。 ## 许可证 [MIT](LICENSE)。无任何担保。请合乎道德地使用。对于您在自己家中或任何其他地方使用隔墙传感技术所做的一切,维护者概不负责。

@heyfinal 构建 · 欢迎通过 Issues 提供贡献和报告 Bug

标签:ESP32, WiFi感知, 人员检测, 信道状态信息, 呼吸监测, 物理安全, 物联网, 逆向工具