カルマンフィルタと逐次ベイズ推定の関係をわかりやすく解説

カルマンフィルタは、逐次ベイズ推定の特殊版と言われています。カルマンフィルタを使ったことがある人でも、なぜベイズ推定の特殊ケースなのかを理解していない人は意外と多いのではないでしょうか。

今回は、逐次ベイズ推定の枠組みを解説し、そこからカルマンフィルタがどのように導出されるかを丁寧にまとめていきます。

本記事の内容

  • 逐次ベイズ推定の枠組み
  • 予測ステップと更新ステップの数学的定式化
  • カルマンフィルタとの対応関係
  • Python での実装と可視化

前提知識

この記事を読む前に、以下の記事を読んでおくと理解が深まります。

逐次ベイズ推定とは

逐次ベイズ推定(Sequential Bayesian Estimation)とは、時刻 $t$ ごとに新しい観測データが得られるような状況で、ベイズの枠組みを使って状態を逐次的に推定する手法です。

時刻 $t$ における状態を $\bm{x}_t$、観測データを $\bm{y}_t$ とすると、逐次ベイズ推定は大きく2つのステップに分けられます。

1. 予測ステップ

前の時刻の事後分布 $p(\bm{x}_{t-1}|\bm{y}_{1:t-1})$ から、現在の状態の予測分布を求めます。

$$ \begin{equation} p(\bm{x}_t|\bm{y}_{1:t-1}) = \int p(\bm{x}_t|\bm{x}_{t-1}) \cdot p(\bm{x}_{t-1}|\bm{y}_{1:t-1}) \, d\bm{x}_{t-1} \end{equation} $$

ここで $p(\bm{x}_t|\bm{x}_{t-1})$ は状態遷移モデル(システムモデル)です。

2. 更新ステップ

新しい観測 $\bm{y}_t$ が得られたら、ベイズの定理を使って予測分布を更新します。

$$ \begin{equation} p(\bm{x}_t|\bm{y}_{1:t}) = \frac{p(\bm{y}_t|\bm{x}_t) \cdot p(\bm{x}_t|\bm{y}_{1:t-1})}{p(\bm{y}_t|\bm{y}_{1:t-1})} \end{equation} $$

ここで $p(\bm{y}_t|\bm{x}_t)$ は観測モデル(尤度関数)です。

この「予測 → 更新 → 予測 → 更新 → …」を繰り返すことで、時々刻々と状態を推定していきます。

カルマンフィルタの仮定

カルマンフィルタは、逐次ベイズ推定に対して以下の仮定を置いたものです。

仮定1: 線形ガウスの状態遷移モデル

$$ \begin{equation} \bm{x}_t = \bm{F}_t \bm{x}_{t-1} + \bm{w}_t, \quad \bm{w}_t \sim \mathcal{N}(\bm{0}, \bm{Q}_t) \end{equation} $$

仮定2: 線形ガウスの観測モデル

$$ \begin{equation} \bm{y}_t = \bm{H}_t \bm{x}_t + \bm{v}_t, \quad \bm{v}_t \sim \mathcal{N}(\bm{0}, \bm{R}_t) \end{equation} $$

仮定3: 初期状態がガウス分布

$$ \begin{equation} p(\bm{x}_0) = \mathcal{N}(\bm{x}_0|\bm{\mu}_0, \bm{P}_0) \end{equation} $$

ここで、$\bm{F}_t$ は状態遷移行列、$\bm{H}_t$ は観測行列、$\bm{Q}_t$ はプロセスノイズの共分散行列、$\bm{R}_t$ は観測ノイズの共分散行列です。

カルマンフィルタの予測ステップの導出

仮定3より、時刻 $t-1$ の事後分布がガウス分布であると仮定します。

$$ p(\bm{x}_{t-1}|\bm{y}_{1:t-1}) = \mathcal{N}(\bm{x}_{t-1}|\hat{\bm{x}}_{t-1}, \bm{P}_{t-1}) $$

(1) 式に (3) 式の状態遷移モデルを代入すると、状態遷移確率は、

$$ p(\bm{x}_t|\bm{x}_{t-1}) = \mathcal{N}(\bm{x}_t|\bm{F}_t \bm{x}_{t-1}, \bm{Q}_t) $$

ガウス分布の線形変換の性質より、予測分布は次のようになります。

$$ \begin{align} p(\bm{x}_t|\bm{y}_{1:t-1}) &= \mathcal{N}(\bm{x}_t|\hat{\bm{x}}_{t|t-1}, \bm{P}_{t|t-1}) \\ \hat{\bm{x}}_{t|t-1} &= \bm{F}_t \hat{\bm{x}}_{t-1} \\ \bm{P}_{t|t-1} &= \bm{F}_t \bm{P}_{t-1} \bm{F}_t^T + \bm{Q}_t \end{align} $$

これがカルマンフィルタの予測ステップです。

カルマンフィルタの更新ステップの導出

(2) 式のベイズ更新を適用します。予測分布と観測モデルがともにガウス分布であるため、事後分布もガウス分布になります。

$$ \begin{align} \hat{\bm{x}}_t &= \hat{\bm{x}}_{t|t-1} + \bm{K}_t (\bm{y}_t – \bm{H}_t \hat{\bm{x}}_{t|t-1}) \\ \bm{P}_t &= (\bm{I} – \bm{K}_t \bm{H}_t) \bm{P}_{t|t-1} \end{align} $$

