
简介面向阵列信号处理与无线通信领域的研究者这份资源提供稀疏阵列场景下稳健稀疏DOA估计的完整Matlab实现核心采用迭代稀疏渐近最小方差SAMV方法在传感器数量受限时实现超分辨方向估计可用于雷达探测、声学成像、无线定位等方向。包内共25个文件以23个.m源码文件为主覆盖SAMV、SPICE、MUSIC、IAA等代表性算法函数以及误差统计、绘图与蒙特卡洛仿真脚本另附README说明和License授权文件整体仅39KB轻量易部署。已有267人学习下载。借助其中包含的数据预处理、非均匀阵列几何建模、L1稀疏优化迭代求解、稳健超分辨处理等环节的可运行代码读者既能横向对比不同算法在不同信噪比下的精度与抗异常值能力也能修改稀疏阵型参数进行二次实验适合具备信号处理基础、希望系统掌握稀疏DOA算法原理并快速复现的研究生和工程技术人员。1. SAMV 稀疏 DOA 估计网格化稀疏重构为什么比波束扫描先声夺人搞阵列DOA的工程人大多先被MUSIC和ESPRIT教育过。这两个子空间方法在均匀线阵上很漂亮但换成稀疏阵列之后协方差矩阵的秩被阵列结构打乱快拍一少谱峰就开始飘低信噪比下甚至出现主峰消失这种玄学现象。SAMVSparse Asymptotic Minimum Variance这类基于协方差拟合的稀疏DOA估计是把角度搜索变成网格上的稀疏重构问题再按渐近最小方差准则做加权迭代天然能吃下sparsearray的非均匀观测结构。这篇笔记把它讲透模型、迭代、参数边界和几个我踩过的坑。适合想用嵌套阵列或互质阵列做稳健超分辨DOA的工程师。2. 从观测模型到稀疏表示SAMV 对 sparsearray 的协方差拟合2.1 稀疏阵列的自由度从哪来虚拟阵元的数量怎么数均匀线阵的阵元间距固定为半波长N个阵元的自由度只有N-1最多分辨N-1个信号。sparsearray的思路是人为把阵元位置拉开用物理阵元之间的位置差合成一个更长的虚拟阵列。以常用的二级嵌套阵为例第一级子阵N1个阵元间距半波长第二级子阵N2个阵元间距是第一级阵元数加一倍也就是(N11)倍半波长。物理阵元总数才N1N2但差集生成的连续虚拟阵元数可以达到2N2(N11)-1。举个例子N13、N23的二级嵌套阵物理阵元位置是[0, 1, 2, 3, 7, 11]单位半波长。把阵元位置两两相减差集从-11到11是连续的也就是说6个物理阵元换来了23个连续虚拟阵元。这个自由度增量直接决定了你能分辨的信号个数和角度间隔。SAMV在处理这类阵列时不需要像MUSIC那样先做空间平滑恢复协方差矩阵的秩而是把虚拟阵列的观测模型直接当作稀疏重构问题来解。这里要区分一点物理阵元位置差会产生重复的虚拟阵元做协方差向量化时同一个虚拟阵元位置对应的协方差项需要取平均。嵌套阵列的好处是差集连续、重复少互质阵列阵元数更少但差集里有空洞需要用补洞策略。工程上优先推荐嵌套阵列因为SAMV的协方差拟合过程对连续虚拟阵列最友好。2.2 SAMV 的代价函数与迭代式最小方差加权到底加在哪角度估计的本质是判断哪些角度方向上有信号把角度范围[-90°, 90°]按网格离散成K个点每个网格点对应一个导向矢量a_i。理想情况下空间功率向量p只有真实信号方向上的分量非零其他位置都是零这就是稀疏DOA的建模思路。观测协方差可以写成R A diag(p) A^H σI其中A是M×K的导向矩阵p是要估计的K维功率向量σ是噪声功率。SAMV不去硬解l1范数最小化而是在协方差拟合框架下找到让估计误差渐近方差最小的p。这种做法的好处是正则化参数隐含在迭代过程中不需要像l1-SVD那样手动调一个影响很大的λ。工程里常见的SAMV风格迭代核心是两步。第一步固定p和σ求协方差矩阵R的逆第二步用加权的最小二乘更新每个网格点的功率更新式可以写成p_i ← p_i · sqrt( (a_i^H R^{-1} S R^{-1} a_i) / (a_i^H R^{-1} a_i) )其中S是样本协方差。这个形式和SPICE的更新很像SAMV的改进在于把谱估计从“拟合误差最小”推向“估计方差最小”反映到更新式上就是把平方根更新换成更强的平方加权p_i ← p_i · (a_i^H R^{-1} S R^{-1} a_i) / (a_i^H R^{-1} a_i)^2分母多乘了一项a_i^H R^{-1} a_i这一项在Capon波束形成里也很常见物理含义是抑制从其他方向漏进来的干扰。这个加权能压低旁瓣让稀疏谱的背景更干净代价是迭代更容易在低信噪比下震荡。后面讲到参数设置时这两种更新形式我会给出切换建议。2.3 用 Python 构造 sparsearray 的观测模型写代码之前先把导向矩阵构造清楚。阵元位置pos以半波长为单位时第k个网格角θ对应的导向矢量是a_k exp(-jπ·pos·sinθ)。下面这段代码生成稀疏阵列的导向矩阵import numpy as np # 二级嵌套阵阵元位置以半波长为单位 pos np.array([0.0, 1.0, 2.0, 3.0, 7.0, 11.0]) M pos.size # 网格从 -90 到 90 度间隔 0.5 度 theta_grid np.linspace(-90, 90, 361) sin_theta np.sin(np.deg2rad(theta_grid)) # 导向矩阵 A维度 (6, 361) A np.exp(-1j * np.pi * pos[:, None] * sin_theta[None, :])这段代码里的pos是半波长的倍数所以相位项是π·pos·sinθ。如果你的阵元坐标直接以米为单位要先用阵元间距除以半波长做归一化否则相位会差一个系数谱峰直接偏掉。网格间隔0.5度对应361个网格点对单次DOA估计来说运算量不大后面迭代主循环里每次要处理361个点的更新完全跑得动。观测数据怎么构造假设有D个信号来波方向已知用于仿真可以先随机生成信号和噪声np.random.seed(42) N 200 # 快拍数 D 2 # 信号数 angles np.array([-8.0, 3.0]) # 信号源随机相位 S_signal np.exp(1j * 2 * np.pi * np.random.rand(D, N)) A_source np.exp(-1j * np.pi * pos[:, None] * np.sin(np.deg2rad(angles))) # 加噪声信噪比 5 dB X A_source S_signal SNR 5.0 noise_power np.var(X) / (10 ** (SNR / 10)) X X np.sqrt(noise_power / 2) * (np.random.randn(M, N) 1j * np.random.randn(M, N)) # 样本协方差矩阵 S (X X.conj().T) / N这里S是M×M的样本协方差矩阵后面SAMV迭代直接吃这个S不需要再单独处理稀疏阵列的差集。真正的sparsearray优势体现在A的构造上——如果你的导向矩阵用的是虚拟阵列只需把pos替换成差集去重后的虚拟阵元位置其余逻辑完全一样。3. 在 Python 里把 SAMV 迭代写出来最小可运行代码与三个必调参数3.1 完整迭代主循环SPICE 型更新与 SAMV 加权切换把上一节的S和A喂给下面的函数就能跑出稀疏功率谱。这个函数是我在实际项目里一直在用的版本几十行代码没有黑匣子def samv_doa(S, A, modespice, n_iter200, tol1e-4): M, K A.shape # 初始化为等功率噪声取样本协方差平均功率 p np.ones(K) / K sigma np.real(np.trace(S)) / M # 对角加载项防止协方差矩阵奇异 lam 1e-4 * np.real(np.trace(S)) / M for it in range(n_iter): # 用当前 p 和 sigma 重建协方差 R A np.diag(p) A.conj().T sigma * np.eye(M) R R lam * np.eye(M) # 求逆小矩阵直接求逆 R_inv np.linalg.inv(R) # H 矩阵对应渐近最小方差准则里的加权项 H R_inv S R_inv p_new np.zeros_like(p) for i in range(K): a A[:, i:i1] if mode spice: # SPICE 型更新单调稳定 num np.real((a.conj().T H a)[0, 0]) den np.real((a.conj().T R_inv a)[0, 0]) p_new[i] p[i] * np.sqrt(num / (den 1e-12)) else: # SAMV 型平方加权低信噪比下背景更干净 num np.real((a.conj().T H a)[0, 0]) den np.real((a.conj().T R_inv a)[0, 0]) p_new[i] p[i] * num / (den * den 1e-12) # 噪声功率更新 sigma_new sigma * np.sqrt( np.real(np.trace(R_inv S R_inv)) / M 1e-12 ) change np.max(np.abs(p_new - p)) p, sigma p_new, sigma_new if change tol: break return p, sigma这段代码最需要注意的是循环里对每个网格点都做了a.conj().T H a这是为了避免一次性构造K×K矩阵占内存。361个网格点还不明显如果网格细化到1801个点稀疏矩阵乘法会快很多但直接用循环是逻辑最清晰的版本。参数方面n_iter我一般给200。SPICE模式通常50到80次迭代就收敛SAMV模式因为加权更强需要多跑一阵。tol设成1e-4已经够用它比较的是p_new和p的最大绝对差而不是相对差所以p的绝对量级对结果有影响。如果你把p初始化成所有网格点功率都是1而不是1/Ktol要相应放宽。3.2 对角加载系数最容易忽视的稳定性开关协方差矩阵求逆是整个算法最脆弱的地方。稀疏阵列阵元M6快拍数N200时S理论上是满秩的但S的较小特征值很小逆矩阵放大了噪声。尤其当两个信号间隔很近导向矢量高度相关时R本身就会病态。代码里的lam是对角加载系数我一般取样本协方差迹的1e-4倍。这个系数太小起不到稳定作用太大则会把谱峰抹平。低信噪比场景下我可以把lam放大到1e-2倍噪底会高一些但至少不会出现逆矩阵爆炸产生的狗牙谱。这个系数不需要精确标定你只需要保证R的最小特征值不低于lam就说得过去。另一个容易被忽略的点是对角线加载放在每次迭代的R重建之后。如果直接把lam加到S上效果不一样因为迭代里的R是模型协方差模型和样本之间的偏差会一直存在。放在R上是给求逆过程一个下限物理意义更清楚。3.3 多快拍联合与实值化加速多快拍信息已经包含在S里不需要单独处理。快拍数N大于阵元数M时S的秩是M直接用N小于M时S的秩最多N-1这时候必须把lam调大一到两个数量级否则求逆都是错的。还有一个提速技巧当阵元数和网格点都不大时可以把这个复数迭代转成实值问题。做法是把导向矩阵A拆成实部和虚部拼成[A.real; A.imag]同时把S拆成[real(S), -imag(S); imag(S), real(S)]。SAMV协方差拟合对实部和虚部是线性操作转换后所有矩阵运算都是实数速度能快一倍不止。实数化之后要注意S的维度翻倍但核心里对R重建和求逆的逻辑不变只是A和S都换成实值版本。我一般在网格点超过1000个时才会用实数化。361个网格点的规模复数版本几十毫秒一次迭代完全不需要加速。4. 稀疏 DOA 的工程参数配置阵元排布、快拍数与信噪比的实际边界4.1 阵元位置排布嵌套阵怎么选 N1 和 N2二级嵌套阵的阵元位置设计有公式但工程实现时不要直接照抄最大自由度配置要看你实际的物理安装条件。N1和N2的比例会影响虚拟阵元连续区间的长度。N13、N23给出23个连续虚拟阵元改成N14、N22自由度变成2×2×5-119但物理孔径差不太多。区别在第二级子阵的间隔N1越大(N11)倍半波长越大阵元间距越大互耦越强。我的经验是优先级依次是互耦、孔径、自由度。贴片天线的互耦在间距小于0.3倍半波长时迅速恶化所以第二级阵元间距最好不要超过3倍半波长太多。N13、N23这个组合对大多数场景是平衡点物理阵元6个连续虚拟阵元23个最远阵元间距11倍半波长互耦在可接受范围。如果你只有5个物理阵元N12、N23自由度是2×3×3-117也够用。还有一个容易犯的错直接把半波长当作物理单位忘记了设计频率。arrays位置以半波长为单位时一旦工作频率偏了5%阵元电尺寸跟着偏导向矩阵的相位模型全错。宽带系统用稀疏阵列要非常慎重SAMV本身是窄带模型频率变了等于阵列变短或变长谱峰位置偏差比均匀线阵大得多。4.2 快拍数与信噪比什么条件下 SAMV 比 MUSIC 更值得用SAMV的优势不在高信噪比大快拍那个区间MUSIC已经足够好。它真正值钱的是低信噪比、少快拍、信号相干这三个场景。少快拍条件下MUSIC需要协方差矩阵的噪声子空间稳定快拍少于3M时子空间估计方差很大SAMV直接拟合协方差对S的秩要求没那么苛刻N30到50也能出峰。低信噪比是另一个维度。我做过一组仿真SNR从10dB降到0dBMUSIC在2dB左右开始出现主峰分裂SAMV在0dB时两个间隔5度的信号还能分辨。原因是SAMV的加权更新天然给强峰更大的权重相当于反复“聚焦”到信号方向。但这不代表SNR可以无限低到-10dB以下稀疏谱开始出现假峰表现为随机旁瓣被抬升需要把mode参数切到spice型更新才压得住。快拍数和信噪比本质上是同一个东西在换N200快拍时0dB信噪比对应足够的统计量N32快拍时需要至少5dB才能稳定。实际项目里不要只看单项最有效的判断方法是跑一次谱数一下背景噪底的起伏。如果噪底上出现和信号峰同量级的毛刺先把快拍加上去比调任何参数都有效。4.3 参数选择对照表不同场景下的推荐起点场景网格间隔快拍数对角加载系数SAMV模式备注快速粗扫1.0°321e-3 × trace(S)/Mspice只用来确认有没有源常规DOA0.5°2001e-4 × trace(S)/Mspice默认参数最稳低SNR精细0.1°5001e-2 × trace(S)/Msamv目标区间先粗搜再细搜相干信号0.5°2001e-3 × trace(S)/Mspice需要配合平滑或去相干虚拟阵列0.1°2001e-4 × trace(S)/Msamv用差分阵元位置构造A表格里的trace(S)/M就是样本协方差的平均特征值量级用它做加载系数可以自适应不同功率水平。注意网格间隔从0.5度换成0.1度时相邻网格的导向矢量相关性上升SAMV平方加权模式更容易震荡所以低SNR场景我仍然推荐spice模式网格细并不等于一定要用激进更新。5. SAMV 使用避坑网格失配、迭代震荡与协方差奇异的三类翻车5.1 谱峰偏出真实角度网格失配要分粗对齐和细拟合两步现象真实来波方向是3.3°网格间隔0.5°SAMV谱峰值出现在3.0°位置看起来只差0.3度但重复实验发现峰值始终偏向网格点不是随机抖动。原因这是网格化模型的系统性偏差。网格化角度估计的误差上限是网格间隔的一半0.5度网格对应最大0.25度偏差但SAMV的加权更新会把能量往最近的网格点聚集表现成固定偏移而不是随机误差。解决分两步走。第一步用0.5度网格粗搜锁定峰所在区间第二步把网格缩到0.1度并且只搜峰附近的30到40个网格点这样运算量增加很小。更快的做法是保留粗网格对峰和左右相邻三个点做抛物线插值校正公式是步长乘以(左功率减右功率)除以(左功率减两倍中功率加右功率)的一半。这个插值在SNR高于5dB时能把误差压到0.1度以内低信噪比下不如直接细搜可靠。5.2 迭代残差不收敛相邻网格强相关造成的权值竞争现象modesamv时谱峰在两个相邻网格点之间来回跳p的最大变化量降不下去迭代到200次还在震荡最终谱上出现一个宽平的伪峰覆盖两三个网格点。原因当网格间隔小于瑞利限时相邻网格的导向矢量相关系数很高SAMV的平方加权让两个网格点互相抢能量功率在两者之间反复倒手形成极限环。这个问题在0.1度细网格上特别容易出现。解决把mode切回spice平方根更新有天然的衰减作用能打破这个倒手循环。如果必须用samv模式就把对角加载系数lam调大一个量级给协方差矩阵一个更厚的地板收敛后峰值会偏低但不再震荡。我的习惯是永远先跑spice模式确认大体位置再用samv模式做一次精细谱不做单次细网格samv。5.3 协方差矩阵奇异快拍数不足和通道不一致怎么救现象快拍数N小于阵元数M时代码报LinAlgError奇异矩阵或者不报错但谱图全是噪声谱峰完全没有规律。原因样本协方差S的秩最多是min(M, N)减1N小于M时S必然秩亏缺。R_inv在数学上不存在实际数值上求逆结果被舍入误差主导。另外阵元通道幅相不一致也会让S偏离理想模型但表现为不奇异、只是谱峰变差。解决首先把N加到大于M且留出余量最少N1.5M。场地受限时把对角线加载系数lam从1e-4提到1e-2量级等价于给S加了一个满秩的基底。注意加载太大等于把协方差矩阵改成对角占优谱分辨力会下降这是物理规律没有免费午餐。通道不一致的救法是校准发射一个已知角度信号把实测导向矢量存下来替换理论A比任何正则化都有效。5.4 支持集在无源方向漂移低信噪比假峰问题现象SNR0dB真实信号在-8°和3°但谱在20°位置出现一个和真实峰差不多高的假峰。迭代次数越多假峰越明显。原因低信噪比下噪声子空间的能量没有被完全压制p和σ的更新互相耦合。σ偏大时R的噪声地板抬高导向矩阵中对噪声敏感的网格点被加权放大形成假峰。这本质上是优化问题陷入局部极小不是算法随机性导致。解决换初始化。默认的等功率初始化会让所有网格点有同样的启动条件低信噪比下旁瓣最先被加权的概率不小。我给三个实用办法用常规波束形成的功率谱做p的初值或者跑一次MUSIC在峰附近网格给略高的初始功率再或者多启动用三组不同随机初值跑同一个数据取三次结果中背景噪底最低的那次。工程上我只用常规波束形成初始化效果好且稳定代价是当前提下有信号才能用。6. 验证 SAMV 分辨力的最小实验两个间隔 3 度的信号怎么分辨6.1 实验设置与判定指标用前面那段代码就能做一个完整的可复现实验。阵元位置用[0, 1, 2, 3, 7, 11]两个信号分别放在0°和3°快拍200SNR5dB。这个间距小于6阵元均匀线阵的瑞利限MUSIC大概率只出一个峰SAMV要能稳定分出两个峰才算有效。跑完实验不要只看谱图记录三个量峰值个数、两个峰的估计角度、谱背景的最大旁瓣功率。判定标准我定得比较严估计角度与真值误差小于0.5°两个峰之间的最小凹点功率低于两侧峰值至少3dB。达不到这个标准说明你的对角加载系数或者网格方式不对不要急着调算法参数。6.2 后续常用的稳健化技巧仿真跑通后真正的阵列数据还会有通道不一致、互耦、阵元位置误差三个问题。通道不一致我前面说过用实测导向矢量替换理论值互耦体现在阵元间距小于半波长的位置嵌套阵列第一级阵元间距就是半波长互耦通常可控阵元位置误差在稀疏阵列上影响最大0.02倍半波长的位置误差就会让谱峰偏移超过0.1度所以用稀疏阵列做DOA之前先对阵列做一次相位校准别急着信仿真里的精度。我个人的一个习惯是所有稀疏DOA实验先跑一遍常规波束形成确认没有增益异常再跑一遍MUSIC做对照最后才用SAMV。这个流程多花几分钟但能让我判断一个异常结果是阵列问题还是算法问题。好的稀疏DOA解决的是分辨力问题不是阵列标校问题硬件没修好之前算法再先进也是白搭。希望这篇笔记能帮你少走几个弯路。本文还有配套的精品资源点击获取