☰
MUSIC算法MATLAB实现:DOA多声源定位源码详解
2026/10/31 10:13:05 网站建设 项目流程

简介:一份基于MATLAB的MUSIC算法实现,面向阵列信号处理与DOA估计初学者及研究者,用于解决多声源定位问题。MUSIC算法通过分离信号子空间与噪声子空间实现高分辨率测向,压缩包内包含两个.m脚本,一个用于搭建接收阵列并生成仿真信号,另一个为核心DOA估计实现,覆盖阵列数据采集、子空间分解、伪谱计算与峰值搜索等完整流程。资源包体积仅2KB,结构精简,便于直接阅读和按需修改。当前已有1605人学习下载,说明该代码在声源定位、智能天线等场景中具备实用参考价值。读者可通过调整阵元数量、信噪比、声源角度等参数,观察不同条件下定位性能的变化,结合仿真图形直观对比不同参数下的定位结果,进而深入理解MUSIC算法的原理与工程实现。

1. MUSIC算法MATLAB程序:一份能直接跑的 DOA 多声源定位源码

做阵列信号处理的人迟早要撞上 MUSIC 算法这个名字。这份 MATLAB 程序包就是围绕 MUSIC 做 DOA 多声源定位的:均匀线阵拿到多路接收数据,程序通过协方差矩阵特征分解把空间切成信号子空间和噪声子空间,再沿角度方向扫一遍伪谱,谱峰位置就是声源来向。压缩包里两个 .m 文件,主程序 doa_music.m 一条线跑通“仿真信号生成—协方差估计—谱峰搜索—角度输出”。它适合正在做阵列信号课程设计、声学定位仿真或刚接触 DOA 估计的人。一个反直觉的结论是:MUSIC 并不直接“算”角度,而是让 -90° 到 90° 的每个方向都拿导向矢量去噪声子空间里投影一次,投影最小的那个方向才是来波方向。理解这一点,后面所有调试都会顺很多。

2. 从阵列接收数据到空间谱:MUSIC 为什么能分辨多个声源

2.1 均匀线阵的信号模型:导向矢量是谱峰搜索的尺子

先把模型立住。M 元均匀线阵,阵元间距 d,一个窄带平面波从 θ 方向入射,θ 以阵列法线为 0°。阵元 0 先收到信号,阵元 m 相比阵元 0 多走的行程是 m·d·sinθ,对应相位滞后 2π·m·d·sinθ/λ。把这些相位关系写成向量,就是导向矢量:

a(θ) = [1, e^(−j2πd sinθ/λ), …, e^(−j2π(M−1)d sinθ/λ)]ᵀ

这是阵列对某个方向的“指纹”。当 K 个声源同时存在时,接收数据矩阵写成:

X(t) = A·S(t) + N(t)

其中 A 是 M×K 的导向矢量矩阵,S(t) 是 K 路信号,N(t) 是空间白噪声。整套 MUSIC 谱峰搜索的本质,就是把候选角度 θ 代进 a(θ),看它和实测数据构造出来的子空间是否“合拍”。所以导向矢量是后面所有公式的地基,坐标写错了后面全错。

