← 返回博客

鼾声识别:嵌入式离线鼾声检测 SDK 与睡眠健康数据新增

鼾声识别的价值(场景、流程)

全球约三分之一的人口睡眠中打鼾,中年男性中比例超过 50%。鼾声不只是"吵"——它可能是单纯性打鼾、上气道阻力综合征,甚至睡眠呼吸暂停(SAS/OSAS)的预警信号,后者影响着全球数亿人的健康。对设备厂商来说,鼾声识别是睡眠产品从"记录睡眠"走向"理解睡眠"的关键一步。

三类需求场景:

医疗健康监测 — ——鼾声特征结合心率变异性(HRV)与血氧饱和度(SpO₂)数据,辅助呼吸暂停事件筛查。记录的关键指标包括:鼾声指数(每小时次数)、单次持续时间、声强分布、频率特征,并生成专业睡眠质量报告供医生诊断参考

智能家居 — ——智能床垫实时调床头角度;智能枕头内置充气装置调整睡姿;智能空调根据鼾声调节温度、湿度与新风量

移动应用 — ——仅凭一部手机即可低成本监测整夜鼾声,自动识别异常并在需要时接入专业医疗诊断流程

鼾声检测与干预流程
整夜采集、事件检测、严重度分级、报告与止鼾联动

完整的夜间闭环:持续采集 → 鼾声事件检测 → 整夜统计(时长/次数/占比)→ 严重程度分级 → 睡眠报告与(中度以上)止鼾干预。全部在设备端完成,音频不出设备。

鼾声的特点(时域与频域)

时域特征

与呼吸周期同步的周期性 — ——约 2–4 秒一个周期;吸气与呼气阶段能量分布不同

准周期包络 — ——相邻周期振幅变化平缓

单次持续 0.5–2 秒 — ——能量呈典型三段式:起始段快速上升 → 中间段相对稳定 → 结束段缓慢衰减

频域特征

基频约 80–120Hz — ——主要能量集中在 **100–850Hz** 频带,高频分量最高约 2000Hz

3–5 个明显谐波 — ——能量随频率升高逐级衰减,形成特征频谱包络

频带占比 — ——低频段 40–60%、中频段 30–40%、高频段不超过 10%

鼾声声学特征
鼾声(周期性爆发)与呼吸声、翻身声对比

下图为一条真实鼾声音频——上为波形图、下为梅尔频谱图,可以看到与呼吸同步的周期性事件及低频集中的能量分布:

真实鼾声:波形与频谱
真实鼾声示例:波形(上)+ 梅尔频谱(下)

试听素材(真实样本,点击播放):

🔊 真实鼾声样本

🔊 另一段鼾声样本

这些特征决定了检测思路:不是"听出像打鼾的声音",而是识别出一串与呼吸同步的事件。

识别方法:预处理 → 特征提取 → 算法

信号预处理

原始音频先降噪,再分帧处理:

帧长 25ms — ——保证信号的短时平稳性

帧移 10ms — ——确保帧间信息连续性

汉明窗 — ——减少频谱泄漏

特征提取

时域 — ——短时能量(强度变化)、过零率(频率特性)、自相关系数(周期性)

频域 — ——MFCC(模拟人耳听觉特性)、频谱质心(频率分布重心)、频带能量比(能量分布特征)

识别算法

引擎以"事件"为工作单位,而非逐帧标注:

1. 先定位事件——检测能量爆发,输出每次鼾声的起止时间与强度

2. 周期性验证——真正的鼾声成串出现、跟随呼吸节律;孤立的突发声(翻身、咳嗽、床架响声)不成序列,被排除

3. 个体泛化——训练覆盖年龄、性别、体型差异,同时抑制空调、风扇、雨声等持续背景噪声

难点所在

个体差异大;持续背景噪声长期存在;还需把鼾声与呼吸声、体动声、床架声区分开。事件化分析 + 针对性负样本训练是应对这三者的工程答案。

准确率与性能

项目
指标
准确率
96.5%
误报率
<2%
模型大小
0.2–1MB(INT8)
推理延迟
灵活可调(视平台资源)
采样率
16kHz
支持平台
ARM Linux / MIPS / x86_64;SVP / Magik 预适配

