嵌入式面试真题第 04 题:资源受限设备中的低功耗短时事件识别架构

在这里插入图片描述

问题

在一款资源受限的嵌入式产品中,业务希望通过一个低成本、低维度、低功耗传感器识别某类短时用户意图或异常事件,例如敲击外壳、碰撞、低功耗唤醒、防拆振动、佩戴交互或机械冲击。但硬件受成本、体积和功耗限制,不能增加更高维度传感器、专用 DSP 或复杂边缘 AI 芯片。

这类事件通常持续时间很短,背景干扰又很多。例如跑步、咀嚼、走路、跌落轻碰、桌面摩擦、线缆拉扯或结构共振,都可能在传感器上产生相似波形。你会如何在 MCU 上设计一套极低功耗的短时事件识别算法,使系统尽量降低误触和漏检,并把“完美屏蔽干扰”这类不可验证需求转化为可落地的工程指标?

具体到本题,目标事件可以实例化为“使用普通单轴加速度计识别双击设备外壳接听电话”。

回答

结论:这类问题不能抽象成“某个传感器值超过固定阈值就触发事件”。更合理的通用设计是“低功耗粗筛 + 短窗口精采样 + 轻量信号处理 + 特征提取 + 状态机/小分类器确认 + 干扰抑制 + 业务门控 + 置信度输出”的事件识别管线。

这类问题的关键不是追求一个孤立算法,而是把传感器信息量、MCU 算力、功耗预算、目标事件时序、干扰分布和业务触发范围一起设计。低维传感器只能观测真实世界事件的一部分投影。如果干扰事件在该观测维度上的短时波形与目标事件足够相似,算法没有额外信息可用于数学意义上的完美区分。因此工程回答必须明确边界:可以通过多级过滤和业务约束把误触率压到可接受范围,但不能承诺“完美屏蔽”。

推荐的通用实现步骤如下。

  1. 低功耗粗筛:传感器或外设长期工作在低功耗监听模式,只检测粗阈值、活动变化、边沿、能量突增或 wake-on-motion。MCU 大部分时间休眠。
  2. 短窗口精采样:粗筛触发后,MCU 在很短时间内提高采样率,抓取目标事件前后波形,而不是持续高频采样。
  3. 预处理与特征提取:对窗口数据做去直流、去慢变、限幅、高通/带通或滑动能量计算,再提取峰值、峰宽、能量、频带比例、过零率、衰减时间、事件间隔和持续时间。
  4. 状态机或小分类器确认:用时序状态机确认“候选事件是否符合目标事件结构”;数据足够时,可用小型逻辑回归、决策树或查表分类器替代部分人工规则。
  5. 干扰抑制:识别持续性、周期性、低频占比高、峰数量异常、衰减过慢或背景活动过强的干扰窗口,并拒绝触发。
  6. 自适应阈值:根据背景噪声、安装差异、用户使用方式和运动状态动态更新基线,使用 baseline + margin,不要依赖全场景固定阈值。
  7. 业务门控:只在业务允许的状态下打开高灵敏识别;非目标业务状态下降低灵敏度、延长冷却时间或关闭该识别路径。
  8. 指标化验收:把“完美屏蔽”改写为检出率、误触率、唤醒频率、平均功耗、响应延迟和边界场景覆盖率。

一句话概括:低功耗外设负责粗筛,MCU 只在短窗口内做精筛,状态机约束事件时序,干扰模型拒绝高风险样本,业务状态限制触发范围,最终输出置信度而不是绝对真值。

本题如何落回单轴双击识别

如果把上述通用问题实例化为“单轴加速度计双击接听电话”,各模块可以对应如下。

通用模块 单轴双击识别中的落地方式
低功耗粗筛 加速度计低功耗阈值中断或 wake-on-motion 唤醒 MCU。
短窗口精采样 中断后临时提高 ODR,采集 100ms 到 300ms 的冲击波形。
特征提取 计算峰值、峰宽、短时能量、过零率、衰减时间、峰间隔和峰数量。
状态机确认 Idle -> FirstTap -> WaitSecondTap -> Confirm/Reject,第二击必须落在合理时间窗内。
干扰抑制 跑步看周期性、低频能量和持续时间;咀嚼看节律、连续峰数量和背景活动强度。
自适应阈值 根据佩戴松紧、外壳结构、环境噪声和运动评分更新触发阈值。
业务门控 只在来电或可接听状态下启用高灵敏双击识别;运动模式或非电话场景降敏。
验收指标 用双击检出率、跑步/咀嚼误触率、平均电流和响应延迟验收,而不是承诺绝对无误触。