% 均匀线阵导向矢量矩阵 % 输入:N 阵元数,d_lambda = d/λ,theta_deg 角度向量(单位度) % 输出:N×length(theta_deg) 的复数矩阵,每列是一个角度的导向矢量 function A = steering_matrix(N, d_lambda, theta_deg) theta_rad = theta_deg * pi / 180; A = exp(-1j * 2 * pi * d_lambda * (0:N-1).' * sin(theta_rad)); end

这里的 d_lambda 用无量纲的 d/λ,而不是直接用波长,是因为相位差只跟阵元间距和波长的比值有关,跟具体频率无关,仿真时换频率不用改代码。0:N-1 是阵元序号,注意 .' 是普通转置,如果写成 ' 会带共轭,对实数序号没影响,但养成用 .' 的习惯可以避免后面处理复数数据时踩坑。

选型上有一条硬约束:阵元数 N 必须大于声源个数 K,工程上至少 N ≥ K+1。原因在第 2.2 节展开——噪声子空间维度是 N−K,如果 K 逼近 N,噪声子空间就剩不下几个维度,谱峰分辨能力会急剧恶化。

2.2 协方差矩阵与特征分解:信号子空间和噪声子空间怎么切开

只有阵列接收数据 X,算法第一步是估计空间协方差矩阵:

R = E[x(t)·xᴴ(t)] ≈ (1/snap)·X·Xᴴ

xᴴ 表示共轭转置,snap 是快拍数,也就是时间上的采样点数。R 是 N×N 的埃尔米特矩阵,它的结构可以展开成:

R = A·Rₛₛ·Aᴴ + σ²I

Rₛₛ 是信号协方差矩阵,σ² 是噪声功率。对 R 做特征分解后,对角线上的 N 个特征值会出现一个明显的落差:前 K 个大特征值对应信号子空间 Us,剩下的 N−K 个小特征值几乎贴在 σ² 附近,对应噪声子空间 Un。这个切分之所以成立,前提是 K 个信号源互不相关,而且噪声是空间白噪声——两条缺一不可。

% 样本协方差矩阵:用有限快拍估计理论协方差 R = X * X' / snap; % 特征分解。注意:eig 返回的特征值默认按升序排列! [E, D] = eig(R); eigvals = diag(D); % 已知声源个数 K 时,最小的 N-K 个特征值对应噪声子空间 Un = E(:, 1:N-K);

这里最容易出问题的是特征值顺序。MATLAB 的 eig 默认升序,所以 E 的第一列对应最小特征值。取噪声子空间要取前 N−K 列;如果代码里先 sort 成降序,就要取后 N−K 列。两条路等价,但必须清楚自己写的到底是哪种。后面第 5 章专门讲这个坑。

实际场景里 K 往往是未知的,一个实用技巧是把 eigvals 画出来看“拐点”:特征值从大到小排列后,在 K 的位置会有一个明显台阶,台阶后面基本平贴在噪声底上。这个信息是 MUSIC 算法顺带送的,不用额外算。快拍数太少时,台阶会被抹平,K 就不好判断了,所以快拍数别省。

2.3 高分辨率的来源:正交投影与伪谱的峰

传统波束形成 CBF 的谱写成 P_CBF(θ) = aᴴ(θ)·R·a(θ),它的分辨率受阵列孔径限制,两个角度靠得比波束宽度还近就糊成一个包。MUSIC 走的是另一条路,把谱定义成:

P_MUSIC(θ) = 1 / (aᴴ(θ)·Un·Unᴴ·a(θ))

分母是候选导向矢量在噪声子空间上的投影能量。理想无噪声情况下,真实来波方向的 a(θₖ) 和 Un 完全正交,分母为 0,谱峰趋向无穷。有噪声时不会严格归零,但真实方向附近会出现一个尖峰。因为它用的是子空间正交性而不是波束形状,理论上只要信噪比和快拍数给够,两个角度离得再近都能分开——这就是“高分辨率”三个字的来源。

% 谱峰搜索:把 -90°~90° 的网格都扫一遍 theta_grid = -90:0.1:90; theta_rad_grid = theta_grid * pi / 180; % 一次生成全部候选导向矢量,避免循环里反复计算 A_grid = exp(-1j * 2 * pi * d_lambda * (0:N-1).' * sin(theta_rad_grid)); % 向量化计算伪谱:每个候选角度在噪声子空间上的投影能量取倒数 P = 1 ./ sum(abs(A_grid' * Un).^2, 2); P = P / max(P); % 归一化,方便画图和对比

画图时建议用 plot(theta_grid, 10*log10(P)),纵轴单位是 dB。注意伪谱是功率量纲,所以是 10log10 而不是 20log10,很多初学的人在这里翻车,画出来的峰形状看着不对。

“伪谱”的“伪”字要特别强调:谱峰高度不代表信号功率,它只代表该方向与噪声子空间的正交程度。想从 MUSIC 谱里读出各声源的功率比例是做不到的,MUSIC 只回答“从哪个方向来”,不回答“来多大”。如果既要角度又要功率,得换求根 MUSIC 或 ESPRIT 这类算法。

3. 拆解 doa_music.m:参数、数据流与谱峰搜索实现

3.1 压缩包里的两个文件:先跑哪个、怎么看结果

解压后是两个 MATLAB 脚本,先看文件分工再动手,不要两个都双击运行。

文件定位建议处理方式
Untitled.m调试过程中留下的草稿,可能是早期信号生成或初始化脚本先放一边,跑通主程序后再回来看有没有需要合并的初始化逻辑
doa_music.mMUSIC 主算法,覆盖信号生成、协方差估计、谱峰搜索、角度输出直接运行,出图后逐步加断点看变量

