在 ESP32 上跑自定义唤醒词:从 TFLite 到离线语音唤醒,一个下午搞定

在 ESP32 上跑自定义唤醒词:从 TFLite 到离线语音唤醒,一个下午搞定

为什么选 TFLite + ESP32?

如果你在做智能家居、机器人或者任何需要语音交互的硬件项目,大概率会遇到一个需求:设备能听懂一句特定的唤醒词

市面上方案不少,但大部分要么贵(云端 ASR),要么绑定特定词("小爱同学"),要么需要联网。而 ESP32-S3 这颗芯片------双核 240MHz、自带 8MB PSRAM、价格不到 30 块------已经足够在本地跑一个完整的神经网络唤醒词检测。

关键组合是 TFLite Micro + ESP-IDF。TFLite 是 Google 的推理引擎,专门为嵌入式设备优化过,ESP-IDF 直接内置了对它的组件支持。模型用 INT8 量化后不到 80KB,推理一次 ~360ms,配合 5 层检测管线,安静环境误触率极低。

一句话:完全离线、延迟可控、想换什么词就换什么词


唤醒词模型到底在算什么?

很多同学听到"神经网络"就觉得门槛高。其实唤醒词模型做的事情非常直观:

scss 复制代码
音频 (16kHz) → Mel 频谱 [98帧 × 32维] → Backbone → [256维向量] → Head → 概率 [0~1]

Mel 频谱:把人耳听到的声音画成一张"图"。横轴是时间,纵轴是频率,颜色深浅代表能量大小。1 秒音频被切成 98 帧,每帧 32 个频率通道。

Backbone:模型的主体,负责从这张"图"里提取特征。常见的是 DS-TCN(深度可分离时序卷积网络)或 SRNN。它输出一个 256 维向量------可以理解为"这段音频的关键特征浓缩版"。

Head :把 256 维向量变成一个概率。常用的 MultiProto head 只有 325 个参数(5 个原型 × 256 维 + 一个线性分类器),体积几乎可以忽略。它做的事:L2 归一化 → 和原型算余弦相似度 → 线性加权 → sigmoid。

ini 复制代码
  Mel 频谱                Backbone                 Head 输出
  ┌──────────┐           ┌────────┐              ┌─────────┐
  │  ········│           │ SRNN   │              │ prob=0.9 │ ← 说出唤醒词
  │  ████████│  ──────→  │  or    │  ──────→     │ prob=0.0 │ ← 安静/噪音
  │  ████████│           │ DS-TCN │              │ prob=0.1 │ ← 其他词
  │  ········│           └────────┘              └─────────┘
  └──────────┘               ↑
     98×32               256维向量

模型到手后只需要两个文件.tflite(backbone,INT8 量化,~76KB)和 head.h(MultiProto 权重,~17KB)。


集成 onnx-wakeword ------ 3 步跑起来

voicute/onnx-wakeword 是一个完整的 ESP-IDF 开源项目,包含唤醒词推理引擎和完整 Demo。

项目结构

bash 复制代码
esp32/
├── components/voicute/    ← 核心库(不依赖具体模型)
├── main/                  ← Demo 应用
│   ├── main.cpp            唤醒词 + 语音命令完整示例
│   └── head.h              模型 head(用户替换)
├── spiffs_content/        ← 放 .tflite 模型
├── build.bat / flash.bat  ← 一键编译烧录
└── sdkconfig.defaults     ← 预配好 PSRAM、Flash

第一步:Clone + 编译

bash 复制代码
git clone https://github.com/voicute/onnx-wakeword.git
cd onnx-wakeword/esp32
build.bat   # Windows

首次编译会下载 ESP-IDF 依赖(esp-tflite-micro、esp-dsp、esp-sr 等),之后双击 build.bat 就行。已经预配好了 ESP32-S3、16MB Flash、PSRAM,开箱即用。

第二步:替换模型

bash 复制代码
cp your_wakeword.tflite spiffs_content/
cp your_head.h main/head.h

第三步:烧录

bash 复制代码
flash.bat
python wake_monitor.py COM5   # 观察实时 prob/RMS

核心代码就这么几行

cpp 复制代码
#include "recognizer.h"
#include "head.h"
#include "kws_postprocess.h"

// 唤醒回调
static void on_wake(voice_event_t ev, voice_evt_data_t d, void *u) {
    if (ev == VOICE_EVT_AWAKEN) ESP_LOGI("APP", ">>> 关键词!");
}