注:文中性能指标基于内部测试环境,实际表现取决于硬件配置与使用场景。

系统与数据呈现

端侧系统结构
采集、处理、引擎、输出、应用五层结构

引擎输出事件流(时间点与强度);应用层完成整夜统计、严重程度分级,并渲染睡眠报告。

整夜与周趋势数据
整夜事件时间线与周趋势(示意)

一份睡眠报告通常包含:整夜事件时间线、总时长与次数对照基线、周趋势——长期视角让"变化"变得有意义。

产品形态

睡眠场景与设备形态
床边监测仪、智能床垫、可穿戴与止鼾设备

床边监测仪(以声学为主、免接触)、智能床垫与智能枕头(睡眠数据 + 物理干预)、可穿戴设备(腕上初筛)、以及专用止鼾设备(实时检测驱动干预动作)。

运行平台与硬件要求

模型运行在睡眠产品常见的低功耗平台上——从 100MHz 主频级芯片起步,INT8 模型在 0.2–1MB 以内,与设备主业务共享 SoC 资源。

C 接口与嵌入式集成

鼾声识别库提供简洁的流式 C 接口:调用方只需持续送入 16kHz 单声道 PCM,拼帧、Mel 预处理与模型推理全部在内部完成,帧级概率经报警策略聚合成「鼾声事件」回调输出。

完整接口声明(snore_detect.h):

snore_detect.hc
/**
 * snore_detect.h — 鼾声识别统一接口
 *
 * 封装 Mel 预处理 + 推理引擎 + 报警策略, 内部模型消费线程处理音频。
 * 与录音模块 (audio_capture.h) 相互独立: 调用者自行决定音频来源
 * (录音回调 / wav 文件 / 网络流), 通过 snore_detect_feed 送入, 数据任意大小。
 *
 * 用法 (实时录音模式):
 *   snore_detect_t *d = snore_detect_create(mgk_path, NULL, NULL);
 *   snore_detect_set_listener(d, on_frame, on_onset, on_offset, NULL);
 *   snore_detect_start(d);                          // 启动内部模型消费线程
 *   audio_capture_start(rec, capture_cb, d);        // 录音回调里调 snore_detect_feed
 *   ...
 *   snore_detect_stop(d);                           // 排空缓冲, 停止线程
 *   snore_detect_destroy(d);
 *
 * 用法 (wav 文件模式):
 *   snore_detect_t *d = snore_detect_create(mgk_path, NULL, NULL);
 *   snore_detect_set_listener(d, on_frame, on_onset, on_offset, NULL);
 *   snore_detect_start(d);
 *   循环读文件: snore_detect_feed(d, pcm, n);       // 任意数据大小
 *   snore_detect_stop(d);
 *   snore_detect_destroy(d);
 */

#ifndef SNORE_DETECT_H
#define SNORE_DETECT_H

#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* 识别事件 (报警策略输出, 用于事件结束回调) */
typedef struct {
    float start_time;       /* 事件开始时间 (秒) */
    float end_time;         /* 事件结束时间 (秒) */
    float confidence;       /* 事件置信度 */
    float max_confidence;   /* 事件内最大帧置信度 */
    int   frame_count;      /* 事件持续帧数 */
} snore_detect_event_t;

/* 帧级回调: 每帧识别结果 (模型线程内执行) */
typedef void (*snore_detect_frame_cb_t)(float snore_prob, float timestamp,
                                        void *user_data);

/* 事件开始回调: 策略判定鼾声事件开始, 只有开始时间 */
typedef void (*snore_detect_onset_cb_t)(float start_time, void *user_data);

/* 事件结束回调: 策略判定鼾声事件结束 (或停止识别时未结束的事件), 完整事件信息 */
typedef void (*snore_detect_offset_cb_t)(const snore_detect_event_t *event,
                                         void *user_data);

typedef struct snore_detect_s snore_detect_t;

/* 创建/销毁; alarm_name/alarm_params 可传 NULL (用默认策略及参数) */
snore_detect_t *snore_detect_create(const char *mgk_path,      /* 模型文件路径 (必填) */
                                    const char *alarm_name,    /* 报警策略名, NULL=默认 */
                                    const char *alarm_params); /* 策略参数 key=val,key=val, NULL=默认 */
