espressif/esp_apa_cry_detection

1.0.0

Latest
uploaded 1 day ago
Infant cry detection from 16 kHz mono PCM on Espressif chips

Readme (zh)

# ESP APA Cry Detection

[English](README.md)

**esp_apa_cry_detection** 是乐鑫推出的轻量级婴儿哭声检测算法组件,可从连续 PCM 流中稳定识别哭声起止。检测器维护滑动音频窗口,按 RMS 能量门控静音帧,运行轻量 int8 CNN 分类,再经指数滑动平均(EMA)平滑和双阈值有限状态机(FSM)输出 START / END 事件。该组件可广泛应用于婴儿监护、智能音箱、看护摄像头、服务机器人及其他 AIoT 看护产品。

## 核心特性

- 16 kHz 单声道 16 位 PCM;窗 1000 ms、hop 150 ms
- 能量门控跳过静音窗,降低 CPU
- 轻量 int8 CNN 输出哭声概率
- EMA + 双阈值 FSM 输出稳定的 START / END,抑制短误触发
- 算法不含降噪、重采样或下混

**支持芯片**:ESP32 · ESP32-C3 · ESP32-C5 · ESP32-C6 · ESP32-C61 · ESP32-S3 · ESP32-S31 · ESP32-P4

---

## 配置

运行时选项在 `esp_apa_cry_detection_config_t`(`include/esp_apa_cry_detection.h`)中。以 `ESP_APA_CRY_DETECTION_DEFAULT_CONFIG()` 为基准,在 `esp_apa_cry_detection_open()` 前按板级需求覆盖。

对外只暴露灵敏度相关项:`gate` 用于静音窗跳过 CNN 以省 CPU;`fsm` 控制 START / END 触发难易。

### 能量门控 `gate`

CNN 前的预滤波:判断当前 1000 ms 窗口是否够响,**不负责**判定是否为哭声。

| 字段 | 默认值 | 说明 |
| ---- | ------ | ---- |
| `enable` | `true` | `true`:RMS 低于门限则跳过 CNN,向 FSM 送入概率 0.0;`false`:每个 hop 都跑 CNN |
| `energy_threshold` | `0.001` | RMS 门限,线性幅度(PCM 归一化到 `[-1, 1]`) |

提高门限可滤掉更多安静噪声;过低哭声被跳过时再降低。默认 `0.001` 可放过安静 / 远场哭声(`0.01` 偏严,容易漏检)。

### FSM / EMA `fsm`

CNN 原始概率先经 EMA 平滑,再经双阈值 FSM 确认,以抑制短误触发,并容忍哭声过程中的短暂停顿。

- **START**:EMA 连续 `start_confirm_count` 个 hop 高于 `start_threshold`
- **END**:EMA 连续 `end_confirm_count` 个 hop 低于 `end_threshold`
- 须保持 `start_threshold` > `end_threshold`(滞回)
- `ema_alpha` 越大越平滑、越慢;越小越跟手、越易抖

hop = 150 ms 时,默认 4 / 6 hop 约对应 600 ms / 900 ms。

| 字段 | 默认值 | 说明 |
| ---- | ------ | ---- |
| `start_threshold` | `0.85` | 进入哭声的 EMA 概率门限,典型 0.7~0.9 |
| `end_threshold` | `0.35` | 退出哭声的 EMA 概率门限,典型 0.2~0.4,须小于 `start_threshold` |
| `start_confirm_count` | `4` | 连续高于进入门限的 hop 数后发出 START |
| `end_confirm_count` | `6` | 连续低于退出门限的 hop 数后发出 END |
| `ema_alpha` | `0.77` | EMA 历史权重,范围 `(0, 1]` |

### 固定参数

| 项 | 取值 |
| -- | ---- |
| 采样率 / 声道 / 位深 | 16 kHz / 单声道 / int16 |
| CNN 窗 | 1000 ms(16000 点) |
| 推理 hop | 150 ms(2400 点) |
| 环形缓冲 | 2000 ms(32000 点) |

### 性能与内存(ESP32-P4)

