尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

moonshine-micro 端上语音活动检测(VAD)实战指南:TinyVadCNN 推理、段切分与 1 秒语音片段提取

moonshine-micro 端上语音活动检测(VAD)实战指南:TinyVadCNN 推理、段切分与 1 秒语音片段提取 moonshine-micro 端上语音活动检测VAD实战指南TinyVadCNN 推理、段切分与 1 秒语音片段提取【免费下载链接】moonshineVery low latency speech to text, intent recognition, and text to speech, for building voice agents and interfaces项目地址: https://gitcode.com/GitHub_Trending/moonshine3/moonshine本篇技术指南聚焦 moonshine-micro 中的micro/vad模块如何在嵌入式设备上以 int8TinyVadCNN模型完成始终开启always-on的语音活动检测用VadSegmenter把每帧语音概率平滑成[start, end)语音段边界并配合MelStreamer流式 log-mel 前端与能量质心对齐提取出供 STT 分类器转录的 1 秒片段。读完本文你将掌握该模块的完整调用链、内存与算力开销、宿主仿真测试方式以及如何用生成脚本产出固件中烧录的 VAD 数据。模块定位与整体架构micro/vad是 moonshine-micro 端上语音流水线中的监听哨兵。它不做转录只负责一件事在始终开启的路径上以每 ~32 ms hop 一次的频率把音频流变成语音段边界STT 分类器随后只对检测到的语音片段做转录从而把宝贵的 MCU 算力集中在真正需要识别的时刻。模块被刻意拆成两个可复用部件见 micro/vad/README.md类职责VadTFLM 封装器输入(n_mels × window_frames)的 log-mel 窗口输出[0, 1]区间的一个语音概率VadSegmenter平滑 分段把逐帧概率变成[start, end)采样点索引形式的语音段边界不存储任何音频喂给Vad的流式 log-mel 前端来自micro/feature-generation模块的MelStreamer每个 hop 只做一次 FFT。应用层按MelStreamer → Vad → VadSegmenter顺序组合参考micro/examples/rp2350的音频路径src/audio_service.cc。模块依赖面极小只有 feature-generation 和 TFLM 两个依赖因此 VAD 可以不加修改地移植到另一个平台的不同示例程序中。模型规格来自仓库证据仓库中的micro/models/tinyvad_cnn_speech_meta.json明确了模型的输入输出契约采样率sample_rate: 16000 HzMel 通道数n_mels: 32窗口帧数window_frames: 16hop 长度hop_length: 512 采样点即 32 ms 16 kHzFFT 长度n_fft: 512频率范围f_min/f_max: 20.0 / 8000.0 Hz输入张量名input_name:log_mel输出张量名output_name:speech_logit模型仓库里提供了三个变体文件tinyvad_cnn_speech_mel.onnx、tinyvad_cnn_speech_mel_fp32.tflite和tinyvad_cnn_speech_mel_head16.tflite。公开 API 与调用方式整个模块只有一个公开头文件micro/vad/include/vad/vad.h头文件顶部注释直接说明了两个半区的职责。README 给出了最核心的调用骨架spelling::Vad vad(model, model_size, arena, arena_size, n_mels, window_frames); float* feats vad.feature_scratch(); streamer.BuildModelInput(feats); float p vad.Predict(feats); // speech probability spelling::VadSegmenter seg(threshold, smooth_frames, hop, look_behind_samples, max_segment_samples); seg.Start(); if (seg.ProcessFrame(p) spelling::VadEvent::kSpeechEnd) { spelling::ExtractClipFrontAligned(audio, n, seg.segment_start_sample(), seg.segment_end_sample(), clip, clip_len); }VadTFLM 模型封装器Vad的构造函数实现见 micro/vad/src/vad.cc接收模型 flatbuffer 指针、由调用方持有的张量 arena生命周期必须长于Vad以及期望的n_mels、window_frames。构造函数会对模型做一系列防错校验失败即while(true)死循环 halt模型 schema 版本必须匹配TFLITE_SCHEMA_VERSIONarena 大小不得小于静态预留区1 KiB 的kStaticsReservation加工作区输入张量必须是 int8输出张量必须是 int8 或 int16对应两种导出变体输入字节数必须等于n_mels × window_frames输出必须是单 logit。值得注意的实现细节Op 解析器MicroMutableOpResolver8注册了 8 个算子——CONV_2D、DEPTHWISE_CONV_2D、PAD、ADD、SUM、FULLY_CONNECTED、RESHAPETinyVadCNN 复用了与 SpellingCNN 相同的 MobileNetV2 模块外加一个QUANTIZE用于 head16 混合精度模型在 int16 头之前的 int8→int16 重量化。纯 int8 模型下QUANTIZE无害且不被使用因此同一个 resolver 可同时服务两种导出变体。arena 复用VAD 与 STT 永远不会同时Invoke()因此 VAD 直接共享应用预分配的 STT tensor arena不增加额外静态 SRAM。量化输入/输出Predict()内部先把 fp32 特征按input_quant_的 scale/zero_point 饱和量化成 int8Saturate8钳位到 [-128, 127]Invoke()后对输出 logit 反量化int16 或 int8 皆可再套 sigmoid 得到[0, 1]语音概率。特征暂存区feature_scratch()返回从 arena 激活叠加区底部借用的 fp32 缓冲区若与 int8 输入槽位重叠会自动偏移错开。契约是先在这里产出特征再调用Predict(it)。模型形状无关模型字节、arena 和(n_mels, window_frames)全部由调用方提供模块自身不携带任何模型或平台依赖。VadSegmenter无堆的滑动平均平滑器VadSegmenter实现见 micro/vad/src/vad_segmenter.cc是纯逻辑部件不碰模型、不碰音频数据。构造函数参数与默认值参数含义默认由生成脚本写入vad_config.hthreshold平滑后概率判定为语音的阈值0.5window_frames滑动平均窗口长度帧数0.5 s 语音对应帧数约 16 帧 32 ms hop上限钳制为kVadSmoothWindowMax 64hop每帧推进的采样点数51232 ms 16 kHzlook_behind_samples语音起始前溯的采样点数81920.5 smax_segment_samples单个语音段的最大长度超过后强制切段15 s关键实现点平滑维护一个固定大小环形prob_window_kVadSmoothWindowMax 64帧约 2 s远大于默认 0.5 s对原始概率逐帧求均值。threshold 0时强制视为语音。最大段长线性衰减当当前段长度超过max_segment_samples * 2/3时平滑概率按比例线性衰减从而在长连续语音中强制制造断点。状态机ProcessFrame()返回VadEvent枚举kNone/kSpeechStart/kSpeechContinuing/kSpeechEnd。段边界以绝对采样索引表示segment_start_采用当前已处理采样数减去 look-behind的前溯策略。末尾冲刷Finish()在流结束时关闭仍然打开的尾随语音段vad_segmenter_test.cc的TrailingSegmentFlushed用例专门验证这一点。零堆成员只有定长数组与计数器RAM 约 0.3 KiB。辅助函数片段提取与能量质心ExtractClipFrontAligned(src, src_len, start, end, out, clip_len)从段起点截取并零填充到定长 clip超出源范围的部分以 0.0f 填充clip 过短时截断。EnergyCentroidIndex(buf, start, end)计算功率加权质心采样索引round(Σ i·buf[i]² / Σ buf[i]²)。静音或空区域返回区域中点。实现刻意使用 int64 累加器1 s / 16 kHz int16 窗口下den ≤ ~1.6e13、num ≤ ~2.7e17远小于 int64 上限既避免了热路径上的浮点运算又保证结果确定可复现。能量质心对齐为什么不要前端对齐README 明确提醒segment_start_sample()是一个有噪声的锚点——滑动平均平滑会滞后真实起始点最多约 0.6 s0.5 s 的 look-behind 只能平均意义上抵消这一滞后合并段或过早触发还会把起点再推偏数百毫秒。因此前端对齐把段起点放在 clip 开头会切掉起始辅音E 集识别的大敌或把单词孤立在窗口边缘。正确的做法是以语音能量为中心用EnergyCentroidIndex把 1 秒 clip 中心对准被说单词的能量质心。宿主仿真scripts/test_streaming_vad.py --align-sweep显示这一改动几乎完全收回了流式 VAD 的精度差距整体 ~7 个点E 集 ~11 个点。audio_service.cc中的实际做法见 micro/examples/rp2350/src/audio_service.cc// buf holds the last WS samples; the segment occupies the tail. std::size_t region (seg_len WS) ? 0 : (WS - seg_len); std::size_t c spelling::EnergyCentroidIndex(buf, region, WS); long shift (long)c - WS / 2; // slide so the word sits at WS/2, // memmove buf by shift, zero-pad the vacated side.计算质心后若单词偏晚shift 0则向左滑动并在尾部补零若偏早shift 0则向右滑动并在头部补零最终让单词落在环形缓冲中点WS/2处。源码注释中还特别解释了索引映射最新采样始终位于window[WS-1]因此绝对采样索引a对应缓冲索引(WS - now a)。内存与算力开销README 给出了完整的资源清单运行目标为 RP2350 250 MHz资源大小说明Flash模型64 KiBint8 TinyVadCNN 权重vad_model_data.*Flash前端~25 KiBVAD mel 表vad_mel_tables.*RAMarena 峰值~36 KiBTFLM 工作集与 STT arena 顺序共享RAMsegmenter~0.3 KiB平滑环形缓冲 计数器无音频缓冲Heap0无动态分配由于 VAD 与 STT 不会同时Invoke()VAD 的 arena 使用不会在应用预分配的 384 KiB 共享 tensor arena 之外增加任何静态 SRAM。延迟 250 MHz操作延迟算力约说明Vad::Predict()每 32 ms 音频约 3.1 ms每 32 ms 音频约 0.8 MMAC约 25 MMAC/sint8 TinyVadCNNInvokeVAD 整步mel 推理每 32 ms 音频约 3.5 ms每 32 ms 音频约 0.8 MMAC约 25 MMAC/s流式 mel Predict每 32 ms 音频只Invoke()一次——在 250 MHz 下有约11 倍余量。segmenter 只是 O(window) 的滑动平均开销可忽略。mel 前端本身的成本~0.4 ms/32 ms单一 512 点 FFT详见 micro/feature-generation/README.md 的延迟表。测试micro/vad/tests/vad_segmenter_test.cc使用 TFLM 的micro_test.h框架在宿主上运行仅 segmenter 逻辑不涉及解释器解释器封装只在目标平台上构建。覆盖的用例包括SingleSegmentDetected20 帧静音 40 帧语音 40 帧静音应只检出一个段且段起点因 look-behind 前溯而早于第一个有声帧TwoSegmentsDetected两段语音被静音分隔应检出两个段TrailingSegmentFlushed语音从不回落到静音时Finish()必须关闭尾随段NoLookBehindStartsLater无 look-behind 时段起点不早于有 look-behind 的情形ExtractClipFrontAlignedZeroPads验证截取与尾部零填充EnergyCentroidIndexWeightsByPower验证功率加权单个响亮采样主导质心、双等能峰取中点、静音区返回中点、尊重[start, end)子区间。其中EnergyCentroidIndex的测试值可以直接当作行为规范{0,0,0,1000,0,0,0,0}的质心是索引 3{0,500,0,0,0,0,500,0}双峰质心是 round(3.5)4全零数组返回中点 2。生成嵌入式 VAD 数据micro/vad/scripts/generate_vad_embedded_data.py负责产出编译进固件的数据 blob默认输出到micro/examples/rp2350/generated/产物内容何时生成vad_config.h音频 / 前端 / segmenter 常量kVadSampleRate、kVadHop、kVadNMels、kVadWindowFrames、kVadThreshold、kVadSmoothWindowFrames、kVadLookBehindSamples、kVadMaxSegmentSamples、kVadClipNumSamples等总是vad_mel_tables.{h,cc}预计算的周期 Hann 窗 CSR Slaney mel 滤波器组模型无关总是vad_model_data.{h,cc}int8 VAD.tflite的 flash 字节数组g_vad_model_data/g_vad_model_data_size提供或发现模型时README 给出的两条命令# config mel tables only (no model file needed) python scripts/generate_vad_embedded_data.py --config-only # also embed the checked-in int8 model python scripts/generate_vad_embedded_data.py \ --tflite ../models/tinyvad_cnn_speech_mel_head16.tflite脚本还支持--out-dir自定义输出目录默认为examples/rp2350/generated/相对路径会锚定到 moonshine-micro 根。不传--tflite时脚本会在micro/models/下自动发现tinyvad_cnn_speech_mel_head16.tflite。脚本从micro/models/vad_net.py导入VAD_F_MIN/F_MAX/HOP/N_FFT/N_MELS/SAMPLE_RATE/WINDOW_FRAMES等模型常量从micro/models/log_mel_pure.py复用hann_window与make_mel_filterbank并把 1 s 提取 clip与 STT 分类器输入一致、阈值 0.5、0.5 s 平滑时长、8192 采样点 look-behind、15 s 最大段长等默认值烘焙进vad_config.h。mel 表采用紧凑 CSR 存储nz_off/idx/val从不明文物化稠密(n_mels × n_freq)矩阵——64 mel 时单次分配约 64 KB会撑爆小 MCU 堆。完整调用链从麦克风到 STT综合 micro/examples/rp2350/src/audio_service.cc 可以还原始终开启路径的完整时序音频采集I2S 以 16 kHz 采集 int16 采样每 512 采样点32 ms组成一个 hop写入滚动窗口缓冲最新采样始终在尾部window[WS-1]同时转换为 fp32 供MelStreamer::PushHop()使用g_vad_hop_f流式前端MelStreamer用kVadNMels、kVadWindowFrames、kVadNFft及共享 FFT 状态构造对最新 hop 做一次 FFT → 功率 → mel → log 列存入环形缓冲然后BuildModelInput(feats)重建归一化的(32 × 16)模型输入VAD 推理Vad::Predict(feats)返回[0,1]语音概率int8 量化输入 sigmoid分段VadSegmenter::ProcessFrame(p)推进状态机在kSpeechStart打印 VAD start在kSpeechEnd记录segment_start_sample()/segment_end_sample()并标记得到片段能量质心对齐在 int16 采集缓冲上对段区域计算EnergyCentroidIndex用memmove平移 补零把单词居中到WS/2然后打印段时长VAD end dur...STT 阶段Vad析构释放 arena后Classifier复用同一 arena 对居中的 1 s clip 做转录。这一流程中VAD 与分类器严格串行、共享 arena 的设计正是微控制器上始终监听 按需转录低功耗方案的关键。【免费下载链接】moonshineVery low latency speech to text, intent recognition, and text to speech, for building voice agents and interfaces项目地址: https://gitcode.com/GitHub_Trending/moonshine3/moonshine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表