
Adaptive PID(比例-積分-微分)自適應 PID,在傳統 PID 控制器在工業與嵌入式領域應用廣泛,但固定參數難以應對動態變化的系統,調參往往耗時又不穩。Adaptive PID 則透過「根據誤差自動調整增益」來彌補這個問題,提供更聰明、更穩定的控制解法。
而更棒的是,我們可以用 ESP32 這顆價格親民、功能強大的微控制器來實現它!這時候,自適應 Adaptive PID 就派上用場了。■什麼是自適應 Adaptive PID?
傳統 PID 控制器的三個參數:Kp、Ki、Kd 通常是固定的。而「自適應 PID」的核心概念是 「讓 PID 參數隨著系統狀態自動調整」:
1.輕負載時降低增益避免震盪
2.誤差大時增加 Kp 加快響應
3.穩定區自動減少 Ki 以避免超調
這種智慧式調整方式,特別適合控制對象動態變化劇烈的場景。
■Adaptive PID 實作邏輯
1.建立 PID 任務(使用 FreeRTOS Task)
2.讀取實際輸入值(如:溫度、速度)
3.輸出控制訊號(PWM 控制馬達、風扇或加熱器)
4.控制回圈週期(可用 Timer 或 vTaskDelayUntil() 精準控制)
●開發環境
在開始編程之前,請確保已完成以下準備工作:
1.安裝 ESP-IDF 開發環境 (至少版本 v5.x 或更高)。
2.ESP32 開發板。
●專案結構
1.建立一個乾淨的 ESP-IDF PID Control 專案如下:
adaptive_pid_voltage/
├── CMakeLists.txt # Top-level build script
├── sdkconfig # Build-generated config file
├── components/ # Reusable modules
│ └── pid_controller/
│ ├── CMakeLists.txt # PID component build script
│ ├── pid_controller.c # PID algorithm
│ └── pid_controller.h # PID API
├── main/
│ ├── CMakeLists.txt # Main app build script
│ ├── main.c # app_main and task setup
│ └── simulation.c # Controlled system simulation
└── README.md # Project overview
2.為了讓讀者更容易上手,本範例將 PID 演算法、ADC 讀取與 PWM 控制整合於同一個檔案(main.c)中。這樣的寫法更加直觀,方便快速理解與示範,適合初學者快速掌握核心概念。
●Adaptive PID 控制器程式碼
#include <stdio.h>
#include <math.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
static const char *TAG = "VoltagePID";
// PID parameters
float Kp = 1.0f, Ki = 0.5f, Kd = 0.1f;
float setpoint = 100.0f; // Target motor speed (RPM)
float integral = 0.0f;
float last_error = 0.0f;
// Simulated control output voltage (0 ~ 3.3V)
float control_voltage = 0.0f;
// Simulated feedback value (motor speed)
float motor_speed = 0.0f;
void update_pid(float input) {
float error = setpoint - input;
float delta_error = error - last_error;
// Adaptive gain tuning based on error magnitude
if (fabsf(error) > 50) {
Kp = 2.0f; Ki = 0.7f; Kd = 0.2f;
} else if (fabsf(error) > 20) {
Kp = 1.5f; Ki = 0.6f; Kd = 0.15f;
} else {
Kp = 1.0f; Ki = 0.5f; Kd = 0.1f;
}
integral += error;
float derivative = delta_error;
float raw_output = Kp * error + Ki * integral + Kd * derivative;
// Simulate control voltage output: clamp between 0V and 3.3V
control_voltage = raw_output / 100.0f; // Scale the output (depends on application)
if (control_voltage > 3.3f) control_voltage = 3.3f;
if (control_voltage < 0.0f) control_voltage = 0.0f;
last_error = error;
// Log output to monitor behavior
ESP_LOGI(TAG, "[PID] Speed: %.2f RPM | Error: %.2f | Kp: %.2f | Ki: %.2f | Kd: %.2f | Output Voltage: %.2f V",
input, error, Kp, Ki, Kd, control_voltage);
}
// Simulate motor response with inertia based on control voltage
void pid_task(void *arg) {
while (1) {
update_pid(motor_speed);
// Simulate motor acceleration (e.g., proportional to voltage)
float target_speed = control_voltage * 40.0f; // e.g., 3.3V -> ~132 RPM
motor_speed += (target_speed - motor_speed) * 0.05f; // Simulate inertia/delay
// Log the simulated speed for visualization
ESP_LOGI(TAG, "[SIM] Target Speed: %.2f RPM | Current Speed: %.2f RPM",
target_speed, motor_speed);
vTaskDelay(pdMS_TO_TICKS(100)); // Control cycle: 100 ms
}
}
void app_main(void) {
ESP_LOGI(TAG, "Starting Voltage-Controlled PID Simulation...");
xTaskCreate(pid_task, "pid_task", 4096, NULL, 5, NULL);
}
●編譯和燒錄
完成程式碼後,您可以使用 ESP-IDF 提供的命令進行編譯、燒錄和監控。
在 VS Code 的左下角 ESP-IDF 工具列:
1.點選 Build project
2.點選 Flash device
3.點選 Monitor device
●輸出說明
以下是模擬控制過程中的部份 log 輸出:
I (0) VoltagePID: Starting Voltage-Controlled PID Simulation…
I (100) VoltagePID: [PID] Speed: 0.00 RPM | Error: 100.00 | Kp: 2.00 | Ki: 0.70 | Kd: 0.20 | Output Voltage: 3.30 V
I (100) VoltagePID: [SIM] Target Speed: 132.00 RPM | Current Speed: 6.60 RPM
I (200) VoltagePID: [PID] Speed: 6.60 RPM | Error: 93.40 | Kp: 2.00 | Ki: 0.70 | Kd: 0.20 | Output Voltage: 3.30 V
…
I (1000) VoltagePID: [PID] Speed: 85.60 RPM | Error: 14.40 | Kp: 1.00 | Ki: 0.50 | Kd: 0.10 | Output Voltage: 1.82 V
I (1000) VoltagePID: [SIM] Target Speed: 72.80 RPM | Current Speed: 74.00 RPM
…
I (2000) VoltagePID: [PID] Speed: 98.20 RPM | Error: 1.80 | Kp: 1.00 | Ki: 0.50 | Kd: 0.10 | Output Voltage: 1.04 V
I (2000) VoltagePID: [SIM] Target Speed: 41.60 RPM | Current Speed: 97.00 RPM
●分析與說明:
1.大誤差時快速響應(起始階段)
一開始馬達尚未轉動,速度為 0 RPM,誤差達 100。此時 Adaptive PID 自動將增益調高(Kp=2.0),使輸出電壓直接飽和到 3.3V,系統加速快速,縮短反應時間。
2.中間誤差階段,控制穩定
當速度進入中間區段,例如誤差約 20~50 時,增益切換為中等(Kp=1.5),電壓輸出也開始降低,系統控制進入「快速收斂、但避免過衝」的平衡狀態。
3.小誤差時精細微調
當誤差小於 20(如 10 以下),控制器自動調整回較小增益(Kp=1.0),避免震盪與過調,讓馬達平穩收斂至設定值附近,控制電壓也相對更平滑。
4.自動增益調整避免手動調參
整個過程中,無需開發者手動調整 PID 參數。Adaptive PID 根據誤差範圍動態調整參數,讓系統既有響應速度,又能避免震盪或不穩。
【視頻】AI Revolution: Design PID Controllers Using DeepSeek-R1 AI Model
and Python Locally