条件:Function EV,CPU **400 MHz**。CPU-loading 按单次 hop 墙钟时间相对 **1000 ms 分析窗** 计算。

| 项 | 占用 |
| -- | ---- |
| CPU-loading | 5.72% |
| Stack | 3 KiB |
| PSRAM | 158.5 KiB |
| InnRAM | 0 KiB |
| Flash | 64.4 KiB |

本组件依赖 `espressif/gmf_fft` 做 Log-Mel 的 STFT。有 PSRAM 的芯片上,算法堆内存申请在 PSRAM。

---

## API 参考

头文件:`include/esp_apa_cry_detection.h`。

输入须为 **16 kHz 单声道 int16**。组件不做重采样、下混或位深转换。

### 生命周期

| 函数 | 说明 |
| ---- | ---- |
| `esp_apa_cry_detection_open(cfg, &handle)` | 分配检测器;失败时 `*handle = NULL`。不创建内部任务 |
| `esp_apa_cry_detection_process(h, pcm, samples, &status)` | 喂入 PCM;凑满 hop 时推理并更新 `status` |
| `esp_apa_cry_detection_close(h)` | 释放全部内部资源 |

`samples` 为 `data` 中的采样点数。同一 handle **非 ISR 安全、非线程安全**。

### 检测器状态:`esp_apa_cry_detection_state_t`

| 值 | 含义 |
| -- | ---- |
| `ESP_APA_CRY_DETECTION_STATE_IDLE` | 空闲,无哭声活动 |
| `ESP_APA_CRY_DETECTION_STATE_DETECTING` | EMA 接近进入门限,尚未确认 |
| `ESP_APA_CRY_DETECTION_STATE_CRYING` | 已确认处于哭声状态 |

### 一次性事件:`esp_apa_cry_detection_event_t`

| 值 | 含义 |
| -- | ---- |
| `ESP_APA_CRY_DETECTION_EVENT_NONE` | 本次 `process()` 无状态跳变 |
| `ESP_APA_CRY_DETECTION_EVENT_START` | 进入 `CRYING` |
| `ESP_APA_CRY_DETECTION_EVENT_END` | 退出 `CRYING` |

产品逻辑应订阅 **event**,而不是每 hop 轮询 `cry_prob`。

### 结果:`esp_apa_cry_detection_status_t`

| 字段 | 类型 | 含义 |
| ---- | ---- | ---- |
| `valid` | `bool` | 字段是否有意义 |
| `audio_active` | `bool` | 能量门控是否打开(即将跑 / 已跑 CNN) |
| `state` | `esp_apa_cry_detection_state_t` | 当前检测器状态 |
| `cry_prob` | `float` | 最近一次原始 P(cry),范围 `[0, 1]` |
| `ema_prob` | `float` | EMA 平滑后的 P(cry),范围 `[0, 1]` |
| `event` | `esp_apa_cry_detection_event_t` | 本次调用中最后一次 START / END;无 hop 则为 `NONE` |
| `inference_count` | `uint32_t` | 已完成的推理 hop 次数 |

无 hop 时 `event` 为 `NONE`,`cry_prob` / `ema_prob` / `state` 保留上一拍快照。一次 `process()` 若跑完多个 hop,`event` 取其中最后一次 START / END。

---

## 示例

实时麦克风演示:[`examples/infant_cry_detection`](examples/infant_cry_detection/)。例程采集 PCM,经独立 `esp_ns`(非 AFE)后按 50 ms 调用 `esp_apa_cry_detection_process()`,在串口打印哭声 START / END。换板请先执行 `idf.py bmgr`(见 [例程 README](examples/infant_cry_detection/README_CN.md))。

```c
#include "esp_apa_cry_detection.h"

esp_apa_cry_detection_config_t cfg = ESP_APA_CRY_DETECTION_DEFAULT_CONFIG();
esp_apa_cry_detection_handle_t cry = NULL;
ESP_ERROR_CHECK(esp_apa_cry_detection_open(&cfg, &cry));

int16_t pcm[800];
esp_apa_cry_detection_status_t st;

while (/* 送入 16 kHz 单声道 PCM */) {
    ESP_ERROR_CHECK(esp_apa_cry_detection_process(cry, pcm, 800, &st));
    if (st.event == ESP_APA_CRY_DETECTION_EVENT_START) {
        /* 哭声开始 */
    } else if (st.event == ESP_APA_CRY_DETECTION_EVENT_END) {
        /* 哭声结束 */
    }
}

ESP_ERROR_CHECK(esp_apa_cry_detection_close(cry));
```

