- 语音
- 音频
- 人工智能
- 嵌入式
- 物联网
【免费下载链接】esp-sr
Speech recognition
ESP-SR 的 MultiNet 模型是乐鑫在 ESP32 系列芯片上离线实现多命令词识别的核心组件,支持最多 200 个自定义命令词、运行时动态增删改,并在 500 ms 内完成识别。本文围绕 MultiNet 的工作原理、不同版本模型的命令词格式要求、三种命令词自定义方法(fst 文件、menuconfig、API)、以及与 AFE/WakeNet 配合的完整调用链展开,帮助你在自己的语音控制应用中落地“唤醒 + 命令词”的离线交互方案。
MultiNet 模型概览
MultiNet 是为 ESP32 系列设计的轻量化多命令词识别模型,其核心特性如下:
- 多语言支持(因芯片而异):ESP32-S3 支持中文和英文命令词识别;ESP32 仅支持中文命令词识别
- 用户自定义命令词:无需重新训练模型即可更换识别词表
- 运行时动态修改:支持运行过程中增加 / 删除 / 修改命令词
- 命令词数量上限:最多支持 200 个命令词
- 两种识别模式:单次识别与连续识别
- 低资源消耗、低延时:延时在 500 ms 内
- 模型单独分区:支持对模型应用独立 OTA(ESP32-S3 还支持中英文模型在线切换)
MultiNet 的输入是经过 AFE(音频前端)处理后的音频,格式固定为16 KHz、16 bit、单声道。不同芯片可使用的 MultiNet 模型版本可通过menuconfig中的ESP Speech Recognition > Chinese/English Speech Commands Model选择,当前仓库 Kconfig.projbuild 中定义的中文模型选项包括:
| Kconfig 选项 | 模型名 | 适用芯片 |
|---|---|---|
SR_MN_CN_MULTINET2_SINGLE_RECOGNITION | mn2_cn | ESP32 |
SR_MN_CN_MULTINET5_RECOGNITION_QUANT8 | mn5q8_cn | ESP32-S3 |
SR_MN_CN_MULTINET6_QUANT | mn6_cn | ESP32-S3 |
SR_MN_CN_MULTINET6_AC_QUANT | mn6_cn_ac(空调控制场景微调版) | ESP32-S3 |
SR_MN_CN_MULTINET7_QUANT | mn7_cn | ESP32-S3 / ESP32-P4 / ESP32-S31 |
SR_MN_CN_MULTINET7_AC_QUANT | mn7_cn_ac | ESP32-S3 / ESP32-P4 / ESP32-S31 |
英文模型对应SR_MN_EN_MULTINET5_SINGLE_RECOGNITION_QUANT8(mn5q8_en)、SR_MN_EN_MULTINET6_QUANT(mn6_en)、SR_MN_EN_MULTINET7_QUANT(mn7_en)等选项,芯片依赖关系与中文模型一致。
注:以
Q8结尾的模型名(如mn5q8_cn)代表模型的 8 bit 量化版本,更加轻量化。
模型选择的完整流程(包括分区表中model分区的大小配置、idf.py flash自动加载模型、Arduino 框架下用 movemodel.py 手动生成srmodels.bin)参见仓库文档 模型选择和加载;各芯片支持的模型清单及资源消耗参见 性能测试结果。
命令词识别原理
命令词识别在系统中的位置如下图所示:麦克风采集的音频先进入 AFE 进行回声消除、降噪、VAD 等处理,再与 WakeNet 唤醒检测、MultiNet 命令词识别串联工作——唤醒成功后启动 MultiNet 检测,识别到命令词后返回命令 ID 与置信度。
命令词格式要求
不同版本的 MultiNet 命令词格式不同,自定义命令词前必须先了解目标版本的输入单元:
| 模型版本 | 中文识别单元 | 英文识别单元 |
|---|---|---|
| MultiNet5 | 汉语拼音(无声调) | 音素(phoneme) |
| MultiNet6 | 汉语拼音 | 字母拼写(grapheme,建议全大写) |
| MultiNet7 | 拼音/字母(fst 文件配置) | 音素,可用 multinet_g2p.py 从字母转音素 |
通用的命令词约束(中文版文档明确要求):
- 不支持中英文混合的命令词
- 不能含有阿拉伯数字和特殊字符
- 命令词 ID 不能为 0
MultiNet5 和 MultiNet6 使用汉语拼音作为中文基本识别单元,比如“打开空调”应写成da kai kong tiao。仓库提供了 tool/multinet_pinyin.py 工具将汉字转为拼音(基于pypinyin并内置了一批多音字纠正词典):
python tool/multinet_pinyin.py --text "打开空调" # in: 打开空调 # out: da kai kong tiao自定义命令词方法
MultiNet 支持在线(API / menuconfig)与离线(fst 文本文件)多种命令词设置方式,并允许运行时动态增删改。
方法一:修改 fst 命令词文件(MultiNet6 / MultiNet7)
MultiNet6 中文:修改 model/multinet_model/fst/commands_cn.txt。格式为每行一条命令,第一个字段是 command id,后面是指令的中文拼音,两者及拼音之间用空格/分隔符隔开,command id 不能为 0:
# command_id command_sentence 1 da kai kong tiao 2 guan bi kong tiao仓库自带的commands_cn.txt预置了 300 余条空调控制场景命令(da kai kong tiao、guan bi kong tiao、tiao gao wen du等),可直接参考其格式。
英文(MultiNet6 / MultiNet7):修改 model/multinet_model/fst/commands_en.txt,格式为command_id,command_grapheme,command_phoneme三列:
# command_id,command_grapheme,command_phoneme 1,TELL ME A JOKE,TfL Mm c qbK 2,SING A SONG,Sgl c Sel- 第 1 列 command ID,从 1 开始,不能为 0;
- 第 2 列 command_grapheme,即命令句子(MultiNet6 建议全大写;MultiNet7 建议小写,专有缩写除外);
- 第 3 列 command_phoneme,音素序列,可选。推荐用 tool/multinet_g2p.py 做 Grapheme-to-Phoneme 转换后填入;若留空,运行时会调用内置 G2P 工具,但由于算法差异准确率会略有下降。
方法二:通过 menuconfig(MultiNet5)
MultiNet5 通过idf.py menuconfig配置命令词:
- 进入
idf.py menuconfig>ESP Speech Recognition>Add Chinese speech commands/Add English speech commands,为每个 Command ID 填入拼音字符串。
注意:单个 Command ID 可以对应多个短语。例如“打开空调”(da kai kong tiao)和“开空调”(kai kong tiao)语义相同,可以写在同一个 Command ID 的词条中,用英文逗号,隔开相邻词条(,前后无需空格)。从 Kconfig.projbuild 可见,CN_SPEECH_COMMAND_ID0的默认值即为da kai kong tiao。
- 在代码中调用 API 将 menuconfig 中的配置应用到模型:
/** * @brief Update the speech commands of MultiNet by menuconfig * * @param multinet The multinet handle * * @param model_data The model object to query * * @param language The language of MultiNet * * @return * - ESP_OK Success * - ESP_ERR_INVALID_STATE Fail */ esp_err_t esp_mn_commands_update_from_sdkconfig(esp_mn_iface_t *multinet, const model_iface_data_t *model_data);方法三:通过 API 动态修改(MultiNet5/6/7 通用)
命令词也可以通过 API 在运行时修改,对 MultiNet5 和 MultiNet6(以及 MultiNet7)都适用。所有添加、移除、修改及清空操作都是先写入缓存链表,在调用esp_mn_commands_update()之后才会被应用到语言模型。API 声明在 src/include/esp_mn_speech_commands.h,实现在 src/esp_mn_speech_commands.c:
- 添加一条新指令,格式不正确返回
ESP_ERR_INVALID_STATE:
/** * @brief Add one speech commands with command string and command ID * * @param command_id The command ID * @param string The command string of the speech commands * * @return * - ESP_OK Success * - ESP_ERR_INVALID_STATE Fail */ esp_err_t esp_mn_commands_add(int command_id, char *string);- 移除一条指令,指令不存在时返回
ESP_ERR_INVALID_STATE:
esp_err_t esp_mn_commands_remove(char *string);- 修改一条指令(旧串不存在时返回
ESP_ERR_INVALID_STATE):
esp_err_t esp_mn_commands_modify(char *old_string, char *new_string);- 清空所有指令:
esp_err_t esp_mn_commands_clear(void);- 应用所有修改(必须在 add/remove/modify/clear 之后调用,否则语言模型不会更新;返回值为无法被 MultiNet 解析的错误短语列表,
NULL表示全部成功):
/** * @brief Update the speech commands of MultiNet * * @Warning: Must be used after [add/remove/modify/clear] function, * otherwise the language model of multinet can not be updated. * * @return * - NULL Success * - others The list of error phrase which can not be parsed by multinet. */ esp_mn_error_t *esp_mn_commands_update();- 打印缓存的指令(
esp_mn_commands_update()之后才生效)与打印当前已生效的指令:
void esp_mn_commands_print(void); void esp_mn_active_commands_print(void);从源码结构看,esp_mn_commands_add() 在插入节点前会调用模型句柄的check_speech_command()校验字符串格式(MultiNet7 下还会先经flite_g2p()转成音素再校验),并通过esp_mn_commands_num()检查数量是否超过ESP_MN_MAX_PHRASE_NUM上限——esp_mn_iface.h 中该宏定义为 400,即链表层面的硬上限,而官方文档承诺的对外支持数量为 200 个。esp_mn_commands_update()的底层实现则是把整条命令链表通过set_speech_commands()下发给具体模型(见 src/esp_mn_speech_commands.c),因此“缓存 + 统一提交”的设计可以保证语言模型(FST/前缀树)一次性整体重建,避免中途状态不一致。
MultiNet 的使用
MultiNet 命令词识别建议与 ESP-SR 中的 AFE 声学算法模块一起运行(参见 AFE 介绍及使用),并在 WakeNet 唤醒触发后再启动命令词检测。
初始化
- 模型加载与初始化:通过
esp_mn_handle_from_name()按模型名获取句柄,再用create()创建模型实例,模型选择与加载流程参见 模型选择和加载。参考 test_apps/esp-sr-multinet/main/test_multinet.cpp 中的标准用法:
srmodel_list_t *models = esp_srmodel_init("model"); char *model_name = esp_srmodel_filter(models, ESP_MN_PREFIX, NULL); esp_mn_iface_t *multinet = esp_mn_handle_from_name(model_name); // duration 为超时时间(ms),例如 6000 model_iface_data_t *model_data = multinet->create(model_name, 6000);- 设置命令词:按上文三种方法之一完成。
运行
MultiNet 与 AFE 配合时,必须满足两条约束:
- 传入 MultiNet 的帧长必须与 AFE
fetch的帧长相等; - 音频格式为 16 KHz、16 bit、单通道,AFE
fetch拿到的数据正是此格式。
先确定每帧应传入的short点数:
int mu_chunksize = multinet->get_samp_chunksize(model_data);mu_chunksize与 AFE 中 fetch 的每帧数据点数完全一致。然后将 AFE 实时fetch到的数据送入识别接口(buff长度为mu_chunksize * sizeof(int16_t)):
esp_mn_state_t mn_state = multinet->detect(model_data, buff);识别结果与识别模式
命令词识别必须和唤醒搭配使用:唤醒后运行 MultiNet 检测。detect()每帧返回当前识别状态mn_state:
ESP_MN_STATE_DETECTING:正在识别中,尚未识别到目标命令词;ESP_MN_STATE_DETECTED:识别到了目标命令词,此时调用get_results()获取结果:
esp_mn_results_t *mn_result = multinet->get_results(model_data);结果存储在返回值中,数据结构定义于 esp_mn_iface.h(ESP_MN_RESULT_MAX_NUM为 5):
typedef struct{ esp_mn_state_t state; int num; // The number of phrase in list, num<=5. When num=0, no phrase is recognized. int phrase_id[ESP_MN_RESULT_MAX_NUM]; // The list of phrase id. float prob[ESP_MN_RESULT_MAX_NUM]; // The list of probability. } esp_mn_results_t;其中state为当前识别状态,num为识别到的词条数目(最多 5 个候选结果,0 表示未识别到),phrase_id为词条对应的 Phrase ID,prob为识别概率、从大到小排列。用户可直接使用phrase_id[0]和prob[0]拿到概率最高的识别结果;仓库中的英文头文件版本还额外包含command_id[]、string[256]、raw_string[256]字段,可直接输出识别到的命令 ID 与文本。
ESP_MN_STATE_TIMEOUT:长时间未检测到命令词,自动退出,等待下次唤醒。
由此定义两种识别模式:
- 单次识别模式:状态返回
ESP_MN_STATE_DETECTED时退出命令词识别; - 连续识别模式:状态返回
ESP_MN_STATE_TIMEOUT时退出命令词识别。
test_apps/esp-sr-multinet/main/test_multinet.cpp 给出了一个完整的“喂帧—检测—取结果”参考循环:逐帧memcpy音频到buffer,调用detect(),若返回ESP_MN_STATE_DETECTED则get_results()打印command_id[0]与string后退出。
资源消耗与精度参考
各芯片上 MultiNet 的资源消耗与性能数据见 性能测试结果。以 ESP32-S3 为例:
| 模型 | Internal RAM | PSRAM | 单帧平均耗时 | 帧长 |
|---|---|---|---|---|
| MultiNet 4 | 16.8 KB | 1866 KB | 18 ms | 32 ms |
| MultiNet 4 Q8 | 10.5 KB | 1009 KB | 11 ms | 32 ms |
| MultiNet 5 Q8 | 16 KB | 2310 KB | 12 ms | 32 ms |
| MultiNet 6 | 32 KB | 4100 KB | 12 ms | 32 ms |
| MultiNet 7 | 18 KB | 2920 KB | 11 ms | 32 ms |
ESP32-P4 上 MultiNet 7 单帧平均耗时约 8 ms。官方还给出了空调控制场景(3 m 距离)的实测准确率:mn5_cn 安静环境 88.9%;mn6_cn 安静环境 98.8%;在空调场景数据集上额外微调的 mn6_cn_ac 安静环境 97.1%、平稳噪声下 95.1%、人声噪声下 96.8%——从这组数据可以推断,对垂直场景(如空调控制)选用场景微调模型(_ac后缀)收益明显。
小结
MultiNet 提供了“模型选择(Kconfig)→ 命令词配置(fst 文件 / menuconfig / API)→ 与 AFE、WakeNet 串联运行 → 状态机取结果”的完整离线命令词识别链路。落地时建议:先按芯片在menuconfig选定模型并配置model分区;按版本要求准备拼音/音素形式的命令词表;在detect()循环中按ESP_MN_STATE_DETECTED/ESP_MN_STATE_TIMEOUT选择单次或连续识别模式,并用get_results()中的phrase_id[0]/prob[0]驱动上层业务逻辑。完整示例可参考 test_apps/esp-sr-multinet 工程。
- 语音
- 音频
- 人工智能
- 嵌入式
- 物联网
【免费下载链接】esp-sr
Speech recognition
相关推荐
esp-sr 离线命令词识别实战:MultiNet 模型架构、命令词自定义与动态增删改全指南
esp sr 离线命令词识别实战:MultiNet 模型架构、命令词自定义与动态增删改全指南 导读 本文围绕 ESP SR(Espressif 语音识别仓库)中
语音音频人工智能嵌入式物联网PaddleSpeech 关键词识别(KWS)实战:从命令行到 MDTC 模型源码解析
PaddleSpeech 关键词识别(KWS)实战:从命令行到 MDTC 模型源码解析 关键词识别(Keyword Spotting, KWS)用于判断一段语音
人工智能语音音频NLP媒体生成ESP-SR项目中关于语音唤醒与命令识别的技术解析
ESP SR项目中关于语音唤醒与命令识别的技术解析 语音唤醒与命令识别的独立性 在ESP SR语音识别框架中,语音唤醒 wake word 识别和语音命令 co
语音音频人工智能嵌入式物联网
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考