声源定位算法5----SRP-PHAT(2)

1. 引言

上一篇文章介绍了 SRP-PHAT 声源定位算法的基本原理。SRP-PHAT 的核心思想是:对于空间中每一个候选点,根据阵列几何关系计算其对应的理论传播时延差,再利用各麦克风对之间的 GCC-PHAT 响应进行匹配和累加,从而构造空间响应图,最终通过搜索峰值实现声源定位。

在实际编程实现中,SRP-PHAT 常见有两种形式:

其一是频域直接实现 ,即在扫描过程中直接对 PHAT 加权互谱进行相位补偿并累加;

其二是时域 GCC-PHAT 实现,即先通过 IFFT 得到各麦克风对的 GCC-PHAT 曲线,再在理论时延对应位置取值并累加。

这两种实现方式在数学上是等价的,但在程序结构和计算效率上存在差异。本文将基于同一套仿真参数,分别给出这两种实现方式的 MATLAB 代码,并说明各自的实现流程。

2. 仿真思路

本文的仿真流程可以概括为以下几个步骤:

首先,建立平面麦克风阵列模型,并设置空间扫描区域;

然后,在扫描平面中放置两个点声源,根据传播距离为每个麦克风生成接收信号;

接着,对阵列接收信号进行频域变换,并构造麦克风对之间的互谱;

最后,分别采用频域直接法和时域 GCC-PHAT 法计算空间响应图,并观察成像结果。

需要说明的是,本文两种实现方式的差别只体现在 SRP-PHAT 响应计算阶段,其余部分,例如阵列模型、声源生成、扫描网格和 FFT 处理,均保持一致。

3. 阵列模型与参数设置

本文采用一个 72 阵元平面阵列,阵元坐标由外部文件导入。阵列位于 z=0平面,扫描平面设置为 z=2。

主要仿真参数如下:

  • 阵元数:72

  • 声速:343 m/s

  • 采样点数:2048

  • 采样频率:64 kHz

  • 扫描范围:

  • 网格步长:0.04 m

同时设置两个点声源,其空间位置分别为:

两个声源分别采用不同频率的正弦信号进行仿真,并加入一定高斯白噪声,以模拟实际场景下的多源和噪声环境。

麦克风阵列

仿真参数设置(与CBF几乎一致):

Matlab 复制代码
%% 参数设置
c = 343;       % 声速 (m/s)
N = 2048;      % 所需信号长度
Fs = 64e3;     % 采样频率 (Hz)
fc1 = 3500;    % 第一个声源频率 (Hz)
fc2 = 4500;    % 第二个声源频率 (Hz)
SNR = 10;      % 信噪比 (dB)
t = 0:1/Fs:1;  % 生成 1 秒长的时间序列

%% 声源位置
source_location1 = [-0.45, 0.6, 2];   % 第一个声源位置
source_location2 = [0.15, -0.2, 2];   % 第二个声源位置

根据仿真参数设置,计算得到两个仿真信号以及最重要的时延信息,时延计算代码如下(网格自定义):

Matlab 复制代码
%% 计算距离差矩阵,模拟出网格大小
Max_x = 1.3;
Max_y = 0.75;
pixel = 0.04;

Site_matrix = -Max_x:pixel:Max_x;
Site_matriy = -Max_y:pixel:Max_y;
Length_matrix = length(Site_matrix);
Length_matriy = length(Site_matriy);

tau = zeros(Length_matrix, Length_matriy, BSN);

for ky = 1:Length_matriy
    site_ky = Site_matriy(ky);
    for kx = 1:Length_matrix
        site_kx = Site_matrix(kx);
        DS = [site_kx, site_ky, 2];   % 探测点位置

        ref_dist = sqrt(DS(1)^2 + DS(2)^2 + DS(3)^2);

        for kki = 1:BSN
            mic_dist = sqrt((BS(1, kki) - DS(1))^2 + ...
                            (BS(2, kki) - DS(2))^2 + ...
                            (BS(3, kki) - DS(3))^2);
            ri = mic_dist - ref_dist;
            tau(kx, ky, kki) = ri / c;
        end
    end
