VAD 详细参数说明
VAD(Voice Activity Detection)用于一句话识别(dictation)模式下的语音起止检测。当前实现基于 Silero VAD ONNX 模型,同时叠加一层动态能量阈值,用来过滤低能量底噪。
设计理念
可以把 VAD 理解成 EasyMrcp 里的“听筒开关”。电话里一直都有音频流进来,但一句话识别不适合把整通电话都持续送给 ASR,它更适合在用户真正开始说话时打开识别,在用户说完后及时收尾。
VAD 做的事情就是判断三件事:
- 用户是不是开始说话了。
- 用户是不是还在继续说话。
- 用户是不是已经停顿足够久,可以认为这一句话结束了。
为了避免误判,当前实现不会只看“声音大不大”,也不会只看模型判断“像不像人声”,而是两个条件一起看:
- Silero VAD 模型判断这段音频像人声;
- 当前音频能量明显高于背景底噪。
这样做的好处是,空调声、电流声、轻微杂音这类“有声音但不像人在说话”的内容不容易触发识别;而真正的人声出现后,系统会回退一小段缓存音频再发给 ASR,尽量避免把句首截掉。当检测到用户停顿超过配置时间后,VAD 会通知 ASR 这一句话可以结束输入。
本文档基于当前源码整理,主要对应以下类:
com.cfsl.easymrcp.vad.VadHandlecom.cfsl.easymrcp.vad.SlieroVadDetectorcom.cfsl.easymrcp.vad.SlieroVadOnnxModelcom.cfsl.easymrcp.rtp.NettyAsrRtpProcessorcom.cfsl.easymrcp.asr.AsrHandler
1. 生效范围
VAD 只在 ASR 识别模式为 dictation 时启用:
ASRConstant.IDENTIFY_PATTERNS_DICTATION实时转写(transliterate)模式不会走 VAD 分段逻辑,而是直接把 RTP 解码后的 PCM 音频发送给 ASR。
2. 基础参数
2.1 模型文件
| 参数 | 当前值 | 说明 |
|---|---|---|
MODEL_PATH | silero_vad.onnx | Silero VAD ONNX 模型文件名。 |
模型路径解析规则:
- 优先查找当前工作目录下的
src/main/resources/silero_vad.onnx。 - 如果不存在,则查找当前工作目录下的
silero_vad.onnx。
2.2 采样率
| 参数 | 当前逻辑 | 说明 |
|---|---|---|
sampleRate | 8000 或 16000 | VAD 检测器只接受 8k 或 16k 采样率。 |
采样率由 ASR 配置中的 reSample 决定:
reSample = upsample8kTo16k时,VAD 使用16000- 其他情况默认使用
8000
2.3 VAD 聚合帧大小
| 场景 | vadFrameSizeBytes | 说明 |
|---|---|---|
| 8k PCM | 2048 字节 | 每次聚合 2048 字节后做一次 VAD 检测。 |
| 16k PCM | 4096 字节 | 8k 上采样到 16k 后,聚合帧字节数同步翻倍。 |
VAD 缓冲区容量为 vadFrameSizeBytes * 3,用于容纳 2 到 3 个 VAD 帧,避免数据积压。
3. 模型概率阈值
| 参数 | 当前值 | 说明 |
|---|---|---|
START_THRESHOLD | 0.4f | 当 Silero 模型输出的人声概率大于等于该值时,模型侧认为可能开始说话。 |
END_THRESHOLD | 0.8f | 已经进入说话状态后,当人声概率低于该值时,进入可能结束判断。 |
语音开始不是只看 START_THRESHOLD。当前实现还要求当前帧 RMS 能量大于等于动态能量阈值。
语音结束判断流程:
- 已处于说话状态。
- 当前帧
speechProb < END_THRESHOLD。 - 静音持续时间达到
MIN_SILENCE_DURATION_MS。 - 确认语音结束。
4. 动态能量阈值
当前实现已经不再使用“全量历史平均能量 × 倍数”的方式计算阈值,而是使用背景底噪估计:
energyThreshold = max(MIN_ENERGY_THRESHOLD_FLOOR, noiseFloorEnergy * ENERGY_THRESHOLD_MULTIPLIER)| 参数 | 当前值 | 说明 |
|---|---|---|
MIN_ENERGY_THRESHOLD_FLOOR | 0.01f | 能量阈值最小下限,避免极安静环境下阈值过低。 |
ENERGY_THRESHOLD_MULTIPLIER | 1.4f | 动态阈值倍数。 |
noiseFloorAlpha | 0.01f | 背景底噪指数滑动平均系数。 |
noiseFloorEnergy | 初始为 0.01f | 估计出来的背景底噪能量。 |
底噪更新逻辑:
if (!triggered) {
noiseFloorEnergy = noiseFloorAlpha * rmsEnergy + (1 - noiseFloorAlpha) * noiseFloorEnergy
}
energyThreshold = max(MIN_ENERGY_THRESHOLD_FLOOR, noiseFloorEnergy * ENERGY_THRESHOLD_MULTIPLIER)说明:
- 只有未进入说话状态时,当前帧 RMS 能量才会参与背景底噪估计。
- 进入说话状态后,不再用人声能量抬高底噪估计。
- 语音开始要求同时满足:
speechProb >= START_THRESHOLDrmsEnergy >= energyThreshold
5. 时间控制参数
| 参数 | 当前值 | 说明 |
|---|---|---|
MIN_SILENCE_DURATION_MS | 默认 300 ms | 判定语音结束所需的最小静音持续时间。 |
SPEECH_PAD_MS | 500 ms | 语音起止点前后的补偿时间。 |
MIN_SILENCE_DURATION_MS 可以通过 DetectSpeech 事件里的 SpeechCompleteTimeout 下发:
{
"StartInputTimers": true,
"NoInputTimeout": 60000,
"SpeechCompleteTimeout": 800,
"AutomaticInterruption": true
}注意:
- 当前代码只在
StartInputTimers=true的分支里读取SpeechCompleteTimeout。 SpeechCompleteTimeout需要在 VAD 初始化前设置才会稳定生效。- VAD 初始化后调用
setSpeechCompleteTimeout不会动态修改已创建检测器,只会记录告警日志。
6. 工作流程
6.1 RTP 到 VAD
NettyAsrRtpProcessor接收 RTP 包。- 根据协商编码解码成 16 位单声道 PCM。
- 如果配置了
upsample8kTo16k,先上采样到 16k。 dictation模式下写入主音频缓冲区和 VAD 环形缓冲区。- VAD 缓冲区达到
vadFrameSizeBytes后,取出一帧送入VadHandle.receivePcm()。
6.2 VAD 检测
- 将 16 位小端 PCM 转为
float采样值。 - 计算当前帧 RMS 能量
rmsEnergy。 - 调用 Silero ONNX 模型得到人声概率
speechProb。 - 在非说话状态下更新
noiseFloorEnergy和energyThreshold。 - 根据模型概率和能量阈值判断语音开始。
- 根据结束阈值和静音持续时间判断语音结束。
6.3 语音开始后的处理
检测到语音开始时:
- 主音频缓冲区读指针回退
500ms - 异步重新连接一句话 ASR
- 将缓冲区中的音频发送给 ASR,避免丢掉句首
6.4 语音结束后的处理
检测到语音结束时:
- 发送主音频缓冲区里的剩余音频
- 调用
sendEof() - 由具体 ASR 实现返回最终识别结果
7. 实时日志
当前 VAD 每 200ms 最多打印一次实时统计日志,字段包括:
vadProbstartThresholdendThresholdrmsEnergyenergyThresholdnoiseFloorEnergytriggered
这些字段可用于判断误触发或漏判原因。
8. 参数调优建议
当前默认参数:
START_THRESHOLD = 0.4
END_THRESHOLD = 0.8
MIN_SILENCE_DURATION_MS = 300
SPEECH_PAD_MS = 500
MIN_ENERGY_THRESHOLD_FLOOR = 0.01
ENERGY_THRESHOLD_MULTIPLIER = 1.4
noiseFloorAlpha = 0.01安静环境
- 保持
MIN_ENERGY_THRESHOLD_FLOOR = 0.01 - 如仍有漏判,可适当降低
ENERGY_THRESHOLD_MULTIPLIER - 不建议轻易降低
START_THRESHOLD,否则更容易误触发
噪音环境
- 可适当提高
ENERGY_THRESHOLD_MULTIPLIER - 可适当增加
MIN_SILENCE_DURATION_MS - 通过实时日志观察
rmsEnergy与energyThreshold的差距,再决定调参
更快断句
- 降低
SpeechCompleteTimeout - 注意过低会把长句中的自然停顿切成多句
更稳断句
- 提高
SpeechCompleteTimeout - 适合用户说话停顿较多、但希望尽量保持整句识别的场景
9. 对接注意事项
DetectSpeech建议始终显式传StartInputTimers。- 如果需要配置
SpeechCompleteTimeout,应与StartInputTimers=true一起传。 - VAD 只控制一句话识别的音频分段,不直接生成
RecognitionComplete。 RecognitionComplete仍由具体 ASR 引擎返回最终结果后触发。- 如果日志中
vadProb已经超过START_THRESHOLD,但没有进入说话状态,优先检查rmsEnergy是否低于energyThreshold。
