要在 ESP32 上用 4 個麥克風實現 360° 聲源定位,通常思路是通過 時延估計(TDOA) + 幾何計算,核心流程:
陣列布置
將 4 個麥克風等間距布置成正方形(或圓形)。
記陣列中心為原點,麥克風編號 M0–M3 坐標已知(例如正方形邊長 D,則 M0=(+D/2,+D/2),M1=(–D/2,+D/2),M2=(–D/2,–D/2),M3=(+D/2,–D/2))。
多路采樣
用 ESP?IDF 的 I2S 驅動,以相同時鐘同步采集 4 路 PCM。
采樣率 fs 建議 ≥ 16?kHz,以滿足較高角分辨率。
GCC?PHAT 時延估計
對任意兩路信號 x_i[n], x_j[n] 做 PHAT 交叉相關,估算它們的時間差 Δt_{ij}。
例如計算 M0 與 M2(對角線)和 M1 與 M3 這兩對 TDOA。
幾何解算角度
已知麥克風間距 d、光速 c≈343?m/s,可通過
Δt=dcos?θc \Delta t = \frac{d \cos\theta}{c}解出到聲源方向 θ。
對兩個軸向(X 軸對角線、Y 軸對角線)分別求 θ_x, θ_y,再合成 360° 角度。
濾波與融合
對多幀結果做滑動平均或卡爾曼濾波,抑制噪聲。
ESP?IDF 示例代碼框架
#include "driver/i2s.h"
#include "esp_log.h"
#include <math.h>#define SAMPLE_RATE 16000
#define I2S_NUM (0)
#define I2S_BUF_LEN (1024)
#define MIC_DIST 0.04f // 麥克風間距 4cm
#define SPEED_OF_SOUND 343.0fstatic const char *TAG = "4mic_doa";// 1. 配置 I2S 同時采集 4 路
static void i2s_init(void) {i2s_config_t cfg = {.mode = I2S_MODE_MASTER | I2S_MODE_RX,.sample_rate = SAMPLE_RATE,.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, // 用后面 slot_map 映射多路.communication_format = I2S_COMM_FORMAT_I2S_MSB,.dma_buf_count = 4,.dma_buf_len = I2S_BUF_LEN,.use_apll = false};i2s_pin_config_t pin_cfg = {.bck_io_num = GPIO_NUM_26,.ws_io_num = GPIO_NUM_25,.data_out_num = I2S_PIN_NO_CHANGE,.data_in_num = GPIO_NUM_22 // SD0};ESP_ERROR_CHECK(i2s_driver_install(I2S_NUM, &cfg, 0, NULL));ESP_ERROR_CHECK(i2s_set_pin(I2S_NUM, &pin_cfg));// 如果外部硬件把 4 路麥克風的 SD 引腳合并到 SD0…SD3// 可用 i2s_set_pin 和 i2s_set_channel_map 指定
}// 2. GCC?PHAT 交叉相關(簡化版,FFT 庫可替代)
static float gcc_phat(const float *x, const float *y, int N) {float max_corr = 0;int best_lag = 0;for (int lag = -N/2; lag < N/2; lag++) {float sum = 0, en = 0, dn = 0;for (int n = 0; n < N; n++) {int m = n + lag;if (m<0||m>=N) continue;sum += x[n] * y[m];en += x[n]*x[n];dn += y[m]*y[m];}float corr = sum / sqrtf(en*dn + 1e-12f);if (corr > max_corr) {max_corr = corr;best_lag = lag;}}return (float)best_lag / SAMPLE_RATE; // 返回 Δt
}// 3. 主循環:采集 + DOA 估計
void app_main(void) {i2s_init();int16_t buf[I2S_BUF_LEN * 4];float mic0[I2S_BUF_LEN], mic1[I2S_BUF_LEN];float mic2[I2S_BUF_LEN], mic3[I2S_BUF_LEN];while (1) {// 從 I2S 讀 N*4 通道×16bitsize_t bytes_read = 0;ESP_ERROR_CHECK(i2s_read(I2S_NUM, buf, sizeof(buf), &bytes_read, portMAX_DELAY));int sample_count = bytes_read/ (sizeof(int16_t)*4);// 拆通道for (int i = 0; i < sample_count; i++) {mic0[i] = buf[4*i + 0];mic1[i] = buf[4*i + 1];mic2[i] = buf[4*i + 2];mic3[i] = buf[4*i + 3];}// 選對角陣對估計 X 和 Y 分量float dt_x = gcc_phat(mic0, mic2, sample_count); // M0-M2float dt_y = gcc_phat(mic1, mic3, sample_count); // M1-M3// 通過時延計算角度 (rad)float theta_x = acosf(fminf(fmaxf(dt_x * SPEED_OF_SOUND / MIC_DIST, -1), 1));float theta_y = acosf(fminf(fmaxf(dt_y * SPEED_OF_SOUND / MIC_DIST, -1), 1));// 合成 360°:這里簡單平均示例,可用更復雜的四象限處理float angle = fmodf((theta_x + theta_y) * 180.0f / M_PI * 0.5f, 360.0f);ESP_LOGI(TAG, "Estimated DOA: %.1f° (dt_x=%.6f, dt_y=%.6f)", angle, dt_x, dt_y);vTaskDelay(pdMS_TO_TICKS(200));}
}
說明:
i2s_init:需根據你所用的 4 路 PDM/I2S 麥克風硬件布局調整
channel_format
和引腳映射。gcc_phat:這里是時域交叉相關,為示例用;生產建議用 FFT+PHAT 算法加速。
角度解算:示例中對角線做估計并平均,實際可擴展到 4 對麥克風、基于最小二乘或卡爾曼濾波的 2D 多點解算。
精度與帶寬:采樣率和緩沖區長度會影響角度分辨率和響應速度,可根據需求調節。
這樣就完成了基于 4 麥克風陣列,在 ESP?IDF 上做 360° 聲源定位的完整示例。