该代码可正常运行,信号使用的是模拟信号,可改为指定信号。
本代码使用了一个基于MVDR(最小方差无失真响应)算法的麦克风阵列声源定位方法。代码首先设置了麦克风阵列的参数,包括阵元数量、采样率、信号频率等,并生成了模拟的麦克风阵列数据。接着,通过计算信号协方差矩阵并进行对角加载处理,确保矩阵的数值稳定性。随后,利用MVDR算法对每个可能的方向进行空间扫描,计算功率谱并找出最大值位置,从而估计声源的到达方向(DOA)。最后,代码通过图形化展示了声源定位结果,并输出了估计的方位角、俯仰角、置信度及处理时间。该算法能够有效定位声源,适用于麦克风阵列的声源检测场景。
% MVDR_DOA_Detection.m% 清除工作区和关闭所有图形窗口
close all
clear all
clcdisp('MVDR麦克风阵列声源定位算法 - 简化版');%% 参数设置
nSample = 2048; % 每帧样本数
M = 16; % 麦克风阵元个数
c = 343; % 声速(m/s)
fs = 48000; % 采样率(Hz)
f = 1000; % 信号频率估计值(Hz)
lamda = c/f; % 波长
R = 0.0425; % 圆阵半径(m)% 创建麦克风位置矩阵 - 16元圆阵
mic_locs = zeros(3, M);
for i = 1:Mangle = 2*pi*(i-1)/M;mic_locs(:, i) = [R*cos(angle); R*sin(angle); 0];
end% 搜索角度范围设置
step = 1; % 角度搜索步长
theta = 0:step:90; % 俯仰角取值范围(0°到90°)
phi = 0:step:360; % 方位角取值范围(0°到360°)
m = (0:1:M-1)'; % 麦克风索引列向量%% 生成模拟数据
% 设置模拟声源角度
source_azimuth = 135; % 度
source_elevation = 30; % 度% 从simulateCircularArrayData函数生成模拟数据
data_valid = simulateCircularArrayData(M, nSample, source_azimuth, source_elevation, fs, f, R, c);% 估计SNR
signal_power = mean(var(data_valid));
noise_floor = min(var(data_valid)) + eps;
snr_estimate = 10*log10(signal_power/noise_floor);
disp(['估计信噪比: ', num2str(snr_estimate), ' dB']);%% 图形初始化
figure('Position', [100, 100, 800, 600], 'Name', 'MVDR声源定位算法');
h1 = mesh(phi, theta, zeros(length(theta), length(phi)));
title('MVDR Algorithm');
xlabel('方位角 Azimuth (°)');
ylabel('俯仰角 Elevation (°)');
zlabel('功率 Power (dB)');
view(-64, 38);
colorbar;%% MVDR算法实现
tic;
% 计算协方差矩阵
% 步骤1:计算信号协方差矩阵
% 在这一步中,我们利用接收到的信号样本计算空间协方差矩阵
% 公式:Rx = X*X'/N,其中X是接收信号矩阵
Rx = data_valid' * data_valid / nSample;% 步骤2:对角加载处理
% 检查并改善协方差矩阵的条件数,防止矩阵奇异或病态
% 若条件数过大,则添加小的对角矩阵以提高数值稳定性
cond_num = cond(Rx);
if cond_num > 1e10% 如果病态,应用对角加载diag_loading = trace(Rx)/M * 0.01;Rx = Rx + diag_loading * eye(size(Rx));disp(['对MVDR协方差矩阵应用了对角加载 (条件数=', num2str(cond_num), ')']);
end% 步骤2.5:计算协方差矩阵的逆矩阵
% 这是MVDR算法的关键步骤,求解协方差矩阵的逆(或伪逆)
% 在实际应用中,使用伪逆可提高算法稳定性
InvR = pinv(Rx);% MVDR波束形成
% 步骤3:空间扫描计算功率谱
% 针对每个可能的方向(方位角和俯仰角组合)计算MVDR波束形成器的输出功率
% MVDR公式:P(θ,φ) = 1/(a'(θ,φ)·R^(-1)·a(θ,φ))
% 其中a(θ,φ)是方向向量(steering vector)
B_mvdr = zeros(length(theta), length(phi));
for p = 1:length(theta)for q = 1:length(phi)% 构建方向矢量a = exp(-1j*2*pi/lamda*R*sind(theta(p))*cosd(phi(q)-rad2deg(2*pi/M*m)));B_mvdr(p, q) = 1 / (a' * InvR * a + eps);end
endB_mvdr = abs(B_mvdr);
Bmax_mvdr = max(max(B_mvdr));
if Bmax_mvdr == 0B_mvdr_dB = zeros(size(B_mvdr));
elseB_mvdr_dB = 10*log10(B_mvdr / Bmax_mvdr); % 归一化功率(dB)
end% 找出MVDR的最大值位置(方位角和俯仰角)
% 步骤4:寻找功率谱最大值
% 找出MVDR响应最大的方向,即为估计的声源到达方向(DOA)
[max_mvdr_val, max_mvdr_idx] = max(B_mvdr(:));
[max_mvdr_theta_idx, max_mvdr_phi_idx] = ind2sub(size(B_mvdr), max_mvdr_idx);
mvdr_elevation = theta(max_mvdr_theta_idx);
mvdr_azimuth = phi(max_mvdr_phi_idx);% 计算置信度指标和检测状态
mvdr_confidence = max_mvdr_val / (Bmax_mvdr + eps);
mvdr_detection = mvdr_confidence > 0.7; % 检测阈值设为0.7
mvdr_time = toc * 1000; % 转换为毫秒% 更新MVDR图形
set(h1, 'ZData', B_mvdr_dB);
title(sprintf('MVDR: Azimuth=%.1f°, Elevation=%.1f° (True: Az=%.1f°, El=%.1f°)', ...mvdr_azimuth, mvdr_elevation, source_azimuth, source_elevation));% 显示结果
fprintf('MVDR估计结果:\n');
fprintf(' 方位角:%.1f° (真实值:%.1f°)\n', mvdr_azimuth, source_azimuth);
fprintf(' 俯仰角:%.1f° (真实值:%.1f°)\n', mvdr_elevation, source_elevation);
fprintf(' 置信度:%.3f\n', mvdr_confidence);
fprintf(' 处理时间:%.2f ms\n', mvdr_time);% 创建用于极坐标显示的图
figure('Position', [100, 700, 600, 600], 'Name', 'MVDR方位角结果 - 极坐标');
polarplot([0 source_azimuth*pi/180], [0 1], 'r-', 'LineWidth', 2); % 真实方向
hold on;
polarplot([0 mvdr_azimuth*pi/180], [0 1], 'b-', 'LineWidth', 2); % 估计方向
legend('真实方向', 'MVDR估计方向');
title(sprintf('方位角估计结果 (真实: %.1f°, 估计: %.1f°)', source_azimuth, mvdr_azimuth));%% 模拟麦克风阵列数据生成函数
function data = simulateCircularArrayData(numMics, numSamples, sourceAzimuth, sourceElevation, fs, freq, radius, c)% 将角度转换为弧度sourceAzimuth = sourceAzimuth * pi / 180;sourceElevation = sourceElevation * pi / 180;% 创建声源方向向量sourceDir = [cos(sourceElevation)*cos(sourceAzimuth); cos(sourceElevation)*sin(sourceAzimuth); sin(sourceElevation)];% 创建麦克风位置micPos = zeros(3, numMics);for i = 1:numMicsangle = 2*pi*(i-1)/numMics;micPos(:, i) = [radius*cos(angle); radius*sin(angle); 0];end% 计算每个麦克风的时间延迟refPos = [0; 0; 0]; % 参考位置(阵列中心)refDist = 1; % 参考距离(任意)delays = zeros(1, numMics);for i = 1:numMics% 将麦克风位置投影到声源方向projDist = micPos(:, i)' * sourceDir;% 计算相对于参考位置的时间延迟delays(i) = (refDist - projDist) / c;end% 生成载波信号t = (0:numSamples-1)' / fs;carrier = sin(2*pi*freq*t);% 添加噪声使载波更真实noise_level = 0.1;noisy_carrier = carrier + noise_level * randn(size(carrier));% 应用延迟创建模拟麦克风信号data = zeros(numSamples, numMics);for i = 1:numMicsdelay_samples = round(delays(i) * fs);% 创建载波的延迟版本if delay_samples >= 0data(1+delay_samples:end, i) = noisy_carrier(1:end-delay_samples);elsedata(1:end+delay_samples, i) = noisy_carrier(1-delay_samples:end);end% 添加一些独特的麦克风噪声data(:, i) = data(:, i) + 0.05 * randn(numSamples, 1);end% 添加一个共同的噪声分量来模拟背景噪声background = 0.02 * randn(numSamples, 1);for i = 1:numMicsdata(:, i) = data(:, i) + background;end
end