---

## FAQ

### 为何一直没有 START?

按下面顺序排查:

| 情况 | 说明 |
| ---- | ---- |
| 窗尚未填满 | 需先积累约 1000 ms,之后每 150 ms 才有一次推理 |
| `audio_active == false` | 能量门过严,CNN 被跳过、概率被置 0;降低 `gate.energy_threshold` 或设 `gate.enable = false` 对照 |
| `ema_prob` 已高但无 START | 确认计数不够;减小 `start_confirm_count` 或略降 `start_threshold` |
| 输入不是 16 kHz 单声道 int16 | 组件不做重采样 / 下混,格式不对会几乎检不到 |

### 为何误触发太多?

提高 `start_threshold`、增大 `start_confirm_count`,或略提高 `gate.energy_threshold` 滤掉环境噪声。`ema_alpha` 略增大可使概率更稳、更不容易被短噪声拉过门限。

### 哭声中间短暂停顿就 END 了?

这是退出滞回偏紧。增大 `end_confirm_count`(默认 6 hop ≈ 900 ms)或略降 `end_threshold`,让短暂停顿仍保持 `CRYING`。

### 一次 `process()` 喂很大一块会怎样?

可以。内部按 hop 边界切窗:每个 hop 使用以该 hop 边界为终点的 1000 ms 窗口。`status->event` 只保留本调用中**最后一次** START / END;`inference_count` 会累加本次完成的 hop 数。

### 需要自己建任务吗?

需要。`open()` 不创建 FreeRTOS 任务。在采集任务里按块调用 `process()` 即可,注意 API 非线程安全,不要多任务同时操作同一 handle。

### 如何按自己的场景调参?

字段以 `ESP_APA_CRY_DETECTION_DEFAULT_CONFIG()` 为起点,在 `open()` 前覆盖,不要改库内默认值。

| 现象 | 改什么 | 建议 |
| ---- | ------ | ---- |
| 远场 / 小声哭漏检,且 `audio_active` 常为 false | `gate.energy_threshold` | 降低(默认 0.001;勿轻易升回 0.01) |
| 环境噪声频繁 START | `start_threshold`、`start_confirm_count`、`gate.energy_threshold` | 提高进入门限或确认 hop;必要时略提高能量门 |
| START 太慢 | `start_confirm_count`、`ema_alpha` | 减少确认 hop(延迟约 `count × 150 ms`);略降 `ema_alpha` |
| 一句哭声被切成多段 | `end_confirm_count`、`end_threshold` | 增大退出确认;略降退出门限 |
| CPU 偏高 | `gate.enable` | 保持门控开启,让静音 hop 跳过 CNN |

```c
esp_apa_cry_detection_config_t cfg = ESP_APA_CRY_DETECTION_DEFAULT_CONFIG();
cfg.gate.energy_threshold    = 0.001f;  /* 远场漏检再降低;噪声误触发再升高 */
cfg.fsm.start_threshold      = 0.85f;
cfg.fsm.end_threshold        = 0.35f;
cfg.fsm.start_confirm_count  = 4;       /* ≈ 600 ms */
cfg.fsm.end_confirm_count    = 6;       /* ≈ 900 ms */
cfg.fsm.ema_alpha            = 0.77f;
ESP_ERROR_CHECK(esp_apa_cry_detection_open(&cfg, &cry));
```

Links

To add this component to your project, run:

idf.py add-dependency "espressif/esp_apa_cry_detection^1.0.0"

download archive

Stats

  • Archive size
    Archive size ~ 1.46 MB
  • Downloaded in total
    Downloaded in total 0 times
  • Downloaded this version
    This version: 0 times

Badge

espressif/esp_apa_cry_detection version: 1.0.0
|