第三章 数字信号处理基础:采样定理、量化、FFT与频谱分析、数字滤波器基础
各位同学,欢迎来到第三章。
这一章,是咱们整个ANC降噪算法的数学根基。说白了,你后面写的所有代码,都是在跟采样、量化、频谱、滤波器打交道。如果这章没吃透,后面调试ANC的时候,你会感觉像在迷雾里开车——完全不知道问题出在哪。
我个人习惯,在讲任何算法之前,先把这个“数字世界”和“模拟世界”的边界搞清楚。咱们开始。
3.1 采样定理:别让你的信号“说谎”
先问一个问题:麦克风采集到的模拟信号,怎么变成芯片能处理的0和1?
第一步就是采样。每隔一段时间,抓一个点的电压值。
那问题来了:抓多快才够?
奈奎斯特采样定理:采样频率 fs 必须大于信号最高频率 fmax 的两倍。
公式:fs > 2 * fmax
我在项目中遇到过一件事。有一次调试前馈ANC,低频降噪效果很好,但到了1kHz以上,不仅没降噪,反而把噪声放大了。查了半天,发现是采样率设成了48kHz,但ADC前端的抗混叠滤波器没做好,导致高频噪声折叠进了低频段。嗯,这就是典型的“混叠”现象。
避坑指南:我曾经因为省成本,用了便宜的RC低通滤波器做抗混叠,结果高频噪声折叠后,ANC算法把折叠后的假信号当成了真实噪声去抵消,反而引入了新的噪声。记住:抗混叠滤波器不是摆设,它是采样定理的第一道防线。
在TWS耳机里,ANC通常处理20Hz到2kHz的噪声。所以采样率一般用48kHz或96kHz。48kHz已经能覆盖到24kHz,绰绰有余。但为什么有人用96kHz?为了给数字滤波器留出更多的过渡带空间,这个后面会讲。
3.2 量化:精度和成本的博弈
采样是把时间离散化,量化是把幅度离散化。
你想想看,一个模拟电压,比如1.2345V,你用8位ADC去量化,只能表示256个等级。那1.2345V就只能近似成1.23V或者1.24V。这个误差,就是量化噪声。
| 量化位数 | 量化等级数 | 动态范围(理论) | 典型应用 |
|---|---|---|---|
| 16-bit | 65536 | 96 dB | CD音质、ANC参考麦克风 |
| 24-bit | 16777216 | 144 dB | 高端音频ADC、专业录音 |
| 32-bit | 约43亿 | 192 dB | 浮点DSP内部处理 |
在TWS耳机里,ADC和DAC通常是16-bit或24-bit。我个人建议,如果芯片算力允许,尽量用24-bit。为什么?因为ANC算法对相位非常敏感,量化噪声虽然听起来不大,但经过反馈环路之后,可能会被放大。
小技巧:我曾经在调试一款低功耗芯片时,发现ANC底噪偏大。后来把ADC从16-bit换成24-bit,底噪立刻降了6dB。代价是功耗多了0.5mA,但效果值得。所以,量化位数不是越高越好,但也不能太低,16-bit是底线。
3.3 FFT与频谱分析:把时间信号“翻译”成频率
时域信号你看不出噪声的分布。比如一个嗡嗡声,你只知道它在响,但不知道是100Hz还是200Hz。FFT就是干这个的——把信号从时域变到频域。
FFT的核心公式其实不复杂,但咱们做工程的不需要死磕数学推导。你只需要记住几个关键参数:
- FFT点数(N): 决定了频率分辨率。分辨率 = fs / N。比如48kHz采样,1024点FFT,分辨率就是46.875Hz。
- 频率范围: 0 到 fs/2。超过fs/2的部分是镜像,没用。
- 窗函数: 直接做FFT会有频谱泄露。加窗可以抑制旁瓣。
我给你们看一段我在ANC调试中常用的频谱分析代码。这段代码我用了很多年,简单实用:
// 简单的FFT频谱分析,用于ANC调试
// 输入:时域数据指针,FFT点数
// 输出:频谱幅度数组
void calc_spectrum(float *time_data, float *mag_spectrum, int fft_size) {
// 1. 加汉宁窗,减少频谱泄露
for (int i = 0; i < fft_size; i++) {
float window = 0.5f * (1.0f - cosf(2 * M_PI * i / (fft_size - 1)));
time_data[i] *= window;
}
// 2. 执行FFT(这里调用芯片厂商提供的FFT库)
arm_rfft_fast_instance_f32 fft_inst;
arm_rfft_fast_init_f32(&fft_inst, fft_size);
arm_rfft_fast_f32(&fft_inst, time_data, fft_output, 0); // 0表示正变换
// 3. 计算幅度谱
for (int i = 0; i < fft_size / 2; i++) {
float real = fft_output[2 * i];
float imag = fft_output[2 * i + 1];
mag_spectrum[i] = sqrtf(real * real + imag * imag);
}
}
重点:在ANC调试中,我习惯用频谱分析仪看三个信号:参考噪声、误差麦克风信号、以及扬声器输出。通过对比这三个频谱,你能直观地看到ANC在哪些频率生效,哪些频率反而被放大。这就是所谓的“频域调试法”。
3.4 数字滤波器基础:FIR与IIR
滤波器是ANC的核心。没有滤波器,你没法对噪声做精确的相位和幅度调整。
3.4.1 FIR滤波器:稳定,但计算量大
FIR的全称是有限脉冲响应。它的特点是:
- 绝对稳定: 没有反馈,不会发散。
- 线性相位: 所有频率的延迟时间一样,不会造成相位失真。
- 计算量大: 阶数越高,需要的乘加运算越多。
在ANC中,FIR常用于参考路径的建模。比如,你需要知道噪声从参考麦克风到扬声器的声学路径,这个路径通常用FIR滤波器来模拟。
// FIR滤波器实现(直接型)
// 输入:新样本x[n],滤波器系数b[0..N-1],状态缓冲区
// 输出:滤波结果y[n]
float fir_filter(float x, float *b, float *state, int N) {
float y = 0.0f;
// 更新状态缓冲区
for (int i = N - 1; i > 0; i--) {
state[i] = state[i - 1];
}
state[0] = x;
// 计算卷积
for (int i = 0; i < N; i++) {
y += b[i] * state[i];
}
return y;
}
个人经验:我曾经在一个项目里,用128阶的FIR做路径建模。结果芯片算力不够,跑完一个样本需要200个时钟周期,而采样间隔只有20.8微秒(48kHz)。最后不得不把阶数降到64阶,牺牲了一点精度,换来了实时性。所以,选FIR阶数时,一定要先算算你的MIPS预算。
3.4.2 IIR滤波器:高效,但小心不稳定
IIR的全称是无限脉冲响应。它引入了反馈,所以:
- 效率高: 同样的滤波效果,IIR的阶数只有FIR的十分之一甚至更少。
- 可能不稳定: 反馈系数如果设计不当,滤波器会自激振荡。
- 非线性相位: 不同频率的延迟不一样,这在ANC中需要特别小心。
在ANC中,IIR常用于均衡器和陷波器。比如,你想在某个特定频率(如200Hz的引擎轰鸣)做深度衰减,用IIR陷波器最合适。
// 二阶IIR滤波器(双二阶滤波器)
// 系数:b0, b1, b2, a1, a2
// 状态:w0, w1, w2
float biquad_filter(float x, float *coeff, float *state) {
float b0 = coeff[0], b1 = coeff[1], b2 = coeff[2];
float a1 = coeff[3], a2 = coeff[4];
float w = x - a1 * state[0] - a2 * state[1];
float y = b0 * w + b1 * state[0] + b2 * state[1];
// 更新状态
state[1] = state[0];
state[0] = w;
return y;
}
避坑指南:我曾经在调试反馈ANC时,用了IIR滤波器做相位补偿。结果因为系数计算时舍入误差,导致滤波器在1.2kHz处产生了自激振荡。耳机里直接发出了刺耳的啸叫声,把测试工程师吓了一跳。从那以后,我每次用IIR,都会在定点仿真时检查极点是否在单位圆内。记住:IIR的稳定性是头等大事。
3.5 本章小结
好了,这一章的内容就这些。我帮你理一下重点:
- 采样定理: 采样率至少是信号最高频率的两倍。别忘了抗混叠滤波器。
- 量化: 16-bit是底线,24-bit更安全。量化噪声会影响ANC的底噪。
- FFT: 频谱分析是ANC调试的“眼睛”。学会看频谱,你就能找到噪声的精确位置。
- FIR vs IIR: FIR稳定但计算量大,IIR高效但要注意稳定性。ANC中两者都会用到,看场景选择。
下一章,我们会进入ANC的核心——自适应滤波算法。你会看到,LMS、NLMS这些算法是怎么一步步把噪声“吃掉”的。到时候,你会感谢今天打下的数字信号处理基础。
咱们下章见。