PID 控制器
🔥 Vibe Prompt
"模擬 PID 溫度控制。目標溫度 100°C。調整 Kp、Ki、Kd 來最小化超調量。"
import numpy as np
class PID:
def __init__(self, Kp, Ki, Kd, setpoint):
self.Kp, self.Ki, self.Kd = Kp, Ki, Kd
self.setpoint = setpoint
self.prev_error = 0
self.integral = 0
def update(self, measurement, dt):
error = self.setpoint - measurement
self.integral += error * dt
derivative = (error - self.prev_error) / dt
self.prev_error = error
return self.Kp*error + self.Ki*self.integral + self.Kd*derivative
pid = PID(Kp=2.0, Ki=0.5, Kd=1.0, setpoint=100)
temp = 25.0
dt = 0.01
time = 0
while time < 10:
output = pid.update(temp, dt)
temp += output * dt * 10
temp -= (temp - 25) * 0.01 * dt
if abs(pid.setpoint - temp) < 0.5 and time > 2:
print(f"在 t={time:.2f}s 時達到穩定溫度 {temp:.1f}°C")
break
time += dt
PID 控制原理
三個控制項的直覺理解
PID 控制器由三個部分組成,每個部分負責不同的控制任務:
| 項 | 名稱 | 直覺理解 | 影響 | |:--:|:----:|---------|------| | P | 比例項 (Proportional) | 「現在有多遠?」— 誤差越大,修正力道越大 | 加快響應,但可能超調 | | I | 積分項 (Integral) | 「過去累積了多少?」— 消除長期誤差 | 消除穩態誤差,但可能不穩定 | | D | 微分項 (Derivative) | 「未來會如何?」— 預測誤差趨勢 | 減少超調,提升穩定性 |
$$u(t) = K_p \cdot e(t) + K_i \cdot \int_0^t e(\tau) d\tau + K_d \cdot \frac{de(t)}{dt}$$
其中 $e(t) = setpoint - measurement$ 是誤差。
PID 參數調整指南
各參數的影響
| 效果 | Kp ↑ | Ki ↑ | Kd ↑ | |:----:|:----:|:----:|:----:| | 上升時間 | ✅ 縮短 | — | 微小變化 | | 超調量 | ❌ 增加 | ❌ 增加 | ✅ 減少 | | 穩定時間 | — | ❌ 增加 | ✅ 減少 | | 穩態誤差 | ✅ 減少 | ✅ 消除 | 無影響 | | 穩定性 | ❌ 下降 | ❌ 下降 | ✅ 提升 |
Ziegler-Nichols 調整法
這是最經典的 PID 參數調整方法:
- 設定 Ki = 0, Kd = 0
- 增加 Kp 直到系統產生穩定振盪(記錄此時的 K_u 為最終增益)
- 測量振盪週期 P_u
- 根據下表設定參數:
| 控制器類型 | Kp | Ki | Kd | |:----------:|:--:|:--:|:--:| | 僅 P | 0.5 × K_u | — | — | | PI | 0.45 × K_u | 0.54 × K_u / P_u | — | | PID | 0.60 × K_u | 1.20 × K_u / P_u | 0.075 × K_u × P_u |
實戰:溫度控制模擬
import numpy as np
import matplotlib.pyplot as plt
class PID:
def __init__(self, Kp, Ki, Kd, setpoint):
self.Kp, self.Ki, self.Kd = Kp, Ki, Kd
self.setpoint = setpoint
self.prev_error = 0
self.integral = 0
def update(self, measurement, dt):
error = self.setpoint - measurement
self.integral += error * dt
derivative = (error - self.prev_error) / dt
self.prev_error = error
return self.Kp*error + self.Ki*self.integral + self.Kd*derivative
# 測試不同參數組合
configs = [
("預設", PID(Kp=2.0, Ki=0.5, Kd=1.0, 目標=100)),
("激進", PID(Kp=5.0, Ki=2.0, Kd=0.5, 目標=100)),
("保守", PID(Kp=0.5, Ki=0.1, Kd=2.0, 目標=100)),
]
plt.figure(figsize=(12, 6))
for name, pid in configs:
temp = 25.0
temps = [temp]
times = [0]
dt = 0.01
t = 0
while t < 10:
output = pid.update(temp, dt)
temp += output * dt * 10
temp -= (temp - 25) * 0.01 * dt
t += dt
temps.append(temp)
times.append(t)
overshoot = (max(temps) - 100) / 100 * 100
steady_error = abs(temps[-1] - 100)
print(f"{name}: 超調={overshoot:.1f}%, 穩態誤差={steady_error:.2f}°C")
plt.plot(times, temps, label=f"{name}", linewidth=2)
plt.axhline(y=100, color='r', linestyle='--', label='目標溫度')
plt.xlabel('時間 (秒)')
plt.ylabel('溫度 (°C)')
plt.title('PID 溫度控制模擬')
plt.legend()
plt.grid(True)
plt.show()
PID 的應用場景
| 領域 | 應用 | |:----:|------| | 🚁 無人機 | 飛控穩定、懸停、航點追蹤 | | 🤖 機器人 | 馬達位置/速度控制、機械臂精確定位 | | 🏭 工業 | 溫度/壓力/流量/液位控制 | | 🚗 汽車 | 定速巡航、電子穩定程式 | | 🔌 電源 | 開關電源穩壓、逆變器控制 | | 🌡️ 家電 | 空調溫度控制、烤箱恆溫 |
PID 的進階變體
| 變體 | 說明 | 適用場景 | |:----:|------|---------| | PI 控制器 | 去掉微分項 | 雜訊大的系統(Kd 會放大雜訊) | | PD 控制器 | 去掉積分項 | 不需要消除穩態誤差的系統 | | 串級 PID | 兩個 PID 串聯(內環+外環) | 無人機飛控(角度環+角速度環) | | 模糊 PID | 用模糊邏輯動態調整參數 | 高度非線性系統 | | 自適應 PID | 根據系統狀態自動調整 | 系統參數隨時間變化的場合 |
關鍵要點
- ✅ P(比例)根據當前誤差進行修正,是控制的基礎
- ✅ I(積分)消除長期累積的穩態誤差
- ✅ D(微分)預測誤差趨勢,減少超調與振盪
- ✅ Ziegler-Nichols 法是經典的 PID 參數調整方法
- ✅ 實際應用中需考慮抗飽和 (Anti-windup)、濾波器等機制
- ✅ PID 不需要系統模型,適用於絕大多數的控制場景
為什麼要學PID 控制器?
PID 控制器 是 algorithm-gradient-descent 課程的核心章節之一。
在真實世界中
PID 控制器 並不是課本上的理論——它在真實的軟體開發中頻繁出現。無論你是正在接案、準備面試,還是想要提升自己的技術深度,理解這個主題都能讓你直接受益。
你將從本章獲得
- 🎯 完整的知識體系:從核心原理到實作細節,條理分明
- 💻 可運行的程式碼:每段程式碼都是完整的,可直接執行
- 🔍 除錯技巧:常見錯誤的分析與解決方案
- 🚀 下一步指引:學完後該往哪個方向繼續深入
銜接下一章
本章為你建立了 PID 控制器 的完整知識基礎。下一章將在此基礎上,帶你探索更進階的真實世界應用場景——你將學會如何將本章所學應用到更複雜的問題中。