ESP-DSP 系列 1 | 用 IIR Biquad 打造高效數位濾波器


ESP-DSP 是 Espressif 官方推出的數位訊號處理函式庫,專為 ESP32 平台優化,能協助開發者快速實現常見的 DSP 任務,如濾波器、FFT、向量運算等功能。在物聯網(IoT)與嵌入式應用中,感測器資料經常受到環境雜訊、電源干擾或 ADC 精度限制影響,導致訊號跳動、漂移,進而影響判斷與控制準確度。

為了解決這些問題,數位濾波器(Digital Filters) 是最常見也最實用的解法之一。本篇文章將聚焦在 IIR(Infinite Impulse Response)Biquad 濾波器 的原理與在 ESP32 上的實作方式,並透過 ESP-DSP 套件中的 API,快速打造一個高效、低成本的資料清理流程。

ESP-DSP

什麼是 ESP-DSP?

ESP-DSP 是 Espressif(樂鑫)官方推出的數位訊號處理(DSP, Digital Signal Processing)函式庫,專為 ESP32 系列晶片最佳化。它以 C 語言實作,針對 Tensilica Xtensa 處理器架構進行性能優化,支援浮點與向量化運算,能在資源有限的微控制器上執行高效能的數位訊號處理任務。

ESP-DSP 提供的功能包含:

  • 常見的訊號視窗(Hann、Hamming、Blackman 等)
  • IIR/FIR 濾波器(含 biquad)
  • FFT / DFT 頻譜分析
  • 向量與矩陣運算
  • 統計與數學運算

IIR Biquad 是什麼?

IIR(無限脈衝響應)是一種常見的數位濾波器類型,相對於 FIR(有限脈衝響應)而言,它的記憶體使用量低、計算效率高,非常適合像 ESP32 這類資源有限的微控制器使用。

Biquad 是一種二階(second-order)IIR 濾波器結構,可透過多個 biquad 相連來建構更高階的濾波器。它的基本公式如下:

y[n] = b0·x[n] + b1·x[n−1] + b2·x[n−2] − a1·y[n−1] − a2·y[n−2]

其中:

  • x[n] 是輸入資料
  • y[n] 是輸出資料
  • b0, b1, b2, a1, a2 是濾波器係數

開發環境

在開始編程之前,請確保已完成以下準備工作:

專案結構

這是 ESP-IDF 預設的專案結構之一。當你使用 idf.py create-project 指令,或透過 VS Code 的官方 ESP-IDF 擴充套件建立新專案時,通常會看到這樣的配置:

esp32-iir-filter/
├── CMakeLists.txt
├── main/
│   ├── CMakeLists.txt
│   └── iir_filter_example.c
└── sdkconfig

程式實作

以下程式碼示範如何使用低通濾波器(Low-pass filter)去除感測器資料中的高頻雜訊,保留穩定且緩慢變化的訊號成分:

#include <stdio.h>
#include <math.h>
#include "esp_log.h"          // ESP32 logging functions
#include "esp_dsp.h"          // ESP32 DSP library main header
#include "dsps_biquad.h"      // Biquad filter specific functions

// Constants and configuration
#define TAG "IIR_FILTER_GEN"  // Tag for ESP logging
#define DATA_LEN 64           // Number of samples to process

// Global buffers
float input[DATA_LEN];        // Input signal buffer
float output[DATA_LEN];       // Filtered output buffer
float coeffs[5];              // Filter coefficients: [b0, b1, b2, a1, a2]
float w[2];                   // Filter state variables (delay elements)


/* This implements the standard digital biquad filter design equations
 * for a low-pass filter. Coefficients are normalized and formatted
 *  specifically for ESP-DSP's biquad implementation.
 */
void compute_lowpass_biquad(float fs, float fc, float Q, float *coeffs)
{
    // Calculate angular frequency
    float w0 = 2.0f * M_PI * fc / fs;
    
    // Intermediate calculations
    float alpha = sinf(w0) / (2.0f * Q);
    float cos_w0 = cosf(w0);

    // Calculate numerator coefficients (feedforward path)
    float b0 = (1.0f - cos_w0) / 2.0f;
    float b1 = 1.0f - cos_w0;
    float b2 = b0;  // Symmetrical coefficients
    
    // Calculate denominator coefficients (feedback path)
    float a0 = 1.0f + alpha;
    float a1 = -2.0f * cos_w0;
    float a2 = 1.0f - alpha;

    // Normalize coefficients and format for ESP-DSP
    // Note: ESP-DSP expects a1 and a2 to be negated
    coeffs[0] = b0 / a0;  // b0
    coeffs[1] = b1 / a0;  // b1
    coeffs[2] = b2 / a0;  // b2
    coeffs[3] = -a1 / a0; // a1 (negated)
    coeffs[4] = -a2 / a0; // a2 (negated)
}

