关键词唤醒(KWS)
概述
关键词唤醒(KWS)是众多语音识别系统的核心,使设备能够响应特定词语或短语。这项技术不仅支撑了 Google Assistant、Amazon Alexa 等流行设备,也完全可以在小型、低功耗设备上实现。本实验将带你在 XIAO ESP32S3 微控制器板上,基于 TinyML 实现 KWS 系统。
XIAO ESP32S3 搭载乐鑫 ESP32-S3 芯片,体积小巧但性能强劲,拥有双核 Xtensa LX7 处理器,集成 Wi-Fi 和蓝牙,兼具算力、能效与多样连接能力,是 TinyML 应用的理想平台。配合扩展板,还可使用摄像头、SD 卡和数字麦克风。本项目中,集成麦克风和 SD 卡将发挥关键作用。
我们将使用 Edge Impulse Studio ,这是一个强大且易用的平台,可简化边缘设备机器学习模型的创建与部署。我们将逐步训练 KWS 模型,并优化后部署到 XIAO ESP32S3 Sense 上。
本实验的模型将识别可用于唤醒设备或触发特定动作(如“YES”)的关键词,让你的项目具备语音激活能力。
借助我们在 TensorFlow Lite for Microcontrollers(EI Studio 底层引擎)的经验,将打造一个能在设备端实时推理的 KWS 系统。
实验将详细拆解每个流程环节——从数据采集与准备,到模型训练与部署,帮助你全面理解如何在微控制器上实现 KWS 系统。
学习目标
- 理解语音助手架构,包括级联检测系统及边缘 KWS 在语音处理流程中的作用
- 掌握音频数据采集技巧,涵盖离线(XIAO ESP32S3 麦克风+SD 卡)与在线(手机集成 EI Studio)两种方式
- 实现音频数字信号处理,包括 I2S 协议基础、16kHz/16bit 采样、时域与频域(MFCC)特征转换
- 训练卷积神经网络进行音频分类,掌握迁移学习、数据增强与四分类(YES、NO、NOISE、UNKNOWN)模型优化
- 在微控制器上部署优化模型,包括 INT8 量化、PSRAM 内存管理、嵌入式实时推理优化
- 开发完整后处理管道,如置信度阈值、GPIO 控制、OLED 显示、打造独立 AI 传感器系统
- 对比无代码平台与传统嵌入式开发流程,理解 TinyML 应用的多种开发路径
KWS 项目简介
语音助手是如何工作的?
关键词唤醒(KWS)是语音助手的关键,使设备能响应特定词语。以 Google Home、Amazon Echo-Dot 为例,只有在检测到特定唤醒词(如“Hey Google”“Alexa”)后,设备才会响应。

换句话说,语音命令识别采用多级模型(级联检测):

第一阶段:设备内的小型微处理器持续监听环境声音,等待关键词出现。此阶段采用边缘 TinyML 模型(KWS 应用)。
第二阶段:仅当 KWS 检测到关键词后,才将数据上传云端,由更大模型进一步处理。
下方视频演示了用树莓派模拟 Google Assistant(第二阶段),Arduino Nano 33 BLE 作为 TinyML 设备(第一阶段)的案例。
想深入了解完整项目,可参考: Building an Intelligent Voice Assistant From Scratch 。
本实验聚焦第一阶段(KWS),使用 XIAO ESP32S3 Sense 的数字麦克风实现关键词检测。
推理流程
下图展示了最终 KWS 应用的推理流程:

本项目将识别四类声音:
- YES(关键词 1)
- NO(关键词 2)
- NOISE(仅背景噪声,无关键词)
- UNKNOWN(除 YES/NO 外的其他词)
实际项目建议加入“噪声/背景”和“未知”类,提高模型稳健性。
机器学习工作流
KWS 应用的核心是模型。我们需用目标关键词、噪声及其他词(unknown)训练模型:

数据集
机器学习流程的关键是数据集。确定关键词(如 YES、NO)后,可利用 Pete Warden 提供的 Speech Commands 数据集 ,包含 35 个关键词(每类 1000+ 样本),如 yes、no、stop、go。我们可获得 yes、no 各 1500 条样本。
可从 Edge Studio 下载精简版数据集( 关键词预置数据集 ),涵盖本项目四类样本:yes、no、noise、background。步骤如下:
- 下载 keywords dataset.
- 解压至本地任意目录
尽管 Pete 的数据集样本丰富,建议补充自录音频。与加速度传感器实验类似,音频分类更依赖于采集设备本身。
声音与音频的本质区别在于能量形态。声音是介质中的机械波能(纵波),音频是电信号(模拟或数字),用于电气表示声音。
说出关键词时,声波需转换为音频数据。转换过程需用麦克风以 16kHz 采样率、16bit 深度采样。
只要设备能生成 16kHz/16bit 音频数据(如 XIAO ESP32S3 Sense、电脑或手机)均可用作采集工具。