因此,原题不是单纯考“单轴加速度计怎么识别双击”,而是在考“低成本传感器 + 低功耗 MCU + 强干扰场景下,如何设计一个可验收的事件识别系统”。

总体架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
flowchart LR
A[单轴加速度计
低功耗活动检测] -->|粗阈值中断| B[MCU 唤醒]
B --> C[短窗口高频采样
100ms ~ 300ms]
C --> D[预处理
去直流/高通/限幅]
D --> E[特征提取
峰值/峰宽/能量/过零率/衰减]
E --> F{场景抑制
跑步/咀嚼/连续振动}
F -->|疑似干扰| G[Reject]
F -->|候选敲击| H[双击状态机]
H --> I{业务门控
来电/可接听/冷却时间}
I -->|允许| J[输出 Double Tap 事件]
I -->|不允许| G
J --> K[进入 refractory 时间]
G --> L[回到低功耗监听]
K --> L

这个架构里,低功耗传感器中断只是候选事件入口,不直接等于双击事件。MCU 被唤醒后也不应长时间连续采样,而是只采一段足够覆盖敲击波形和第二击等待窗口的数据。真正的双击判断由短窗口波形特征、状态机、干扰抑制和业务上下文共同完成。

通用机制与芯片/开源实现的对应关系

通用机制 可参考机制 能否直接使用 主要参考价值
低功耗唤醒 活动检测、wake-up interrupt、wake-on-motion 如果传感器支持可直接使用;普通单轴传感器至少要用阈值中断 让 MCU 长时间休眠,只在候选振动出现时唤醒。
短窗口采样 传感器高 ODR、FIFO、数据就绪中断 取决于具体传感器;没有 FIFO 时由 MCU 定时采样 中断后临时提高采样率,避免长期高功耗。
单/双击时序 tap threshold、quiet time、latency window 高端加速度计可能内置;普通单轴传感器需要 MCU 实现 双击不是两个过阈值点,而是峰形、静默时间和第二击窗口组合。
轻量滤波 IIR/FIR、高通、滑动能量、峰值检测 MCU 上直接实现;Cortex-M 可参考 CMSIS-DSP 去除姿态慢变和低频运动,只保留敲击相关瞬态。
干扰抑制 活动识别、步态周期、连续振动检测 单轴上只能近似实现 跑步和咀嚼多为持续性或周期性振动,不能只看瞬时峰值。
业务门控 来电态、佩戴态、运动态、冷却时间 必须结合产品业务实现 把算法工作范围限制到真正需要触发的场景。

很多三轴加速度计已经内置 tap/double tap、活动检测、FIFO 或自动低功耗切换功能;本题的限制是“最普通的单轴传感器”,所以要在 MCU 上软件化实现类似能力。参考这些芯片特性不是为了假设硬件已经具备功能,而是为了理解成熟实现通常会同时配置阈值、静默时间、最大双击间隔、唤醒中断和功耗状态切换。

低功耗入口如何设计

平时系统应处于粗筛状态,而不是一直高频采样。

1
2
3
4
5
6
7
8
9
10
stateDiagram-v2
[*] --> SleepListen
SleepListen --> WakeCapture: 传感器粗阈值中断
WakeCapture --> Analyze: 采样窗口结束
Analyze --> WaitSecondTap: 第一击成立
Analyze --> SleepListen: 拒绝
WaitSecondTap --> Confirm: 第二击成立
WaitSecondTap --> SleepListen: 超时或干扰
Confirm --> Refractory
Refractory --> SleepListen: 冷却结束

低功耗入口建议分成三层。

