ToF 测距与 PIR 存在感知运用与发展历程
目录
9. ESP-IDF / Zephyr / Nordic 多平台对比
1. 由来与历史背景
1.1 ToF:从激光雷达到 10 元芯片
ToF(Time of Flight,飞行时间):发射调制红外光,测量光往返时间(ps 级)直接得距离——相比三角测量法(短距精度高但体积大、量程近)与超声波(厘米级、怕风、多机串扰),光学 ToF 在 2010 年代被 ST(VL53L0X,2016)平民化:940nm VCSEL + SPAD 单光子阵列 + 体内 TDC 时钟,几毫米×几毫米的芯片测 2m 距离 ±3%,成本从激光雷达的千元级打到 10 元级。
应用从此爆发:手机激光对焦(2018 后)、扫地机器人悬崖/避障(2020 后)、感应水龙头/皂液器(非接触防疫,2020 疫情催化)、会议机器人跟随测距、多区 ToF(8×8 阵列 = 小型深度图)做近场手势。
1.2 PIR:一元的常开哨兵
PIR(Passive Infrared,热释电红外):人体(~37°C)发出的 8~14μm 红外经菲涅尔透镜分束打到热释电材料上,"温度变化的移动"产生电荷——1960 年代安防传感器发明,BISS0001 一类专用 IC(内置两级放大/窗口比较/输出锁存)把整个前端集成,是"人来了"检测的最廉价方案(1~3 元含透镜)。
与 ToF 的分工:PIR 是"有没有人动了"的 μW 级常开哨兵(无源、不供电发射端),ToF 是"距离多少"的按需确认——两者构成"PIR 唤醒、ToF/雷达精判"的级联(19 章触发级联的传感侧起点)。
1.3 与毫米波/超声波的四方对比
| 维度 | PIR | ToF | 超声波 | 毫米波 |
|---|---|---|---|---|
| 检测物理 | 热源移动 | 光往返时间 | 声往返时间 | 多普勒微动 |
| 静止人体 | ✘ 漏检 | ✔(光学可见) | ✔(有声学反射) | ✔(呼吸微动) |
| 距离信息 | ✘ | ✔(±3%) | ✔(厘米级) | ✔(可解) |
| 功耗 | 10μA 级 | 会话 100mW | 会话数十 mA | 常开 30~100mW |
| 成本 | 1~3 元 | 6~40 元 | 1~3 元 | 30~80 元 |
| 串扰 | 无 | 光学(同场多机) | 声学严重 | 低 |
| 失效场景 | 热气流/阳光直射 | 黑体吸光/镜面 | 风/软物吸声 | 金属遮挡 |
PIR 检"动"不检"静"(静止人无热变化 = 漏),毫米波检"微动"(呼吸胸廓起伏 = 静态存在可检)——养老设备的"久坐/跌倒后静止"场景毫米波是刚需,PIR 只能做"来人触发";但 PIR 功耗 10μA 级——"PIR 常开、毫米波按需"是功耗与能力的标准组合。
2. 传感原理分类与状态模型(原理深潜)
2.1 ToF 的两种物理实现
iToF(间接式): 连续波相位差测距(VL53L0X主线) 发射940nm调幅连续光(相位编码) → 接收相位差φ → d = φ/2π × c/(2×f_mod) 量程受相位模糊限制(f_mod对应最大单值距离) 时间预算(timing budget)越长 → 积分次数越多 → 精度越好(σ∝1/√N) dToF(直接式): SPAD单光子计时(VL53L8/手机dToF) 发射ps级短脉冲 → SPAD捕获首光子 → TDC记时间 → 直读距离 直方图统计多脉冲(首峰=真目标, 后峰=多径) → 多径免疫力强 单脉冲能量低 → 需SPAD阵列高灵敏度(单光子级)
量程档位的物理:短距(0.2~1.2m,感应水龙头)/中距(0~2m,跟随)/长距(0~4m,机器人避障)多模式——timing budget 档位换精度:20ms 快糙 / 200ms 慢准 / 33ms 连续默认。环境光免疫:940nm 避开阳光主要谱段 + SPAD 计数统计剔除背景(VCSEL 短脉冲编码,与背景直流分离)——户外可用但直射强光仍降距。
目标反射率影响:白纸 2m、黑布可能只剩 0.5m——量程指标按"白卡"标定,产品手册要读小字。多机串扰:同场景多 ToF 互相照射(同 940nm)——分时触发(TDMA 槽位)或不同调制频率错开。
2.2 SPAD 与盖玻片串扰
盖玻片(防水/美观)的副作用: 内表面反射部分发射光直达接收窗 → 串扰信号(xtalk)与真回波叠加 → 近距读数偏小/远距失真 校准: 对已知距离白板测偏差 → 拟合xtalk系数 → 写OTP/NVS → 驱动运行时补偿 未校准的后果: 黑玻片盖板下 0.5m 目标读 0.2m(近距区全偏) — 量产质量项 污染演进: 油污/灰尘改变xtalk → 高可靠场景周期重校(现场触发)或告警
2.3 PIR 的信号链物理
热释电元件(双元差分结构) → BISS0001两级运放(增益RC设定) → 窗口比较器(阈值±) → 输出锁存(时间RC: 2~30s) → 数字电平输出(高=检测) 菲涅尔透镜: 分束成N个明暗区 → 人移动跨区=信号调制(静止人=直流,被差分抵消) → 这就是"PIR只检动"的物理: 差分结构天然滤除直流(静止热源) 双元差分的另一个红利: 整体温漂(环境温度变化)同时作用于两元 → 共模抑制 误报物理: 暖气流(暖气对流)/阳光直射瞬变 → 窗口比较器误触发 → 级联确认抑制 上电稳定: 热平衡建立30~60s(自检期输出不可信, 必须静默)
2.4 级联状态模型
PIR常开(10μA,热动触发) → 触发事件(去抖: 2s内≥2脉冲) → 唤醒级联(ToF/毫米波上电精判: 距离/存在确认) → 确认事件 → 业务响应(灯亮/跟随/上报) → 静默计时(无触发2min) → 回PIR常开(级联下电) 误报路径: 热气流/宠物 → 级联确认"无人" → 抑制+遥测 维持路径: 已存在态 → 低频复测(1Hz短会话) → 静默2min退场
2.5 VCSEL 光源与光安全
ToF的发射端: VCSEL(垂直腔面激光器) 940nm红外 为什么940nm: 人眼不敏感(不刺眼/无红曝或弱红曝) + 硅探测器响应峰附近 850nm探测效率更高但"红曝"可见 — 安防隐蔽性差 光安全等级: IEC 60825 Class 1(连续直视安全) → 芯片厂商按"最坏镜头聚焦"算限额 — 产品改镜头/加透镜要重评估 → 工程红线: 别在VCSEL前方加聚光元件(把Class 1变Class 1M+) 驱动时序: VCSEL以占空比工作(测距会话内ns级脉冲串) → 平均功率远低于峰值 — "脉冲闪光"既是测距物理也是安全物理 PIR无光源: 纯被动 — 光安全/隐蔽性/功耗三重零负担 → "无主动光"是PIR在隐私敏感场景(卧室/卫浴)的独特优势
2.6 环境光与反射率的量程物理
环境光抑制: 调制解调(相关器只认调制频率)天然抗直流环境光 → 但强阳光下噪声底抬升: SNR降 → 量程缩水(标称2m阳光直射下可能0.8m) → 户外产品选"高环境光抑制"档芯片(如多脉冲累积+窄带滤光片) 反射率-量程表(典型, 940nm): 白纸(90%) ~2.0m / 木色(50%) ~1.6m / 深色布(20%) ~1.2m / 黑橡胶(5%) ~0.5m / 镜面/透明玻璃 无效 → "标称量程"是白纸口径 — 产品按最低反射率目标的0.5倍设计余量 信号质量指标: 返回信号强度(ReturnRate/有效SPAD数) → 低于阈值的读数应标"低置信"而非硬给距离(6.2的中值滤波前置判) → 幅值监控是"镜头脏污/遮挡自检"的数据源(6.7)
2.7 多区 ToF 与手势语义
多区(zones): 芯片把视场分成2×8/4×4/8×8分区, 每区独立距离 → 一帧 = 64个点的"迷你深度图" — 介于单点与相机之间 手势识别最小闭环(2×8横阵为例): 帧1: 左半区近 右半区远 → 帧2: 相等 → 帧3: 右近左远 → 质心轨迹横移+速度 → "左挥/右挥"判定(挥手开灯的物理) 存在语义: 区域占用mask(哪几区有近物) + 稳定性N帧 → "面前有人"的几何判定, 比单点多一道"范围"确认 功耗档: 多区模式功耗×4~8(单区的像素级代价) → 手势会话用, 存在监测用单区/PIR — 仍然是"事件升档"哲学
2.8 PIR 透镜光学与覆盖图
菲涅尔透镜的作用: 分束(把视场切成十几瓣) + 聚光(增益) → "人移动"经过瓣边界 = 热源在双元上的快速通断 → 触发 → 静止的人: 无瓣跨越, 直流被差分滤掉 — "检动不检静"的光学根源 透镜形态三档: 广角(110°~140°): 房间覆盖(壁挂安防) — 瓣多而密 幕帘式(垂直窄带): 走廊/门窗过线检测 — 一片"光幕" 吸顶式(360°俯视): 顶部安装, 俯角覆盖半径=安装高度×~1.2 覆盖图的工程纪律: 安装高度×俯角×透镜型号 → 盲区图(近墙根死角) → 案例八: 改高度不改覆盖评估 = 漏检事故 → 产品手册给"覆盖俯视图模板"(每安装高度一图)
2.9 温漂与老化
PIR温漂: 双元差分依赖两元件匹配 — 温度剧烈变化(空调直吹/西晒) 产生共模残余 → 偶发误报 → 对策: 硬件共模抑制选型(双元匹配度指标) + 软件温漂静默窗 (芯片温度变化率>阈值时暂时提高触发门限) ToF温漂: VCSEL波长/驱动电流随温度 → 距离读数缓漂(0.1~1mm/°C级) → 芯片内置补偿表; 高精度产品加"周期自校"(已知距离参考面, 6.6) 老化: PIR透镜黄变(户外)/窗口脏污 — 灵敏度衰减是渐变过程 → "灵敏度年衰减"进产测基线对比(6.7健康自检的年度对照)
2.10 级联时间轴物理(各传感器的时序账)
PIR: 中断触发 0ms | 稳定(无, 常开) | 判定 RC展宽2~5s ToF: 上电→首帧 ~50ms(含VCSEL预热) | 会话10Hz采样 | 单帧20~200ms(TB) 摄像头(15章级联终点): 上电~300ms | 曝光/AE收敛 200~500ms 级联总账: PIR触发(0) → ToF首帧(+80ms) → 确认"人近"(+200ms) → 摄像头已并行预热(触发即启动, 不等确认) → 抓拍(+500ms) 设计要点: 级联确认与下游预热"流水线化" — 确认未通过就预热 下游是"用功耗换时延"的对赌(误报代价=白预热300ms, 可接受) → 千万别"串行等待"(触发→确认→才开摄像头 = 抓拍晚1s, 人走了)
3. 计费与成本视角
-
PIR:1~3 元(含透镜),BISS0001 类 IC + 菲涅尔透镜;μW 级功耗免电源设计——"1 元买一个常开哨兵"。
-
ToF:短距 6~12 元、多区(8×8,VL53L5 类)15~40 元;盖玻片校准增加产线工位(校准治具 + 2s/台)。
-
功耗账:PIR 常开 10~20μA(电池设备标配);ToF 单次测距 20~40mJ(100ms@100mW 均值)——按需触发设计,日均 100 次触发 ≈ 0.1mAh(可忽略);ToF 常开连续 100mW = 电池杀手(严禁,19 章触发级联纪律)。
-
对比账:PIR+ToF 组合(3~15 元)vs 毫米波单芯片(30~80 元)——"有无+距离"够用的场合组合方案便宜 5 倍;需要"静止存在+呼吸"才上毫米波。
-
误报成本:PIR 误报 → 联动摄像头唤醒/云端上报:误报率 10 次/天 × 万台 = 云计算与流量浪费——级联确认是"省钱设计"。
4. 业务场景与典型用法
-
感应灯/夜灯:PIR 触发 + ToF 距离分级(近 1m 全亮/2m 微亮)——养老夜灯"床边自动亮"。
-
水龙头/皂液器:ToF 短距模式(0.1~0.5m)非接触触发,抗环境光档位。
-
扫地机器人:ToF 长距避障(悬崖/矮障碍超声波互补)。
-
会议/家庭机器人跟随:ToF 测目标距离控制跟随速度(近停远随,16 章运动联动)。
-
屏幕唤醒:PIR 来人亮屏 + ToF 在屏前(<0.5m)才亮(14 章显示联动)——省电与隐私双优。
-
宠物设备:PIR 区分人宠(高度双探头分立)触发拍摄。
-
安防摄像头:PIR 从深度睡眠唤醒录制(预录缓冲 2s,12 章联动)。
-
垃圾桶/洁具:ToF 手挥检测(近场手势:挥手开盖)——多区 ToF 手势识别的前身。
5. 架构设计与分层职责
五层:驱动层(I2C ToF 初始化/量程档位/读距;PIR GPIO 中断 + RC 时间常数理解);校准层(ToF 串扰标定存储、安装偏移写入 OTP/NVS);事件层(PIR 脉冲去抖/计数窗口;ToF 距离阈值/迟滞);级联层(PIR 触发 → ToF/毫米波上电确认 → 抑制误报);业务层(灯/屏/跟随/上报的动作路由)。
关键设计原则:
-
PIR 中断驱动:GPIO 上升沿中断(不轮询),中断内只置标志(12 章纪律);上电 60s 自检静默。
-
ToF 会话式:上电初始化(40ms)→ 测距 N 次取中值 → 下电——单次会话 150ms 内完成,绝不常开。
-
阈值迟滞:近 0.5m 进/0.7m 出(防边界抖动);中值滤波(3~5 次采样)抗单帧飞点。
-
状态化而非数值化:业务消费"有人/无人/近/远"状态(带迟滞),不消费原始毫米数——传感层完成语义化。
6. 改写版代码实现(四模块)
6.1 PIR 常开哨兵(中断+去抖)
/**
* @file pir_sentinel.c
* @brief PIR常开哨兵: 中断去抖+自检静默(改写版,开源风格)
*/
#include "pir_sentinel.h"
#define PIR_DEBOUNCE_PULSES 2
#define PIR_WARMUP_MS 60000U
#define PIR_MIN_GAP_MS 300 /* 两脉冲最小间隔(噪声滤除) */
static volatile uint32_t s_pulse_ts[2];
static uint32_t s_boot_ms;
void pir_gpio_isr(void *arg)
{
(void)arg;
uint32_t now = now_ms_isr();
if (now - s_pulse_ts[0] > PIR_MIN_GAP_MS) { /* 300ms内重复=噪声 */
s_pulse_ts[1] = s_pulse_ts[0];
s_pulse_ts[0] = now;
}
}
void pir_init(void)
{
s_boot_ms = now_ms();
gpio_irq_install(PIN_PIR, EDGE_RISING, pir_gpio_isr);
}
bool pir_confirmed(void)
{
if (now_ms() - s_boot_ms < PIR_WARMUP_MS) return false; /* 自检静默 */
return s_pulse_ts[0] &&
(s_pulse_ts[0] - s_pulse_ts[1]) < 2000U; /* 2s窗2脉冲 */
}
uint32_t pir_last_pulse_ms(void) { return s_pulse_ts[0]; }
6.2 ToF 会话式测距(档位+中值滤波)
/**
* @file tof_session.c
* @brief ToF会话: 档位切换+中值滤波+串扰补偿(改写版)
*/
#include "tof_session.h"
#define TOF_SAMPLES 3
typedef struct { uint8_t mode; uint8_t tb_ms; } tof_profile_t;
static const tof_profile_t PROF[] = {
[RANGE_SHORT] = { MODE_SHORT, 20 }, /* 水龙头: 快糙 */
[RANGE_MID] = { MODE_MID, 50 }, /* 跟随: 中庸 */
[RANGE_LONG] = { MODE_LONG, 200 }, /* 避障: 慢准 */
};
int tof_session_open(range_t r)
{
const tof_profile_t *p = &PROF[r];
if (i2c_dev_power(TOF_PWR_EN) != 0) return -1;
rtos_task_delay(40); /* 上电稳定 */
if (tof_cfg_profile(p->mode, p->tb_ms) != 0) return -2;
tof_apply_xtalk(nvs_read_u32(NVS_TOF, "xtalk")); /* 产线校准值 */
tof_apply_offset(nvs_read_i32(NVS_TOF, "offs"));
return 0;
}
/* N次采样取中值(抗单帧飞点), 全程一个会话内完成 */
int32_t tof_session_range_median(void)
{
int32_t v[TOF_SAMPLES];
for (int i = 0; i < TOF_SAMPLES; i++) {
if (tof_read_range(&v[i]) != 0) v[i] = -1;
}
tof_session_close();
return median3(v[0], v[1], v[2]);
}
void tof_session_close(void)
{
i2c_dev_power(TOF_PWR_EN); /* 测完即关(功耗纪律) */
tof_bus_release();
}
6.3 级联状态机(触发/维持/退场/误报抑制)
/**
* @file zone_cascade.c
* @brief PIR→ToF级联: 触发确认+静默退场+误报遥测(改写版)
*/
#include "zone_cascade.h"
#define TOF_NEAR_MM 500
#define TOF_NEAR_EXIT_MM 700
#define QUIET_TIMEOUT_MS 120000U
typedef enum { ZS_ABSENT = 0, ZS_PRESENT, ZS_NEAR } zone_st_t;
static zone_st_t s_zone = ZS_ABSENT;
void zone_task(void *arg)
{
(void)arg;
pir_init();
rtos_task_delay(60000); /* PIR自检静默期 */
for (;;) {
if (s_zone == ZS_ABSENT) {
rtos_task_delay(100); /* 常开仅PIR中断(10μA级) */
if (!pir_confirmed()) continue;
tof_session_open(RANGE_MID);
int32_t d = tof_session_range_median();
if (d < 0 || d > 2500) { /* 确认失败: PIR误报 */
evt_post(EVT_PIR_FALSE, (uint32_t)d);
continue; /* 抑制(灯不亮/云不上报) */
}
s_zone = (d < TOF_NEAR_MM) ? ZS_NEAR : ZS_PRESENT;
evt_post(EVT_ZONE_ENTER, (uint32_t)d);
} else {
/* 已存在: 1Hz短会话维持 + 静默判定 */
rtos_task_delay(1000);
tof_session_open(RANGE_MID);
tof_cfg_profile(MODE_MID, 20);
int32_t d;
int ok = tof_read_range(&d) == 0;
tof_session_close();
uint32_t since = now_ms() - pir_last_pulse_ms();
if (!ok || since > QUIET_TIMEOUT_MS) {
s_zone = ZS_ABSENT; /* 静默2min: 退场 */
evt_post(EVT_ZONE_EXIT, since);
} else {
if (s_zone == ZS_PRESENT && d > 0 && d < TOF_NEAR_MM) {
s_zone = ZS_NEAR; evt_post(EVT_ZONE_NEAR, (uint32_t)d);
} else if (s_zone == ZS_NEAR && d > TOF_NEAR_EXIT_MM) {
s_zone = ZS_PRESENT; evt_post(EVT_ZONE_FAR, (uint32_t)d);
}
}
}
}
}
6.4 串扰校准产线流程(治具模式)
/**
* @file tof_calib.c
* @brief 盖玻片串扰产线校准(改写版)
*/
#include "tof_calib.h"
/* 治具: 白板固定在已知距离L0, 带盖玻片的整机进入校准模式 */
int tof_calib_run(int32_t l0_mm)
{
tof_session_open(RANGE_MID);
tof_apply_xtalk(0); tof_apply_offset(0); /* 清零后测原始偏差 */
int32_t raw = 0;
for (int i = 0; i < 5; i++) {
int32_t d;
if (tof_read_range(&d) == 0) raw += d;
else { prodtest_report("read fail"); tof_session_close(); return -1; }
}
raw /= 5;
/* 偏差拟合: 近距偏差主要为xtalk主导(简化模型) */
int32_t err = raw - l0_mm;
if (err < -200 || err > 200) { /* 超阈: 装配问题(盖玻片错位) */
prodtest_report("xtalk out of range %d", err);
tof_session_close(); return -2;
}
nvs_write_u32(NVS_TOF, "xtalk", xtalk_from_err(err));
nvs_write_i32(NVS_TOF, "offs", err);
tof_session_close();
/* 复测验证: 校准后读数必须±10mm */
tof_session_open(RANGE_MID);
int32_t v = tof_session_range_median();
prodtest_report("calib ok err=%d final=%d", err, v);
return (v > 0 && abs_(v - l0_mm) < 10) ? 0 : -3;
}
6.5 多区 ToF 手势识别(质心轨迹法)
/**
* @file tof_gest.c
* @brief 2×8多区: 质心轨迹手势判定(改写版,开源风格)
*/
#include "tof_gest.h"
#define ZN 8
#define GEST_N 24 /* 轨迹窗24帧(~1.2s @ 20Hz) */
typedef struct { int8_t pos; uint32_t t; } pt_t;
static pt_t s_tr[GEST_N];
static uint8_t s_tr_n;
static int8_t zone_centroid(const uint16_t *dist /*mm*/)
{
/* 只取"近于手势距离门(700mm)"的区做质心 */
int32_t num = 0, den = 0;
for (int i = 0; i < ZN; i++) {
if (dist[i] == 0 || dist[i] > 700) continue;
num += (int32_t)(i + 1) * (700 - dist[i]); /* 越近权重越大 */
den += (700 - dist[i]);
}
return den ? (int8_t)(num * 10 / den) : -1; /* 质心×10, 无=-1 */
}
/* 每20ms喂一帧: 轨迹拟合 → 方向+速度判定 */
gest_t tof_gest_feed(const uint16_t *dist)
{
int8_t p = zone_centroid(dist);
if (p < 0) return GEST_NONE;
s_tr[s_tr_n % GEST_N] = (pt_t){ p, now_ms() };
s_tr_n++;
if (s_tr_n < 6) return GEST_NONE;
/* 一元线性回归斜率: 质心位置 vs 时间 */
int32_t n = s_tr_n > GEST_N ? GEST_N : s_tr_n;
pt_t *w = &s_tr[s_tr_n % GEST_N]; /* 环形最近n条 */
int32_t sx = 0, sy = 0, sxy = 0, sxx = 0;
for (int32_t i = 0; i < n; i++) {
int32_t t = w[i].t / 20, y = w[i].pos;
sx += t; sy += y; sxy += t * y; sxx += t * t;
}
int32_t slope = (n * sxy - sx * sy) / (int32_t)(n * sxx - sx * sx + 1);
if (slope > 8) return GEST_SWIPE_RIGHT; /* 质心右移>阈值 */
if (slope < -8) return GEST_SWIPE_LEFT;
/* 距离突变(靠近): 全区质心深度二次导 → GENTLE_APRROACH类 */
return GEST_NONE;
}
6.6 温度补偿与周期自校
/**
* @file tof_cal.c
* @brief 周期自校: 已知参考面的缓漂跟踪(改写版)
*/
#include "tof_cal.h"
/* 产线写入: 参考面真值(治具白板 @500mm) + 出厂温度点 */
static int32_t s_ref_mm; /* NVS: cal_ref */
static int16_t s_cal_t0; /* NVS: cal_temp */
static int16_t s_bias; /* 运行期缓漂修正(不落NVS, 重算) */
/* 每24h一次: 无人时段(级联状态机判定的静默期)自校 */
void tof_cal_periodic(void)
{
if (presence_state() != PS_ABSENT) return; /* 有人不自校(被遮挡) */
if (tof_session_open(30 /*ms*/) != 0) return;
uint16_t d[4]; uint32_t sum = 0; uint8_t ok = 0;
for (uint8_t i = 0; i < 4; i++) {
if (tof_read_mm(&d[i]) == 0 && d[i] > 100 && d[i] < 1500) {
sum += d[i]; ok++;
}
rtos_task_delay(40);
}
tof_session_close();
if (ok == 4) {
int32_t measured = sum / 4;
int32_t new_bias = s_ref_mm - measured; /* 治具口径的期望差 */
/* 温漂模型: 每°C的修正量 × 温度差 + 旧bias 加权缓变
* — 缓变防单次异常读数污染(中值思想的低通版) */
int32_t temp_term = (chip_temp_c() - s_cal_t0) * 15 /*um/°C级*/;
s_bias = (s_bias * 3 + (new_bias - temp_term / 1000)) / 4;
health_score_update(HS_TOF, s_bias < 30 ? 100 : 40);
}
}
/* 语义: 自校不追"快变"(那是距离变化), 只追"缓漂"(温漂/老化)
* → 与6.2的会话测距解耦: bias只影响绝对准度, 不影响触发语义 */
6.7 传感器健康自检(脏污/遮挡/失效检测)
/**
* @file sen_health.c
* @brief 运行期健康: 幅值/触发率/一致性三指标(改写版)
*/
#include "sen_health.h"
typedef enum {
HS_OK = 0, HS_DIRTY, HS_BLOCKED, HS_DEAD, HS_NOISY
} health_t;
/* ToF健康: 返回信号幅值是"窗口状态"的镜子 */
health_t tof_health_check(void)
{
uint16_t d; uint16_t rate;
if (tof_read_with_rate(&d, &rate) != 0)
return HS_DEAD; /* 总线/I2C无应答 */
static uint32_t s_base_rate; /* 出厂基线(NVS产测写入) */
if (rate < s_base_rate / 4) {
/* 幅值掉到1/4以下: 镜头脏污(渐变)或遮挡(突变) */
if (rate < s_base_rate / 20) return HS_BLOCKED;
return HS_DIRTY; /* 通知用户清洁(14章UI) */
}
if (rate > s_base_rate * 2) return HS_NOISY; /* 强光直射/串扰 */
return HS_OK;
}
/* PIR健康: 触发统计的"长尾"监测(30天窗口) */
health_t pir_health_check(void)
{
/* 失效模式: PIR损坏后"永远不触发" — 用注入自检打破静默 */
uint32_t trig30 = pir_trig_count_30d();
if (trig30 == 0 && uptime_day() > 30)
return pir_selftest_inject() == 0 ? HS_DEAD : HS_OK;
/* 注入自检: 部分PIR IC带测试脚(拉高模拟触发) — 无响应=失效 */
if (pir_false_rate_24h() > 50) return HS_NOISY;
return HS_OK;
}
/* 健康事件上报(遥测进运维看板 — "传感器也会老"的资产化视角) */
void sen_health_task(void *arg)
{
(void)arg;
for (;;) {
rtos_task_delay(3600U * 1000U);
health_t t = tof_health_check(), p = pir_health_check();
if (t != HS_OK || p != HS_OK) {
char pl[64];
snprintf_(pl, sizeof(pl), "{\"tof\":%d,\"pir\":%d}", t, p);
mq_publish("dev/none/evt/senhealth", pl, strlen_(pl),
MQ_QOS1, 2);
}
}
}
6.8 隐私与合规设计
/**
* @file sen_priv.c
* @brief 隐私档: 卧室/卫浴场景的"零影像"设计(改写版)
*/
#include "sen_priv.h"
/* 隐私三原则的代码化:
* ①感知最小化: 只回答业务问题(有/无人), 不留"能力面"
* ②事件化上报: 不上报连续原始数据(轨迹/频率都是隐私)
* ③本地闭环: 触发逻辑在端侧, 云端只见结论 */
typedef struct {
bool present; /* 上报: 只有布尔级结论 */
uint8_t confidence; /* 置信度(运维用) */
uint32_t since; /* 进入时刻(小时粒度, 不精确到秒) */
} priv_evt_t;
void priv_report(void)
{
priv_evt_t e = {
.present = presence_state() == PS_PRESENT,
.confidence = presence_conf_pct(),
.since = presence_since_hour(), /* 小时截断 */
};
char pl[48];
snprintf_(pl, sizeof(pl), "{\"p\":%d,\"c\":%u,\"h\":%u}",
e.present, e.confidence, e.since);
mq_publish("dev/none/evt/presence", pl, strlen_(pl), MQ_QOS1, 2);
/* 不上报: 距离原始流/多区深度图/PIR波形 — "能力在端, 结论上云" */
}
/* 合规联动: 无摄像头+几何级感知 = GDPR"最小必要"的硬件级满足
* → 老老看护(卧室夜间)场景的产品卖点级设计(19章用户模型联动) */
7. Linux 平台实践
7.1 IIO 子系统与设备树
Linux 的 ToF 走 IIO(Industrial I/O)子系统:上游驱动(vl53l0x-i2c、vl53l1x)+ 设备树声明 + sysfs 标准接口。
/* 设备树: ToF挂I2C1 */
&i2c1 {
tof@29 {
compatible = "st,vl53l1x";
reg = <0x29>;
interrupt-parent = <&gpio1>;
interrupts = <5 IRQ_TYPE_EDGE_LOW>;
};
};
# sysfs读距(单次)与缓冲模式(连续) cat /sys/bus/iio/devices/iio:device0/in_distance_raw # 触发缓冲采样(跟随类需要连续流) echo 1 > /sys/bus/iio/devices/iio:device0/buffer/enable cat /dev/iio:device0 | xxd # 采样流(应用解析)
-
PIR 走 input/gpiod:
libgpiod(gpiomon监听沿中断)或映射成 input 事件(gpio-keys设备树节点,按键语义承载"PIR 触发")。 -
产品化:机器人/网关侧把"级联状态机"放用户态守护进程(systemd 服务),内核只提供 IIO/GPIO 通道——状态语义与业务路由的迭代不碰内核。
-
调试:
iio_info、gpiomon、示波器抓 PIR 输出波形(时间常数验证)。
7.2 性能与多传感器
Linux 主机(机器人)常见多 ToF(前/左/右避障)——IIO 多设备实例 + 触发同步(同一 trigger 驱动,分时读取防光学串扰)。DMA/中断负载轻(ToF I2C 读距 10ms 级一次)。
8. Android 平台实践
8.1 系统传感框架
Android 的 ToF/PIR 语义进 Sensor HAL:ToF 激光对焦(相机 HAL 内部)、接近传感器(PROXIMITY 类型,PIR/ToF 均可承载——通话时灭屏的经典语义)、存在检测(Android 12+ MOTION_DETECT/HEART_RATE 等 wake-up 传感器族)。手机之外的自研 Android 设备(平板机器人/智能屏)通过自定义 HAL 接入。
App(SensorManager, SensorEventCallback) → SensorService(wake-up路径: 不唤醒AP的传感器节点由DSP/GNSS协处理器处理) → Sensor HAL(厂商实现: I2C读ToF→转语义) → ToF/PIR硬件
-
wake-up vs non-wake-up:PIR 触发想唤醒 AP(亮屏)需要 wake-up 传感器路径(
isWakeUpSensor)+ 显著动作框架(SMD/DSP 侧常开)——手机级 SoC 的常开域(sensor hub)就是为 μW 哨兵准备的(本篇级联思想在 Android 里的官方形态)。 -
接近语义的标准红利:通话灭屏/口袋防误触直接复用系统行为(PROXIMITY 事件 +
PROXIMITY_SCREEN_OFF),免 App 开发。 -
自定义设备(智能屏/机器人):HAL 侧实现级联状态机(同 6.3 逻辑移植到 C++ HAL),上报
TYPE_DEVICE_PRIVATE_BASE + n私有类型。
8.2 典型坑
-
接近传感器校准:盖玻片串扰问题在手机行业同样存在——HAL 侧开机校准流程(同 6.4 治具逻辑转为运行时自校准)。
-
Doze 与传感器:后台 App 收传感器事件受限制——常开感知在系统层(SMD),App 只订阅结果。
9. ESP-IDF / Zephyr / Nordic 多平台对比
| 特性 | ESP-IDF | Zephyr | Nordic | Linux |
|---|---|---|---|---|
| ToF 驱动 | i2c + 官方 API 库 | sensor 子系统(vl53 类上游) | sensor 子系统 | IIO 子系统 |
| PIR | GPIO 中断 + 自研 | GPIO/自研 | GPIOTE 低功耗中断 | input/gpiod |
| 校准存储 | NVS | settings 子系统 | settings | sysfs/eeprom |
| 低功耗路径 | light sleep + GPIO 唤醒 | PM 子系统 + 唤醒源 | PPI + SystemOff | 系统电源管理 |
| 场景 | 卡式设备/夜灯 | 统一框架 | 电池级联哨兵 | 机器人/网关 |
要点:Nordic 的 SystemOff + GPIOTE 唤醒是电池级联哨兵的标杆路径(PIR 中断直接唤醒,全系统 2μA);Zephyr sensor 子系统的 sensor_attr_set 统一档位切换——跨平台移植成本最低;Linux/IIO 把缓冲采样标准化。
10. 运用要点与事故案例库
10.1 运用要点
-
ToF 光学设计:窗口开孔(透红外的 PC/亚克力)、安装角(正对目标 ±10°)、盖玻片串扰校准(深色盖玻片串扰大,产线必标)。
-
量程/时序档位:快糙(20ms)适合跟随闭环、慢准(200ms)适合静态存在——档位按场景切换而非一套到底。
-
PIR 安装纪律:避开热源直视(暖气/西晒窗)、高度 2~2.4m(俯角设计)、幕帘式用于通道(过线检测)。
-
组合去误报:PIR+ToF(本篇)确认"人近"、PIR+毫米波(首篇)确认"人静"、双 PIR 分高(人宠区分)——按误报代价选组合深度。
-
夜间静音:PIR 触发灯但夜间档亮度降级 + 时段表(14 章灯语策略联动)。
-
调试:ToF 原始距离流(12 章 LOG 调试通道)、PIR 脉冲时序录制(误报分析)、安装角度-覆盖图(现场勘测模板)。
10.2 事故案例库
案例一(深色盖玻片测距全偏):外观件选了黑色盖玻片(好看),未做串扰校准——近距区(<0.5m)读数全部偏小 30%(黑色玻片回反射强),水龙头"伸手 0.3m 才响应"变成"0.2m",体验翻车。产线加校准工位(治具白板 + 写 NVS)后批一致。教训:盖玻片(颜色/厚度/材质)是 ToF 的"光学系统一部分",换外观件必须重新校准流程验证。
案例二(PIR 上电误报风暴):设备上电即信任 PIR 输出——开机头 60s(热平衡建立期)PIR 输出抖动,安防设备每天重启就产生一条"有人"误报。加"上电 60s 静默 + 自检期日志标记"后消失。教训:PIR 的上电稳定期是器件物理决定的,任何固件都绕不过——上电静默期是标配不是优化。
案例三(ToF 常开电池事故):跟随机器人为了"响应快"把 ToF 设为常开连续 10Hz——ToF 100mW 常开 + 主控不能深睡,"8 小时续航"变 3 小时。改为级联(PIR 哨兵 + 事件唤醒 ToF 会话)+ 运动中提高频率(IMU 联动)后续航恢复。教训:ToF 常开是产品设计错误不是性能选择——"快"用触发级联解决,不用持续供电解决。
案例四(多 ToF 同场串扰):产线多工位同时测试 ToF——工位间光互串(同为 940nm),测距数据随机跳变 0.5m,误判良率问题。测试程序加"工位分时触发"(TDMA 槽位错开)后稳定。教训:光学 ToF 的同场串扰在产线/密集部署(双灯对装走廊)都会出现——多机场景必须设计分时或频率错开。
案例五(PIR 宠物误报):宠物相机被猫触发(猫 38°C 也是热源)——双探头高度分立(下探头屏蔽 <40cm 热源)+ 级联 ToF 确认"高度 >1.2m 才算人"后误报降 90%。教训:PIR 不区分人宠是物理极限(都是热源),高度/体积的多传感器几何分立才是工程解。
案例六(阳光直射 ToF 量程崩塌):户外门铃产品实测"阴天测距准、晴天中午测不到人"——正午阳光的 940nm 波段辐照是室内千倍级,SPAD 被环境光子灌满,信噪比崩塌,有效量程从 2m 缩到 0.4m,"人在门口站定反而无检测"(走出盲区才触发,方向反了)。换"阳光抑制档"芯片(窄带滤光片+多脉冲累积)+ 阈值改"触发率而非单帧"后过检。教训:户外产品的 ToF 选型必须看"强光档量程"参数(datasheet 第二个量程表),室内标称值在户外没有工程意义;阳光是最强的干扰源,比一切人为干扰都稳定地存在。
案例七(PIR 装在空调出风口):某项目探测器安装在空调出风口侧墙——夏季制冷时每 20 分钟一串误报(冷热气流交替扫过透镜,双元差分被"温度流"触发)。移装位置(避开气流路径)+ 温漂静默窗(板温变化率 >2°C/min 时提高触发门限)双措施后清零。教训:PIR 的物理敌人是"任何温度变化",不只有人——安装勘测清单必须含"气流路径"项(空调/风扇/门口对流),这个清单比任何软件滤波都值钱。
案例八(改了安装高度没改覆盖评估):样机阶段验证安装高度 2.2m,量产时装修标准改为 2.6m——俯角不变的情况下覆盖半径扩大,但近墙根盲区也从 0.3m 变 0.6m;验收时"贴墙走过"的测试用例(正好在盲区)批量漏检,安防验收打回。按"每档安装高度一张覆盖俯视图"重做手册 + 固件加"安装高度档位选择"(不同档不同触发距离门限)后通过。教训:覆盖图是"安装高度的函数"——高度是现场变量,产品必须把"高度→覆盖"的映射写进文档与固件,而不是标定在某个假想高度上一劳永逸。
案例九(镜头脏污长期误判"无人"):某加油站的 ToF 检测半年后误报率上升——运维排查发现镜头积了油膜,返回幅值掉到基线 1/3,距离读数整体偏大(信噪比差的首峰误判),"车在面前却判无车"的漏检偶发。此前幅值从未被监控(6.7 的健康自检缺失),脏污是渐变过程人工巡检发现不了。加"幅值基线监控 + 脏污告警上报"(低于 1/4 基线即上报运维清洁)后可控。教训:渐变失效是传感器运维的隐形杀手——"突然坏"会被发现,"慢慢瞎"只会造成莫名误判;运行期健康自检(幅值/触发率基线对照)是户外产品的标配。
案例十(级联参数照抄导致"静坐被判离场"):看护场景的级联状态机参数照抄安防场景(PIR 触发 → ToF 确认 → 2min 无触发判退场)——老人静坐看电视 5 分钟,PIR 无动可检、ToF 持续在场但状态机的"维持条件"只看 PIR 触发频率,第 2 分钟即判离场关灯,老人起身才重新触发。状态机加"ToF 在场即维持"(维持条件是多传感或的关系)+ 退场判定延长到 10 分钟后修复。教训:级联状态机的"触发/维持/退场"三条件都要多传感器投票——不同业务的"目标行为模型"不同(安防:动了才算在;看护:在着就算在),参数表是业务资产不是通用配置。
11. 发展历程与演进趋势
-
PIR:1960s 安防发明 → BISS0001 类 IC 平民化(2000s)→ 与无线联动(433/LoRa 报警器)→ 当代"常开哨兵 + 级联确认"角色。
-
ToF:激光雷达(测绘千元级)→ 2016 VL53L0X 平民化(10 元)→ 多区阵列(8×8 小型深度图,2020)→ dToF SPAD 阵列小型化(手机/机器人标配)。
-
趋势:ToF 与毫米波/超声的传感器融合(雨/雾/透明物互补)、多区 ToF 做手势识别(近场交互替代触控)、PIR 与 AI 联动(触发摄像头后端侧推理,19 章级联)。
-
与 AI 设备:触发级联的传感侧起点(PIR μW 哨兵 → ToF/雷达确认 → 摄像头/模型精判)——边缘 AI 功耗纪律的物理基础。
12. FAQ 常见问题
Q1:ToF 为什么测不了黑色物体? 黑体吸光——返回光子数低于 SPAD 检测阈值,芯片报"无效"。同理镜面(全反射偏离接收窗)与透明玻璃(光直接穿过去)都是弱项。黑色物体用超声波、透明物用超声波/毫米波、镜面用多角度多机——单 ToF 不万能,融合是正解(本篇四方对比表就是选型依据)。
Q2:PIR 能检测静止的人吗? 不能,物理上不行(差分结构滤除静止热源的直流)。静止存在检测用毫米波(呼吸微动,15 篇首篇)或 ToF/摄像头的"图像存在"。工程组合:PIR 触发进入 → 毫米波维持存在判定(覆盖静止期)→ 静默退出——把三个传感器的物理长处拼成完整时间轴。
Q3:timing budget 调到最短有什么代价? 20ms 快档:积分次数少 → σ 精度差 3~5 倍 + 远距反射率低的物体直接丢——跟随闭环(连续动态修正)可用快档(单帧误差被闭环滤波掉),静态阈值判定(存在确认)必须用中/慢档。档位是"场景函数"不是"全局配置"。
Q4:PIR 输出的高电平时间(RC)怎么定? BISS0001 的输出锁存时间 = RC 积分——设短(2s):触发灵敏但要软件自己维持状态;设长(30s):硬件维持但退出判定迟钝。工程惯例:RC 设 2~5s(短),"存在状态"由软件状态机维持(静默 2min 判退场)——硬件 RC 只做"脉冲展宽",状态语义在软件层。
Q5:级联的确认延时(PIR→ToF 150ms)会拖慢响应吗? 对"灯亮"类业务:人感知阈 ~200ms,150ms 确认在预算内且换来误报抑制 10 倍——值得。对"SOS 级"业务:PIR 单独触发即上报(ToF 只做后续精判补数据)——按业务分级设计确认策略,安全业务跳过确认,体验业务要确认。
Q6:ToF 在强阳光下还有救吗? 分三档应对:选型档——户外直接选"阳光抑制"芯片(窄带滤光片 ~30nm + 多脉冲累积,强光下仍保 50%+ 量程),这是根本解;安装档——避开正对东西向的"正午直射"(朝向偏移 10° 收益巨大),给镜头加遮光檐;算法档——触发判定从"单帧距离"改"多帧触发率"(强光下帧丢弃率高,但只要 20% 帧有效,30ms 窗口内仍能凑出判定)。三档全上,户外可用;只想靠算法救选型的坑是徒劳的。
Q7:PIR 到底装多高、什么角度? 标准三档:壁挂安防 2.0~2.4m(俯角 10~15°,覆盖扇形 12×12m 级);吸顶存在 2.4~3.6m(360° 俯视,覆盖半径 ≈ 高度 ×1.2);幕帘过线 1.8~2.2m(垂直光幕对准通道)。两条铁律:①别对着阳光直射面(西晒窗是误报之源,透镜聚焦会让温度变化放大);②高度决定盲区——每个高度档的"近墙死角"要进产品手册俯视图(案例八的教训)。勘测清单:气流(空调/风扇)、热源(暖气/冰箱散热)、震动(洗衣机上方)——三项现场确认后选位。
Q8:怎么知道"传感器还活着"? 失效模式对应自检手段(本篇 6.7):ToF——幅值基线对照(脏污/遮挡渐变)、I2C 应答(总线死)、已知距离参考面自校(本篇 6.6,治具口径周期回测);PIR——"永远不触发"的静默失效用注入自检打破(部分 IC 带 TEST 脚模拟触发,无响应即失效)+ 30 天触发计数长尾监控;上报——健康事件进运维看板(HS_DIRTY 触发清洁工单,HS_DEAD 触发换货)。原则:传感器是会老的资产——健康自检让它"坏得有通知"而不是"坏得无感"。
Q9:ToF 和超声波/其他光学设备会互相干扰吗? 同波段光学会:多台 940nm ToF 对射(走廊双灯、产线多工位)互串——分时触发(TDMA 槽位)是标准解(案例四)。跨类不干扰:超声波(40kHz 声波)与 ToF(940nm 光波)物理正交——但两者会在"同一目标"上读数不一致(声速温漂 vs 光速不变、镜面/吸音面特性相反),融合时要按各自置信度加权。避开:与红外遥控(38kHz 调制的 940nm LED!)同孔径近距离安装——遥控器按键瞬间可能给 ToF 一帧"神秘短距",安装位置错开 5cm+。
Q10:极致省电的时序表怎么排? 以伴身卡(CR2032 目标 8 个月)为例的完整时序:常态——PIR 中断唤醒(μA 睡眠)+ MCU 每 30s 秒表醒(保持状态机);PIR 触发——0ms 醒,+20ms 读 ToF 单帧(快糙档)确认,确认过 +100ms 上报(BLE/MQTT),未过直接回睡;交互态(确认后 30s 内)——ToF 降到 1Hz 轮询维持存在,30s 无交互回常态。时序表的本质:每个传感器只在"它的事件窗口"醒来——PIR 永远在岗(它便宜)、ToF 只在事件里存在(它贵)、射频只带结论(它最贵)。级联是功耗架构,不只是误报架构。
Q11:多区 ToF 能替代触控吗? 近场(<15cm)能,且有"非接触"优势(14 篇触摸的互补):挥手/捏合/悬停按——多区质心轨迹可分类(本篇 6.5)。但精度天花板低:分区分辨率(8~64 区)比触摸屏(千点级)差两个量级,"划字/点按"做不到,只适合手势命令(粗粒度、方向性)。产品定位:多区 ToF 是"免接触交互"(厨房油手/医院无菌),不是"触摸替代"——两者并存(触摸为主,手势为辅)是正解。
Q12:户外产品的传感器维护策略怎么做? 三件套(案例九的工程化):自检——幅值基线监控(脏污告警)+ 温漂自校(季度)+ 注入测试(年度);通知——健康事件上云(HS_DIRTY 触发"请清洁镜头"的远程工单,比现场巡检省 90% 成本);冗余——关键探测点双传感器异构冗余(ToF+毫米波,一方脏污/强光失效时另一方兜底)。成本账:一套健康自检的开发成本 ≈ 两次现场巡检;户外部署规模上去后,"传感器健康运维"是从成本中心变省钱中心的典型设计。
13. 小结
核心要点:PIR 是"1 元的常开哨兵"(只检动不检静,上电静默期是物理属性)、ToF 是"会话式测距"(严禁常开,档位是场景函数);iToF/dToF 的物理分野(相位模糊 vs 首峰多径免疫);盖玻片串扰校准是量产质量项(换外观件必重校);级联确认(PIR 触发→ToF 精判)是功耗与误报的双重治理;阈值迟滞 + 中值滤波 + 状态语义化(而非原始数值)是传感层三纪律;多 ToF 同场要分时防串扰;静止存在场景升级毫米波(物理极限告知产品)。
学习路径
-
BISS0001 类模块 + GPIO 中断 + 去抖(示波器看 RC 波形)→ 2. ToF I2C 读距、档位与中值滤波 → 3. 实现级联状态机(触发/维持/退场/误报抑制)→ 4. 盖玻片串扰校准流程 → 5. Linux IIO/libgpiod 移植 → 6. 与毫米波/摄像头组成三级级联。
术语速查
-
ToF/iToF/dToF:飞行时间/间接(相位差)/直接(SPAD 计时)测距;SPAD:单光子雪崩二极管;VCSEL:垂直腔面发射激光器(940nm);TDC:时间数字转换器;菲涅尔透镜:PIR 分束聚光透镜;双元差分:PIR 元件的共模抑制结构;BISS0001:PIR 专用信号 IC 语义;Timing Budget:ToF 单次测量时序预算;串扰(Xtalk):盖玻片反射测距偏差;幕帘式透镜:垂直窄带探测透镜;IIO:Linux 工业 I/O 子系统;GPIOTE:Nordic 低功耗 GPIO 中断;wake-up 传感器:Android 可唤醒 AP 的传感器路径;SMD:显著动作检测(DSP 常开域)。
工程师自检清单
- ToF 是否会话式(用完即关,禁常开)?档位是否按场景切换(跟随快/判定准)?
- PIR 是否中断驱动 + 上电 60s 静默 + 脉冲去抖 + RC 只展宽不维持状态?
- 业务是否消费状态(迟滞语义)而非原始毫米数?
- 盖玻片串扰校准是否入产线(治具+2s+复测验证)?换外观件是否重跑校准?
- PIR 安装是否避开热源直视(暖气/西晒)?人宠区分是否几何分立?
- 多 ToF 同场是否分时触发(产线/对装场景)?
- 误报是否级联确认 + 遥测统计(热气流/宠物分类)?
- 静止存在场景是否已升级毫米波方案(PIR 局限告知产品)?
- 确认策略是否按业务分级(安全跳过/体验要确认)?
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)