void app_main() {
    recognizer_config_t cfg = {
        .model_path   = "/spiffs",
        .threshold    = 0.70f,
        .l5_enabled   = 1,    // 能量跳变过滤(防音乐/视频误触)
        .l5_delta     = 200.0f,
        .postprocess  = kws_postprocess,
    };
    recognizer_start(&cfg);
    recognizer_register_callback(0, on_wake, NULL);

    while (1) {
        // 从麦克风采集 16192 采样点
        int64_t now = esp_timer_get_time() / 1000;
        recognizer_run_frame(pcm, rms, now);
    }
}

Demo 还包括 MultiNet 语音命令(唤醒后说"灯光变红/蓝/绿"),完整展示了唤醒→进入命令模式的流程。不需要的话删掉 sdkconfig.defaults 里相关配置即可。


5 层检测管线

拿概率只是一个基础。真正决定体验的是误触发控制。onnx-wakeword 内置了和 Android 端同款的 L1-L5 检测管线:

作用 说明
L1 连续帧确认 N 帧连续超阈值才放行
L2 峰/底比 峰值必须远高于背景概率
L3 冷却 1.5s 内不重复触发
L4 突发阻断 短时间连触发 → 拉黑 5s
L5 能量跳变 当前音量必须明显高于历史窗口 → 排除平稳音乐/视频声

L5 是最核心的过滤器。它的逻辑是:如果 RMS 突然跳变(说明有人在说话),进入待确认;等 400ms 后检查尾音是否降回安静水平------是就确认唤醒,否则拦截。这个机制能挡住绝大多数 TV、视频、音乐误触。

调参实战

如果你是第一次跑,推荐配置:L5 开,其他层关。ESP32 推理一帧 ~360ms,唤醒词 ~500ms,最多被一帧覆盖,L1 连续帧确认不适用;L2/L3 按需开。

如果遇到误触发,两步排查:

  1. 先开 L5l5_enabled=1),把 l5_delta 设到 200 。这个值适合安静环境,意味着"当前音量比之前 0.52 秒的最低音量高 200 才放行"。安静时底噪 RMS 一般在 1050,说话时能到 300~1000,200 的差值足够区分。

  2. 如果还是容易误触发 ,把 l5_delta 调大:

cpp 复制代码
.l5_enabled = 1,
.l5_delta   = 800.0f,  // 从 200 加大到 800,嘈杂环境用
环境 建议 delta 安静 RMS 说话 RMS
安静房间 200~400 10~50 200~500
普通室内 400~800 50~150 300~800
嘈杂/有音乐 800~1200 100~300 500~1500

如果怎么调都唤不醒(l5_delta 太大导致说话也被拦截),就看串口日志里的 L5 steady: curRms=xxx preMin=xxx,把 delta 调到比你说话时的 RMS 差值略小就行。反之,如果没说话也莫名其妙触发,delta 往大了加。


获得自己的唤醒词模型

不用准备数据,不用 GPU,不用懂深度学习。

打开 voicute.com,输入你想用的关键词,点击生成,直接下载两个文件:

  • xxx.tflite → 放到 spiffs_content/
  • head.h → 替换 main/head.h

然后重新编译烧录就行了。从改词到跑通,十分钟。


总结

你需要的 去哪搞
ESP-IDF 推理引擎 + Demo github.com/voicute/onn...
自定义唤醒词模型 voicute.com 输关键词直接下载

从零到在自己的 ESP32 设备上喊出专属唤醒词,最快一个下午。离线、免费、想换就换。

相关推荐
hunterandroid1 小时前
Room 数据库迁移:让本地数据升级更稳
android·前端
徐小夕1 小时前
倒腾2天,我开源了一款AI驱动的实时 3D CAD / 参数化建模工作台
前端·架构·github
少年白马醉春风丶1 小时前
任务分解的经典范式 · 和 ReAct「走一步看一步」到底差在哪
前端
是烨笙啊1 小时前
从 npm 与 Bun 的选择看 AI 时代的“最佳实践”焦虑
前端·人工智能·npm
lichenyang4532 小时前
从 Web 容器到本地 HAR:我做了一个 HarmonyOS JSBridge 运行时框架 Demo
前端
超神熊猫2 小时前
🧀🧀🧀前端仔如何快速上手python
前端·python
阿黎梨梨2 小时前
AI 推理太慢用户要退钱?前端流式输出(Stream)究竟是何方神圣?
前端·vue.js
只一2 小时前
从0到1吃透AI流式输出:Vue3+DeepSeek实战,解锁丝滑对话体验
前端·vue.js
JZC_xiaozhong2 小时前
OA调价审批后,采购调价单如何自动同步到ERP?解决漏录错价难题
大数据·linux·服务器·前端·数据库·数据孤岛解决方案·数据集成与应用集成