简介:面向无线通信、雷达与声纳系统研究者的深度学习波束形成设计项目包,主要解决利用神经网络自动生成优化波束策略、提升复杂信道环境下系统增益的问题,适合有一定信号处理基础并希望入门智能波束设计的学生与工程师。压缩包共11个文件,包含4个MATLAB脚本或数据、3个Python文件(训练、测试及工具模块)、3张系统模型与环境效果示意图,以及1份Markdown说明文档,整体仅312KB;其中MATLAB文件便于快速验证算法,Python脚本适合工程化训练与推理,结构轻量清晰。已有201人学习浏览。通过这份小体量资源,读者可以了解数据集的构造方式、模型选型与训练评估流程,比较传统算法与深度学习方法在波束形成中的性能差异,并借助示意图快速把握系统模型和信号环境,便于在仿真中复现、修改和扩展自己的设计思路,也可作为将人工智能引入物理层信号处理的教学型参考。
1. DL 波束形成:把阵列设计的“老手艺”交给数据
波束形成在阵列信号处理里属于最老的一批工具箱:加权、延迟求和、期望响应约束,这些四十年前提出的方法到今天还在大规模 MIMO、车载毫米波雷达和麦克风阵列里服役。但工程现场给到波束形成器的输入从来都不干净——来波方向估计有偏差、干扰形成相干多径、可用快拍少到协方差矩阵根本无法可靠估计,MVDR 这类自适应滤波器的输出信干噪比会直接塌掉。深度学习进入这个领域后改变的不是加权公式本身,而是把“怎么设计权矢量”这件事从显式建模转换成从数据中学习映射。BF-design-with-DL 这类项目把目标锁定在权矢量而非端到端输出信号,既保留了阵列原有的物理接口,又能让模型在模型失配、低快拍、强干扰场景下补上经典算法的短板。适合通信物理层、雷达、声学 AI 以及想在自适应波束形成器外面加一层数据驱动外壳的工程师阅读。
2. 波束形成的信号基础与 DL 介入的切入点
2.1 均匀线阵的接收模型:导向矢量与协方差矩阵怎么进网络
先建立最常用的信号模型。考虑 M 个阵元组成的均匀线阵,阵元间距 d = 0.5λ,一个远场窄带信号从角度 θ 入射,那么第 m 个阵元相对参考点的相位延迟是 2πdm·sin(θ)/λ,整个阵列的导向矢量写作:
a(θ) = [1, e^{-j2πd sin(θ)/λ}, ..., e^{-j2π(M-1)d sin(θ)/λ}]^T
接收信号可以表示成 x(t) = a(θ_s)s(t) + Σ a(θ_i)i(t) + n(t),其中期望信号、干扰和噪声叠加在一起。实际处理时不会直接用单次快拍,而是取 N 个快拍估计样本协方差矩阵,R = (1/N) Σ x(t)x^H(t),这是几乎所有自适应波束形成算法的输入接口。
在决定神经网络输入时有一个关键分叉:用原始快拍序列 X ∈ C^{N×M},还是用样本协方差矩阵 R ∈ C^{M×M}。我一般会选择后者,原因有三个:其一,协方差矩阵对信号波形的具体实现不敏感,网络学到的是空间二阶统计量,这正好匹配波束形成问题本身的物理结构;其二,R 是随阵元位置呈谐波关系的二维结构,实部虚部拆开后类似条纹图像,卷积核天然适合提取这种模式;其三,快拍数可以动态变化,只要重新估计 R 就能适应不同时长。
2.1.1 实虚部拆分:从复数矩阵到双通道图像
PyTorch 的卷积层不支持复数直接参与运算,常见做法是把 R 的实部和虚部分别当作两个通道堆叠,得到形状 [2, M, M] 的张量。这个做法保留了完整的相位信息,代价是模型要自己学会厄米特对称性。另一种做法是只取上三角部分再展平,维度从 M² 降到 M(M+1)/2,但会丢掉卷积天然的二维空间归纳偏置,实际效果反而不如双通道图像形式。后面章节的代码均采用实虚部双通道输入。
2.2 经典自适应波束形成的短板:模型失配、低快拍与相干干扰
MVDR(最小方差无失真响应)是理解 DL 价值的最佳参照系。MVDR 的解析解为 w = R^{-1}a(θ_s) / (a^H(θ_s)R^{-1}a(θ_s)),其指导思想是在期望方向响应恒为 1 的约束下最小化输出功率。这个解在理论上最优,但工程应用中有三个系统性痛点。
第一个痛点是导向矢量失配。当假设的来波方向与实际方向偏差超过 1° 到 3° 时,MVDR 会把期望信号当成干扰来抑制,形成“自零陷”,输出 SINR 可能比传统延迟求和还低。第二个痛点是协方差矩阵估计质量。阵列处理手册通常建议快拍数至少是阵元数的 2 到 3 倍,而实际通信系统中导频段往往只有十几个符号;快拍不足时 R 病态,求逆放大了噪声本征值,输出增益极不稳定。第三个痛点是相干干扰。当干扰是期望信号的多径反射时,两者在协方差层面高度相关,MVDR 的干扰相消机制会误伤期望信号,这类场景下线性约束类方法基本无能为力。
2.2.1 对角加载为什么只是“止痛药”
针对上述问题,工程上最常见的补救是对角加载:R_loaded = R + γI,γ 取为噪声功率的 10 倍左右。对角加载能缓解病态求逆和低快拍问题,但 γ 的取值对性能很敏感,而且它本质上是在方差和偏差之间做折中,无法处理相干干扰。DL 方法不需要显式设置这类正则系数,它通过训练数据隐式学习“什么样的协方差模式对应什么样的补偿策略”,这是二者最根本的差异。
2.3 DL 要拟合的映射关系:从协方差矩阵到滤波器权矢量
把问题形式化:给定样本协方差矩阵 R,目标输出是复权矢量 w* ∈ C^M,使得波束方向图在期望方向增益最大、在干扰方向形成零陷、同时保持可接受的白噪声增益。经典方法用解析表达式从 R 计算 w,DL 方法则是学习一个函数 f_θ:C^{M×M} → C^M,即 f_θ(vec(R)) ≈ w*。
这里值得注意的一点是,学习目标不是信号本身而是权矢量。端到端方案直接输出增强后的信号波形,确实能获得更高上限,但失去了阵列信号处理的核心可解释资产——权矢量可以随时画方向图、检查零陷深度、计算 SINR。BF-design-with-DL 这类项目选择输出 w,等于把深度学习嵌进了传统信号处理链路里,后续的波束扫描、多目标测向、干扰评估全部复用既有代码,这是工程落地上很重要的考量。
3. 搭建 DL 波束形成网络:仿真数据集与模型结构是重头
3.1 合成有“难度”的样本:干扰、相干多径与功率随机的数据生成
DL 波束形成项目的成败七成在数据。如果只在白噪声背景加单个非相干干扰,那任何线性滤波器都能做到接近最优,网络学不到对抗模型失配的能力。我生成训练数据时会刻意覆盖几个让经典方法翻车的场景:干扰与期望角度接近(小于 8°)、期望信号的相干多径、阵元增益相位不一致,以及信噪比从 -5 dB 到 15 dB 的大范围变化。下面是一段可运行的数据生成代码。
import numpy as np def simulate_batch(batch_size=64, M=8, snapshots=64, snr_range=(-5, 15), inr_range=(10, 30)): """ 生成一批训练样本:每个样本包含期望信号、0~2 个非相干干扰、 以及一个相干多径源。返回快拍、理想权矢量、协方差矩阵。 """ Xs, Ws, Rs = [], [], [] d = 0.5 # 阵元间距,半波长 for _ in range(batch_size): # 随机期望方向,覆盖 -60° ~ 60° theta_s = np.random.uniform(-60, 60) # 干扰方向与期望方向保持至少 8° 间隔,越接近越考验分辨力 theta_i = np.random.uniform(-60, 60) while abs(theta_i - theta_s) < 8: theta_i = np.random.uniform(-60, 60) # 导向矢量:指数项为各阵元相对参考点的相位差 idx = np.arange(M) * d a_s = np.exp(-1j * 2 * np.pi * idx * np.sin(np.deg2rad(theta_s))) a_i = np.exp(-1j * 2 * np.pi * idx * np.sin(np.deg2rad(theta_i))) # 随机信噪比与干噪比,单位 dB,每次采样都不同 snr = np.random.uniform(*snr_range) inr = np.random.uniform(*inr_range) ps = 10 ** (snr / 10) pi = 10 ** (inr / 10) # 复高斯源信号与噪声 s = np.random.randn(snapshots) + 1j * np.random.randn(snapshots) i = np.random.randn(snapshots) + 1j * np.random.randn(snapshots) n = np.random.randn(snapshots, M) + 1j * np.random.randn(snapshots, M) # 叠加场景:期望 + 干扰 + 噪声 X = (np.outer(a_s, s).T * np.sqrt(ps) + np.outer(a_i, i).T * np.sqrt(pi) + n) # 样本协方差矩阵:后接模型的标准输入 R = (X.conj().T @ X) / snapshots Xs.append(X) Ws.append(np.concatenate([a_s.real, a_s.imag])) Rs.append(R) return np.stack(Xs), np.stack(Ws), np.stack(Rs)这段代码里最关键的是角度间隔约束和功率随机化。角度间隔小于 8° 的样本逼迫网络学习超高分辨能力,而不是简单地“对准期望、压制干扰”;信噪比和干噪比同时随机化,则让网络在不同动态范围下都保持有效。理想权矢量直接取期望信号的导向矢量作为监督,是为了让网络先掌握匹配滤波的基本行为。准备数据时要注意 batch 里的每个样本都是独立随机生成的,这比固定测试集更能模拟真实环境的非平稳性。
3.1.1 多径与阵列误差注入
上面代码只包含非相干干扰。要让模型在真实阵列上不退化的关键一招是注入相干多径:生成一个与期望方向相同但幅度相位随机、延迟一拍的相关分量叠加进观测,这样协方差矩阵里会出现信号自相关的“鬼影”,网络必须学会区分直达径与多径。另一个高性价比技巧是给每个样本随机乘上通道增益误差 g_m = 1 + 0.1·randn(M),模拟阵元不一致性。
3.2 特征工程选择:实虚部双通道为什么比幅度相位更稳
在数据送入网络前,需要把复数协方差矩阵转换为实数张量。常见有三种拆分方式:
| 特征形式 | 张量形状 | 相位信息 | 实际效果 |
|---|---|---|---|
| 实部 + 虚部双通道 | [2, M, M] | 完整保留 | 收敛最稳,推荐 |
| 幅度 + 相位双通道 | [2, M, M] | 保留但有 2π 跳变 | 相位在 ±π 边界突变,模型难学 |
| 实虚部拼接成单通道 | [M, 2M] | 完整保留 | 丢失二维卷积结构,效果最差 |
我在项目中统一用实虚部双通道。有一个细节值得展开:协方差矩阵对角线元素是实的且恒为正,非对角线元素满足 R_{mn} = R*_{nm},这意味着网络输入存在明显的对称冗余。卷积网络不会显式利用这个先验,所以可以用更小的卷积核。另一种做法是把 R 的实虚部做成三通道,额外加入一个全 1 的掩码通道来标记对角线位置,这在阵元数较多时能小幅提升精度。
def prepare_input(R_complex): """把 [B, M, M] 的复数协方差矩阵转成 [B, 2, M, M] 的实数张量。""" real = R_complex.real imag = R_complex.imag # 归一化:按矩阵迹缩放,去除绝对功率影响 trace = real.diagonal(axis1=-2, axis2=-1).sum(-1, keepdims=True) real = real / (trace[..., None] + 1e-8) imag = imag / (trace[..., None] + 1e-8) return np.stack([real, imag], axis=1).astype(np.float32)归一化这一步经常被忽略,但它是跨信噪比泛化的关键。协方差矩阵的绝对幅度随 SNR 变化几个数量级,如果不归一化,网络只能记住训练集中见过的功率范围,换到强信号或弱信号场景直接失效。按矩阵迹归一化后,输入只保留阵元间的相对相位与相对功率分布——这才是决定波束方向图的物理量。
3.3 模型主体:从 2D 卷积到复数编码器
模型设计遵循“小阵列用浅网络”的原则。对于 8 阵元的典型规模,三层卷积加一个全连接头已经足够。网络过大会在仿真数据上过拟合,并在真实阵列上表现出对训练分布的过度依赖。下面给出一个可直接训练的模型定义。
import torch import torch.nn as nn class CovBeamformer(nn.Module): """ 输入:[B, 2, M, M] 的协方差矩阵(实虚部双通道) 输出:[B, M] 的复数权矢量(实部虚部交织在最后一维) """ def __init__(self, M=8): super().__init__() self.M = M # 特征提取:2 层小卷积,捕获协方差矩阵的局部谐波结构 self.backbone = nn.Sequential( nn.Conv2d(2, 16, kernel_size=3, padding=1, bias=False), nn.BatchNorm2d(16), nn.ReLU(inplace=True), nn.Conv2d(16, 32, kernel_size=3, padding=1, bias=False), nn.BatchNorm2d(32), nn.ReLU(inplace=True), nn.AdaptiveAvgPool2d((1, 1)), # 全局池化,得到 32 维特征 ) # 输出头:每个阵元需要 1 个实数 + 1 个虚数,共 2M 维 self.head = nn.Linear(32, 2 * M) def forward(self, x): feat = self.backbone(x).flatten(1) w = self.head(feat).view(-1, self.M, 2) # 最后一维拆成实部虚部,组合成复数权矢量 return torch.complex(w[..., 0], w[..., 1])设计上的两个细节。第一,卷积核大小固定为 3×3,因为协方差矩阵相邻元素的相位差对应相邻阵元的空间采样,局部 3×3 窗口已经足以计算两两阵元间的相位关系;更大的核会引入不必要的全局耦合,在小阵列上还会加剧过拟合。第二,最后用 AdaptiveAvgPool2d 而不是展平后直接接全连接,这样可以屏蔽输入矩阵尺寸变化——如果将来阵元数从 8 扩展到 16,backbone 部分不用改,只调整 head 的输入输出维数即可。
3.3.1 训练时的数据增强策略
数据增强是让模型从仿真走向真实阵列的最有效手段。常规的随机旋转在阵列信号里不适用,因为阵元位置不能随意置换,但有两种增强方式物理上合理且效果显著:随机相位扰动和随机通道遮蔽。相位扰动是在导向矢量上乘 exp(jφ_m),φ_m 为均值为 0、标准差 0.02 rad 的随机噪声,相当于模拟真实阵列的校准误差;通道遮蔽是随机将 1 到 2 个阵元的输入置零,让网络学会在部分通道失效时依然输出可用权值。
4. 训练与评估:损失函数怎么设计才能保住阵增益
4.1 为什么 MSE 直接压在权矢量上训不动
把网络输出与理想权矢量直接做均方误差,是新手最常见的做法,但效果往往很差。根本原因在于权矢量存在整体相位模糊:w 和 w·e^{jφ} 对应的波束方向图完全相同,输出 SINR 也一样,但 MSE 会把二者当成完全不同的目标。训练过程中网络学不到这种等价性,只好在多个最优解之间取平均,最终得到一个幅度衰减、相位漂移的权矢量,方向图主瓣指向偏移。
改进方式有两种。一种是去掉监督的绝对相位,只对归一化后的权矢量计算弦距离或负相关系数;另一种是根本不使用显式权值监督,改成对波束形成的最终目标进行优化。第二种方式更贴合物理含义,也是我更常采用的做法。
4.2 结合频谱抑制与 SINR 的组合损失设计
波束形成实质上是三个目标的联合优化:期望方向响应尽量接近 1、干扰方向响应尽量接近 0、输出总功率尽量小。这三个目标可以直接构成损失函数,不需要中间人为定义“理想权值”。下面是我常用的组合损失函数。
def beamforming_loss(w, R, a_s, a_i): """ w: [B, M] 复数权矢量 R: [B, M, M] 复数协方差矩阵 a_s: [B, M] 期望方向导向矢量 a_i: [B, M] 干扰方向导向矢量 """ # 期望方向响应:w^H a_s,取模平方后接近 1 resp_s = torch.abs(torch.einsum('bm,bm->b', w.conj(), a_s)) ** 2 # 干扰方向响应:w^H a_i,逼近 0 resp_i = torch.abs(torch.einsum('bm,bm->b', w.conj(), a_i)) ** 2 # 输出总功率:w^H R w,实部 out_power = torch.einsum('bm,bmn,bn->b', w.conj(), R, w).real # 组合:保信号 + 抑干扰 + 白噪声增益正则 loss = (-resp_s.mean() + 0.5 * resp_i.mean() + 0.05 * (out_power / (resp_s + 1e-8)).mean()) return loss三个损失项的权重系数需要解释一下。第一项取负号是为了最大化期望方向响应,它保证了权矢量不会退化成全零解。第二项的 0.5 给干扰抑制留出平衡空间,如果加大到 1.0,模型会牺牲主瓣增益来追求更深的零陷。第三项的 0.05 是白噪声增益约束,out_power / resp_s 的本质是总输出功率除以信号响应功率,防止网络通过放大噪声来满足前两项——这是波束形成里最容易出现的不稳定模式。训练时如果发现权矢量范数异常增大,优先调大这一项的系数。
4.3 离线评估方法与 MVDR 的对照实验
训练完成后,评估不能只看损失曲线。波束形成器最核心的指标是输出 SINR,定义为:
SINR_out = P_s |w^H a_s|² / (w^H R_{i+n} w)
其中 R_{i+n} 是只含干扰和噪声的协方差矩阵,需要在生成数据时单独保存。评估时把测试集划分为四组:角度精确、角度偏差 3°、快拍数为 16、含相干多径。每组分别运行 MVDR 和训练好的 DL 模型,记录平均 SINR 相对输入 SINR 的提升量。典型的对照结果趋势如下表。
| 测试场景 | MVDR 提升 | DL 模型提升 |
|---|---|---|
| 角度精确,快拍 64 | 8~10 dB | 7~9 dB |
| 角度偏差 3° | 2~4 dB | 6~8 dB |
| 快拍 16 | 1~3 dB | 5~7 dB |
| 含相干多径 | 负增益 | 4~6 dB |
这张表揭示了一个重要规律:在条件理想的场景里,MVDR 仍然略微占优;DL 的优势集中在条件恶化时,尤其是角度失配和低快拍。因此一个稳健的项目落地策略是把 DL 定位成“难场景补偿器”,而不是完全替代经典算法。验证时还需要画出权矢量对应的波束方向图,检查零陷是否对准干扰、旁瓣是否被抬升。这个可视化检查往往比数值指标更能暴露训练中的隐蔽问题。
5. 把 DL 波束形成推上阵列:部署细节与三个现场坑
5.1 坑一:输入归一化的尺度漂移问题
仿真训练时按矩阵迹归一化效果不错,但真实阵列的数据可能有偶发的大幅度脉冲干扰,导致 R 的迹被某个异常快拍主导,归一化后所有阵元的相对幅度被压缩到接近零。应对策略是部署端先做中值滤波去脉冲,再计算协方差矩阵,最后归一化。另一层保障是让训练数据里混入一定比例的异常值样本,让网络见过“坏输入”。
5.2 坑二:通道损坏与阵元失效
真实阵列经常出现个别通道增益衰减甚至完全失效。如果在训练时没有做过通道遮蔽增强,模型会把失效通道的数值当作有效信息,输出的权矢量在该阵元上出现尖峰。训练时按 10% 概率随机丢弃 1 到 2 个阵元的数据,能教会网络在通道缺失时自动降低对应权值的模值。现场运行时还可以在协方差矩阵输入前叠加一个通道健康掩码,把已知失效阵元的位置直接置零,进一步提升鲁棒性。
5.3 坑三:非平稳干扰下的推理时序
仿真数据通常假设干扰方向在整个快拍窗内不变,真实环境中干扰可能移动。滑窗长度设置要权衡:窗口太短,协方差矩阵样本不足,网络输入噪声大;窗口太长,干扰移动被平均掉,网络输出的零陷跟不上目标变化。我常用的折中是 16 个快拍作为推理窗,配合 50% 重叠滑窗。如果算力允许,可以在时间维上做轻量 LSTM 后处理,让网络参考前几帧的权矢量输出,抑制单帧估计抖动。
5.3.1 ONNX 导出与混合推理的快速上手
模型量化与导出是整个部署流程最顺滑的部分。由于网络只有卷积和全连接,PyTorch 导出到 ONNX 几乎不需要额外适配。
torch.onnx.export( model, dummy_input, "bf_dl.onnx", input_names=["cov_parts"], output_names=["weights"], opset_version=18, dynamic_axes={"cov_parts": {0: "batch"}, "weights": {0: "batch"}} )导出时设置 dynamic_axes 保证推理时的 batch 可动态变化,方便在单片机上逐帧处理。运行时只需把协方差矩阵归一化后直接送入 ONNX Runtime,得到复数权矢量后,后续的加权求和、方向图扫描和 SINR 估计全部沿用经典代码。部署建议是保留 MVDR 作为默认模式,当检测到输出 SINR 低于阈值或协方差矩阵条件数异常大时,再切换到 DL 链路;这个二段式结构既享受了 DL 在恶劣场景下的增益,又不牺牲正常工况下经典算法的可解释性与算力开销。
本文还有配套的精品资源,点击获取