void snore_detect_destroy(snore_detect_t *det);

/* 设置事件回调 (create 后调用, 也可在运行中调整); 不需要的回调传 NULL */
void snore_detect_set_listener(snore_detect_t *det,
                               snore_detect_frame_cb_t  on_frame,
                               snore_detect_onset_cb_t  on_onset,
                               snore_detect_offset_cb_t on_offset,
                               void *user_data);

/* 启动/停止识别: 启动内部模型消费线程 / 排空缓冲后停止线程 */
int  snore_detect_start(snore_detect_t *det);
void snore_detect_stop(snore_detect_t *det);
int  snore_detect_is_running(snore_detect_t *det);

/* 设置事件识别策略 (可在运行中调整) */
int snore_detect_set_alarm(snore_detect_t *det, const char *alarm_name,
                           const char *alarm_params);

/* 送入 PCM 数据 (16bit 单声道 16kHz), 线程安全, 任意数据大小 */
int snore_detect_feed(snore_detect_t *det, const int16_t *pcm, int num_samples);

#ifdef __cplusplus
}
#endif

#endif /* SNORE_DETECT_H */

最简 wav 推理示例(节选,完整文件位于 SDK 的 src/snoreDetect/c/snore_demo.c):

snore_demo.cc
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "snore_detect.h"

#define DEFAULT_MGK  "snore_detect_v7.mgk"  /* 模型文件 */
#define DEFAULT_WAV  "test_snore.wav"       /* 16kHz 单声道 16bit PCM */
#define FEED_CHUNK   16000                  /* 每次送入 1 秒音频 */

/* 帧级回调: 每帧输出鼾声概率 (约 1 秒一帧) */
static void on_frame(float snore_prob, float timestamp, void *user_data)
{
    (void)user_data;
    printf("%8.2fs  snore=%.4f\n", timestamp, snore_prob);
}

/* 事件开始回调: 策略判定鼾声事件开始 */
static void on_onset(float start_time, void *user_data)
{
    (void)user_data;
    printf("[EVENT] snore start at %.2fs\n", start_time);
}

/* 事件结束回调: 应用层可据此做整夜统计或分级响应 */
static void on_offset(const snore_detect_event_t *ev, void *user_data)
{
    (void)user_data;
    printf("[EVENT] snore end at %.2fs (dur=%.2fs, conf=%.3f, frames=%d)\n",
           ev->end_time, ev->end_time - ev->start_time,
           ev->confidence, ev->frame_count);
}

static int read_wav_pcm(const char *path, int16_t **pcm, int *n, int *sr);  /* 完整实现见源文件 */

int main(void)
{
    snore_detect_t *det;
    int16_t *pcm = NULL;
    int num_samples = 0, sample_rate = 0;
    int pos;

    if (read_wav_pcm(DEFAULT_WAV, &pcm, &num_samples, &sample_rate) != 0)
        return 1;

    /* 1. 创建识别器: .mgk 模型 + 默认报警策略 (NULL) */
    det = snore_detect_create(DEFAULT_MGK, NULL, NULL);
    if (!det) return 1;

    /* 2. 注册回调 (均为可选) */
    snore_detect_set_listener(det, on_frame, on_onset, on_offset, NULL);

    /* 3. 启动内部模型消费线程 */
    snore_detect_start(det);

    /* 4. 分块送入 PCM; 实时录音时改在录音回调里 feed */
    for (pos = 0; pos < num_samples; pos += FEED_CHUNK) {
        int n = num_samples - pos;
        if (n > FEED_CHUNK) n = FEED_CHUNK;
        snore_detect_feed(det, pcm + pos, n);
    }

    /* 5. 停止并销毁 */
    snore_detect_stop(det);
    snore_detect_destroy(det);
    free(pcm);
    return 0;
}

编译与运行(示例):

buildbash
$(CC) snore_demo.c -I. -L. -lsnoredetect -lpthread -lm -o snore_demo
./snore_demo

结语

鼾声识别的价值,是把整夜的呼吸事件变成可读的健康数据——以及在需要时给出真实的干预。如果你正在为睡眠产品评估鼾声识别方案,我们提供在线试用与完整的技术支持。