3. 麦克风与阵列基础
做麦克风阵列,说白了就是在跟「波」打交道。声波在时域里看起来就是一条上下抖动的曲线,但真正有价值的信息往往藏在频率里。我刚开始接触阵列信号处理时,总觉得时域波形挺直观的,直到有一次做声源定位,发现两个不同方向的说话人在时域上完全混在一起,根本分不开。后来转到频域一看,嘿,清清楚楚。
这一章我们就来啃下时频分析这块硬骨头。别怕,我会把傅里叶变换、STFT、MFCC这些概念掰开揉碎了讲,再配上Python代码,让你能直接上手跑起来。
3.1 傅里叶变换:从时域到频域的桥梁
傅里叶变换的核心思想其实很简单:任何连续的时间信号,都可以分解成一系列不同频率的正弦波之和。你想想看,一个复杂的声音,比如钢琴和小提琴同时演奏,在时域里波形乱成一团,但到了频域,每个乐器的基频和泛音就各自站好了位置。
连续傅里叶变换的公式长这样:
X(f) = ∫ x(t) · e^(-j2πft) dt
但在实际工程中,我们处理的是离散采样信号。所以用的是离散傅里叶变换(DFT):
X[k] = Σ x[n] · e^(-j2πkn/N), n=0 to N-1
嗯,这里要注意,DFT的计算量是O(N²),如果采样点一多,计算时间会让人崩溃。好在有快速傅里叶变换(FFT),把复杂度降到了O(N log N)。
numpy.fft或scipy.fft。别自己手写FFT,除非你在做学术研究。实际项目中,np.fft.rfft更常用——它只计算正频率部分,速度更快,内存更省。
3.2 频谱分析实战:Python实现
光说不练假把式。我们直接写一段代码,生成一个混合信号,然后看看它的频谱长什么样。
import numpy as np
import matplotlib.pyplot as plt
# 参数设置
fs = 16000 # 采样率 16kHz
t = np.arange(0, 1, 1/fs) # 1秒时长
# 生成信号:1000Hz正弦波 + 3000Hz正弦波 + 噪声
f1, f2 = 1000, 3000
x = 0.5 * np.sin(2 * np.pi * f1 * t) + 0.3 * np.sin(2 * np.pi * f2 * t)
x += 0.1 * np.random.randn(len(t))
# 做FFT
N = len(x)
X = np.fft.rfft(x) # 只取正频率
freqs = np.fft.rfftfreq(N, 1/fs)
mag = np.abs(X) / N * 2 # 幅度归一化
# 绘制频谱
plt.figure(figsize=(10, 4))
plt.plot(freqs, mag)
plt.xlim(0, 5000)
plt.xlabel('频率 (Hz)')
plt.ylabel('幅度')
plt.title('信号频谱')
plt.grid(True)
plt.show()
跑完这段代码,你会看到在1000Hz和3000Hz处有两个明显的尖峰。这就是频域的魅力——把混在一起的信号拆得明明白白。
np.fft.fft(x, n=2**int(np.ceil(np.log2(len(x)))))来强制对齐长度。
3.3 短时傅里叶变换(STFT):时频分析的利器
傅里叶变换有个硬伤:它假设信号在整个时间轴上是平稳的。但语音信号是时变的——同一个人的发音,不同时刻的频率成分完全不一样。你想想看,一个「啊」字从开始到结束,频率一直在变。
STFT的思路很直接:把信号切成一小段一小段,假设每段内信号是平稳的,然后对每一段做FFT。这样我们就得到了一个「时间-频率」二维矩阵,也就是语谱图。
STFT的数学表达:
X(m, k) = Σ x[n] · w[n - mH] · e^(-j2πkn/N)
其中w[n]是窗函数,H是帧移,m是帧索引。
Python里用scipy.signal.stft就能搞定:
from scipy.signal import stft
f, t, Zxx = stft(x, fs=16000, window='hann', nperseg=1024, noverlap=512)
# 绘制语谱图
plt.pcolormesh(t, f, np.abs(Zxx), shading='gouraud')
plt.ylabel('频率 [Hz]')
plt.xlabel('时间 [sec]')
plt.title('STFT 语谱图')
plt.colorbar()
plt.show()
- 窗长(nperseg):决定了频率分辨率。窗越长,频率分辨率越高,但时间分辨率越低。我一般用1024点(64ms @16kHz)。
- 帧移(noverlap):通常设为窗长的50%~75%。重叠越多,时间轴越平滑,但计算量也越大。
- 窗函数:汉宁窗(hann)是语音处理的标准选择,旁瓣抑制好。
3.4 梅尔频率倒谱系数(MFCC):人耳感知的数学建模
MFCC是语音识别和声学处理中最常用的特征之一。为什么?因为人耳对频率的感知不是线性的——低频区域我们分辨得很细,高频区域则比较粗糙。梅尔刻度就是模拟这种感知特性。
梅尔频率与线性频率的转换关系:
mel = 2595 * log10(1 + f/700)
MFCC的提取流程,我用一张SVG图来展示:
Python实现MFCC提取,我推荐用librosa库,一行代码搞定:
import librosa
# 加载音频
y, sr = librosa.load('speech.wav', sr=16000)
# 提取MFCC,默认13维
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13, n_fft=1024, hop_length=512)
print(mfcc.shape) # (13, 帧数)
3.5 本章小结
这一章我们走完了从时域到频域,再到时频分析,最后到MFCC的完整路径。核心就三件事:
- FFT:把信号从时域搬到频域,看清频率成分
- STFT:在时间轴上滑动窗口做FFT,得到时频图
- MFCC:模拟人耳感知,提取紧凑的声学特征