void app_main(void)
{
    ESP_LOGI(TAG, "Initializing ESP-DSP IIR Biquad Filter Example");

    // Filter parameters
    float fs = 1000.0f;  // Sampling rate (Hz)
    float fc = 30.0f;    // Cutoff frequency (Hz)
    float Q = 0.707f;    // Quality factor (Butterworth response)

    // 1. Compute filter coefficients
    compute_lowpass_biquad(fs, fc, Q, coeffs);

    // Log the computed coefficients
    ESP_LOGI(TAG, "Generated Coefficients:");
    ESP_LOGI(TAG, "b0 = %.4f", coeffs[0]);
    ESP_LOGI(TAG, "b1 = %.4f", coeffs[1]);
    ESP_LOGI(TAG, "b2 = %.4f", coeffs[2]);
    ESP_LOGI(TAG, "a1 = %.4f", coeffs[3]);
    ESP_LOGI(TAG, "a2 = %.4f", coeffs[4]);

    // 2. Initialize filter state variables
    w[0] = 0;  // w[n-1]
    w[1] = 0;  // w[n-2]

    // 3. Generate test signal (sine wave + random noise)
    for (int i = 0; i < DATA_LEN; i++) {
        // Fundamental signal component (50Hz sine wave)
        float signal = 0.7f * sinf(2 * M_PI * i / 16.0f);
        
        // Add uniform random noise (-0.1 to 0.1)
        float noise = 0.2f * ((float)rand() / RAND_MAX - 0.5f);
        
        input[i] = signal + noise;
    }

    // 4. Apply the biquad filter
    // Parameters: input, output, length, coefficients, state variables
    dsps_biquad_f32(input, output, DATA_LEN, coeffs, w);

    // 5. Print input vs filtered results
    ESP_LOGI(TAG, "Index\tInput\t\tFiltered");
    for (int i = 0; i < DATA_LEN; i++) {
        ESP_LOGI(TAG, "%2d\t% .4f\t% .4f", i, input[i], output[i]);
    }
}

程式說明 :

這段程式使用的 compute_lowpass_biquad() 函數是根據二階 IIR(Biquad)低通濾波器的公式,依據三個參數來計算濾波器係數:

  • fs:取樣率(資料一秒來幾筆)
  • fc:截止頻率(超過這個頻率的訊號會被濾掉)
  • Q:品質因子,控制通帶的銳利程度(這裡用 0.707f 是標準設定)

產生出一組 5 個係數(b0, b1, b2, -a1, -a2)後,再透過 ESP-DSP 函式 dsps_biquad_f32() 把這個濾波器套用到感測器資料上。

Sine wave + random noise (amplitude 0.2)

編譯和燒錄

完成程式碼後,您可以使用 ESP-IDF 提供的命令進行編譯、燒錄和監控。

在 VS Code 的左下角 ESP-IDF 工具列:

  • 點選 Build project
  • 點選 Flash device
  • 點選 Monitor device

輸出與測試結果

程式啟動後,可以從 ESP_Log 查看輸出結果 :

I (123) IIR_FILTER_GEN: Initializing ESP-DSP IIR Biquad filter example
I (124) IIR_FILTER_GEN: Generated coefficients:
I (124) IIR_FILTER_GEN: b0 = 0.0201
I (125) IIR_FILTER_GEN: b1 = 0.0402
I (126) IIR_FILTER_GEN: b2 = 0.0201
I (127) IIR_FILTER_GEN: -a1 = 1.5610
I (128) IIR_FILTER_GEN: -a2 = -0.6414
I (129) IIR_FILTER_GEN: Index	Input		Filtered
I (130) IIR_FILTER_GEN:  0	 0.0874	 0.0018
I (131) IIR_FILTER_GEN:  1	 0.2612	 0.0141
I (132) IIR_FILTER_GEN:  2	 0.4068	 0.0437
I (133) IIR_FILTER_GEN:  3	 0.5795	 0.0913
I (134) IIR_FILTER_GEN:  4	 0.7062	 0.1564
I (135) IIR_FILTER_GEN:  5	 0.8289	 0.2377
I (136) IIR_FILTER_GEN:  6	 0.9025	 0.3302
I (137) IIR_FILTER_GEN:  7	 0.9450	 0.4270
I (138) IIR_FILTER_GEN:  8	 0.9000	 0.5193
I (139) IIR_FILTER_GEN:  9	 0.8192	 0.5990

結論

在處理感測器資料時,雜訊與不穩定訊號往往會影響系統判斷與控制準確度。透過 ESP-DSP 套件提供的 IIR Biquad 濾波器,我們可以在不犧牲執行效率與資源的情況下,實現即時、穩定的訊號清理。這使得 ESP32 成為物聯網與邊緣運算中,同時兼顧效能與成本的理想平台。

ESP-DSP 為 ESP32 提供了高效能的數位信號處理 (DSP) 能力,特別適合用於:

  • 感測器資料濾波
  • 音頻處理
  • 信號分析
  • 即時控制系統

透過 IIR Biquad 濾波器,開發者可以有效地去除信號中的雜訊,同時保持系統的即時性和低資源消耗。這使得 ESP32 成為物聯網和邊緣計算應用的理想選擇。