跳转至

说明

FIR — 有限脉冲响应滤波器

无反馈、始终稳定,可实现线性相位。通过窗函数法设计,卷积方式滤波。


算法原理

设计流程(窗函数法)

\[ \text{理想频率响应} \xrightarrow{\text{IFFT}} \text{理想脉冲响应(无限)} \xrightarrow{\text{加窗截断}} \text{FIR 系数} \;(h[0], \dots, h[M-1]) \]

Step 1 — 确定指标:截止频率 \(f_c\)、过渡带宽 \(\Delta f\)、阻带衰减

Step 2 — 生成理想脉冲响应(以低通为例):

\[ hd[n] = \frac{\sin(\omegac (n - M/2))}{\pi (n - M/2)}, \quad \omegac = 2\pi fc / f_s \]

高通/带通/带阻通过对低通原型做频谱搬移获得。

Step 3 — 加窗截断:\(h[n] = h_d[n] \cdot w[n]\)

  • 矩形窗


    主瓣最窄,旁瓣 -13 dB
    过渡带最窄

  • Hanning


    主瓣中等,旁瓣 -31 dB
    通用场景

  • Hamming


    主瓣中等,旁瓣 -41 dB
    语音处理

  • Blackman


    主瓣最宽,旁瓣 -57 dB
    高衰减需求

滤波算法

批量处理(直接型):

\[ y[n] = \sum_{k=0}^{M-1} h[k] \cdot x[n - k], \quad n = 0, \dots, L-1 \]

本质是卷积运算,在 ESP32 上由 ESP-DSP 向量化加速。

实时流式处理

初始化时分配延迟线,每输入一个采样调用 tiny_fir_process_sample 即可得到滤波结果。适合逐采样点处理的嵌入式实时场景。

为什么 FIR 始终稳定?

只有零点,没有极点。传递函数 \(H(z) = \sum h[k] z^{-k}\) 所有极点都在原点。

线性相位条件

系数对称(\(h[k] = h[M-1-k]\))时 FIR 具有线性相位,适合需要保持波形相位的应用。


API 参考

滤波器设计

// 设计低通 FIR,返回设计的系数个数
int tiny_fir_design_lowpass(float *coeffs, int max_taps,
                            float cutoff_norm, tiny_fir_window_t window);
// 高通 / 带通 / 带阻类似

归一化频率:\(f{\text{norm}} = f / (f_s / 2)\),范围 [0, 1]}

批量滤波

// 一次性滤波整个信号(本质是卷积)
tiny_error_t tiny_fir_filter_f32(const float *input, int len,
                                 const float *coeffs, int num_taps,
                                 float *output);

实时滤波

typedef struct {
    float *coefficients;   // 滤波器系数
    int num_taps;          // 抽头数
    float *delay_line;     // 延迟线(内部状态)
    // ...
} tiny_fir_state_t;

tiny_error_t tiny_fir_init(tiny_fir_state_t *state,
                           const float *coeffs, int num_taps);
tiny_error_t tiny_fir_deinit(tiny_fir_state_t *state);
float        tiny_fir_process_sample(tiny_fir_state_t *state, float input);
void         tiny_fir_reset(tiny_fir_state_t *state);

注意事项

抽头数越多,计算量越大

每个输出采样需 \(M\) 次乘加。ESP32-S3 @ 240 MHz 上 \(M=128\) 时约 0.5 ms。

过渡带 ≈ 窗函数主瓣宽度

窗函数决定过渡带宽和阻带衰减的 trade-off。要求陡峭过渡带 → 矩形窗(但旁瓣高);要求高衰减 → Blackman。

归一化频率

所有设计函数使用归一化频率 \([0, 1]\),其中 1 = Nyquist (\(f_s/2\))。避免混淆采样率和截止频率。