
ESP-IDF LP VAD 语音活动检测硬件驱动指南低功耗语音唤醒与 HP/LP 双核协同【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idfVADVoice Activity Detection语音活动检测是语音唤醒与多媒体功能中第一级算法的硬件加速实现。ESP-IDF 通过 LP VAD 硬件外设驱动让开发者能够以极低的功耗持续监听麦克风输入在检测到语音活动后唤醒 HP高性能核心或 LP低功耗核心从而实现低功耗语音唤醒方案。本文以 vad.rst 文档为骨架结合 ESP-IDF 仓库中的驱动源码与测试用例完整讲解lp_vad_config_t配置结构、状态机工作逻辑、HP 驱动与 LP 驱动的资源分配与唤醒流程并提供可直接落地的代码示例。引言VAD 模块能做什么VAD 模块利用硬件实现语音活动检测的第一级算法是语音唤醒voice wake-up及各类多媒体功能的起点。与在 HP 核上运行的纯软件 VAD 算法相比LP VAD 的硬件实现带来两个核心收益始终在线监听VAD 硬件可以在芯片处于睡眠模式时持续监听音频输入无需 HP 核保持运行极低功耗只有在检测到语音活动时才唤醒更高功耗的核心为电池供电设备如智能音箱、语音遥控器提供低功耗语音唤醒的硬件支撑。从源码结构看VAD 功能与 LP I2SLow Power Inter-IC Sound模块强耦合VAD 依赖 LP I2S 的 RX 通道接收音频数据。LP I2S 在睡眠模式下仍可接收数据且不使用 DMA而是使用独立的内部存储空间存放数据参见 lp_i2s.rst。因此要使用 VAD必须先创建 LP I2S RX 通道。目前该功能仅在支持SOC_LP_I2S_SUPPORT_VAD/SOC_LP_VAD_SUPPORTED特性的芯片上可用例如 ESP32-P4见 soc_caps.h 中#define SOC_LP_VAD_SUPPORTED 1与#define SOC_LP_I2S_SUPPORT_VAD (1)。实际使用前请确认目标芯片的 SOC capabilities 是否包含这两项宏。硬件状态机理解 VAD 的判定逻辑VAD 的检测过程并不是简单的能量超过阈值即触发而是一个包含两个状态的状态机speak-activity-listening-state语音活动监听态与speak-activity-detected-state语音活动已检测态。LP VAD 驱动通过配置结构 lp_vad_config_t 来调节这个状态机的行为。配置结构字段详解lp_vad_config_t结构体的每个字段都直接映射到状态机的转移条件字段类型作用init_frame_numint初始化帧数。VAD 使用这些初始帧进行去噪帮助降低误触发率。注意取值过大会导致语音活动漏检voice activity missmin_energy_threshint最小能量阈值。能量高于此值的语音活动才会被检测到skip_band_energy_threshbool是否跳过启用带通能量检测。该检查判断带通能量在总频域能量中的占比是否满足阈值要求。不同环境下开启它可能降低误触发率但也可能提高漏检率speak_activity_threshint处于speak-activity-listening-state时若检测到的语音活动数高于此值VAD 进入speak-activity-detected-statenon_speak_activity_threshint处于speak-activity-detected-state时若检测到的语音活动数高于min_speak_activity_thresh但低于max_speak_activity_thresh检测到的非语音活动数高于此值则回到监听态低于此值则保持检测态min_speak_activity_threshint处于speak-activity-detected-state时语音活动数高于此值但低于max_speak_activity_thresh时状态转移取决于non_speak_activity_threshmax_speak_activity_threshint处于speak-activity-detected-state时若检测到的语音活动数高于此值VAD 回到speak-activity-listening-state状态转移图原文档给出的状态机如下其中三个关键转移条件分别是speak_activity_thresh进入检测态、min_speak_activity_thresh non_speak_activity_thresh保持检测态、max_speak_activity_thresh回到监听态┌──────────────────────────────────┐ │ │ ┌─────────────┤ speak-activity-listening-state │ ◄───────────────┐ │ │ │ │ │ └──────────────────────────────────┘ │ │ ▲ │ │ │ │ detected speak activity │ │ detected speak activity │ detected speak activity │ │ │ speak_activity_thresh │ │ min_speak_activity_thresh │ max_speak_activity_thresh │ │ │ │ │ │ │ │ detected non-speak activity │ │ │ │ │ │ non_speak_activity_thresh │ │ │ │ │ ┌───────────┴─────────────────────┐ │ │ │ │ │ └───────────► │ speak-activity-detected-state ├─────────────────┘ │ │ └─┬───────────────────────────────┘ │ ▲ │ │ detected speak activity │ │ │ │ min_speak_activity_thresh │ │ │ │ detected non-speak activity └─────────────────────┘ non_speak_activity_thresh该状态机的注释在驱动头文件 lp_i2s_vad.h 与 LP 核共享头文件 ulp_lp_core_lp_vad_shared.h 中均有完整保留是理解阈值参数之间关系的权威参考。设计思路解读init_frame_num负责去噪、min_energy_thresh负责能量门限两者共同压制环境噪声的误触发而speak_activity_thresh/non_speak_activity_thresh/min_speak_activity_thresh/max_speak_activity_thresh四个计数阈值共同构成迟滞逻辑避免在语音与非语音边界上频繁抖动既不会一检测到声音就立刻触发唤醒也不会在短暂停顿如词间停顿时立即丢失检测状态。在驱动源码中验证配置项的落地配置结构中的每个字段都会在创建 VAD 单元时被写入硬件寄存器。在 lp_i2s_vad.c 的lp_i2s_vad_new_unit函数中可以看到完整的映射链lp_vad_ll_set_init_frame_num(unit-hw, init_config-vad_config.init_frame_num); lp_vad_ll_set_init_min_energy(unit-hw, init_config-vad_config.min_energy_thresh); lp_vad_ll_set_speak_activity_thresh(unit-hw, init_config-vad_config.speak_activity_thresh); lp_vad_ll_set_non_speak_activity_thresh(unit-hw, init_config-vad_config.non_speak_activity_thresh); lp_vad_ll_set_min_speak_activity_thresh(unit-hw, init_config-vad_config.min_speak_activity_thresh); lp_vad_ll_set_max_speak_activity_thresh(unit-hw, init_config-vad_config.max_speak_activity_thresh); lp_vad_ll_skip_band_energy(unit-hw, init_config-vad_config.skip_band_energy_thresh);这些lp_vad_ll_*是 LLLow Level层寄存器操作接口定义在 hal/lp_i2s_ll.h若该文件路径在当前仓库中不可直接访问可搜索components/hal目录下的同名头文件。可见vad_config中的全部七个参数在创建阶段即一次性下发到硬件后续 enable/disable 只做使能位切换不重复写阈值。同时注意两个从源码中可以确认的细节init_frame_num有硬件取值范围限制驱动通过LP_VAD_LL_INIT_FRAME_MIN/LP_VAD_LL_INIT_FRAME_MAX校验越界会返回ESP_ERR_INVALID_ARG见 lp_i2s_vad.cVAD 单元句柄的内存通过heap_caps_calloc(..., MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)从内部 RAM 分配lp_i2s_vad.c保证关键路径数据不经过外部 PSRAM。驱动内部状态管理VAD 驱动内部维护一个vad_fsm_t状态VAD_FSM_INIT/VAD_FSM_ENABLE见 lp_i2s_vad.c并据此校验 API 调用时机lp_i2s_vad_new_unit创建单元同时通过原子操作s_vad_claim占用对应的vad_id防止同一 VAD 被重复创建返回ESP_ERR_NOT_FOUNDlp_i2s_vad_enable仅在VAD_FSM_INIT状态下允许调用将驱动切到VAD_FSM_ENABLE并打开硬件lp_i2s_vad_disable反向操作回到VAD_FSM_INIT并停止硬件lp_i2s_vad_del_unit仅在VAD_FSM_INIT状态下允许调用释放vad_id占用并释放内存。这保证了init → enable → disable → del的生命周期顺序任何非法调用都会返回ESP_ERR_INVALID_STATE。HP 驱动让 VAD 唤醒 HP 核HP 驱动HP Driver用于配置 LP VAD 在 HP 核的控制下工作并且当检测到语音活动时VAD 也可以唤醒 HP 核。资源分配HP 侧的使用以 lp_vad_init_config_t 为初始化结构它包含两个成员lp_i2s_chanLP I2S 通道句柄lp_i2s_chan_handle_t类型。由于 VAD 依赖 LP I2S 的 RX 通道输入音频必须先创建 LP I2S 通道再将其句柄传入vad_config上文详述的lp_vad_config_t阈值配置。创建 LP I2S 通道的代码来自 lp_i2s.rst 与测试用例lp_i2s_chan_handle_t rx_handle NULL; lp_i2s_chan_config_t config { .id 0, .role I2S_ROLE_SLAVE, // LP I2S 仅支持作为 I2S Slave 工作例如 ESP32-P4 .threshold 512, }; ESP_ERROR_CHECK(lp_i2s_new_channel(config, NULL, rx_handle));随后通过lp_i2s_channel_init_std_mode以标准模式初始化 RX 通道测试中使用LP_I2S_STD_PCM_SHORT_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)配置 16-bit 立体声 PCM 短槽格式再调用lp_i2s_vad_new_unit创建 VAD 单元。完整的创建、使用与回收流程如下vad_unit_handle_t vad_handle NULL; lp_vad_init_config_t init_config { .lp_i2s_chan rx_handle, .vad_config { .init_frame_num 100, .min_energy_thresh 100, .speak_activity_thresh 10, .non_speak_activity_thresh 30, .min_speak_activity_thresh 3, .max_speak_activity_thresh 100, }, }; ESP_ERROR_CHECK(lp_i2s_vad_new_unit(vad_id, init_config, vad_handle)); // ... 使用 VAD ... ESP_ERROR_CHECK(lp_i2s_vad_del_unit(vad_handle));上述示例中的数值直接取自原文档与 test_vad_wakeup.c、test_lp_core_vad.c 两个测试用例是一组经过实际验证、可直接运行的参考参数100 个初始化帧用于去噪、能量阈值 100、语音活动计数阈值 10、非语音活动计数阈值 30、最小/最大语音活动计数阈值 3/100。实际项目建议根据麦克风灵敏度与环境噪声实测调整。使能与关闭在开始检测前必须调用lp_i2s_vad_enable使能 VAD 单元lp_i2s_vad_enable驱动状态从init切换到enable同时打开 VAD 硬件lp_i2s_vad_disable反向操作驱动回到init状态硬件停止。使能前还需要先使能 LP I2S RX 通道lp_i2s_channel_enable因为 VAD 的输入数据来自该通道。HP 核唤醒esp_sleep_enable_vad_wakeup()将 VAD 注册为 HP 核的唤醒源。要让 VAD 在睡眠期间持续工作系统需要保持 RTC 域与 XTAL 供电。调用方式极为简洁ESP_ERROR_CHECK(esp_sleep_enable_vad_wakeup());从 sleep_modes.c 的源码实现可以看到这个函数实际做了三件事esp_sleep_acquire_lp_use_xtal()获取 LP 域使用 XTAL 的权限保证 VAD 所需的时钟在睡眠中不关闭esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON)强制保持 RTC 外设域上电s_config.wakeup_triggers | RTC_LP_VAD_TRIG_EN在睡眠唤醒触发寄存器中置位 VAD 触发使能。唤醒后可通过esp_sleep_get_wakeup_causes()与ESP_SLEEP_WAKEUP_VAD位定义于 esp_sleep.h判断唤醒原因是否来自 VAD。一个完整的 HP 核唤醒验证流程见 test_vad_wakeup.c配置 VAD 唤醒源 → 创建并初始化 LP I2S RX 通道 → 创建并初始化 VAD 单元 → 使能 RX 通道与 VAD → 调用esp_light_sleep_start()进入浅睡眠 → 由外部设备通过 HP I2S TX 通道注入 8 kHz PCM 测试音频 → 等待 VAD 唤醒并打印唤醒原因。测试中 HP I2S 与 LP I2S 的引脚连接为BCK 4/7、WS 5/8、DIN 6/21LP/HP 各自一侧。LP 驱动让 VAD 唤醒 LP 核LP 驱动LP Driver主要用于 LP 核即 ULP LP 协处理器唤醒VAD 在 HP 核的控制下完成配置检测到语音活动后唤醒 LP 核。资源分配LP 侧驱动将 HP 驱动的配置结构直接复用为lp_core_lp_vad_cfg_ttypedef lp_vad_init_config_t lp_core_lp_vad_cfg_t见 ulp_lp_core_lp_vad_shared.h因此两者的初始化参数完全一致。核心 API 为lp_core_lp_vad_init(vad_id, init_config)初始化 VAD LP 驱动lp_core_lp_vad_deinit(vad_id)回收已分配资源lp_core_lp_vad_enable(vad_id)/lp_core_lp_vad_disable(vad_id)使能 / 关闭硬件。从实现看ulp_lp_core_lp_vad_shared.cLP 侧驱动本质上是对 HP 侧驱动的封装lp_core_lp_vad_init内部调用lp_i2s_vad_new_unit并保存句柄到全局变量s_vad_handleenable/disable/deinit则分别转发到对应的lp_i2s_vad_*函数。源码中通过IS_ULP_COCPU宏做了双端编译隔离在 ULP 协处理器侧编译时这些函数直接返回ESP_ERR_NOT_SUPPORTED因此这些初始化与配置 API 只能在 HP 核上调用。LP 核唤醒在ulp_lp_core_cfg_t配置结构中将wakeup_source设置为ULP_LP_CORE_WAKEUP_SOURCE_LP_VAD即可让 VAD 作为 LP 核的唤醒源。原文档给出的完整流程如下static void load_and_start_lp_core_firmware(ulp_lp_core_cfg_t* cfg, const uint8_t* firmware_start, const uint8_t* firmware_end) { TEST_ASSERT(ulp_lp_core_load_binary(firmware_start, (firmware_end - firmware_start)) ESP_OK); TEST_ASSERT(ulp_lp_core_run(cfg) ESP_OK); } ulp_lp_core_cfg_t cfg { .wakeup_source ULP_LP_CORE_WAKEUP_SOURCE_LP_VAD, }; load_and_start_lp_core_firmware(cfg, lp_core_main_vad_bin_start, lp_core_main_vad_bin_end);这套流程在 test_lp_core_vad.c 测试用例中被完整复现其完整调用顺序为创建 LP I2S RX 通道并初始化标准模式以lp_core_lp_vad_init(0, init_config)初始化 VADvad_id传 0加载 LP 核固件并以 VAD 为唤醒源启动 LP 核ulp_lp_core_load_binaryulp_lp_core_run使能 LP I2S RX 通道与 VADlp_i2s_channel_enablelp_core_lp_vad_enable通过 HP I2S TX 通道回放 8 kHz PCM 测试音频轮询 LP 核共享变量ulp_vad_wakup检测到唤醒后完成回收disable 通道、删除通道、释放内存。该测试还展示了测试音频的来源方式PCM 数据以二进制段形式链接进固件_binary_test_vad_8k_pcm_start/_binary_test_vad_8k_pcm_end通过i2s_channel_preload_data预载入 HP I2S TX 通道的 DMA 缓冲区后循环写入。两种唤醒路径的选型对比维度HP 驱动HP 核唤醒LP 驱动LP 核唤醒配置入口lp_i2s_vad_new_unitesp_sleep_enable_vad_wakeuplp_core_lp_vad_initULP_LP_CORE_WAKEUP_SOURCE_LP_VAD唤醒目标HP 核应用主核LP 核ULP LP 协处理器睡眠要求保持 RTC 域与 XTAL 供电依赖 LP I2S / VAD 硬件持续工作典型场景语音唤醒后立即执行完整应用逻辑语音唤醒后先由 LP 核做轻量处理如简单特征判断必要时再唤醒 HP 核唤醒原因查询esp_sleep_get_wakeup_causes()的ESP_SLEEP_WAKEUP_VAD位轮询 LP 核共享变量 / LP 核程序逻辑从源码结构看LP 驱动是对 HP 驱动的薄封装两者的硬件配置路径完全一致差异只在于唤醒目标与固件加载方式。实际项目可以根据功耗预算与响应需求选择VAD → LP 核 →需要时HP 核的多级唤醒链将功耗压到最低。设计建议与调试要点先调init_frame_num与min_energy_thresh这两个参数决定基础的去噪与能量门限。init_frame_num过大容易漏检真实语音过小则去噪不足、误触发率高min_energy_thresh应结合环境噪声实测取略高于稳态噪声的能量值。再调四个计数阈值形成迟滞speak_activity_thresh决定多快进入检测态调小响应更快、但更容易误触发non_speak_activity_thresh决定多快退出检测态调大更抗词间停顿干扰max_speak_activity_thresh是强制回归监听态的保护阈值避免长时间非语音能量被误判为语音后长期卡在检测态。skip_band_energy_thresh按环境取舍开启带通能量占比检查可压制带外噪声但在某些频段环境可能提高漏检率需要通过实测对比误触发率与召回率。严格遵循 API 生命周期new_unit → enable → disable → del_unit的顺序由驱动内部状态机强制保证越序调用返回ESP_ERR_INVALID_STATE启用状态下的del_unit同样会被拒绝。测试参考仓库中的两个测试用例test_vad_wakeup.c 与 test_lp_core_vad.c是完整的端到端参考实现覆盖了 HP I2S 音频注入、LP I2S 接收、VAD 配置、睡眠与唤醒验证的全链路非常适合作为新项目的起点模板。相关文档导航LP I2S 外设完整指南lp_i2s.rstVAD 的音频输入通道来源VAD 驱动头文件含状态机与 API 声明lp_i2s_vad.hVAD 驱动实现lp_i2s_vad.cLP 核共享 VAD 驱动ulp_lp_core_lp_vad_shared.c、ulp_lp_core_lp_vad_shared.h睡眠唤醒接口esp_sleep.h、实现 sleep_modes.cHP 核唤醒测试test_vad_wakeup.cLP 核唤醒测试test_lp_core_vad.c【免费下载链接】esp-idfEspressif IoT Development Framework. Official development framework for Espressif SoCs.项目地址: https://gitcode.com/GitHub_Trending/es/esp-idf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考