From 99db948b9624e1fb8a20c7735d0f843d1963740a Mon Sep 17 00:00:00 2001 From: hrz <1710360675@qq.com> Date: Wed, 22 Oct 2025 10:46:57 +0800 Subject: [PATCH] =?UTF-8?q?update:=E6=81=A2=E5=A4=8Dvad=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/config.yaml | 2 +- main/xiaozhi-server/core/connection.py | 2 +- .../core/providers/vad/silero.py | 37 +++++++++++-------- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/main/xiaozhi-server/config.yaml b/main/xiaozhi-server/config.yaml index 4a410399..b2cc9fe6 100644 --- a/main/xiaozhi-server/config.yaml +++ b/main/xiaozhi-server/config.yaml @@ -454,7 +454,7 @@ VAD: threshold: 0.5 threshold_low: 0.3 model_dir: models/snakers4_silero-vad - min_silence_duration_ms: 200 # 如果说话停顿比较长,可以把这个值设置大一些,实际延迟为:min_silence_duration_ms // 32 * 32ms + min_silence_duration_ms: 200 # 如果说话停顿比较长,可以把这个值设置大一些 LLM: # 所有openai类型均可以修改超参,以AliLLM为例 diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index fe213558..92043e9b 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -115,10 +115,10 @@ class ConnectionHandler: # vad相关变量 self.client_audio_buffer = bytearray() self.client_have_voice = False + self.client_voice_window = deque(maxlen=5) self.last_activity_time = 0.0 # 统一的活动时间戳(毫秒) self.client_voice_stop = False self.last_is_voice = False - self.continuous_false_count = 0 # asr相关变量 # 因为实际部署时可能会用到公共的本地ASR,不能把变量暴露给公共ASR diff --git a/main/xiaozhi-server/core/providers/vad/silero.py b/main/xiaozhi-server/core/providers/vad/silero.py index 70cb5459..2263fcb7 100644 --- a/main/xiaozhi-server/core/providers/vad/silero.py +++ b/main/xiaozhi-server/core/providers/vad/silero.py @@ -1,6 +1,6 @@ import time -import torch import numpy as np +import torch import opuslib_next from config.logger import setup_logging from core.providers.vad.base import VADProviderBase @@ -26,12 +26,15 @@ class VADProvider(VADProviderBase): threshold_low = config.get("threshold_low", "0.2") min_silence_duration_ms = config.get("min_silence_duration_ms", "1000") - # 双阈值判断参数 self.vad_threshold = float(threshold) if threshold else 0.5 self.vad_threshold_low = float(threshold_low) if threshold_low else 0.2 - # 静默阈值,连续多少帧低于最低值才判断为静音 - self.stop_false_count = int(min_silence_duration_ms) // 32 if min_silence_duration_ms else 31 + self.silence_threshold_ms = ( + int(min_silence_duration_ms) if min_silence_duration_ms else 1000 + ) + + # 至少要多少帧才算有语音 + self.frame_window_threshold = 3 def is_vad(self, conn, opus_packet): try: @@ -56,24 +59,26 @@ class VADProvider(VADProviderBase): # 双阈值判断 if speech_prob >= self.vad_threshold: - client_have_voice = True + is_voice = True elif speech_prob <= self.vad_threshold_low: - client_have_voice = False + is_voice = False else: - client_have_voice = conn.last_is_voice - - if not client_have_voice: - conn.continuous_false_count += 1 - else: - conn.continuous_false_count = 0 + is_voice = conn.last_is_voice # 声音没低于最低值则延续前一个状态,判断为有声音 - conn.last_is_voice = client_have_voice + conn.last_is_voice = is_voice - # 如果之前有声音,且连续为静音帧,达到静默阈值要求帧数,则认为已经说完一句话 - if conn.client_have_voice and conn.continuous_false_count >= self.stop_false_count: - conn.client_voice_stop = True + # 更新滑动窗口 + conn.client_voice_window.append(is_voice) + client_have_voice = ( + conn.client_voice_window.count(True) >= self.frame_window_threshold + ) + # 如果之前有声音,但本次没有声音,且与上次有声音的时间差已经超过了静默阈值,则认为已经说完一句话 + if conn.client_have_voice and not client_have_voice: + stop_duration = time.time() * 1000 - conn.last_activity_time + if stop_duration >= self.silence_threshold_ms: + conn.client_voice_stop = True if client_have_voice: conn.client_have_voice = True conn.last_activity_time = time.time() * 1000