层级 动作 目的
传感器低功耗层 配置低 ODR、阈值中断、活动检测或 wake-on-motion 让传感器承担最低成本的粗筛。
MCU 睡眠层 MCU 进入 Stop/Standby/Light Sleep,只保留 GPIO/EXTI/RTC 降低常驻功耗。
临时精筛层 中断后提高采样率并开启短窗口算法 只在候选事件出现后消耗算力。

传感器如果支持活动/非活动联动,可以让传感器在低功耗模式下检测活动,并通过中断唤醒 MCU。若传感器只提供最简单的模拟或数字数据输出,没有活动中断能力,则需要用更低频的 RTC 周期采样做粗筛,但功耗会更高,响应也会更差。

采样窗口如何设计

敲击外壳通常是短时冲击响应。它的典型表现不是持续大幅运动,而是一个陡峭峰值后接快速衰减的振铃。因此采样窗口要覆盖峰值、峰宽、衰减和双击间隔。

建议参数如下,实际需要按产品结构、传感器安装位置和外壳材料实测调整。

参数 建议范围 说明
粗筛 ODR 6Hz ~ 50Hz 只用于唤醒候选事件,越低越省电但越容易错过短冲击。
精筛 ODR 200Hz ~ 1000Hz 覆盖敲击瞬态和振铃;外壳刚性越强,所需频率越高。
单击分析窗口 40ms ~ 120ms 判断峰值、峰宽、衰减和静默时间。
双击等待窗口 80ms ~ 350ms 第二击太近可能是一击振铃,太远用户感知不像双击。
冷却时间 500ms ~ 1500ms 防止同一次动作或连续振动重复触发。

短窗口策略的功耗优势在于:高频采样不是常态,而是中断后的短时行为。平均功耗可以用以下方式估算:

1
2
3
I_avg = I_sensor_lp
+ N_wake_per_s * (I_sensor_hr * T_capture + I_mcu_active * T_compute)
+ I_sleep_leakage

其中 N_wake_per_s 是每秒候选唤醒次数。误唤醒越多,平均功耗越高,所以粗筛阈值不能过低。粗筛阈值的目标不是尽量灵敏,而是在漏检和唤醒频率之间取得平衡。

预处理与特征提取

单轴数据可以记为 x[n]。预处理目标是去掉静态重力投影、佩戴姿态慢变和低频运动,只保留敲击瞬态。

推荐处理顺序如下。

1
2
3
4
5
6
raw x[n]
-> 去直流/慢变基线
-> 高通或带通滤波
-> 限幅与异常点处理
-> 峰值检测
-> 特征计算

一个低成本高通可以用一阶 IIR 实现:

1
2
baseline[n] = baseline[n-1] + alpha * (x[n] - baseline[n-1])
x_hp[n] = x[n] - baseline[n]

其中 alpha 要足够小,使 baseline 跟踪姿态和慢变运动,而不吞掉敲击瞬态。若 MCU 有足够余量,也可以用短阶 FIR/IIR 带通,把低频步态和高频噪声压掉。

建议提取以下特征。

特征 含义 对误触抑制的价值
peak_abs 窗口内最大绝对峰值 粗略判断是否有冲击。
peak_width 超过阈值的持续时间 敲击通常窄;挤压、晃动通常宽。
energy_short 短窗口能量 判断冲击强度。
energy_low_ratio 低频能量占比 跑步和晃动低频占比通常更高。
zero_cross_rate 过零次数或符号翻转次数 敲击振铃可能更明显;慢变运动较少。
decay_time 峰后能量衰减时间 外壳敲击通常快速衰减;持续振动衰减慢。
peak_count 时间窗内候选峰数量 跑步、咀嚼可能出现连续节律峰。
inter_tap_gap 两次敲击间隔 双击必须落在合理时间窗内。

不要用单一峰值阈值替代这些特征。单轴传感器在跑步落地、线缆碰撞、牙齿咬合或外壳刮擦时都可能出现大峰值;峰形、时序和上下文比峰值本身更重要。

单击候选如何判定

单击候选可以用硬门限加评分的方式判断。硬门限用于快速拒绝明显不符合的波形,评分用于处理边界样本。

1
2
3
4
5
6
7
is_tap_candidate =
peak_abs >= tap_peak_th &&
peak_width <= tap_width_max &&
energy_short >= tap_energy_th &&
decay_time <= tap_decay_max &&
low_ratio <= low_ratio_max &&
peak_count <= peak_count_max