用 Edge Impulse + 手机在线采集音频
在运动分类与异常检测实验中,我们用设备直连 Edge Impulse Studio 采集数据(50~100Hz)。但音频(16kHz)速率过高,EI CLI 的 Data Forwarder 不适用。采集到的数字音频需转为 WAV 文件,通过 Data Uploader 上传 Studio(与 Pete 数据集操作一致)。
若需直接在 Studio 采集音频,可用手机在线连接,详见 EI 官方文档 。
用 XIAO ESP32S3 Sense 离线采集音频
板载麦克风为 MSM261D3526H1CPM ,PDM 数字输出 MEMS 麦克风,通过 I2S 总线连接 ESP32S3,IO41(数据)、IO42(时钟)。

I2S 简介
I2S(Inter-IC Sound)是数字音频传输标准,由飞利浦半导体(现 NXP)开发,广泛用于数字信号处理器、音频处理器及带音频功能的微控制器。
I2S 至少包含三根线:

- 1. 时钟线(BCLK/CLK):指示新数据位开始(IO42)
- 2. 字选择线(WS):指示新字开始(左/右声道),WS 频率决定采样率。本项目麦克风 L/R 接地,仅用左声道(单声道)。
- 3. 数据线(SD):传输音频数据(IO41)
I2S 数据流以帧为单位,每帧含左/右声道数据,适合立体声,也可用于单声道或多声道。
下面演示如何用麦克风采集原始数据。可参考 GitHub 项目 下载 XIAOEsp2s3_Mic_Test 示例:
⚠️ 注意
- Xiao ESP32S3 必须启用 PSRAM。可在 Arduino IDE 菜单栏
Tools
–>PSRAM:OPI PSRAM
检查- Arduino Library(
esp32 by Espressif Systems
)应为 2.017 版本,请勿升级
/*
XIAO ESP32S3 简单麦克风测试
*/
#include <I2S.h>
void setup() {
Serial.begin(115200);
while (!Serial) {
}
// 16kHz,16bit 采样
I2S.setAllPins(-1, 42, 41, -1, -1);
if (!I2S.begin(PDM_MONO_MODE, 16000, 16)) {
Serial.println("Failed to initialize I2S!");
while (1); // do nothing
}
}
void loop() {
// 读取采样
int sample = I2S.read();
if (sample && sample != -1 && sample != 1) {
Serial.println(sample);
}
}
该代码通过 I2S 接口采集 16kHz/16bit 音频,并持续输出采样值到串口监视器。
主要流程:
- 引入 I2S 库,配置 I2S 时钟(42)与数据(41)引脚
- 初始化 I2S,PDM 单声道,16kHz/16bit
- 循环读取音频采样,输出到串口
串口绘图器可实时观察音量变化:

保存录音样本
使用板载 SD 卡保存 .wav 音频,需先启用 PSRAM。
ESP32-S3 芯片内存有限,XIAO 板载 8MB PSRAM,可通过 SPI 与主控通信,需在 Arduino IDE
Tools->PSRAM:"OPI PSRAM"->OPI PSRAM
启用。
插入 FAT32 格式 SD 卡:

IDE 菜单启用 PSRAM:

- 下载 Wav_Record_dataset 示例。
该代码通过 I2S 录音,保存为 .wav 文件到 SD 卡,并可通过串口命令控制录音。文件名可自定义(建议用作训练标签),每次录音自动编号。支持音量增益。
主要流程:
#include <I2S.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
- I2S.h:音频输入
- FS.h:文件系统
- SD.h:SD 卡操作
- SPI.h:SPI 通信
#define RECORD_TIME 10
#define SAMPLE_RATE 16000U
#define SAMPLE_BITS 16
#define WAV_HEADER_SIZE 44
#define VOLUME_GAIN 2
- RECORD_TIME:录音时长(秒)
- SAMPLE_RATE/SAMPLE_BITS:音频质量
- WAV_HEADER_SIZE:wav 头大小
- VOLUME_GAIN:音量增益
int fileNumber = 1;
String baseFileName;
bool isRecording = false;
- 文件编号、基础文件名、录音状态
void setup() {
Serial.begin(115200);
while (!Serial);
I2S.setAllPins(-1, 42, 41, -1, -1);
if (!I2S.begin(PDM_MONO_MODE, SAMPLE_RATE, SAMPLE_BITS)) {
Serial.println("Failed to initialize I2S!");
while (1);
}
if(!SD.begin(21)){
Serial.println("Failed to mount SD Card!");
while (1);
}
Serial.printf("Enter with the label name\n");
}
- 初始化串口、I2S、SD 卡,失败则报错并停止
void loop() {
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command == "rec") {
isRecording = true;
} else {
baseFileName = command;
fileNumber = 1;
Serial.printf("Send rec for starting recording label \n");
}
}
if (isRecording && baseFileName != "") {
String fileName = "/" + baseFileName + "."
+ String(fileNumber) + ".wav";
fileNumber++;
record_wav(fileName);
delay(1000);
isRecording = false;
}
}
- 串口输入标签名,输入 rec 开始录音,文件名自动编号
void record_wav(String fileName)
{
...
File file = SD.open(fileName.c_str(), FILE_WRITE);
...
rec_buffer = (uint8_t *)ps_malloc(record_size);
...
esp_i2s::i2s_read(esp_i2s::I2S_NUM_0,
rec_buffer,
record_size,
&sample_size,
portMAX_DELAY);
...
}
- 打开文件,分配 PSRAM 缓冲区,I2S 采集音频,写入 wav 文件
// 增加音量
for (uint32_t i = 0; i < sample_size; i += SAMPLE_BITS/8) {
(*(uint16_t *)(rec_buffer+i)) <<= VOLUME_GAIN;
}
- 音量增益处理
// 写入 wav 文件
Serial.printf("Writing to the file ...\n");
if (file.write(rec_buffer, record_size) != record_size)
Serial.printf("Write file Failed!\n");
free(rec_buffer);
file.close();
Serial.printf("Recording complete: \n");
Serial.printf("Send rec for a new sample or enter
a new label\n\n");
- 写入数据,释放内存,关闭文件,提示下次操作
void generate_wav_header(uint8_t *wav_header,
uint32_t wav_size,
uint32_t sample_rate)
{
...
memcpy(wav_header, set_wav_header, sizeof(set_wav_header));
}
- 生成 wav 文件头
上传代码后,串口输入标签(如 yes),再输入 rec 开始录音,每次 rec 自动编号保存。更换标签后继续录音。
串口交互示例:

输入标签(如 yes),等待 rec 命令:

每次 rec 录音,文件自动编号保存:

最终 SD 卡保存的文件如下:

这些文件可直接上传至 Edge Impulse Studio。
离线音频采集 App
采集音频方式多样,最简单的是用手机或 PC 作为 Edge Impulse Studio 的“连接设备”。
采集音频需 16kHz 采样率、16bit 深度。
也可用专用 App,如 Voice Recorder Pro (iOS),录音保存为 .wav 文件后传至电脑。

使用 Edge Impulse Studio 训练模型
数据上传
当原始数据集(Pete 的数据集 + 自录关键词)准备好后,应在 Edge Impulse Studio 新建项目:

项目创建后,在数据采集(Data Acquisition)部分选择“Upload Existing Data”工具,选择要上传的文件:

将数据上传到 Studio(可自动划分训练/测试集)。对所有类别和原始数据重复此操作。

上传后,样本会出现在数据采集区:

Pete 数据集的样本长度均为 1 秒,但前面录制的样本为 10 秒,需拆分为 1 秒片段以兼容。
点击样本名后的三点菜单,选择“Split sample”:

进入工具后,将数据拆分为 1 秒片段。如有需要可增删片段:

所有样本均需重复此操作。
注意:若音频文件较长(数分钟),可先拆分为 10 秒片段,再用工具拆分为 1 秒片段。
如果上传时未自动划分训练/测试集,可手动操作(三点菜单单独移动样本),或在 Dashboard 的 Danger Zone 区域使用“Perform Train / Test Split”。
可通过 Data Explorer 标签页检查所有数据集。
创建 Impulse(预处理/模型定义)
Impulse 是指将原始数据通过信号处理提取特征,再用学习模块对新数据进行分类的流程。

首先,使用 1 秒窗口采集数据,进行数据增强,窗口每 500ms 滑动一次。注意启用 zero-pad data 选项,确保不足 1 秒的样本用零填充(有时为避免噪声和尖峰,会在拆分工具中缩短窗口)。
每个 1 秒音频样本需预处理并转为图像(如 $13\times 49\times 1$)。我们采用 MFCC(Mel 频率倒谱系数)特征提取,非常适合人声。

接下来选择 KERAS 作为分类器,构建卷积神经网络(CNN)进行图像分类。
预处理(MFCC)
下一步是生成用于训练的特征图像:
可保持默认参数,或使用 DSP Autotune 自动调参(本例采用自动调参)。

结果显示预处理仅需 16KB 内存,但估算处理时间较高(675ms,基于 Espressif ESP-EYE,240kHz 时钟,CPU 性能略低于 ESP32S3 的 LX7)。实际推理时间会更短。
如需进一步降低推理延迟,可返回预处理阶段,调整 FFT 长度(如降至 256)、系数数量等参数。
本例保持自动调参结果,保存参数并生成特征。

想深入了解时序数据转图像(如 FFT、频谱图等),可参考此 CoLab: Audio Raw Data Analysis.
模型设计与训练
本项目采用卷积神经网络(CNN)模型。基础结构为两组 Conv1D + MaxPooling(分别含 8 和 16 个神经元),加 0.25 Dropout,最后一层 Flatten 后输出 4 个神经元(对应四类):

超参数设置:学习率 0.005,训练 100 轮(epochs),并加入数据增强和噪声。训练结果良好:

如需深入理解,可下载数据集,在 Jupyter Notebook 中分析每轮准确率:

相关 CoLab Notebook 见: KWS Classifier Project - Looking “Under the hood” Training/xiao_esp32s3_keyword_spotting_project_nn_classifier.ipynb)。
测试
用预留的测试集测试模型,准确率约 87%:

F1 分数显示 YES 类为 0.95,非常理想(本项目用 YES 触发后处理,如点亮 LED);NO 类为 0.90,UNKNOWN 类略低但可接受。
可继续项目,也可在设备部署前用手机进行 Live Classification。进入 Live Classification,点击 Connect a Development board:

用手机扫码并访问链接:

手机连接 Studio 后,选择 Classification,实时测试关键词,验证模型效果:

部署与推理
Studio 会自动打包所需库、预处理函数和模型,下载到本地。选择 Arduino Library,底部菜单选 Quantized (Int8),点击 Build。

接下来进行真实推理测试。我们将修改 ESP32 示例代码(esp32_microphone),适配本设备。
在 Arduino IDE 的 File/Examples 中找到项目,选择 esp32/esp32_microphone:

该代码为 ESP-EYE 麦克风设计,需适配 XIAO ESP32S3。
首先更换 I2S 总线相关库:

替换为:
#include <I2S.h>
#define SAMPLE_RATE 16000U
#define SAMPLE_BITS 16
在 setup() 中初始化 I2S 麦克风:
void setup()
{
...
I2S.setAllPins(-1, 42, 41, -1, -1);
if (!I2S.begin(PDM_MONO_MODE, SAMPLE_RATE, SAMPLE_BITS)) {
Serial.println("Failed to initialize I2S!");
while (1) ;
...
}
在 static void capture_samples(void* arg) 函数中,将第 153 行读取 I2S 麦克风数据的代码:

替换为:
/* read data at once from i2s */
esp_i2s::i2s_read(esp_i2s::I2S_NUM_0,
(void*)sampleBuffer,
i2s_bytes_to_read,
&bytes_read, 100);
在 static bool microphone_inference_start(uint32_t n_samples) 函数中,注释或删除 198~200 行的麦克风初始化代码(setup() 已初始化,无需重复)。

最后,在 static void microphone_inference_end(void) 函数中,将第 243 行:

替换为:
static void microphone_inference_end(void)
{
free(sampleBuffer);
ei_free(inference.buffer);
}
完整代码见 项目 GitHub 。上传代码到开发板,测试实际推理效果:
⚠️ 注意
- Xiao ESP32S3 必须启用 PSRAM。可在 Arduino IDE 菜单栏
Tools
–>PSRAM:OPI PSRAM
检查- Arduino Library(
esp32 by Espressif Systems
)应为 2.017 版本,请勿升级

后处理
在边缘 AI 应用中,推理结果只有被有效利用才有价值。串口输出便于调试,但实际部署需即时、直观的人机反馈,无需外接显示器。
下面介绍两种后处理方式:用 XIAO 内置 LED 和 XIAOML Kit 的 OLED 显示屏。
LED 指示
模型能正确检测关键词后,可修改代码使每次检测到 YES 时点亮内置 LED。
初始化 LED:
#define LED_BUILT_IN 21
...
void setup()
{
...
pinMode(LED_BUILT_IN, OUTPUT); // 设置为输出
digitalWrite(LED_BUILT_IN, HIGH); // 关闭
...
}
在 loop() 的 // print the predictions 部分修改为:
int pred_index = 0; // 初始化 pred_index
float pred_value = 0; // 初始化 pred_value
// 输出预测结果
ei_printf("Predictions ");
ei_printf("(DSP: %d ms., Classification: %d ms., Anomaly: %d ms.)",
result.timing.dsp, result.timing.classification,
result.timing.anomaly);
ei_printf(": \n");
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: ", result.classification[ix].label);
ei_printf_float(result.classification[ix].value);
ei_printf("\n");
if (result.classification[ix].value > pred_value){
pred_index = ix;
pred_value = result.classification[ix].value;
}
}
// 根据推理结果控制 LED
if (pred_index == 3){
digitalWrite(LED_BUILT_IN, LOW); // 点亮
}
else{
digitalWrite(LED_BUILT_IN, HIGH); // 熄灭
}
完整代码见 项目 GitHub 。上传后测试实际推理:

