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 实现对应同一个空间响应量;但在离散实现中,若时域查表仅采用最近邻取值,则会引入时延量化误差,因此其结果通常只是对频域直接实现的近似。通过插值可进一步提高时域实现精度,使其更接近频域直接实现结果。
欢迎大家关注我,后续我会继续更新更多信号处理与深度学习的知识。