EN

Voice Activity Detection (VAD):用 Python 检测「有没有人在说话」

语音应用的第一道关卡,做好它能省一大半识别成本

返回教程列表🌐 Read in English
进阶9 分钟
AI Skill Navigation 编辑团队发布于 2026年7月22日

Voice Activity Detection (VAD):用 Python 检测「有没有人在说话」

语音应用的第一道关卡,做好它能省一大半识别成本

Voice Activity Detection(VAD)判断音频中哪段是人声,是语音应用省成本的第一关。本文用 Python 实战 webrtcvad 与 Silero VAD:硬约束、原理对比、Whisper 串联与实时流式。

做语音应用时,第一个该解决的不是「识别说了什么」,而是「这段音频里到底有没有人在说话」。这就是 VAD(Voice Activity Detection,语音活动检测)的核心任务。VAD 在语音流水线中扮演着关键角色——它把静音和噪声段过滤掉,只保留人声段,从而大幅提升后续语音识别(ASR)的效率与准确性。无论是构建实时语音助手、处理长录音,还是优化 API 调用成本,VAD 都是不可或缺的基础组件。

为什么 VAD 如此重要

一段录音通常混合着:人说话、停顿静音、背景噪声。VAD 的任务是给每一小段音频打标签——「这是语音」还是「这不是语音」。典型用途包括:

  • 实时语音助手:检测到用户开始说话才唤醒,说完了才提交识别。这避免了持续发送静音数据,降低了带宽和计算开销。
  • 降低 ASR 成本:只把人声段送去识别,静音段不送。以 Whisper 为例,静音段识别不仅浪费计算资源,还可能产生无意义的幻觉文本(如「嗯…」或随机噪声的误识别)。
  • 语音分段:长录音按说话/停顿切成片段,便于后续处理,如说话人分离(diarization)或逐段转写。
  • VAD 的精度直接影响整个语音流水线的质量。一个误报(将噪声判为语音)会导致 ASR 产生垃圾文本;一个漏检(将语音判为静音)则会丢失关键信息。因此,选择合适的 VAD 方案至关重要。

    方案一:webrtcvad(轻量、快速)

    Google WebRTC 项目中的 VAD 实现,极轻量,纯靠信号特征(如能量、过零率、频谱特征)判断,无需加载任何模型,速度飞快。它基于高斯混合模型(GMM)对语音和噪声的统计分布进行建模,在干净环境下表现可靠。

    python
    import webrtcvad

    创建 VAD 实例,aggressiveness 参数 0-3

    0 最宽松(更多被判为语音),3 最激进(更多被判为非语音)

    vad = webrtcvad.Vad(2)

    音频必须满足硬约束:

    - 采样率:8000、16000、32000 或 48000 Hz

    - 帧长:10ms、20ms 或 30ms(必须严格匹配)

    - 格式:单声道、16-bit PCM

    假设 frame_bytes 是 30ms 的 16kHz 音频数据

    30ms * 16kHz * 2字节/采样 = 960 字节

    frame_bytes = b'\x00\x00' * 480 # 示例:30ms 静音帧 is_speech = vad.is_speech(frame_bytes, sample_rate=16000) print(f"是否语音: {is_speech}")

    优点:零模型依赖、CPU 开销极低、适合嵌入式或资源受限场景。在树莓派或低功耗 MCU 上也能实时运行。 缺点:在噪声环境下容易误判——它分不清「人声」和「像人声的噪声」(如风扇声、键盘声)。aggressiveness 调高会减少误报但增加漏检,尤其在轻声或语速慢的场景下。

    方案二:Silero VAD(准确、抗噪)

    基于神经网络的 VAD,准确率显著更高,尤其抗噪声能力强。模型很小(约 2MB),在 CPU 上即可实时运行。它使用深度神经网络(如 LSTM 或 CNN)学习语音与噪声的复杂模式,对未见过的噪声类型也有一定泛化能力。

    python
    

    方式一:通过 silero-vad 包(推荐)

    pip install silero-vad

    from silero_vad import load_silero_vad, get_speech_timestamps, read_audio

    model = load_silero_vad() wav = read_audio('audio.wav', sampling_rate=16000) # 支持 8000 和 16000 Hz speech_ts = get_speech_timestamps( wav, model, sampling_rate=16000, threshold=0.5, # 概率阈值,默认 0.5 min_speech_duration_ms=250, # 最小语音段长度 min_silence_duration_ms=100, # 最小静音段长度 speech_pad_ms=30 # 前后 padding )

    speech_ts: [{'start': 12000, 'end': 35000}, ...] 采样点区间

    print(f"检测到 {len(speech_ts)} 个语音段")

    python
    

    方式二:通过 torch.hub(备选)

    import torch model, utils = torch.hub.load('snakers4/silero-vad', 'silero_vad') (get_speech_timestamps, _, read_audio, _, _) = utils

    后续用法同上

    优点:准确、抗噪、多语言通用、模型轻量。在嘈杂的咖啡馆或工厂环境中,Silero VAD 的误报率远低于 webrtcvad。 缺点:需要加载模型(约 2MB 内存),比 webrtcvad 稍重,但绝大多数场景这点开销值得。首次加载模型需要下载权重文件,建议离线缓存。

    原理对比:能量/统计特征法 vs 神经网络模型法

    方法原理适用场景误检特点

    能量/统计特征法(如 webrtcvad)基于短时能量、过零率、频谱平坦度等手工特征 + 高斯混合模型安静环境、资源受限设备噪声下易将非语音判为语音(误报);安静时准确 神经网络模型法(如 Silero VAD)基于深度神经网络(如 LSTM、CNN)学习语音与噪声的复杂模式噪声环境、高精度要求噪声下准确率高;但模型可能对未见过的噪声类型失效

    关键差异:webrtcvad 是「规则驱动」,Silero VAD 是「数据驱动」。前者在干净环境下表现良好,后者在复杂声学环境中显著更优。实际项目中,如果对延迟和资源消耗不敏感,优先选择 Silero VAD。

    与 ASR 串联:VAD + Whisper 省成本

    VAD 切段后再送 Whisper(或 faster-whisper),可减少静音段识别开销,同时避免静音段产生幻觉文本。这在处理长录音或实时流式音频时效果显著。

    python
    import torch
    from silero_vad import load_silero_vad, get_speech_timestamps, read_audio
    from faster_whisper import WhisperModel

    1. VAD 检测语音段

    model_vad = load_silero_vad() wav = read_audio('long_audio.wav', sampling_rate=16000) speech_ts = get_speech_timestamps(wav, model_vad, sampling_rate=16000)

    2. 加载 Whisper 模型

    model_asr = WhisperModel("base", device="cpu", compute_type="int8")

    3. 逐段识别

    full_text = [] for seg in speech_ts: segment = wav[seg['start']:seg['end']] # faster-whisper 接受 numpy 数组 segments, _ = model_asr.transcribe(segment, beam_size=5) for s in segments: full_text.append(s.text)

    print("识别结果:", " ".join(full_text))

    省成本原理:假设一段 10 分钟录音,实际人声只有 3 分钟。不加 VAD 时,Whisper 需处理全部 10 分钟;加 VAD 后只处理 3 分钟,计算量减少 70%。对于 API 调用(如 OpenAI Whisper API),直接按音频时长计费,省成本效果更明显。此外,VAD 切段还能避免 Whisper 在静音段产生「嗯…」之类的幻觉文本,提升输出质量。

    实时流式 vs 离线处理设计差异

    维度离线处理实时流式

    输入完整音频文件音频流(分块到达) 缓冲无需缓冲需要环形缓冲(ring buffer)管理帧 端点检测简单:检测到静音即结束需要 hangover(拖尾)机制:检测到静音后等待一段时间才确认结束 延迟无实时要求通常要求 100-300ms 内响应

    实时流式 VAD 示例(伪代码逻辑)

    python
    

    环形缓冲 + hangover 机制

    ring_buffer = [] hangover_frames = 0 HANGOVER_THRESHOLD = 10 # 连续 10 帧非语音才确认结束

    while streaming: frame = get_audio_frame() # 从麦克风获取 30ms 帧 ring_buffer.append(frame) is_speech = vad.is_speech(frame, 16000) if is_speech: hangover_frames = 0 # 继续累积音频 else: hangover_frames += 1 if hangover_frames >= HANGOVER_THRESHOLD: # 确认语音段结束,处理 ring_buffer 中的音频 process_speech_segment(ring_buffer) ring_buffer.clear()

    实时流式 VAD 的关键在于 hangover 机制:检测到静音后不立即结束,而是等待若干帧确认。这避免了因短暂停顿(如说话间隙)而错误切段。同时,环形缓冲用于管理音频帧,确保在语音段结束时能完整输出。

    实际调参时有两个关键点。一是灵敏度:安静办公室里中等参数即可,但呼叫中心、车载等噪声场景通常要更严格(webrtcvad 调高 aggressiveness,Silero 调高语音概率阈值),代价是可能切掉轻声细语——务必用真实部署环境的录音来调,别用干净的演示音频。二是帧长:30ms 帧调用次数少、CPU 开销低,10ms 帧粒度更细、端点判定更快。常见的生产做法是 VAD 按 30ms 跑,段边界再由 ASR 的词级时间戳做精修。

    常见坑与解决方案

  • 采样率不匹配:webrtcvad 只支持 8000/16000/32000/48000 Hz。如果音频是 44100 Hz,必须先重采样。
  • python
       import librosa
       audio, _ = librosa.load('audio.wav', sr=16000, mono=True)
       

  • 帧长不对:webrtcvad 帧长必须严格为 10/20/30ms。计算帧字节数:帧长(秒) * 采样率 * 2
  • python
       frame_duration_ms = 30  # 30ms
       frame_size = int(16000 * frame_duration_ms / 1000) * 2  # 960 字节
       

  • 噪声下 aggressiveness 调整:webrtcvad 的 aggressiveness 参数 0-3。噪声环境建议用 2 或 3,但会漏检轻声。Silero VAD 可通过 threshold 参数调整灵敏度,降低阈值可捕获更多语音,但可能增加误报。
  • pyaudio 采集示例
  • python
       import pyaudio
       import webrtcvad
       
       CHUNK = 480  # 30ms @ 16kHz
       FORMAT = pyaudio.paInt16
       CHANNELS = 1
       RATE = 16000
       
       p = pyaudio.PyAudio()
       stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE,
                       input=True, frames_per_buffer=CHUNK)
       vad = webrtcvad.Vad(2)
       
       while True:
           data = stream.read(CHUNK)
           is_speech = vad.is_speech(data, RATE)
           # 处理语音段...
       

    如何选择

    场景推荐

    资源极受限 / 嵌入式webrtcvad 环境安静、要极致快webrtcvad 有噪声 / 要准Silero VAD 大多数应用Silero VAD

    实话说,除非你跑在很弱的设备上,默认选 Silero VAD,省心又准。它的准确性和抗噪能力远超 webrtcvad,而模型加载开销在当今硬件上几乎可以忽略。

    更多资源

    VAD 切出人声段后,下一步就是送去 ASR 做识别。更多 ASR 接入方案见 API 集成专题,想自建 Whisper 等模型的推理服务见 模型部署专题。此外,对于需要区分说话人的场景,VAD 的输出可作为说话人分离(diarization)的输入,进一步细化处理。

    FAQ

    Q: webrtcvad 的 aggressiveness 参数 0-3 具体有什么区别? A: 0 最宽松(更多帧被判为语音),3 最激进(更多帧被判为非语音)。噪声环境下建议用 2 或 3,但会漏检轻声或语速慢的语音。实际使用中,建议在目标场景下测试不同值,找到误报和漏检的平衡点。

    Q: Silero VAD 支持哪些采样率? A: 官方支持 8000 Hz 和 16000 Hz。其他采样率需要先重采样。推荐使用 16000 Hz,因为这是大多数 ASR 模型(如 Whisper)的标准输入。

    Q: VAD 能区分不同说话人吗? A: 不能。VAD 只检测「有没有人说话」,不区分是谁在说。说话人分离(diarization)需要专门的模型,如 pyannote-audio 或 NVIDIA NeMo 中的说话人分离模块。

    Q: 为什么 VAD 切段后要加 padding? A: 避免切掉语音的起始和结尾部分(如辅音、尾音)。通常前后各加 30-100ms 的 padding。Silero VAD 的 speech_pad_ms 参数就是为此设计。

    Q: 实时流式 VAD 的延迟如何控制? A: 主要取决于帧长和 hangover 时间。30ms 帧长 + 10 帧 hangover ≈ 300ms 延迟。可通过减小帧长(如 10ms)或 hangover 阈值来降低延迟,但可能增加误判。对于实时交互场景,建议延迟控制在 200ms 以内。

    Q: Silero VAD 需要 GPU 吗? A: 不需要。Silero VAD 的模型非常小巧(权重约 2MB),在 CPU 上就能远超实时速度运行——一台普通 VPS 也能同时处理多路音频流。只有在批量处理海量历史录音、追求极限吞吐时,GPU 才有意义。

    *最后更新:2026 年 7 月。请以各工具官方文档为准。*

    相关工具

    Silero VADwebrtcvadWhisperPyTorch