ここで $\bm{K}_t$ はカルマンゲインと呼ばれ、次のように定義されます。

$$ \begin{equation} \bm{K}_t = \bm{P}_{t|t-1} \bm{H}_t^T (\bm{H}_t \bm{P}_{t|t-1} \bm{H}_t^T + \bm{R}_t)^{-1} \end{equation} $$

カルマンゲインは、予測の不確かさと観測の不確かさのバランスを取る重み係数の役割を果たしています。

逐次ベイズ推定とカルマンフィルタの対応

逐次ベイズ推定 カルマンフィルタ
事前分布 $p(\bm{x}_t \mid \bm{y}_{1:t-1})$ 予測分布 $\mathcal{N}(\hat{\bm{x}}_{t \mid t-1}, \bm{P}_{t \mid t-1})$
尤度 $p(\bm{y}_t \mid \bm{x}_t)$ 観測モデル $\mathcal{N}(\bm{H}_t \bm{x}_t, \bm{R}_t)$
事後分布 $p(\bm{x}_t \mid \bm{y}_{1:t})$ フィルタリング分布 $\mathcal{N}(\hat{\bm{x}}_t, \bm{P}_t)$
状態遷移 $p(\bm{x}_t \mid \bm{x}_{t-1})$ 線形遷移 $\mathcal{N}(\bm{F}_t \bm{x}_{t-1}, \bm{Q}_t)$

カルマンフィルタは、すべての分布がガウス分布で、モデルが線形であるという仮定のもとで、逐次ベイズ推定を解析的に解いたものと言えます。

Python での実装

1次元の位置推定問題を例に、カルマンフィルタを逐次ベイズ推定として実装してみましょう。

import numpy as np
import matplotlib.pyplot as plt

# シミュレーション設定
np.random.seed(42)
T = 50           # 時間ステップ数
F = 1.0          # 状態遷移行列(等速運動)
H = 1.0          # 観測行列
Q = 0.1          # プロセスノイズの分散
R = 1.0          # 観測ノイズの分散

# 真の状態と観測データの生成
x_true = np.zeros(T)
y_obs = np.zeros(T)
x_true[0] = 0.0
for t in range(1, T):
    x_true[t] = F * x_true[t-1] + np.random.normal(0, np.sqrt(Q))
y_obs = H * x_true + np.random.normal(0, np.sqrt(R), T)

# カルマンフィルタ(逐次ベイズ推定)
x_est = np.zeros(T)    # 推定状態
P_est = np.zeros(T)    # 推定共分散
x_pred = np.zeros(T)   # 予測状態
P_pred = np.zeros(T)   # 予測共分散

# 初期化
x_est[0] = 0.0
P_est[0] = 1.0

for t in range(1, T):
    # 予測ステップ(事前分布の計算)
    x_pred[t] = F * x_est[t-1]
    P_pred[t] = F * P_est[t-1] * F + Q

    # 更新ステップ(事後分布の計算)
    K = P_pred[t] * H / (H * P_pred[t] * H + R)  # カルマンゲイン
    x_est[t] = x_pred[t] + K * (y_obs[t] - H * x_pred[t])
    P_est[t] = (1 - K * H) * P_pred[t]

# 可視化
fig, axes = plt.subplots(2, 1, figsize=(12, 8))

# 上段: 状態推定
ax1 = axes[0]
ax1.plot(x_true, 'k-', linewidth=1.5, label='True state')
ax1.scatter(range(T), y_obs, c='gray', s=20, alpha=0.6, label='Observations')
ax1.plot(x_est, 'b-', linewidth=1.5, label='Kalman estimate')
ax1.fill_between(range(T),
                 x_est - 2*np.sqrt(P_est),
                 x_est + 2*np.sqrt(P_est),
                 alpha=0.2, color='blue', label='95% CI')
ax1.set_xlabel('Time step', fontsize=12)
ax1.set_ylabel('State', fontsize=12)
ax1.set_title('Sequential Bayesian Estimation (Kalman Filter)', fontsize=13)
ax1.legend(fontsize=10)
ax1.grid(True, alpha=0.3)

# 下段: カルマンゲインと共分散の推移
ax2 = axes[1]
K_history = [0]
for t in range(1, T):
    K_val = P_pred[t] * H / (H * P_pred[t] * H + R)
    K_history.append(K_val)
ax2.plot(K_history, 'r-', linewidth=1.5, label='Kalman Gain')
ax2.plot(P_est, 'g-', linewidth=1.5, label='Posterior variance')
ax2.set_xlabel('Time step', fontsize=12)
ax2.set_ylabel('Value', fontsize=12)
ax2.set_title('Kalman Gain and Posterior Variance', fontsize=13)
ax2.legend(fontsize=10)
ax2.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

カルマンゲインは初期に大きく、時間が経つにつれて収束していく様子が確認できます。これは、最初は観測データを重視し、推定が安定するにつれて予測を重視するようになることを意味します。

まとめ

本記事では、カルマンフィルタと逐次ベイズ推定の関係について解説しました。

  • 逐次ベイズ推定は「予測ステップ」と「更新ステップ」の2段階で状態を逐次推定する枠組み
  • カルマンフィルタは、線形ガウスモデルの仮定のもとで逐次ベイズ推定を解析的に解いたもの
  • カルマンゲインは予測と観測の不確かさのバランスを取る重み係数
  • ガウス分布の再生性と線形変換の性質により、全ステップでガウス分布が維持される

次のステップとして、以下の記事も参考にしてください。