每次检测到 YES,LED 点亮。类似地,也可用作外部设备的触发信号。
OLED 显示
XIAOML Kit 配备的 0.42 英寸 OLED 显示屏(72×40 像素)是关键的后处理组件,可将 ML 推理结果直接以类别名和置信度显示在设备上,实现真正的独立边缘 AI 部署,适用于工业、农业、零售等场景,便于现场快速确认 AI 预测。
可修改代码自动适配 Edge Impulse 训练的模型,直接读取类别名和数量。代码见 GitHub: xiaoml-kit_kws_oled 。
运行效果如下:

总结
本实验完整演示了基于 XIAOML Kit 的关键词唤醒系统开发流程,展示了现代 TinyML 平台如何让复杂音频 AI 在资源受限设备上变得易用可落地。通过动手实践,我们将理论机器学习知识与嵌入式 AI 部署紧密结合。
技术亮点:
项目实现了从原始声音采集到实时推理的完整音频处理管道。利用 XIAO ESP32S3 集成数字麦克风,以专业级(16kHz/16bit)采集音频,并用 MFCC 特征提取。部署的 CNN 模型能高效区分目标关键词(“YES”、“NO”)与背景(“NOISE”、“UNKNOWN”),推理延迟适合实时应用。
平台集成:
Edge Impulse Studio 作为嵌入式 MLOps 平台,涵盖数据采集、标注、训练、优化与部署。云端训练与边缘部署无缝衔接,Arduino IDE 则为自定义后处理提供灵活性。
实际应用:
所学技术远不止关键词检测。语音控制、工业安全监测、医疗呼吸分析、环境声音监测等均可借鉴本项目音频处理方法。级联检测架构(边缘 KWS 触发云端处理)是现代语音助手的基础。
嵌入式 AI 原则:
本项目强调了 TinyML 的关键考量:功耗管理、PSRAM 内存优化、模型复杂度与推理速度权衡。神经网络在微控制器上实时音频分析,证明 AI 能力已从桌面走向电池供电设备。
开发方法论:
涵盖多种开发路径:离线 SD 卡采集与在线流式采集、Edge Impulse 自动库生成与 Arduino 自定义实现。灵活适配不同项目需求。
未来方向:
本实验为更高级音频 AI 应用打下基础。多关键词识别、说话人识别、情感检测、环境声音分类等均可基于本技术拓展。OLED 显示与 GPIO 控制集成,展示了 KWS 作为智能 IoT 接口的潜力。
声音分类远不止语音识别,相关技术可广泛应用于:
- 安防:破窗、入侵、枪声检测
- 工业 IoT:设备健康监测、异常检测
- 医疗:睡眠监测、呼吸分析、老人护理
- 环境监测:野生动物追踪、城市噪声分析、智能建筑声学管理
- 智能家居:多房间语音控制、家电声音状态监测
核心收获:
XIAOML Kit 证明了专业级 AI 开发可用平价工具实现。强大硬件(ESP32S3+PSRAM+ 多传感器)、成熟平台(Edge Impulse Studio)、丰富软件库,让复杂 AI 概念变为可用系统。
本实验展示了 AI 的未来不仅在数据中心,更在能实时感知、理解和响应环境的智能边缘设备——为普惠、隐私友好、响应迅速的人工智能系统打开了新可能。
参考资源
- XIAO ESP32S3 代码
- XIAOML Kit 代码
- Google Speech Commands 数据集子集
- KWS MFCC 分析 Colab
- KWS CNN 训练 Colab
- XIAO ESP32S3 后处理代码
- Edge Impulse 项目