From 9ee6ac649c1cce099827747b11000036039cdb48 Mon Sep 17 00:00:00 2001 From: kslr Date: Sun, 22 Feb 2026 11:12:46 +0000 Subject: [PATCH 1/2] feat(xiaozhi): improve wake word robustness and remove boost path --- examples/xiaozhi/README.md | 8 +++---- examples/xiaozhi/config.py | 2 -- .../xiaozhi/services/audio/kws/__init__.py | 22 +++++++++++++++++++ .../xiaozhi/services/audio/kws/sherpa.py | 4 ++-- .../xiaozhi/xiaozhi/services/audio/stream.py | 8 +------ 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/examples/xiaozhi/README.md b/examples/xiaozhi/README.md index a8e352b..beda4ba 100644 --- a/examples/xiaozhi/README.md +++ b/examples/xiaozhi/README.md @@ -136,15 +136,13 @@ APP_CONFIG = { ### Q:唤醒词一直没有反应? -由于小爱音箱远场拾音音量较小,有时可能会识别不清,你可以调大 `config.py` 配置文件里的 `boost` 参数,然后重启应用 / Docker 试试看。 +如果唤醒词还是不敏感,可以先调低 `vad.threshold`,然后重启应用 / Docker 试试看。 ```py APP_CONFIG = { "vad": { - # 小爱音箱录音音量较小,需要后期放大一下 - "boost": 100, - # boost 调大后,语音检测阈值可能也需要一起调大些 - "threshold": 0.50, + # 语音检测阈值(0-1,越小越灵敏) + "threshold": 0.05, }, # ... 其他配置 } diff --git a/examples/xiaozhi/config.py b/examples/xiaozhi/config.py index aa843f8..fd316e5 100644 --- a/examples/xiaozhi/config.py +++ b/examples/xiaozhi/config.py @@ -53,8 +53,6 @@ APP_CONFIG = { "after_wakeup": after_wakeup, }, "vad": { - # 录音音量增强倍数(小爱音箱录音音量较小,需要后期放大一下) - "boost": 10, # 语音检测阈值(0-1,越小越灵敏) "threshold": 0.10, # 最小语音时长(ms) diff --git a/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py b/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py index e7b0286..15f6e8a 100644 --- a/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py +++ b/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py @@ -3,6 +3,8 @@ import os import threading import time +import numpy as np + from config import APP_CONFIG from xiaozhi.event import EventManager from xiaozhi.ref import get_speaker, get_xiaoai, get_xiaozhi, set_kws @@ -11,9 +13,13 @@ from xiaozhi.services.audio.stream import MyAudio from xiaozhi.services.protocols.typing import AudioConfig, DeviceState from xiaozhi.utils.base import get_env +KWS_MIN_RMS = 300.0 +KWS_MIN_TRIGGER_INTERVAL_MS = 1500 + class _KWS: def __init__(self): + self.last_trigger_ms = 0.0 set_kws(self) def start(self): @@ -45,6 +51,12 @@ class _KWS: def resume(self): self.paused = False + def _rms(self, frames: bytes) -> float: + samples = np.frombuffer(frames, dtype=np.int16).astype(np.float32) + if samples.size == 0: + return 0.0 + return float(np.sqrt(np.mean(samples * samples) + 1e-6)) + def _detection_loop(self): SherpaOnnx.start() self.stream.start_stream() @@ -65,8 +77,18 @@ class _KWS: time.sleep(0.01) continue + # 静音门控:低能量帧跳过 KWS 推理 + if self._rms(frames) < KWS_MIN_RMS: + time.sleep(0.005) + continue + result = SherpaOnnx.kws(frames) if result: + # 防抖:限制连续触发频率,降低重复误触发 + now_ms = time.monotonic() * 1000.0 + if now_ms - self.last_trigger_ms < KWS_MIN_TRIGGER_INTERVAL_MS: + continue + self.last_trigger_ms = now_ms print(f"🔥 触发唤醒: {result}") self.on_message(result) diff --git a/examples/xiaozhi/xiaozhi/services/audio/kws/sherpa.py b/examples/xiaozhi/xiaozhi/services/audio/kws/sherpa.py index 667c22a..b9d231b 100644 --- a/examples/xiaozhi/xiaozhi/services/audio/kws/sherpa.py +++ b/examples/xiaozhi/xiaozhi/services/audio/kws/sherpa.py @@ -9,10 +9,10 @@ class _SherpaOnnx: self.keyword_spotter = sherpa_onnx.KeywordSpotter( provider="cpu", num_threads=1, - max_active_paths=4, + max_active_paths=8, keywords_score=2.0, keywords_threshold=0.2, - num_trailing_blanks=1, + num_trailing_blanks=0, keywords_file=get_model_file_path("keywords.txt"), tokens=get_model_file_path("tokens.txt"), encoder=get_model_file_path("encoder.onnx"), diff --git a/examples/xiaozhi/xiaozhi/services/audio/stream.py b/examples/xiaozhi/xiaozhi/services/audio/stream.py index ca11d4e..a5a7489 100644 --- a/examples/xiaozhi/xiaozhi/services/audio/stream.py +++ b/examples/xiaozhi/xiaozhi/services/audio/stream.py @@ -1,9 +1,6 @@ import uuid from typing import Any, Callable, ClassVar, Optional -import numpy as np - -from config import APP_CONFIG from xiaozhi.ref import get_xiaoai @@ -88,10 +85,7 @@ class MyStream: return if len(data) > 0: - samples = np.frombuffer(data, dtype=np.int16) - # 小爱音箱录音音量较小,需要后期放大一下 - samples = samples * APP_CONFIG["vad"]["boost"] - self.input_bytes.extend(samples.tobytes()) + self.input_bytes.extend(data) def read(self, num_frames=None, exception_on_overflow=False) -> bytes: if num_frames is None: From 68b5484da59740ee1d4516312485d5af7424d68c Mon Sep 17 00:00:00 2001 From: kslr Date: Tue, 24 Feb 2026 15:19:33 +0800 Subject: [PATCH 2/2] revert(xiaozhi): remove kws rms gate and debounce --- .../xiaozhi/services/audio/kws/__init__.py | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py b/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py index 15f6e8a..e7b0286 100644 --- a/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py +++ b/examples/xiaozhi/xiaozhi/services/audio/kws/__init__.py @@ -3,8 +3,6 @@ import os import threading import time -import numpy as np - from config import APP_CONFIG from xiaozhi.event import EventManager from xiaozhi.ref import get_speaker, get_xiaoai, get_xiaozhi, set_kws @@ -13,13 +11,9 @@ from xiaozhi.services.audio.stream import MyAudio from xiaozhi.services.protocols.typing import AudioConfig, DeviceState from xiaozhi.utils.base import get_env -KWS_MIN_RMS = 300.0 -KWS_MIN_TRIGGER_INTERVAL_MS = 1500 - class _KWS: def __init__(self): - self.last_trigger_ms = 0.0 set_kws(self) def start(self): @@ -51,12 +45,6 @@ class _KWS: def resume(self): self.paused = False - def _rms(self, frames: bytes) -> float: - samples = np.frombuffer(frames, dtype=np.int16).astype(np.float32) - if samples.size == 0: - return 0.0 - return float(np.sqrt(np.mean(samples * samples) + 1e-6)) - def _detection_loop(self): SherpaOnnx.start() self.stream.start_stream() @@ -77,18 +65,8 @@ class _KWS: time.sleep(0.01) continue - # 静音门控:低能量帧跳过 KWS 推理 - if self._rms(frames) < KWS_MIN_RMS: - time.sleep(0.005) - continue - result = SherpaOnnx.kws(frames) if result: - # 防抖:限制连续触发频率,降低重复误触发 - now_ms = time.monotonic() * 1000.0 - if now_ms - self.last_trigger_ms < KWS_MIN_TRIGGER_INTERVAL_MS: - continue - self.last_trigger_ms = now_ms print(f"🔥 触发唤醒: {result}") self.on_message(result)