然后再计算置信度:

1
2
3
4
5
6
score = w1 * norm_peak
+ w2 * norm_energy
+ w3 * norm_decay
+ w4 * norm_zcr
- w5 * norm_low_ratio
- w6 * norm_periodic_score

如果产品没有足够训练数据,优先使用规则状态机,不要一开始就上复杂模型。等产测和用户测试积累了足够样本后,可以把规则特征输入到一个很小的逻辑回归、决策树或查表分类器中。低功耗 MCU 上不建议把问题一开始就设计成大模型推理。

双击状态机如何设计

双击不是两个峰值过阈值这么简单。第一击后需要有静默约束,第二击需要落在合理间隔内,并且两次峰形都要像敲击。

1
2
3
4
5
6
7
8
9
10
stateDiagram-v2
[*] --> Idle
Idle --> FirstTap: tap_candidate && context_ok
FirstTap --> WaitSecondTap: quiet_ok
FirstTap --> Reject: quiet_fail / ringing_too_long
WaitSecondTap --> Confirm: second_tap && gap_ok && score_ok
WaitSecondTap --> Reject: timeout / motion_suppress / context_fail
Confirm --> Refractory
Reject --> Idle
Refractory --> Idle: refractory_timeout

建议状态参数如下。

参数 典型范围 作用
tap_width_max 10ms ~ 60ms 限制单击候选的冲击持续时间。
quiet_min 30ms ~ 80ms 第一击后必须有短静默,避免把振铃当第二击。
gap_min 80ms ~ 120ms 第二击不能太近。
gap_max 250ms ~ 450ms 第二击不能太远。
score_confirm_th 按实测标定 双击确认阈值。
refractory_ms 500ms ~ 1500ms 触发后冷却,避免重复识别。

状态机必须把 reject 原因记录下来,例如 REJECT_TOO_LONGREJECT_PERIODICREJECT_LOW_FREQREJECT_CONTEXTREJECT_TIMEOUT。这些原因对后续调参非常重要,否则现场误触只能看到一个失败结果,无法定位是阈值、状态机还是业务门控问题。

如何抑制跑步误触

跑步误触的典型问题是周期性冲击。落地冲击可能在单轴上产生很高峰值,但跑步不是孤立双击,而是连续、周期性、持续时间较长的振动序列。

抑制策略如下。

检测项 跑步特征 拒绝策略
周期性 300ms ~ 900ms 左右重复出现峰值,具体与步频有关 维护 1s ~ 3s 活动窗口,若峰值周期稳定则提高阈值或禁用双击。
持续能量 能量不是一次快速衰减,而是持续存在 若连续多个窗口能量高于背景,进入 motion_suppress
低频占比 步态低频成分更强 energy_low_ratio 超阈值时拒绝。
峰数量 一个等待窗口内可能出现多个非敲击峰 peak_count 超过上限时拒绝。
业务状态 跑步模式、运动检测、用户正在移动 由业务状态提高门槛或关闭该手势。

工程上可以维护一个活动评分:

1
2
3
4
motion_score = a * low_freq_energy
+ b * periodic_peak_score
+ c * continuous_energy_time
+ d * wake_frequency

motion_score 高于阈值时,不是直接永久关闭识别,而是进入抑制状态:提高 tap_peak_th、缩短等待窗口、要求更高静默质量,或者仅在来电前几秒内开启识别。

如何抑制咀嚼误触

咀嚼误触在耳机、眼镜、头戴或贴近头部的设备上更常见。咀嚼的振动可能沿结构传到外壳,单轴上也可能出现明显峰值。但咀嚼通常具有持续性和节律性,不像两次孤立敲击后快速静默。

抑制策略如下。

检测项 咀嚼特征 拒绝策略
持续时间 往往持续数秒,而不是 300ms 内结束 连续活动时间超过阈值时进入抑制。
节律性 多个相似峰按节律出现 peak_count 和周期稳定性超阈值时拒绝。
峰形 可能较宽、衰减慢,或伴随多个小峰 限制 peak_widthdecay_time 和峰后静默。
触发上下文 非来电状态下误触价值低 非电话可接听状态关闭高灵敏度识别。