end

4. 频域直接实现

4.1 实现思路

频域直接实现的核心思想是:

对于每一个麦克风对,先计算其 PHAT 加权互谱;然后在空间扫描过程中,根据候选点对应的理论时延,对互谱进行相位补偿并在频率维度上累加;最后对所有麦克风对的结果进行求和,得到该候选点的空间响应。

这种方式的特点是表达形式直接,与公式推导关系清晰,但由于每个扫描点都要重复进行频率累加,因此在网格较密时,计算量通常较大。

4.2 频域直接实现代码

Matlab 复制代码
%% 功率谱成像 - SRP-PHAT(频域直接实现)
tic

% FFT
X_fft = zeros(BSN, N);
for ki = 1:BSN
    X_fft(ki, :) = fft(s(ki, :));
end

% 频带设置
fre_step = 100; 
fre_range = 3000:fre_step:5200;
N_range = (round(min(fre_range) * N / Fs) + 1) : (round(max(fre_range) * N / Fs) - 1);

% 角频率
f_all = (0:N-1) * Fs / N;
omega = 2 * pi * f_all;

% 麦克风对数
num_pairs = BSN * (BSN - 1) / 2;

num_freq = length(N_range);

% 预存所有麦克风对的 PHAT 加权互谱
pair_i = zeros(num_pairs, 1);
pair_j = zeros(num_pairs, 1);
R_phat = zeros(num_pairs, num_freq);

pair_idx = 0;
for ki = 1:(BSN - 1)
    for kj = (ki + 1):BSN
        pair_idx = pair_idx + 1;
        pair_i(pair_idx) = ki;
        pair_j(pair_idx) = kj;

        Rij = X_fft(ki, N_range) .* conj(X_fft(kj, N_range));   % 互功率谱
        R_phat(pair_idx, :) = Rij ./ (abs(Rij) + eps);          % 只做一次 PHAT 加权
    end
end

SUM_ij = zeros(Length_matrix, Length_matriy);

parfor ky = 1:Length_matriy
    SUM_local = zeros(Length_matrix, 1);

    for kx = 1:Length_matrix
        val = 0;

        for p = 1:num_pairs
            ki = pair_i(p);
            kj = pair_j(p);

            dtau = tau(kx, ky, ki) - tau(kx, ky, kj);
            direction_vectors = exp(-1i * omega(N_range) * dtau);

            val = val + sum(R_phat(p, :) .* direction_vectors);
        end

        SUM_local(kx) = real(val);
    end

    SUM_ij(:, ky) = SUM_local;
end

toc

4.3 频域实现仿真结果

5. 时域 GCC-PHAT 实现

5.1 实现思路

时域实现的核心思想是:

先对每个麦克风对构造 PHAT 加权互谱,并通过 IFFT 得到对应的 GCC-PHAT 曲线;然后在空间扫描时,根据每个候选点的理论时延,从 GCC-PHAT 曲线中取出对应位置的值,并对所有麦克风对进行累加。

这种方式相当于先把频率维度的信息压缩到时延域,再进行空间扫描。与频域直接实现相比,它在实际工程中通常具有更高的计算效率,尤其是在扫描点较多时优势更明显。

5.2 时域 GCC-PHAT 实现代码

Matlab 复制代码
%% 功率谱成像 - SRP-PHAT(时域 GCC-PHAT 实现)
tic

% FFT
X_fft = zeros(BSN, N);
for ki = 1:BSN
    X_fft(ki, :) = fft(s(ki, :));
end

% 频带设置
fre_step = 100;
fre_range = 3000:fre_step:5200;
N_range = (round(min(fre_range) * N / Fs) + 1) : (round(max(fre_range) * N / Fs) - 1);

% 麦克风对数
num_pairs = BSN * (BSN - 1) / 2;

% 预存所有麦克风对
pair_i = zeros(num_pairs, 1);
pair_j = zeros(num_pairs, 1);
gcc_phat_all = zeros(num_pairs, N);