文件名带 Untitled 的基本是没整理完的草稿,这不是贬义——工程里经常先在草稿里验证一段计算,再把有价值的逻辑并进主程序。所以第一步是打开 doa_music.m,按 Ctrl+Enter 逐节运行,看工作区里 X、R、Un、P 这些变量的尺寸和数值,确认每一步都在自己预期内。逐段断点比一次性跑完更能建立“数据长什么样”的直觉。

3.2 完整主程序:参数区与仿真信号生成

我把 doa_music.m 整理成可复用函数的形式,方便后面改参数和做批量实验。参数区集中放在函数头部,改场景只需要动这 7 个值。

function [theta_est, P, theta_grid] = doa_music(seed) % MUSIC 主程序:DOA 多声源定位 % 输入 seed:随机种子,保证结果可复现 % 输出 theta_est:估计角度向量;P:归一化伪谱;theta_grid:角度网格 % ---- 参数区 ---- N = 8; % 阵元数,必须大于声源个数 K snap = 500; % 快拍数,时间采样点数 K = 2; % 声源个数 true_theta = [-25 15];% 真实来波方向,单位度 SNR_dB = 15; % 信噪比,单位 dB d_lambda = 0.5; % 阵元间距/波长,0.5 就是半波长 rng(seed); % ---- 1. 仿真信号生成 ---- % K 路互不相关的复高斯窄带信号,功率归一 S = (randn(K, snap) + 1j * randn(K, snap)) / sqrt(2); signal_power = 10^(SNR_dB / 10); S = sqrt(signal_power) * S; % 导向矢量矩阵,N×K theta_rad = true_theta * pi / 180; A = exp(-1j * 2 * pi * d_lambda * (0:N-1).' * sin(theta_rad)); % 阵列接收数据 = 信号部分 + 空间白噪声 X = A * S; noise = (randn(N, snap) + 1j * randn(N, snap)) / sqrt(2); X = X + noise; end

信号生成里有一个容易忽略的细节:为什么复高斯要除以 sqrt(2)。randn 生成的是实标准正态,randn + 1j*randn 的功率是实部虚部功率之和,等于 2。除以 sqrt(2) 后功率归一为 1,再乘 sqrt(signal_power),信号功率就严格等于 10^(SNR_dB/10),噪声功率为 1,SNR 定义和 dB 值严格对齐。如果少了这一步,你设的 15dB 实际会偏大 3dB,后面写论文或对比算法时数据对不上。

K 路信号用独立的 randn 生成,天然满足 MUSIC 需要的“互不相关”前提。如果这里改成让 S 的第二行复用第一行的延迟副本,就会制造相干源,直接触发第 5 章要讲的经典翻车。

3.3 协方差矩阵、特征分解与噪声子空间

接着上面代码继续。

% ---- 2. 协方差矩阵与特征分解 ---- R = X * X' / snap; % 样本协方差,N×N [E, D] = eig(R); % 特征分解,特征值默认升序 Un = E(:, 1:N-K); % 最小 N-K 个特征值对应噪声子空间 % 可以用这行检查特征值落差,辅助判断 K 是否合理 disp(diag(D).');

协方差矩阵除以 snap 是取平均,不是写不写都行的细节。R = X*X' 其实是个未归一化的 Gram 矩阵,除不除 snap 不影响特征向量方向,但影响特征值的绝对大小,也就影响噪声底的位置。统一除以快拍数,后续改 snap 时各次实验的特征值才具备可比性。

噪声子空间取 E 的前 N−K 列,依赖 eig 升序这一行为。有的教程写成 [V,D] = eig(R) 后先 [~,idx]=sort(diag(D),'descend') 再取前 K 列当信号子空间,两种写法等价。问题在于代码一旦混合两种风格,比如降序排序后还取第 1:N−K 列,那取到的就是最大的那些特征值对应的向量,整个谱峰关系颠倒。这里建议在代码里固定一种写法,注释写明顺序依赖。

3.4 谱峰搜索与角度提取:用局部最大代替工具箱函数