需要注意,咀嚼和轻敲在某些结构上可能高度相似。单轴加速度计无法可靠识别“用户意图”,只能识别波形模式。因此文档、需求评审和测试报告里不要写“完全屏蔽咀嚼”,应写“在定义测试集和业务场景下,误触率低于目标阈值”。

自适应阈值如何做

固定阈值的问题是样机、量产机、佩戴松紧、结构胶、外壳材料和个体习惯都会改变波形幅值。阈值应随背景噪声和场景调整。

建议维护以下统计量。

1
2
3
4
5
noise_floor      = 背景高通能量的滑动估计
peak_baseline = 非触发窗口峰值的分位数估计
wake_rate = 单位时间粗筛唤醒次数
motion_score = 当前运动干扰评分
user_sensitivity = 用户或产品档位

阈值可以写成:

1
2
3
tap_peak_th   = max(factory_min_peak, peak_baseline + k1 * noise_floor + k2 * motion_score)
tap_energy_th = max(factory_min_energy, energy_baseline + k3 * noise_floor)
score_th = base_score_th + k4 * motion_score

自适应要有两个限制。

  1. 只在非候选敲击窗口更新背景,避免把真正敲击学习成噪声。
  2. 设置上下限,避免阈值越调越高导致漏检,或越调越低导致误触。

如果业务允许,可以把灵敏度分成高、中、低三档。来电态用中高灵敏度,运动态用低灵敏度,非电话态关闭或只保留很粗的事件记录。

业务门控必须参与识别

双击接听电话不是通用常开手势,而是强业务相关事件。业务门控越严格,误触越容易控制。

推荐门控条件如下。

业务状态 策略
无来电、非通话、非可接听 不输出接听事件;可不启用高灵敏度识别。
来电响铃 开启高灵敏度双击识别。
已接通通话 根据产品定义切换为挂断、静音或关闭该手势。
音乐播放 若双击已有其他语义,避免复用造成冲突。
运动模式或高活动评分 提高阈值,要求更严格静默,必要时关闭。
刚触发过事件 进入 refractory 时间,不重复输出。

很多误触不是算法本身完全解决的,而是靠业务状态降低损失。例如用户跑步时没有来电,算法即使检测到疑似双击,也不应触发接听动作。把手势限定在“来电可接听”这一窄场景,误触风险会显著下降。

MCU 实现建议

MCU 上建议使用定点实现,避免浮点和复杂频域计算。短窗口样本数很少,规则特征足够高效。

数据结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#define TAP_MAX_SAMPLES        256U
#define TAP_FEATURE_Q 15

typedef enum {
TAP_STATE_IDLE = 0,
TAP_STATE_FIRST_TAP,
TAP_STATE_WAIT_SECOND,
TAP_STATE_CONFIRMED,
TAP_STATE_REFRACTORY,
} tap_state_t;

typedef struct {
int16_t peak_abs;
uint16_t peak_width_ms;
uint32_t energy_short;
uint16_t zero_cross_count;
uint16_t decay_time_ms;
uint16_t peak_count;
uint16_t low_energy_ratio_q15;
uint16_t score_q15;
} tap_feature_t;

typedef struct {
tap_state_t state;
uint32_t first_tap_time_ms;
uint32_t refractory_until_ms;
int32_t baseline;
uint32_t noise_floor;
uint16_t motion_score_q15;
} tap_detector_t;

处理流程

1
2
3
4
5
6
7
8
9
10
11
12
accel_irq_handler()
-> 记录唤醒时间
-> 启动高频采样定时器或传感器高 ODR
-> 采满短窗口后投递 TAP_PROCESS 事件

tap_process_window(samples)
-> preprocess()
-> extract_feature()
-> update_motion_score()
-> tap_state_machine()
-> 输出 NONE / DOUBLE_TAP
-> 恢复低功耗监听

计算复杂度

若窗口为 256 点,每点只做一阶滤波、能量累加、峰值检测和少量比较,则一次识别通常是 O(N) 复杂度。对 Cortex-M0/M3/M4 这类 MCU 来说,这比持续 FFT 或神经网络推理更适合低功耗场景。

