整合多频段检测、动态阈值调整和持续时长验证的完整代码实现,包含详细注释:
#include "esp_dsp.h"
#include "driver/i2s.h"
#include "esp_log.h"
#include "math.h"
static const char* TAG = "ADV_FREQ_DETECT";
/*======= 系统配置 =======*/
#define FFT_SIZE 1024 // FFT点数
#define SAMPLE_RATE 16000 // 采样率
#define NOISE_BINS 50 // 噪声基底计算区间
#define MIN_DURATION 5 // 最小持续帧数(50ms/帧)
#define THRESHOLD_OFFSET 20.0 // 阈值相对噪声基底的偏移量
/*======= 多频段配置 =======*/
typedef struct {
int target_freq; // 目标频率(Hz)
float threshold_db; // 检测阈值(dB)
int sustain_count; // 持续计数
bool triggered; // 触发状态
} freq_band_t;
// 配置需要检测的三个频段
freq_band_t freq_bands[] = {
{1000, 0, 0, false}, // 初始阈值设为0,实际运行时动态计算
{2000, 0, 0, false},
{3000, 0, 0, false}
};
const int BAND_COUNT = sizeof(freq_bands)/sizeof(freq_band_t);
/*======= 硬件配置 =======*/
__attribute__((aligned(16)))
float fft_input[FFT_SIZE*2]; // FFT输入缓冲
__attribute__((aligned(16)))
float fft_mag[FFT_SIZE/2]; // 幅度谱
__attribute__((aligned(16)))
float window[FFT_SIZE]; // 汉宁窗系数
// I2S配置(根据硬件调整)
#define I2S_CHANNEL I2S_NUM_0
#define I2S_BUF_SIZE 1024
#define I2S_BCK_PIN 32
#define I2S_WS_PIN 25
#define I2S_DATA_PIN 33
/*======= 核心算法函数 =======*/
// 计算噪声基底(使用前NOISE_BINS个bin)
float calculate_noise_floor(float *spectrum) {
float sum = 0;
for(int i=1; i<=NOISE_BINS; i++) { // 从1开始避免DC分量
sum += spectrum[i];
}
return 20 * log10f(sum / NOISE_BINS); // 转换为dB
}
// 检测指定频段是否激活
void check_frequency_band(freq_band_t *band, float *spectrum, float noise_floor) {
const float freq_res = (float)SAMPLE_RATE / FFT_SIZE;
const int target_bin = band->target_freq / freq_res;
const int bin_range = (FREQ_TOLERANCE / freq_res) + 1;
// 搜索目标区间峰值
float peak_mag = 0;
int start_bin = target_bin - bin_range;
int end_bin = target_bin + bin_range;
start_bin = start_bin < 1 ? 1 : start_bin; // 跳过DC
end_bin = end_bin >= FFT_SIZE/2 ? FFT_SIZE/2-1 : end_bin;
for(int i=start_bin; i<=end_bin; i++) {
if(spectrum[i] > peak_mag) {
peak_mag = spectrum[i];
}
}
// 计算实际dB值
float peak_db = 20 * log10f(peak_mag);
bool current_detect = (peak_db > (noise_floor + THRESHOLD_OFFSET));
// 持续时间验证
if(current_detect) {
if(++band->sustain_count >= MIN_DURATION) {
if(!band->triggered) {
band->triggered = true;
ESP_LOGI(TAG, "频段%dHz触发! 强度:%.1fdB",
band->target_freq, peak_db);
}
}
} else {
band->sustain_count = 0;
if(band->triggered) {
band->triggered = false;
ESP_LOGI(TAG, "频段%dHz释放", band->target_freq);
}
}
}
/*======= 信号处理流水线 =======*/
void audio_processing_task(void *arg) {
int16_t raw_data[FFT_SIZE]; // 原始采样缓冲
while(1) {
// 1. 采集音频数据
size_t bytes_read = 0;
i2s_read(I2S_CHANNEL, raw_data, sizeof(raw_data), &bytes_read, portMAX_DELAY);
// 2. 预处理:加窗转换
for(int i=0; i<FFT_SIZE; i++) {
fft_input[i*2] = (float)raw_data[i] * window[i]; // 实部
fft_input[i*2+1] = 0; // 虚部
}
// 3. 执行FFT
dsps_fft2r_fc32(fft_input, FFT_SIZE);
dsps_bit_rev_fc32(fft_input, FFT_SIZE);
dsps_cplx2real_fc32(fft_input, FFT_SIZE);
// 4. 计算幅度谱
for(int i=0; i<FFT_SIZE/2; i++) {
float real = fft_input[i*2];
float imag = fft_input[i*2+1];
fft_mag[i] = sqrtf(real*real + imag*imag);
}
// 5. 动态阈值计算
float noise_floor = calculate_noise_floor(fft_mag);
// 6. 多频段检测
for(int i=0; i<BAND_COUNT; i++) {
check_frequency_band(&freq_bands[i], fft_mag, noise_floor);
}
vTaskDelay(pdMS_TO_TICKS(10)); // 10ms周期
}
}
/*======= 初始化函数 =======*/
void init_system() {
// 初始化I2S
i2s_config_t i2s_cfg = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM,
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_STAND_I2S,
.dma_buf_count = 4,
.dma_buf_len = I2S_BUF_SIZE,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1
};
i2s_pin_config_t pin_cfg = {
.bck_io_num = I2S_BCK_PIN,
.ws_io_num = I2S_WS_PIN,
.data_in_num = I2S_DATA_PIN,
.data_out_num = -1
};
i2s_driver_install(I2S_CHANNEL, &i2s_cfg, 0, NULL);
i2s_set_pin(I2S_CHANNEL, &pin_cfg);
// 初始化FFT
dsps_fft2r_init_fc32(NULL, FFT_SIZE);
dsps_wind_hann_f32(window, FFT_SIZE); // 生成窗函数
ESP_LOGI(TAG, "系统初始化完成,开始运行...");
}
void app_main() {
init_system();
xTaskCreate(audio_processing_task, "audio_proc", 4096, NULL, 5, NULL);
}
关键增强功能说明
-
多频段独立检测:
-
使用
freq_band_t
结构体管理每个频段的参数 -
并行检测多个频段,各频段独立维护触发状态
-
示例配置检测1000Hz、2000Hz、3000Hz三个频段
-
-
自适应噪声基底:
// 噪声基底计算(排除直流分量) |[1-50] bins| --> 平均值 --> dB转换 --> 噪声基底 // 动态阈值 = 噪声基底 + 固定偏移(20dB)
-
持续时长验证机制:
graph LR A[当前检测到信号] --> B{持续计数 >=5?} B -->|是| C[标记触发] B -->|否| D[计数+1] E[未检测到信号] --> F[复位计数器]
-
状态管理优化:
-
触发时记录时间戳,避免重复报警
-
释放时明确状态变更
-
提供完整的触发-保持-释放状态周期
-