% ---- 3. 谱峰搜索 ---- theta_grid = -90:0.1:90; A_grid = exp(-1j * 2 * pi * d_lambda * (0:N-1).' * sin(theta_grid * pi / 180)); % 向量化计算 MUSIC 伪谱,不再写循环 P = 1 ./ sum(abs(A_grid' * Un).^2, 2); P = P / max(P); % 归一化到 [0,1] % ---- 4. 提取峰值对应的角度 ---- % 不依赖 findpeaks,避免 MATLAB 缺 Signal Processing Toolbox 时报错 is_peak = [false; (P(2:end-1) > P(1:end-2)) & (P(2:end-1) > P(3:end)); false]; peak_idx = find(is_peak); peak_idx = peak_idx(P(peak_idx) > 0.1); % 过滤低于 0.1 的低矮假峰 [~, order] = sort(P(peak_idx), 'descend'); theta_est = theta_grid(peak_idx(order(1:min(K, numel(order))))); fprintf('估计到的方向:'); fprintf('%.2f° ', theta_est); fprintf('\n'); % 画图 plot(theta_grid, 10*log10(P), 'LineWidth', 1.2); xlabel('角度 (deg)'); ylabel('归一化伪谱 (dB)'); grid on;

向量化那行是这段代码的核心:A_grid' 是网格数×N,乘上 Un 得到网格数×(N−K) 的投影矩阵,abs 平方后按行求和,得到每个角度的投影能量,再取倒数就是伪谱。整套操作里没有循环,比逐点 for 快一个数量级,网格取 0.01° 甚至 0.001° 也带得动。

峰值提取没有用 findpeaks,这是有意为之——findpeaks 属于 Signal Processing Toolbox,不少人装的是基础版 MATLAB,跑起来直接报“未定义函数”。局部最大的写法只有三行:比左右邻居都高就算峰,首尾补 false 防止越界。过滤阈值 0.1 的意思是低于主峰 20dB(10log10(0.1) ≈ −10dB)的毛刺不算数,这个阈值要根据你的 SNR 调,SNR 很低时可以放宽到 0.05。

取峰个数用 min(K, numel(order)) 保护:当谱峰数少于声源数时,不要硬凑 K 个,说明当前的阵列配置分辨不开或者 SNR 不够,先把物理条件调好再谈算法。

4. 多声源定位仿真:从两源到三源,改参数看谱峰变化

4.1 三声源场景:角度设置与谱图解读

把第 3 章函数里的参数改成三声源场景,是最快看到 MUSIC 优势的方式。设 N=8,K=3,true_theta=[-40 10 35],SNR=12,快拍 500,其他不动。

运行后谱图上会出现三个清晰主峰,对应的估计角度基本落在真实值附近。比如实测可能得到 -39.8°、10.1°、34.9°。这个 0.2° 左右的误差主要来自网格步长 0.1° 的量化:真实角度落在两个网格点之间时,峰值只能选邻近点。如果想验证这一点,把 theta_grid 改成 -90:0.01:90 再跑一次,误差会缩到 0.01° 量级,但计算量涨了 10 倍。

三声源能同时估计的前提是 N ≥ K+1。N=8、K=3 时噪声子空间有 5 个维度,正交约束很足;如果 K 加到 6,噪声子空间只剩 2 维,谱峰之间的旁瓣会互相干扰,弱源容易被强源的旁瓣吞掉。多声源场景下,声源数量不是越多越好,MUSIC 的能力边界由“阵元数减声源数”这个差值决定。

4.2 分辨率实验:阵元数量与信噪比的影响

两个声源角度间隔很近时,MUSIC 能不能分开,最直接的影响因素是阵元数。下面这组实验保持 K=2、快拍 500、信噪比 15dB,只改 N 和角度间隔:

阵元数 N角度间隔信噪比谱峰表现
85°15dB两个主峰可辨,中间有谱谷
83°15dB两个峰合成一个宽峰,判定失败
123°15dB两个峰清晰分离,谱谷低于主峰 10dB
123°5dB出现锯齿状毛刺,但两个主峰仍可辨

这组对照透出的规律是:阵元数本质上是物理孔径。同样半波长间距,N 从 8 涨到 12,阵列总孔径从 4λ 涨到 6λ,波束变窄,MUSIC 的极限分辨角度也跟着变小。MUSIC 没有闭式的分辨率公式,工程上标定阵列能力就靠这种扫间隔实验:固定 N 和 SNR,把角度间隔从宽到窄扫一遍,记录“什么时候谱峰合成一个”,那个临界点就是这套配置的分辨率极限。

信噪比的影响在第三、四行对比里很明显:15dB 时谱谷干净,5dB 时旁瓣抬起来变成锯齿。MUSIC 对噪声的敏感度比 CBF 高,这是高分辨率的代价——它利用的是子空间结构,结构一旦被噪声搅浑,性能掉得比传统波束形成快。

4.3 快拍数的影响:谱峰稳定性的观察