参数如何量化

参数不能只凭感觉设置,应先由产品目标反推。

1
2
3
4
P_false_target = 允许误触率,例如每用户每天小于 1 次
P_miss_target = 允许漏检率,例如标准敲击样本检出率大于 95%
T_response = 用户可接受响应延迟,例如小于 500ms
I_budget = 常驻平均电流预算

然后建立测试集。

数据集 内容 用途
正样本 不同用户、不同力度、不同位置、不同佩戴状态下的双击 标定检出率和双击时间窗。
跑步负样本 慢跑、快跑、上下楼、跳跃、手持晃动 标定运动抑制策略。
咀嚼负样本 轻咀嚼、用力咀嚼、说话、大笑、头部动作 标定持续节律干扰。
结构干扰 桌面碰撞、外壳摩擦、线缆拉扯、跌落轻碰 标定峰宽、衰减和冷却时间。
业务场景 来电、非来电、通话中、音乐中、运动模式 验证业务门控。

评价指标至少包括检出率、误触率、唤醒频率、平均功耗和响应延迟。只看识别准确率是不够的,因为低功耗识别系统的失败可能表现为功耗超标、响应慢或频繁误唤醒。

为什么不能承诺完美屏蔽

原因很直接:单轴传感器只输出一个方向上的加速度投影,它没有三维方向信息,也没有声音、压力、电容触摸或结构应变信息。如果干扰在该轴上的时间波形与真实敲击足够接近,算法没有额外信息可用。

可以做到的是:

  1. 用波形特征降低非敲击冲击误判。
  2. 用状态机降低振铃和单击误判。
  3. 用运动抑制降低跑步、咀嚼等持续节律振动误判。
  4. 用业务门控降低误触造成业务动作的概率。
  5. 用测试数据定义误触率目标,而不是承诺绝对无误触。

需求评审时应把“完美屏蔽”改写为可验证指标,例如:

1
2
3
4
在定义佩戴方式和测试动作集合下,来电态双击检出率 >= 95%;
跑步、咀嚼、说话、日常走动负样本误触率 <= X 次/小时;
常驻平均电流增加 <= Y uA;
双击触发响应延迟 <= Z ms。

这比“完美屏蔽”更适合工程验收。

调试与量产验证

建议固件保留轻量日志能力,但不要长期记录原始高频数据。量产固件可只记录特征和 reject reason。

1
2
3
4
5
6
7
8
9
10
11
12
13
timestamp
wake_reason
peak_abs
peak_width_ms
energy_short
low_energy_ratio
zero_cross_count
decay_time_ms
peak_count
motion_score
state
reject_reason
business_context

调试阶段可以通过串口、RTT、BLE debug 或离线日志导出少量原始窗口数据。量产阶段只保留统计计数,避免功耗、存储和隐私风险。

参数更新建议采用灰度策略。先在实验室和小批量用户中收集误触与漏检样本,再更新阈值表或固件参数。不要只凭单个开发板上的手敲效果就固化量产阈值。

最终回答组织方式

回答这类题时,可以按以下顺序展开:

  1. 先把问题抽象为“资源受限设备中的低功耗短时事件识别”,而不是只讨论某个传感器阈值。
  2. 再说明约束边界:低维传感器信息不足,不能承诺数学意义上的完美屏蔽,只能工程化降低误触率。
  3. 接着讲低功耗架构:外设低功耗粗筛,MCU 短窗口精采样,避免长期高频运行。
  4. 然后讲信号处理:去直流、高通/带通、峰值、峰宽、能量、频带比例、过零率和衰减时间。
  5. 再讲事件确认:用状态机或小分类器约束目标事件的时序、形态和置信度。
  6. 补充干扰抑制:持续性、周期性、低频占比高或背景活动过强的窗口优先拒绝。
  7. 最后讲业务门控和验收指标:只在业务相关状态下启用高灵敏识别,用检出率、误触率、功耗和延迟验收。

一句话概括:这类题的通用答案不是“某个动作怎么判”,而是“如何用弱传感器在强约束下构建一条低功耗、可解释、可调参、可验收的短时事件识别管线”;单轴双击接听电话只是这个框架下的一个具体实例。

参考链接