實作解讀 — 追蹤 1 維的最小實作
把 Python / C 的最小實作與式子、變數名稱、角色一一對照,讓你能追蹤 1 個迴圈。
把目前為止的內容寫成程式碼,1 維卡爾曼濾波器就能寫得非常短。重要的不是「行數」,而是能追出每一行對應到哪個式子。
Python 最小實作
把狀態(x_hat、P)和參數(Q、R)整合在類別中。如果用 global 改寫模組變數,會導致無法同時運行多個濾波器、測試也較不方便,因此避免使用。
class KalmanFilter1D:
def __init__(self, x_hat=0.0, P=10.0, Q=0.05, R=2.0):
self.x_hat = x_hat
self.P = P
self.Q = Q
self.R = R
def step(self, z):
x_pred = self.x_hat
P_pred = self.P + self.Q
K = P_pred / (P_pred + self.R)
self.x_hat = x_pred + K * (z - x_pred)
self.P = (1.0 - K) * P_pred
return self.x_hat, self.P
寫成 (1.0 - K) 是為了避免被當成整數運算。Python 中即使寫 1 - K,只要 K 是 float,結果也會是 float,因此運作上相同;但這樣寫是為了與移植到 C 等其他語言時的行為一致,明確使用 1.0。
C 最小實作
C 版同樣將狀態與參數整合進結構體,函式接收結構體並對其進行寫入。不使用全域變數。
typedef struct {
double x_hat;
double P;
double Q;
double R;
} KalmanFilter1D;
double kf_step(KalmanFilter1D *kf, double z) {
double x_pred = kf->x_hat;
double P_pred = kf->P + kf->Q;
double K = P_pred / (P_pred + kf->R);
kf->x_hat = x_pred + K * (z - x_pred);
kf->P = (1.0 - K) * P_pred;
return kf->x_hat;
}
使用範例:以 KalmanFilter1D kf = { .x_hat = 0.0, .P = 10.0, .Q = 0.05, .R = 2.0 }; 初始化後,每次觀測都呼叫 kf_step(&kf, z)。
公式與變數的對應
x̂⁻(方法內的區域變數)P⁻(方法內的區域變數)x̂(濾波器的狀態)P(濾波器的狀態)理解檢核 1 — 用手追蹤 1 個迴圈
設 Q = 0.5、R = 2、前一時刻 x̂ = 4、P = 1、觀測 z = 7,依實作的順序追蹤 1 個迴圈。
Q1. 執行第 1 行 x_pred = self.x_hat 後,預測值 x̂⁻ 是多少?
因為是穩態模型,所以 x̂⁻ = x̂ = 4。
Q2. 執行第 2 行 P_pred = self.P + self.Q 後,預測變異數 P⁻ 是多少?
P⁻ = 1 + 0.5 = 1.5。
Q3. 執行第 3 行 K = P_pred / (P_pred + self.R) 後,卡爾曼增益是多少?
K = 1.5 / (1.5 + 2) ≈ 0.4286。
Q4. 執行第 4 行 self.x_hat = x_pred + K * (z - x_pred) 後,更新後的估計值 x̂ 是多少?
觀測與預測的差是 7 − 4 = 3,因此 x̂ ≈ 4 + 0.4286 × 3 ≈ 5.2857。
Q5. 執行第 5 行 self.P = (1.0 - K) * P_pred 後,更新後的變異數 P 是多少?
P ≈ (1 − 0.4286) × 1.5 ≈ 0.8571。
解讀時的重點
- 先把預測的 2 行與更新的 3 行分開
- 確認
K依賴於什麼 - 最後確認變異數
P是否正確更新
理解檢核 2 — 讀懂程式碼的意義
用文字說明實作的每一行對應到式子的哪個部分。
Q1. 下列哪一行對應「吸收觀測後讓不確定性變小」的處理?
self.P = (1.0 - K) * P_pred 就是吸收觀測之後讓不確定性變小的那一行。
Q2. 實作裡不小心寫成 K = self.R / (P_pred + self.R) 的話,會發生什麼問題?
原本 R 越大應該越懷疑觀測,把式子倒過來會變成大 R 時反而強烈採用觀測。
理解檢核 3 — 觀測與預測的差為 0 的情況
確認觀測與預測一致時這個特殊情況下的行為。
Q1. 預測值 x̂⁻ = 5、觀測 z = 5、K = 0.6 時,更新後的 x̂ 是多少?
觀測與預測的差是 5 − 5 = 0,所以估計值維持在 x̂⁻ = 5 不會移動。
Q2. 接著預測變異數 P⁻ = 4 時,更新後的變異數 P 是多少?
P = (1 − 0.6) × 4 = 1.6。估計值雖然沒動,但因為吸收了觀測,不確定性還是會減少。