自定义语音唤醒词:从训练到部署的完整链路实践

自定义语音唤醒词:从训练到部署的完整链路实践

为什么你需要一个自定义唤醒词?

"小爱同学"、"Hey Siri"、"小度小度"------这些唤醒词你可能每天都会用。但如果你是嵌入式开发者、智能硬件创业者,或者只是想给自己的 DIY 项目加个语音唤醒,你会发现一个问题:

市面上的方案要么太贵,要么太封闭。

百度、思必驰的商业方案需要商务对接;Snowboy 早已停止维护;Porcupine 虽然能自定义唤醒词,但需要在线激活授权,按设备数量收费,小团队难以承受。想自定义一个词?要么花钱,要么自己造轮子。

本文带你走通一条完整的开源链路:训练模型 → 导出 ONNX → 端侧推理 → 实际部署

整体架构

javascript 复制代码
┌──────────────────────┐
│   训练阶段            │
│  OpenWakeWord / 听词  │  →  ONNX 模型文件
│  自定义数据 / 纯合成   │
└──────────────────────┘
           ↓
┌──────────────────────┐
│   推理阶段            │
│  onnx-wakeword       │  →  跨平台 C++/Android/Linux/Web/ESP32
│  轻量级 ONNX Runtime  │
└──────────────────────┘
           ↓
┌──────────────────────┐
│   部署阶段            │
│  ESP32 / Android     │  →  离线运行,无需联网
│  Linux / Web          │
└──────────────────────┘

第一步:从哪里搞到模型?

方案 A:自己训练(OpenWakeWord)

OpenWakeWord 是目前最活跃的开源唤醒词训练框架。它用合成数据(TTS 生成)+ 真实噪音混合,不需要你录制任何音频。

bash 复制代码
pip install openwakeword
# 准备一个包含唤醒词的文本文件
# 运行训练脚本,指定你的唤醒词

优点是完全可控,缺点是调参门槛不低,GPU 训练时间较长。

另外 nanoWakeWordWeKws 也是不错选择:前者轻量级、专注嵌入式,后者基于 WeNet 生态、中文支持好。模型格式各有不同,但都能导出 ONNX。

方案 B:在线平台生成

听词这类平台,输入关键词直接拿 ONNX 模型。底层用合成语音 + 自研训练管线,几分钟出结果。

python 复制代码
输入:"你好曼波"
输出:nihaomanbo_basic_dcnn_v9.0.zip (约 94KB)
  ├── dscnn_multiscale_nihaomanbo.onnx
  └── model_info.json

不用管线、不用 GPU、不用调参,适合快速验证和小批量项目。

第二步:推理引擎选型

拿到 ONNX 模型后,如果要自己写推理代码,你需要处理:

  • Mel-Spectrogram 特征提取(C++ 实现 STFT/FFT/Mel 滤波器组)
  • ONNX Runtime 加载和推理
  • 后处理:sigmoid → softmax → 滑动窗口去抖
  • 多层级防误触发逻辑

这些写起来大概 2000 行 C++。不过轮子已经有人造好了:

🔧 onnx-wakeword:一套代码,四端运行

github.com/voicute/onn... 是一个专门为唤醒词场景设计的轻量级推理引擎。

bash 复制代码
onnx-wakeword/
├── onnx-wakeword.cpp       # C++ 核心引擎(Mel 特征提取 + 推理 + 防误触发)
├── android/                # Android AAR,支持 ort-android + NNAPI
├── esp32/                  # ESP-IDF Component,适配 ESP32-S3
├── linux/                  # Linux 可执行文件 + Python bindings
├── web/                    # WebAssembly,浏览器端运行
└── models/                 # 预训练模型示例(曼波、来福、咕咕嘎嘎等)

核心设计

Mel 特征提取内置。 不需要单独加载 melspectrogram 模型,引擎内部用高效的 C++ 实现了完整的音频处理管线:

c 复制代码
PCM 16kHz → 分帧加窗 → STFT → Mel 滤波器组 → log-mel 特征 → 模型输入

支持两种模型结构:

模型类型 特点 模型大小
DSCNN (Multiscale) 速度快,适合 MCU ~130KB
Embedding-based 精度高,适合 CPU ~500KB+

内置五层防误触发(DetectionLogic):

层级 策略 说明
L1 连续帧确认 连续 N 帧都检测到才初步确认
L2 峰值/背景比 唤醒信号必须显著高于背景噪音
L3 冷却时间 触发后一段时间内不再触发
L4 多关键词互斥 多个唤醒词同时激活时做 softmax 决策
L5 前后静音检测 真正的人声唤醒前后会有能量跳变

这些逻辑在引擎内部闭环,调用方只需要注册一个回调:

