ESP32 CSI 人体检测:采集、鲁棒特征、session 标定与误报评估
作者 Wu Jun · 更新于 2026-10-29 · 约 6 分钟
ESP32 能把 Wi-Fi CSI 做成便宜的运动传感器,但“串口有数据”离“可用于自动化”还有几步: 固定发送端、确认 CSI 字段、建立空房基线、按 session 标定,以及报告误报/漏报。
本文把“人体检测”严格定义为运动代理:能发现活动,不能证明静止的人一定存在,也不能识别身份或人数。 方法级背景可参考 CSI 感知综述 [1]。
0. 先看结论
| 问题 | 正确回答 |
|---|---|
| 一块 ESP32 自己就能发射并接收测量吗? | 不能;必须有路由器、第二块板或其他持续发包端 |
| 包率达到 100Hz,CSI 就是 100Hz 时间序列吗? | 不一定;按实际匹配包、时间戳和丢失统计 |
| ESP32 一定是 30–64 个子载波吗? | 不固定;取决于 LTF、20/40MHz、STBC 和固件字段 |
| motion score 能证明房间无人吗? | 不能;静止、遮挡和多径都可能漏检 |
| 一段 30 秒数据能证明准确率吗? | 不能;至少要独立 session,并报告误报/小时和延迟 |
1. 选采集拓扑
| 路线 | 硬件 | 优点 | 主要风险 |
|---|---|---|---|
| 单 ESP32 + 路由器 | 1 板 + AP | 最便宜 | 发包率受 AP/省电/信道影响 |
| 双 ESP32 | 发送板 + 接收板 | 发送节奏更可控 | 两板 target、固件与时间轴需匹配 |
| ESPectre / ESPHome | 受支持 ESP32 | 直接生成 HA 实体 | 单区域覆盖,仍需现场标定 |
官方 esp-csi 提供 csi_recv_router、csi_recv 和 csi_send 等示例
[2]。ESPectre 则在设备端做运动检测并输出实体
[3]。
1.1 采集前必须确认的字段
len与本固件 CSI 缓冲区一致;data的元素数量与len一致;first_word_invalid:某些 ESP32-S3 帧前 4 字节无效 [4];rssi、noise_floor、local_timestamp持续更新;- 停止发送流量后,CSI 帧数明显下降,证明确实是收包触发。
ESP32 CSI 的每个复数样本通常是 int8 imaginary + int8 real,不能沿用路由器 int16 I/Q 的解析器。
2. 第一步不是算法,而是数据质量
先采 10 秒,输出以下统计:
frames_total
valid_csi_frames
duration = last_ts - first_ts
effective_rate = (valid_csi_frames - 1) / duration
gap_p50 / gap_p95 / gap_max
len histogram
RSSI min / mean / max
first_word_invalid count
如果有效帧率不足、包间隔不可解释或数据长度频繁变化,就不要进入阈值阶段。看起来“有波形”的 错误时间轴会让后续结论全部失效。
3. 先做逐子载波归一化
假设 H 是经过对齐的 frames × subcarriers 幅度矩阵。不要直接对原始幅度设置一个全局阈值:
不同子载波的噪声、衰减和缺失状态不同。
下面的代码只用“训练/空房 session”计算基线和尺度,再用它处理验证 session:
import numpy as np
def baseline_fit(H_quiet):
baseline = np.median(H_quiet, axis=0)
mad = np.median(np.abs(H_quiet - baseline), axis=0)
scale = np.maximum(1.4826 * mad, 1e-6)
return baseline, scale
def motion_series(H, baseline, scale, fs):
# z 越大,表示当前子载波偏离空房基线的程度越高
z = np.abs((H - baseline) / scale)
frame_score = np.median(z, axis=1) # 中位数抑制单个坏子载波
width = max(1, int(round(fs))) # 约 1 秒窗口
kernel = np.ones(width) / width
return np.convolve(frame_score, kernel, mode="same")
这只是透明、可复现的基线方法,不代表最优模型。任何深度学习或 ESPectre 模型也必须在独立 session 上验证; 不能在用于调参的空房数据上报告准确率。
4. 数据采集协议
| 类别 | 每段时长 | 建议 session 数 | 记录内容 |
|---|---|---|---|
| 空房 | 10 分钟 | ≥ 3,不同时间 | 空调、窗帘、宠物、邻居干扰 |
| 正常走动 | 10 分钟 | ≥ 3 | 不同速度、路径、距离 |
| 坐下/起立 | 5 分钟 | ≥ 5 | 事件前后各 30 秒 |
| 静止坐着 | 10 分钟 | ≥ 3 | 测试静止漏检 |
| 无人/有人交替 | 10 轮 | ≥ 2 天 | 进入/离开时刻作为真值 |
记录:
- ESP32 型号、target、ESP-IDF 与固件 commit;
- 发送/接收板位置、高度、朝向、距离;
- 信道、带宽、RSSI、noise floor;
- 有效帧率、最大 gap、串口丢行;
- 受试者、房间、家具状态和标签来源。
采集协议至少覆盖两个位置、两个受试者;否则阈值很容易只拟合一个人和一个房间。训练/验证按 session 或日期切分,不能把相邻重叠窗口随机分到两边。
5. 阈值、滞回和保持时间
先分别得到:
q_quiet = 训练空房 motion_series 的 P99
q_active = 训练活动 motion_series 的 P10
只有 q_quiet < q_active 且分离明显时,阈值才有意义。取中间区域并加滞回;如果两个分布重叠,
说明当前位置或特征无法区分,应先改硬件布局,而不是硬调阈值。
ENTER = q_quiet
EXIT = max(q_quiet * 0.8, q_active * 0.5)
MIN_ON_S = 0.5
MIN_OFF_S = 3.0
state = False
on_time = off_time = 0.0
dt = 1.0 / fs
for score in motion:
if not state:
on_time = on_time + dt if score > ENTER else 0.0
if on_time >= MIN_ON_S:
state, on_time, off_time = True, 0.0, 0.0
else:
off_time = off_time + dt if score < EXIT else 0.0
if off_time >= MIN_OFF_S:
state, on_time, off_time = False, 0.0, 0.0
这些是示例参数,不是各房间通用常数。真正的保持时间应由误报成本决定:开灯可以短, 安防报警必须长,并且需要人工复核。
6. 事件级评估
逐帧 accuracy 会对“没有运动”的多数样本过于乐观。家庭场景更应报告:
| 指标 | 定义 |
|---|---|
| False alarms per hour | 无人时每小时误触发次数 |
| Event recall | 真实事件在容差窗口内被发现的次数 / 总事件数 |
| Miss rate while still | 静止受试者被误判为无人的比例 |
| Detection latency | 事件真实开始到状态变 true 的时间 |
| Release latency | 人离开到状态变 false 的时间 |
| Unavailable time | MQTT/串口/HA 中断占比 |
若没有同步视频或人工事件表,只能报告“未标定运动代理”,不能报告准确率。建议在报告里给出 原始数据路径、代码 commit、阈值、包率、位置和失败 session。
7. 真实房间中的失败模式
| 现象 | 可能原因 | 正确修法 |
|---|---|---|
| 串口有日志但没有 CSI | 未连上 AP或没有持续流量 | 先 ping/发送端,查 CSI enable |
| 包率抖动 | 信道竞争、省电、串口带宽 | 固定发送端、关闭省电、降速绘图 |
| 子载波长度变化 | 不同 LTF/带宽/STBC 包 | 保留 len、mode 与 first_word_invalid |
| 空房一直报警 | 空调/窗帘/宠物/邻居 | 重新采空房、延长窗口、剔除异常子载波 |
| 有人静止时显示无人 | 没有足够微动 | 加独立传感器,不能伪造 presence |
| 办公室可用家里不可用 | 多径与 AP 位置变化 | 每个房间独立标定 |
8. 什么时候换路由器或增加传感器
如果目标覆盖多个房间,一台 MT6000 可共享采集和网络,减少每屋一块 ESP32 的维护;代价是要处理 mt76 固件、独立 AP、CSI 采样率与路由器性能。参考MT6000 复现教程。
如果目标是可靠安防或跌倒报警,单一 CSI 传感器不够。应增加独立传感器、人工复核、故障告警和 隐私合规评估,而不是用不存在的准确率承诺替代工程设计。
9. 从零开始的验收顺序
- 用官方示例得到
CSI_DATA; - 验证
len、data数量、时间戳和包率; - 采空房/行走两个 session,画 motion score;
- 用独立 session 检查分布是否重叠;
- 设置滞回和保持时间;
- 连续跑 24 小时,统计误报和不可用时间;
- 最后才接 Home Assistant、灯或空调。
相关:ESP32 CSI 入门 · 接入 Home Assistant · 对比 mmWave 雷达 · Wi-Fi CSI 雷达
参考与数据来源
本文的每条外部结论都标注来源与证据等级(分级标准);标注 E 级的是本项目可复现的实测数据。
- A 级 · 标准/综述
WiFi Sensing with Channel State Information: A Survey
W. Ma, W. Zhou, X. Wang · ACM Computing Surveys · 2019
doi:10.1145/3310194 · 被引 857 · 核实: CrossRef API, 2026-10-11 · 领域内最常被引用的 CSI 感知综述(被引 857 次,核实于 CrossRef)。 - C 级 · 官方资料
espressif/esp-csi — Applications based on Wi-Fi CSI
Espressif Systems
github.com/espressif/esp-csi · 核实: GitHub API 2026-10-29 (stars 1,723; README confirms the complete ESP32 series list and three sender/receiver routes) · 官方 README 原文:“All ESP32 series support CSI, including ESP32 / ESP32-S2 / ESP32-C3 / ESP32-S3 / ESP32-C5 / ESP32-C6 / ESP32-C61.” - C 级 · 官方资料
francescopace/espectre — Wi-Fi CSI motion sensing for ESP32 (ESPHome)
F. Pace
github.com/francescopace/espectre · 核实: GitHub API and official README fetched 2026-10-29 (stars 9,700; GPLv3; ESPHome source path, Direct API port 62587 and supported chips documented) · 官方项目提供 ESPHome、Native MQTT、Matter 与浏览器烧录路线;同一块 ESP32 覆盖一个区域,不识别身份或人数。 - C 级 · 官方资料
ESP-IDF — Wi-Fi Channel State Information (ESP32-S3)
Espressif Systems
docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/wifi-driver/wifi-vendor-features.html#wi-fi-channel-state-information · 核实: official docs fetched 2026-10-29 (ESP32-S3 CSI section: LTF/byte-count table and the first_word_invalid note) - C 级 · 官方资料
StevenMHernandez/ESP32-CSI-Tool — active/passive CSI extraction for ESP32
S. M. Hernandez
github.com/StevenMHernandez/ESP32-CSI-Tool · 核实: GitHub API 2026-10-11 (stars 571, MIT, last push 2024-07-16)