2. 波束成形数学预备:复数运算回顾、矩阵运算基础、傅里叶变换与短时傅里叶变换、相关矩阵与协方差矩阵
好,咱们正式开始第二讲。说实话,很多同学一上来就急着调波束成形的算法,结果被复数、矩阵这些基础概念卡得死死的。我个人习惯是,先把数学工具磨锋利了,再去做算法实现。这一章,咱们就把波束成形里最常用的几个数学工具过一遍。
2.1 复数运算回顾
波束成形里,信号几乎都是用复数表示的。为什么?因为麦克风采集到的信号是实信号,但经过希尔伯特变换或者傅里叶变换之后,就变成了复数形式。说白了,复数能同时表示信号的幅度和相位,而波束成形恰恰就是靠调整相位来改变指向性的。
复数的基本形式:z = a + jb,其中 j 是虚数单位(j² = -1)。
几个关键运算:
- 共轭:
z* = a - jb。这个在计算功率谱和协方差矩阵时天天用。 - 模:
|z| = sqrt(a² + b²),代表信号的幅度。 - 相位:
∠z = atan2(b, a),代表信号的延迟。 - 乘法:
z₁ × z₂ = (a₁a₂ - b₁b₂) + j(a₁b₂ + a₂b₁),其实就是幅度相乘、相位相加。
避坑指南:我曾经在写C代码时,直接用 atan 代替 atan2,结果相位算出来总是差个π。记住,atan2 能处理四个象限,而 atan 只能处理两个象限。在波束成形里,相位差一点点,指向性就偏了。
2.2 矩阵运算基础
麦克风阵列有多个通道,每个通道的数据放在一起,就是一个向量。多个时间点的数据放在一起,就是一个矩阵。矩阵运算,说白了就是批量处理这些数据。
最常用的几个操作:
- 向量内积:
xᴴy,其中ᴴ表示共轭转置。这个在计算波束成形输出时,就是加权求和。 - 矩阵乘法:
AB,注意维度要匹配。我习惯用(M×N) × (N×P) = (M×P)来检查。 - 矩阵求逆:
A⁻¹,在自适应波束成形(比如MVDR)里,这一步是核心。
举个例子,假设有4个麦克风,每个麦克风采集了100个采样点。那么数据矩阵就是 4×100 的。波束成形的加权向量是 4×1 的。输出就是 1×100 的向量:
// 伪代码示例
// x: 4x100 矩阵,每行是一个麦克风的数据
// w: 4x1 向量,每个元素是复权重
// y: 1x100 向量,波束成形输出
for (int t = 0; t < 100; t++) {
y[t] = 0;
for (int m = 0; m < 4; m++) {
y[t] += conj(w[m]) * x[m][t]; // 共轭转置
}
}
小技巧:在Python里,我推荐用NumPy的 np.conj() 和 @ 运算符。千万别自己写循环,NumPy的底层是C语言实现的,快得多。
2.3 傅里叶变换与短时傅里叶变换
傅里叶变换,就是把时域信号变到频域。为什么需要这个?因为波束成形很多时候是在频域做的。你想想看,不同频率的信号,波长不同,麦克风阵列的响应也不同。在频域处理,可以针对每个频率单独设计波束。
离散傅里叶变换(DFT)的公式:
X[k] = Σ x[n] * exp(-j * 2π * k * n / N)
但实际应用中,我们很少直接用DFT。为什么?因为信号是非平稳的。比如语音信号,它在几十毫秒内是平稳的,但长时间看变化很大。所以,我们得用短时傅里叶变换(STFT)。
STFT的思路很简单:把信号切成一小段一小段,每段加窗,然后做FFT。我一般用汉宁窗,旁瓣衰减好一些。
个人经验:我在做助听器算法时,帧长通常选 32ms(采样率16kHz时就是512点),帧移选 16ms(256点)。这样既能保证频率分辨率,又能实时处理。帧长太短,低频就看不清楚;帧长太长,延迟就大了,助听器用户会感觉声音「闷」。
STFT的流程:
- 对信号分帧,每帧加窗。
- 对每帧做FFT,得到复数频谱。
- 在频域做波束成形处理。
- 用IFFT变回时域,重叠相加合成信号。
2.4 相关矩阵与协方差矩阵
这两个概念,是自适应波束成形的灵魂。很多同学搞不清它们的区别,我简单说一下。
相关矩阵:描述两个信号之间的相似程度。对于复数信号,相关矩阵 R 定义为:
R = E[ x * xᴴ ]
其中 x 是麦克风阵列的观测向量,E[·] 是期望(实际中用时间平均代替)。
协方差矩阵:其实和相关矩阵很像,只是减去了均值。对于零均值的信号(比如语音信号,通常去直流后就是零均值),协方差矩阵就等于相关矩阵。
在实际代码中,我们这样计算:
// Python示例
import numpy as np
# x: MxN 矩阵,M个麦克风,N个采样点
# 计算协方差矩阵
R = (x @ x.conj().T) / N # 注意:这里假设信号是零均值的
注意:协方差矩阵必须是厄米特矩阵(Hermitian),即 R = Rᴴ。如果你算出来不对称,那肯定是代码写错了。我曾经在调试时发现协方差矩阵不对称,查了半天,原来是忘记对转置取共轭了。
协方差矩阵的对角线元素,代表每个麦克风接收到的信号功率。非对角线元素,代表不同麦克风之间的相关性。在波束成形里,我们正是利用这种相关性来抑制噪声的。
举个例子,如果两个麦克风接收到的噪声是不相关的(比如热噪声),那么协方差矩阵的非对角线元素就接近0。而目标信号(比如说话人的声音)在两个麦克风之间是相关的,非对角线元素就不为0。自适应波束成形算法,就是利用这个差异来分离信号和噪声的。
避坑指南:计算协方差矩阵时,样本数N不能太少。我一般要求 N > 3M,否则矩阵是奇异的,没法求逆。如果样本不够,可以用对角加载(Diagonal Loading)来稳定矩阵。
好了,这一章的内容就到这里。数学工具准备好了,下一章咱们就开始动手写波束成形的代码了。