cpp 复制代码
engine.setCallback([](const std::string& word, float prob) {
    printf("检测到唤醒词: %s (置信度: %.2f)\n", word.c_str(), prob);
});
engine.start();

C++ 集成(一行搞定)

cpp 复制代码
#include "onnx-wakeword.h"

auto engine = OnnxWakeWord::create(
    "model.onnx",   // 唤醒词模型
    "mel.onnx"      // melspectrogram 模型(可选,DSCNN 模式不需要)
);
engine->setThreshold(0.5);
engine->start();

Android 集成

kotlin 复制代码
// build.gradle
implementation(files("libs/onnx-wakeword.aar"))

// Kotlin
val engine = OnnxWakeWord.create(assetManager, "model.onnx", "mel.onnx")
engine.setCallback { word, prob ->
    Log.i("WakeWord", "检测到 $word ($prob)")
}
engine.start()

// 输入麦克风音频
engine.feedPcm(audioBuffer)

ESP32 集成

c 复制代码
// 作为 ESP-IDF component 引入
// CMakeLists.txt
set(EXTRA_COMPONENT_DIRS components/onnx-wakeword-esp32)

// main.c
wakeword_handle_t handle;
wakeword_init(&handle, model_data, model_size);
wakeword_set_threshold(handle, 0.5f);
wakeword_start(handle);

// 在 I2S 回调中喂音频
wakeword_feed_pcm(handle, samples, sample_count);

Web 浏览器端

javascript 复制代码
import { createDetector } from 'onnx-wakeword-web'

const detector = createDetector()
await detector.loadModels('model.zip', 'melspectrogram.onnx')
await detector.startMic((word, prob) => {
    console.log(`检测到: ${word} (${prob})`)
})

第三步:实际部署踩坑记录

Android 上的实测数据

指标 数值
模型大小 ~130KB(未压缩)
内存占用 ~20MB(含 ONNX Runtime)
单次推理耗时 < 5ms(高通骁龙 8 Gen2)
实时率 0.05-0.1(远超实时)
误触发率 < 1次/小时(五层检测全开)

测试设备:小米 14,Android 14。低端机型(如骁龙 660)推理耗时约 15-20ms,仍远低于实时要求。推荐使用 ort-android 包,支持 CPU + NNAPI 双后端。

唤醒词设计原则

别随便取一个词就当唤醒词。好的唤醒词能帮你把召回率从 80% 拉到 95%:

  1. 3-5 个字,4 个字最佳
  2. 有爆破音(b/p/d/t/k),能量足、好检测
  3. 避免叠词("叮叮"、"咕咕"),识别难度翻倍
  4. 避免混音/近音("小希" vs "小西")
  5. 不要太常见,否则日常对话中误触发满天飞

别问为什么知道------我们训练过"咕咕嘎嘎"和"优米优米",惨痛教训。

ONNX Runtime 版本兼容

不同平台需要不同版本的 ONNX Runtime:

平台 ORT 版本 备注
Linux x86_64 1.18+ 最新版即可
Android arm64 1.17-1.18 需交叉编译
Web (WASM) 1.20.1 jsDelivr CDN 加载

DSCNN Multiscale 算子兼容性最好,基本所有版本都能跑。Embedding 模型需要较新的 Runtime。

总结

一条完整链路走下来:

复制代码
选词 → 训练/生成 ONNX 模型 → onnx-wakeword 引擎加载 → 回调里点个灯/发个 HTTP → 完事

整个流程可以控制在 10 分钟内 (如果你用现成平台生成模型),或者半月到一个月(如果你自己训练,很折腾但是很锻炼人)。

开源方案已经足够能打了。想要更低的误触发、更快的响应速度?去看代码然后调参吧。

相关链接:

相关推荐
用户5191495848451 小时前
libcurl Headers API 释放后重利用漏洞:跨请求复用头句柄导致堆内存安全风险
人工智能·aigc
用户5191495848451 小时前
CVE-2025-1094 PostgreSQL SQL注入与WebSocket劫持远程代码执行利用工具
人工智能·aigc
IT_陈寒2 小时前
SpringBoot自动配置这个坑,我踩进去又爬出来了
前端·人工智能·后端
冬奇Lab14 小时前
Agent 系列(23):Web Agent——让 Agent 真正浏览网页
人工智能·llm·agent
冬奇Lab14 小时前
每日一个开源项目(第135篇):codebase-memory-mcp - 给 AI Agent 一张代码库的知识图谱
人工智能·开源·llm
IT_陈寒16 小时前
JavaScript的闭包把我坑惨了,说好的内存会自动回收呢?
前端·人工智能·后端
jooloo20 小时前
Codex 间歇性 400 之谜:一条对话里,它为什么有时候用 chat/completions,有时候切到 responses?
人工智能