RealtimeSTT 唤醒词(Wake Word)完整实战指南:Porcupine 与 OpenWakeWord 双后端配置、调参与源码解析
【免费下载链接】RealtimeSTTA robust, efficient, low-latency speech-to-text library with advanced voice activity detection, wake word activation and instant transcription.项目地址: https://gitcode.com/GitHub_Trending/re/RealtimeSTT
RealtimeSTT 支持在正式录制语音之前先等待一个唤醒词(Wake Word),只有检测到指定唤醒词后才开始录制并转写后续语音。本文以 docs/wake-words.md 为核心,系统讲解其两种后端(Porcupine 与 OpenWakeWord)的安装、配置、模型文件管理、灵敏度与时机参数、回调函数及排障方法,并结合仓库源码(RealtimeSTT/core/wakeword.py、RealtimeSTT/core/recording.py、RealtimeSTT/audio_recorder.py)深入解析其底层实现原理,帮助开发者从"能跑通"进阶到"会调优"。
唤醒词模式概述与启用方式
唤醒词模式是 RealtimeSTT 在 VAD(语音活动检测)之外提供的一种"由关键词触发的录制"机制:系统持续监听麦克风,只有检测到预设的唤醒词后,才会进入录制状态并开始转写其后的语音。
根据 docs/wake-words.md 的说明,RealtimeSTT 支持两种唤醒词后端:
- Porcupine:通过可选的
pvporcupine包实现,由 Picovoice 提供,内置多种开箱即用的英文关键词; - OpenWakeWord:通过可选的
openwakeword包实现,支持加载用户自训练的 ONNX/TFLite 自定义模型。
唤醒词模式在以下两种条件下被激活(两者满足其一即可):
- 设置了
wake_words参数(此时后端会默认选择 Porcupine,以保证向后兼容); wakeword_backend显式选择了 OpenWakeWord(oww或openwakeword)。
这一逻辑在源码中有直接体现。RealtimeSTT/core/initialization.py 中的_assign_initial_attributes计算:
recorder.use_wake_words = bool( init_args["wake_words"] or normalized_wakeword_backend in OPENWAKEWORD_BACKENDS )而在 RealtimeSTT/core/wakeword.py 中,_normalize_wakeword_backend负责后端名称归一化,且当wakeword_backend为空但提供了wake_words时,会自动回退到 Porcupine:
def _normalize_wakeword_backend(wakeword_backend, wake_words): backend = (wakeword_backend or "").strip().lower().replace("-", "_") if not backend and wake_words: return "pvporcupine" return backend对应的单元测试位于 tests/unit/test_wakeword.py,其中test_bare_wake_words_default_to_porcupine验证了"仅设置 wake_words 时默认走 Porcupine"的行为,test_no_wake_words_keep_backend_empty验证了未设置任何唤醒词参数时后端保持为空。
支持的唤醒词后端别名
后端别名映射定义在 RealtimeSTT/core/wakeword.py 顶部:
| 后端 | 可用别名 |
|---|---|
| Porcupine | pvp、pvporcupine、porcupine |
| OpenWakeWord | oww、openwakeword、openwakewords、open_wakeword、open_wakewords |
需要注意后端名称中的连字符会被归一化为下划线(如open-wakeword等价于open_wakeword),test_openwakeword_backend_normalizes_hyphen测试用例专门验证了这一点。若传入的后端既不属于 Porcupine 集合也不属于 OpenWakeWord 集合,setup_wakeword_detection会抛出ValueError,提示仅支持pvporcupine与openwakeword。
Porcupine 后端:安装、使用与内置关键词
安装 Porcupine 可选依赖
Porcupine 属于可选后端,需要先安装对应的 extra 才能使用:
python -m pip install "RealtimeSTT[porcupine]"在 setup.py 中可以看到,porcupine、pvporcupine、pvp三个 extra 名均映射到同一份porcupine_requirements(即pvporcupine包)。因此以下三种安装方式等价:
python -m pip install "RealtimeSTT[porcupine]" python -m pip install "RealtimeSTT[pvporcupine]" python -m pip install "RealtimeSTT[pvp]"如果希望同时安装其他可选依赖,可以用逗号分隔多个 extra,例如python -m pip install "RealtimeSTT[faster-whisper,porcupine]"。
最小可用示例
from RealtimeSTT import AudioToTextRecorder if __name__ == "__main__": recorder = AudioToTextRecorder( wakeword_backend="pvporcupine", wake_words="jarvis", ) print('Say "Jarvis" and then speak.') print(recorder.text()) recorder.shutdown()运行后,程序会持续监听麦克风;当检测到用户说出 "Jarvis" 后,才开始录制并转写随后的语音,最终把转写文本打印出来。
内置关键词列表
Porcupine 后端自带一批由 Picovoice 预置的关键词(keyword),wake_words参数直接传入名称即可使用:
alexaamericanoblueberrybumblebeecomputergrapefruitsgrasshopperhey googlehey sirijarvisok googlepicovoiceporcupineterminator
同时启用多个关键词
多个 Porcupine 关键词可以用逗号分隔,一次性监听多个唤醒词:
recorder = AudioToTextRecorder(wake_words="jarvis,computer")在 RealtimeSTT/core/wakeword.py 的setup_wakeword_detection中,wake_words字符串会被按逗号拆分、去除空白并转为小写,得到wake_words_list:
recorder.wake_words_list = [ word.strip() for word in wake_words.lower().split(',') if word.strip() ] if wake_words else []随后为每个关键词生成一份灵敏度列表(每个关键词可独立使用相同的wake_words_sensitivity):
recorder.wake_words_sensitivities = [ float(wake_words_sensitivity) for _ in range(len(recorder.wake_words_list)) ]最终通过pvporcupine.create(keywords=..., sensitivities=...)一次性创建多关键词检测器。需要特别注意的是:Porcupine 后端必须有wake_words,否则会抛出ValueError(源码中明确提示 "Porcupine wake word detection requires wake_words")。
Porcupine 初始化与音频参数的联动
从源码可以观察到,初始化 Porcupine 后,录制器的buffer_size和sample_rate会被 Porcupine 引擎自身的参数覆盖:
recorder.porcupine = pvporcupine.create( keywords=recorder.wake_words_list, sensitivities=recorder.wake_words_sensitivities ) recorder.buffer_size = recorder.porcupine.frame_length recorder.sample_rate = recorder.porcupine.sample_rate这意味着 Porcupine 对音频帧长和采样率有硬性要求,RealtimeSTT 会主动跟随引擎设定,确保送入porcupine.process()的每个 PCM 数据块长度与引擎期望的frame_length完全一致。音频处理循环位于 RealtimeSTT/core/recording.py 的_recording_worker中:process_wakeword将原始音频按 16-bit 小端有符号整数(struct.unpack_from("h" * buffer_size, data))解包后交给porcupine.process(),返回porcupine_index;若返回值>= 0,则判定唤醒词命中。开启debug_mode=True时,每次检测的索引值会输出到日志,便于调试。
OpenWakeWord 后端:安装、自定义模型与推理框架
安装 OpenWakeWord 可选依赖
python -m pip install "RealtimeSTT[openwakeword]"在 setup.py 中,openwakeword与oww两个 extra 名均映射到openwakeword_requirements(即openwakeword包)。注意:openwakeword包安装时会自动拉取其 ONNX 推理运行时依赖,因此无需额外安装onnxruntime。
使用示例(自定义模型)
from RealtimeSTT import AudioToTextRecorder if __name__ == "__main__": recorder = AudioToTextRecorder( wakeword_backend="oww", openwakeword_model_paths="models/hey_assistant.onnx", wake_words_sensitivity=0.35, wake_word_buffer_duration=1.0, ) print("Say the trained wake word and then speak.") print(recorder.text()) recorder.shutdown()后端选择上,wakeword_backend="oww"与wakeword_backend="openwakeword"完全等价。
关键特性:OpenWakeWord 的唤醒词名称是从模型文件中自动推断的,因此使用自定义模型路径时无需再设置wake_words。这在源码中体现为:OpenWakeWord 分支不会读取wake_words_list来构造检测器,而是直接基于openwakeword_model_paths加载模型。
仓库中的 OpenWakeWord 实测示例
仓库 tests/openwakeword_test.py 提供了一个可直接运行的 OpenWakeWord 测试脚本,展示了"唤醒词 + 回调"的完整编排:监听唤醒词 "samantha"(模型文件suh_man_tuh.onnx、suh_mahn_thuh.onnx位于 tests 目录),检测到唤醒词后录制并转写,超时则提示用户重新说出唤醒词:
def on_wakeword_detected(): global detected detected = True def on_wakeword_timeout(): global detected if not detected: print(f"Timeout. {say_wakeword_str}") detected = False with AudioToTextRecorder( spinner=False, model="large-v2", language="en", wakeword_backend="oww", wake_words_sensitivity=0.35, openwakeword_model_paths="suh_man_tuh.onnx,suh_mahn_thuh.onnx", on_wakeword_detected=on_wakeword_detected, on_wakeword_timeout=on_wakeword_timeout, on_wakeword_detection_start=on_wakeword_detection_start, wake_word_buffer_duration=1, ) as recorder: while True: recorder.text(text_detected)此外,仓库还附带 tests/vad_test.py、tests/openwakeword_test.py 等测试脚本,可在本地快速验证唤醒词 + VAD 的组合效果。
OpenWakeWord 初始化流程(源码解读)
在 RealtimeSTT/core/wakeword.py 的 OpenWakeWord 分支中,初始化逻辑如下:
- 通过
import_module加载openwakeword及其openwakeword.model.Model类;若缺少依赖,会抛出带有安装提示的ModuleNotFoundError; - 调用
openwakeword.utils.download_models(),确保 OpenWakeWord 的默认/辅助模型资源可用; - 若提供了
openwakeword_model_paths,按逗号拆分后传入Model(wakeword_models=model_paths, inference_framework=...);否则使用Model(inference_framework=...)加载默认模型; - 记录加载的模型数量与每个模型的名称(
owwModel.models.keys()),便于日志排查。
在音频处理循环中,process_wakeword对 OpenWakeWord 的处理方式与 Porcupine 不同:它将原始 PCM 直接转换为 NumPy 数组(np.frombuffer(data, dtype=np.int16)),调用owwModel.predict(pcm)得到各模型的预测分数,然后对prediction_buffer中每个模型的最新分数逐一比较:
for idx, mdl in enumerate(recorder.owwModel.prediction_buffer.keys()): scores = list(recorder.owwModel.prediction_buffer[mdl]) if scores[-1] >= recorder.wake_words_sensitivity and scores[-1] > max_score: max_score = scores[-1] max_index = idx return max_index # 未命中时返回 -1即:任一模型的最近一帧预测分数达到wake_words_sensitivity阈值时即视为命中,并返回得分最高的模型索引。
模型文件:Porcupine 自定义关键词与 OpenWakeWord 模型路径
Porcupine 自定义关键词
Porcupine 的内置关键词由pvporcupine包直接提供,开箱即用。对于自定义关键词,官方流程是通过 Picovoice Console 训练生成模型(.ppn 文件)与关键词定义,然后通过 Porcupine 包的keyword_paths等选项加载。RealtimeSTT 当前的唤醒词抽象层尚未直接暴露 Porcupine 的keyword_paths选项,因此自定义 Porcupine 关键词需要等待该抽象层扩展后,通过 Porcupine 包自身的选项传入——也就是说,现阶段使用 Porcupine 时请优先使用上述内置关键词列表。
OpenWakeWord 模型路径
OpenWakeWord 的openwakeword_model_paths接受逗号分隔的多个模型文件路径:
openwakeword_model_paths="word1.onnx,word2.onnx"每个模型对应一个独立的唤醒词。源码中正是通过openwakeword_model_paths.split(',')得到路径列表后逐个加载。
支持的推理框架
OpenWakeWord 支持两种推理框架:
onnx(Open Neural Network Exchange,默认)tflite(TensorFlow Lite)
通过openwakeword_inference_framework显式指定:
openwakeword_inference_framework="onnx"该参数在 RealtimeSTT/audio_recorder.py 中默认值为"onnx",与openwakeword包的默认行为一致。
模型格式转换:TFLite 转 ONNX
如果手里只有 TensorFlow Lite 模型而希望使用 ONNX 推理,可以使用tf2onnx进行转换:
python -m pip install -U tf2onnx python -m tf2onnx.convert --tflite my_model.tflite --output my_model.onnx训练与转换的一般流程
OpenWakeWord 项目本身提供训练 notebook 与格式转换指导。完整工作流为:
- 在 RealtimeSTT之外完成数据采集、模型训练与格式转换;
- 将最终得到的
.onnx(或.tflite)模型文件放入本地目录; - 通过
openwakeword_model_paths将模型路径传给AudioToTextRecorder。
灵敏度与时机参数详解
以下参数控制唤醒词检测的灵敏度与检测后的时机行为。默认值取自 RealtimeSTT/audio_recorder.py 顶部常量定义(INIT_WAKE_WORDS_SENSITIVITY = 0.6、INIT_WAKE_WORD_ACTIVATION_DELAY = 0.0、INIT_WAKE_WORD_TIMEOUT = 5.0、INIT_WAKE_WORD_BUFFER_DURATION = 0.1):
| 参数 | 默认值 | 含义 |
|---|---|---|
wake_words_sensitivity | 0.6 | 检测阈值,取值0到1。调低可减少漏检(false negatives),但可能增加误报(false positives)。 |
wake_word_activation_delay | 0.0 | 进入唤醒词激活状态前的延迟(秒)。若初始未检测到语音,系统会在该延迟之后切换到唤醒词监听状态;设为0则立即启用唤醒词激活。 |
wake_word_timeout | 5.0 | 唤醒词命中后等待语音的秒数。若在此窗口内未检测到后续语音,系统回到唤醒词模式,等待下一次唤醒。 |
wake_word_buffer_duration | 0.1 | 唤醒词检测前后缓冲/移除的音频时长(秒),目的是让唤醒词本身尽量不进入最终转写文本。 |
参数在构造函数中的完整定义可参见 RealtimeSTT/audio_recorder.py(第 152~166 行附近的唤醒词参数段),这些参数最终通过 RealtimeSTT/core/recorder_config.py 的build_recorder_init_args统一映射后进入初始化流程。
参数背后的源码行为
wake_word_activation_delay:在 RealtimeSTT/core/recording.py 的录制循环中,wake_word_activation_delay_passed表示从开始监听算起已超过该延迟;在延迟尚未过去时,系统走常规的 VAD 激活逻辑(start_recording_on_voice_activity),延迟过去后才进入唤醒词检测分支(process_wakeword)。这提供了一种混合模式:先尝试普通语音激活,超时后才切换为等待唤醒词。
wake_word_timeout:唤醒词命中时记录self.wake_word_detect_time = time.time();当time.time() - self.wake_word_detect_time > self.wake_word_timeout时,系统判定超时并调用on_wakeword_timeout回调,随后恢复等待下一次唤醒。
wake_word_buffer_duration:唤醒词命中时,系统计算需要从录制缓冲中移除的采样数:
wakeword_samples_to_remove = int(self.sample_rate * self.wake_word_buffer_duration)这部分音频(唤醒词本身)会从录音缓冲中剔除,避免其混入后续语音的转写结果。若发现唤醒词字样仍然出现在最终文本中,应调大该参数。
OpenWakeWord 的灵敏度起点建议
对于 OpenWakeWord 自定义模型,官方建议以0.35左右的灵敏度作为首次测试起点,再根据真实房间环境下的录音效果逐步调优(具体数值与麦克风、环境噪声、说话人距离密切相关)。仓库测试脚本 tests/openwakeword_test.py 正是使用了wake_words_sensitivity=0.35。
回调函数:唤醒词生命周期事件
RealtimeSTT 为唤醒词的完整生命周期提供了 4 个回调钩子:
| 回调 | 触发时机 |
|---|---|
on_wakeword_detection_start | 系统开始监听唤醒词时 |
on_wakeword_detection_end | 系统结束监听唤醒词时 |
on_wakeword_detected | 成功检测到唤醒词时 |
on_wakeword_timeout | 唤醒词超时(检测到唤醒词但后续无语音,或进入监听后超时)时 |
基本用法
def detected(): print("wake word detected") def timeout(): print("wake word timeout") recorder = AudioToTextRecorder( wake_words="jarvis", on_wakeword_detected=detected, on_wakeword_timeout=timeout, )回调在源码中的触发点
on_wakeword_detection_start/on_wakeword_detection_end在 RealtimeSTT/core/state.py 的状态切换逻辑中被调用:set_recorder_state在进入/离开唤醒词监听状态时分别触发这两个回调;on_wakeword_detected在 RealtimeSTT/core/recording.py 中唤醒词命中分支调用:检测到wakeword_index >= 0后,设置wakeword_detected = True并执行回调,随后系统进入"等待 VAD 激活"阶段;on_wakeword_timeout有两个触发场景:一是在激活延迟刚过去(wake_word_activation_delay_passed首次变为 True)且设置了延迟时触发一次;二是在唤醒词已命中但超过wake_word_timeout仍未检测到语音时触发。
回调默认在录制线程中同步执行;若回调逻辑较重,可通过构造参数start_callback_in_new_thread=True让回调在独立线程中运行,避免阻塞音频处理循环。回调的具体分发逻辑由run_callback统一封装。
完整实战示例:唤醒词 + 回调 + 持续对话
结合以上内容,一个完整的"唤醒词驱动对话"脚本如下:
from RealtimeSTT import AudioToTextRecorder def on_wakeword_detected(): print("Wake word detected, start speaking...") def on_wakeword_timeout(): print("No speech after wake word, waiting for wake word again.") def on_recording_start(): print("Recording...") def on_recording_stop(): print("Transcribing...") def text_detected(text): print(f">> {text}") recorder = AudioToTextRecorder( model="small", language="en", wakeword_backend="pvporcupine", wake_words="jarvis,computer", wake_words_sensitivity=0.5, wake_word_buffer_duration=0.3, on_wakeword_detected=on_wakeword_detected, on_wakeword_timeout=on_wakeword_timeout, on_recording_start=on_recording_start, on_recording_stop=on_recording_stop, ) print("Say 'Jarvis' or 'Computer' and then speak.") while True: recorder.text(text_detected)该示例同时演示了:多关键词监听、灵敏度与缓冲时长调优、四个唤醒词生命周期回调、以及recorder.text()的持续轮询模式。
故障排查(Troubleshooting)
结合 docs/wake-words.md 的官方建议与源码实现,常见问题及处理如下:
1. 唤醒词始终不触发
逐项排查以下配置:
- 确认
wakeword_backend选择了正确的后端(pvporcupine/oww),且对应的 extra 已安装——未安装时 RealtimeSTT/core/wakeword.py 会抛出带安装提示的ModuleNotFoundError; - 确认麦克风设备(
input_device_index)与采样率配置正确; - 确认模型路径有效:Porcupine 需使用内置关键词名,OpenWakeWord 需提供真实存在的模型文件。
2. OpenWakeWord 模型文件缺失或路径歧义
优先使用绝对路径传入openwakeword_model_paths,消除相对路径带来的歧义。
3. 误报(false positive)过多
- 调高
wake_words_sensitivity(阈值越高越难触发); - 降低房间环境噪声,或调整麦克风摆放位置;
- 若是自定义 OpenWakeWord 模型,使用包含更多负样本(非唤醒词语音)的数据重新训练。
4. 唤醒词出现在最终转写文本中
调大wake_word_buffer_duration,让系统从录制缓冲中移除更多唤醒词音频。默认0.1秒,测试中常见取值为1.0秒(见 tests/openwakeword_test.py)。
5. 使用调试日志辅助定位
开启debug_mode=True后,每次唤醒词检测的porcupine_index或 OpenWakeWord 的max_index/max_score都会输出到日志,可据此确认引擎是否在正常工作、分数是否接近阈值。
与录音状态机的协同
唤醒词模式并非孤立功能,它与 RealtimeSTT 的录音状态机深度集成。在 RealtimeSTT/core/recording.py 中可以看到完整的状态流转:未录制时,若use_wake_words为真且已过激活延迟,状态置为"wakeword",持续调用process_wakeword监听;唤醒词命中后,状态切换到等待 VAD;检测到语音后开始正式录制,进入"recording"状态;wake_word_timeout内无语音则回到"wakeword"状态重新等待。
此外,RealtimeSTT/core/lifecycle.py 中的录制停止逻辑也会判断use_wake_words标志,确保唤醒词模式下停止录制后正确地回到唤醒词监听状态,而非直接进入普通 VAD 监听。这些状态通过 RealtimeSTT/core/state.py 的set_recorder_state统一管理,并顺带触发on_wakeword_detection_start/on_wakeword_detection_end回调。需要了解完整状态机的读者可进一步阅读 docs/configuration.md 与 RealtimeSTT/core/state.py。
总结
RealtimeSTT 的唤醒词功能以wakeword_backend+wake_words两个核心参数为入口,提供了 Porcupine(内置关键词、即插即用)与 OpenWakeWord(自定义模型、ONNX/TFLite 双框架)两种成熟后端。通过wake_words_sensitivity、wake_word_activation_delay、wake_word_timeout、wake_word_buffer_duration四个参数的组合调节,开发者可以在漏检与误报之间找到适合自身场景的平衡点;而四个生命周期回调则让唤醒词事件可以无缝接入 UI 提示、状态机切换等业务逻辑。本文介绍的安装方式、配置示例、调优思路与排障方法均以仓库源码(RealtimeSTT/core/wakeword.py、RealtimeSTT/core/recording.py、RealtimeSTT/audio_recorder.py、setup.py)为据,读者可在此基础上结合自己的麦克风环境与业务场景进一步迭代调优。
【免费下载链接】RealtimeSTTA robust, efficient, low-latency speech-to-text library with advanced voice activity detection, wake word activation and instant transcription.项目地址: https://gitcode.com/GitHub_Trending/re/RealtimeSTT
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考