pair_idx = 0;
for ki = 1:(BSN - 1)
    for kj = (ki + 1):BSN
        pair_idx = pair_idx + 1;
        pair_i(pair_idx) = ki;
        pair_j(pair_idx) = kj;

        Rij_band = X_fft(ki, N_range) .* conj(X_fft(kj, N_range));
        Rphat_band = Rij_band ./ (abs(Rij_band) + eps);

        tmp_spec = zeros(1, N);
        tmp_spec(N_range) = Rphat_band;

        mirror_idx = N - N_range + 2;
        valid_mask = mirror_idx >= 1 & mirror_idx <= N;
        tmp_spec(mirror_idx(valid_mask)) = conj(Rphat_band(valid_mask));

        % IFFT 
        gcc_tmp = real(ifft(tmp_spec));

        gcc_tmp = fftshift(gcc_tmp);

        gcc_phat_all(pair_idx, :) = gcc_tmp;
    end
end

SUM_ij = zeros(Length_matrix, Length_matriy);

parfor ky = 1:Length_matriy
    SUM_local = zeros(Length_matrix, 1);

    for kx = 1:Length_matrix
        val = 0;

        for p = 1:num_pairs
            ki = pair_i(p);
            kj = pair_j(p);

            dtau = tau(kx, ky, kj) - tau(kx, ky, ki);

            lag_idx = round(dtau * Fs) + (N / 2 + 1);

            if lag_idx >= 1 && lag_idx <= N
                val = val + gcc_phat_all(p, lag_idx);
            end
        end

        SUM_local(kx) = val;
    end

    SUM_ij(:, ky) = SUM_local;
end

toc

5.3 时域实现仿真结果

6. 总结

本文基于 MATLAB 给出了 SRP-PHAT 声源定位算法的两种常见仿真实现方式:频域直接实现和时域 GCC-PHAT 实现。两种方法共享同一套阵列模型、声源模型与扫描网格,仅在空间响应计算阶段采用不同的实现思路。

频域直接实现与理论公式对应关系更直接,适合用于理解算法结构;

时域 GCC-PHAT 实现则在计算效率上更具优势,更接近实际工程中的常见实现方式。

通过这两种实现方式的对比,可以更清楚地理解 SRP-PHAT 的本质:

它并不是简单地做能量叠加,而是在空间中寻找与多通道时延关系最一致的位置,并利用这种一致性构造声源定位图。

但需要注意:

从连续形式上看,频域直接实现与时域 GCC-PHAT 实现对应同一个空间响应量;但在离散实现中,若时域查表仅采用最近邻取值,则会引入时延量化误差,因此其结果通常只是对频域直接实现的近似。通过插值可进一步提高时域实现精度,使其更接近频域直接实现结果。

欢迎大家关注我,后续我会继续更新更多信号处理与深度学习的知识。

相关推荐
DeepModel1 小时前
【概率分布】伯努利分布详解
算法·概率论
CappuccinoRose1 小时前
MATLAB学习文档 - 汇总篇
学习·算法·matlab
菜鸟小九1 小时前
hot100(31-40)
java·算法
gfdhy2 小时前
【Linux】服务器网络与安全核心配置|静态IP+SSH加固+防火墙,公网服务器必学实操
linux·服务器·网络·tcp/ip·算法·安全·哈希算法
Frostnova丶2 小时前
LeetCode 1888 使二进制字符串交替的最少翻转次数
算法·leetcode
王码码20352 小时前
Flutter for OpenHarmony:es_compression — 高性能 Brotli 与 Zstd 算法实战
算法·flutter·elasticsearch
苏纪云3 小时前
蓝桥杯知识点——day2
数据结构·算法·蓝桥杯
Wect3 小时前
LeetCode 52. N 皇后 II:回溯算法高效求解
前端·算法·typescript
iFlyCai3 小时前
数据结构与算法之希尔排序
数据结构·算法·排序算法