快拍数控制的是协方差矩阵的估计质量。把 snap 从 500 改成 50,谱峰位置开始抖动,旁瓣变高;改成 2000 后谱峰更尖锐,稳定度明显改善。原因是样本协方差 R = (1/snap)·XXᴴ 的估计误差方差近似正比于 σ²/snap,快拍越少,特征分解后的噪声子空间越偏离理想值。

% 同一个场景换不同快拍数,对比谱峰稳定性 snap_list = [50 200 500 2000]; figure; for s = 1:numel(snap_list) snap = snap_list(s); % 重新生成数据和协方差 S = sqrt(10^(SNR_dB/10)) * (randn(K, snap) + 1j*randn(K, snap)) / sqrt(2); X = A * S + (randn(N, snap) + 1j*randn(N, snap)) / sqrt(2); R = X * X' / snap; [E, D] = eig(R); Un = E(:, 1:N-K); P = 1 ./ sum(abs(A_grid' * Un).^2, 2); P = P / max(P); subplot(2, 2, s); plot(theta_grid, 10*log10(P)); title(['snap = ', num2str(snap)]); grid on; end

仿真阶段建议快拍控制在 500 以上,低于 100 时谱峰抖动会让“分辨成功”这个结论变得不稳定。如果你要做的是实时定位仿真,快照数受硬件帧率限制只能给到几十,那就得接受几度的角度抖动,或者用对角加载把协方差矩阵拍平一点,第 5 章会讲具体做法。

5. 避坑与排查:MUSIC 代码最常见的 5 个翻车点

5.1 特征值没排序:谱峰全乱或角度完全不对

现象:真实角度在 -25° 和 15°,谱峰却在完全无关的位置,或者只有一个峰,怎么调 SNR 都没用。

原因:很多人按“前 K 个特征向量是信号子空间”的记忆写代码,但 MATLAB 的 eig 默认升序排列特征值,E 的前几列其实对应最小特征值。如果直接拿前 K 列当信号子空间去构造谱,相当于把信号和噪声的投影关系整个颠倒,谱峰位置全错。更隐蔽的是,这类错误在不同矩阵上表现不一致——有些随机种子下还能撞出看起来像样的峰,换一组就彻底乱掉。

解决:统一用噪声子空间写法,取 E 的前 N−K 列,并在代码里注释清楚顺序依赖。用这个检查特征值分布:

[E, D] = eig(R); disp(diag(D).'); % 检查是否升序:前几个接近噪声底,中间有台阶 Un = E(:, 1:N-K); % 升序下最小的 N-K 个对应噪声子空间

5.2 阵元间距超过半波长:角度模糊与栅瓣伪峰

现象:真实角度 -30°,谱峰在 -30° 和 -16° 位置出现高度几乎相同的双峰,分不清真假。

原因:阵列相位差 2π·d·sinθ/λ 以 2π 为周期。当 d/λ 大于 0.5 时,存在另一个角度 θ_g 满足 sinθ_g = sinθ ± qλ/d,q=±1 时 θ_g 落在可见区 [-90°, 90°] 内,两个角度产生完全相同的阵元间相位差,谱图上就是等高的双峰。这是阵列本身的空间混叠,不是算法缺陷。

解决:把 d_lambda 设成 0.5 或更小。仿真里改一个参数就行,但要注意波长一致性:如果信号频率对应 λ=0.1m,间距 d 超过 0.05m 就开始有模糊风险。实测场景里阵元间距是硬件决定的话,先算清楚工作频率下 d/λ 的值,再决定能不能直接上标准 MUSIC。

5.3 声源相干:MUSIC 失效与空间平滑补救

现象:明明有两个声源,谱图只出一个峰,提高 SNR 也没用。

原因:MUSIC 的秩分解要求信号源互不相关,信号协方差矩阵的秩等于声源数 K。相干源(比如同一声音经墙面反射后与直达声同时到达)会让信号部分秩降低,特征值台阶消失,噪声子空间维度不再等于 N−K,正交关系失效。多径环境里这是最常见的“算法突然不灵”的原因。

解决:先去相干再做特征分解。工程标准做法是前向空间平滑,把 N 元阵切成多个子阵,子阵协方差平均后恢复满秩:

M_sub = N - 2; % 子阵尺寸,需大于声源数 K L_sub = N - M_sub + 1; % 子阵个数 R_smooth = zeros(M_sub, M_sub); for ii = 1:L_sub X_sub = X(ii:ii+M_sub-1, :); R_smooth = R_smooth + X_sub * X_sub' / snap; end R_smooth = R_smooth / L_sub; % 之后用 R_smooth 代替 R 做特征分解 [E, D] = eig(R_smooth); Un = E(:, 1:M_sub-K);

空间平滑的代价是有效阵元数从 N 降到 M_sub,分辨率小幅下降,但在相干场景里是“先活下来再说”的取舍。M_sub 至少要取 K+1,否则平滑后还是缺秩。

5.4 搜索网格太粗:估计角度永远踩不准

现象:真实角度 10.0°,估计出来 10.5°,改网格步长以外的参数都没变化。

原因:当网格步长是 1° 时,峰值只能落在离散网格点上,估计误差被量化限制在 ±0.5° 内。这不是算法精度不够,是搜索策略的问题。全域用 0.01° 可以解决,但 18001 个点的谱计算在低性能机器上会卡几秒,没必要。

解决:两段式搜索,先粗后细:

% 第一段:粗网格找大致区域 theta_grid1 = -90:1:90; P1 = compute_music_spectrum(theta_grid1); % 复用前面向量化代码 [~, idx1] = max(P1); coarse_angle = theta_grid1(idx1); % 第二段:在粗峰附近 ±3° 内用细网格精搜 theta_grid2 = (coarse_angle-3):0.01:(coarse_angle+3); P2 = compute_music_spectrum(theta_grid2); [~, idx2] = max(P2); fine_angle = theta_grid2(idx2);

第二段只有 601 个点,计算量是全域 0.01° 的零头,精度却一样。多声源场景对每个粗峰都做一次局部细搜即可。

5.5 快拍数太少或信噪比过低:谱峰漂移与假峰

现象:同一场景重复运行,谱峰位置每次差 2° 以上,谱图里出现随机的毛刺峰。

原因:样本协方差 R 的估计误差随快拍数减少而增大,低 SNR 时噪声特征值发散,噪声子空间不再准确,伪谱里就会冒出假峰。这不是随机种子的问题,是统计量本身不够稳。

解决:优先加快拍。快拍数受硬件限制不能加时,用对角加载稳定协方差矩阵:

alpha = 0.01; % 加载系数,常用 0.001~0.1 R = R + alpha * trace(R) / N * eye(N);

对角加载相当于给噪声特征值加一个底,防止它们发散后污染噪声子空间。代价是谱峰会变钝一点,分辨率轻微下降,但换来的是重复实验的稳定结果。工程里我一般先试 alpha=0.01,看谱峰不再乱跳就够用了。

6. 进阶用法:用 Monte Carlo 把分辨概率曲线跑出来

单次谱图好看不能说明算法可靠,特别是两个声源角度接近时,一次跑通可能是随机种子运气好。把 doa_music 改造成函数后,第一件值得做的事是把同一场景重复跑几十次,统计“分辨成功”的概率,画一条随角度间隔变化的分辨概率曲线。

先把成功判据定死:两个真实角度正负对称放置,比如 -Δ/2 和 +Δ/2,估计结果里在各自真实角度 ±1.5° 范围内都有峰值,才算成功。写成代码:

% 角度间隔从 2° 扫到 20° angle_sep_list = 2:2:20; num_trials = 50; success_rate = zeros(size(angle_sep_list)); for s = 1:numel(angle_sep_list) sep = angle_sep_list(s); ok = 0; for t = 1:num_trials [theta_est, ~, ~] = doa_music(100*s + t); % 不同种子,保证统计独立 if has_peak_near(theta_est, -sep/2, 1.5) && has_peak_near(theta_est, sep/2, 1.5) ok = ok + 1; end end success_rate(s) = ok / num_trials; end plot(angle_sep_list, success_rate, 'o-'); xlabel('角度间隔 (deg)'); ylabel('分辨成功率'); grid on;

has_peak_near 是在估计角度向量里找有没有落在目标角度附近的值,几行 diff 就能写出来。曲线会呈现明显的 S 形:角度间隔大时成功率 100%,小到一定程度后断崖式下跌到接近 0。成功率跌到 50% 附近的那个间隔,就是这套“阵元数 + 信噪比”配置下的分辨率极限。

把 SNR 也放进外层循环,画出一族曲线,就能直观看到信噪比对分辨极限的影响:10dB 时极限可能 6°,20dB 时压到 2°,这组数据比任何单条谱峰都有说服力。从那以后,我每次拿到新的 DOA 代码包,第一件事都是先跑一条分辨概率曲线,不急着看单条谱峰好不好看——谱峰图会骗人,统计曲线不会。希望这能帮你在自己的定位仿真里少走两步弯路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询