MVDR源码(可直接运行)

该代码可正常运行,信号使用的是模拟信号,可改为指定信号。

本代码使用了一个基于MVDR(最小方差无失真响应)算法的麦克风阵列声源定位方法。代码首先设置了麦克风阵列的参数,包括阵元数量、采样率、信号频率等,并生成了模拟的麦克风阵列数据。接着,通过计算信号协方差矩阵并进行对角加载处理,确保矩阵的数值稳定性。随后,利用MVDR算法对每个可能的方向进行空间扫描,计算功率谱并找出最大值位置,从而估计声源的到达方向(DOA)。最后,代码通过图形化展示了声源定位结果,并输出了估计的方位角、俯仰角、置信度及处理时间。该算法能够有效定位声源,适用于麦克风阵列的声源检测场景。

Matlab 复制代码
% MVDR_DOA_Detection.m

% 清除工作区和关闭所有图形窗口
close all
clear all
clc

disp('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:M
    angle = 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
end

B_mvdr = abs(B_mvdr);
Bmax_mvdr = max(max(B_mvdr));
if Bmax_mvdr == 0
    B_mvdr_dB = zeros(size(B_mvdr));
else
    B_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:numMics
        angle = 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:numMics
        delay_samples = round(delays(i) * fs);
        
        % 创建载波的延迟版本
        if delay_samples >= 0
            data(1+delay_samples:end, i) = noisy_carrier(1:end-delay_samples);
        else
            data(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:numMics
        data(:, i) = data(:, i) + background;
    end
end
相关推荐
卷卷的小趴菜学编程27 分钟前
算法篇----二分查找
算法
2301_794461571 小时前
力扣-快乐数
算法·leetcode·职场和发展
chenyuhao20241 小时前
链表面试题9之环形链表进阶
数据结构·算法·链表·面试·c#
chenyuhao20242 小时前
链表的面试题8之环形链表
数据结构·算法·链表·面试·c#
Panesle2 小时前
Index-AniSora模型论文速读:基于人工反馈的动漫视频生成
人工智能·算法·机器学习·计算机视觉·开源·大模型·生成模型
liuzhangfeiabc2 小时前
[luogu12542] [APIO2025] 排列游戏 - 交互 - 博弈 - 分类讨论 - 构造
c++·算法·题解
晴空闲雲3 小时前
数据结构与算法-线性表-循环链表(Circular Linked List)
数据结构·算法·链表
Jolen_xie3 小时前
Pytorch分布式训练,数据并行,单机多卡,多机多卡
人工智能·算法
珹洺3 小时前
C++从入门到实战(十六)String(中)String的常用接口(构造接口,析构接口,迭代器,遍历修改,容量管理与数据访问)
开发语言·c++·算法·stl
是店小二呀4 小时前
【优选算法 | 栈】深入栈模拟题:从题型特征到实现技巧
c++·算法