状態方程式と観測方程式が次のように与えられているとき
線形カルマンフィルタ(LKF; Linear Kalman Filter)は μt, Σt, ut, yt+1 を入力として、 μt+1, Σt+1を出力する。1ステップのプロセスは以下のとおり。(状態方程式)
(観測方程式)
(ノイズ)
(フィルタ分布)
# prediction
# update(現在の推定値)
(現在の誤差行列)
観測を得るごとにPredictionとUpdateを繰り返すことで、現在の状態を推定します。(観測残差)
(観測残差の共分散)
(最適カルマンゲイン)
(更新された現在の推定値)
(更新された現在の誤差行列)
導出は後述(予定)。
例題を。
2次元座標において、あるロボットがt=0に原点を出発して、速度(2,2)で動くとする。ロボットの進路は風などの影響を受け(σx=σy=1)、毎秒ごとに観測できるGPSによる位置座標には計測誤差(σx=σy=2)があるとする。このとき、観測された軌跡から実際の軌跡を推定する。係数は
初期値は
コードは以下。要SciPy。(やってる事自体はSciPyは必須ではないのだけど。)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# -*- coding: utf-8 -*- | |
import numpy as np | |
import matplotlib.pyplot as plt | |
def lkf(T, Y, U, mu0, Sigma0, A, B, C, Q, R): | |
'''Linear Kalman Filter | |
- 状態方程式 | |
x = A * x_ + B * u + w, w ~ N(0,Q) | |
- 観測方程式 | |
y = C * x + v, v ~ N(0,R) | |
Parameters | |
========== | |
- T : ステップ数 | |
- Y : 観測列 | |
- U : 入力列 | |
- mu0 : 初期状態推定値 | |
- Sigma0 : 初期誤差共分散行列 | |
- A, B, C, Q, R : カルマンフィルタの係数 | |
Returns | |
======= | |
- M : 状態推定値列 | |
''' | |
mu = mu0 # 初期状態推定値 | |
Sigma = Sigma0 # 初期誤差共分散行列 | |
M = [mu] # 状態推定値列 | |
for i in range(T): | |
# 推定 | |
mu_ = A * mu + B * U[i] | |
Sigma_ = Q + A * Sigma * A.T | |
# 更新 | |
yi = Y[i+1] - C * mu_ | |
S = C * Sigma_ * C.T + R | |
K = Sigma_ * C.T * S.I | |
mu = mu_ + K * yi | |
Sigma = Sigma_ - K * C * Sigma_ | |
M.append(mu) | |
return M | |
def main(): | |
# 状態方程式 | |
# x = A * x_ + B * u + w, w ~ N(0,Q) | |
A = np.mat([[1,0], [0,1]]) | |
B = np.mat([[1,0], [0,1]]) | |
Q = np.mat([[1,0], [0,1]]) | |
# 観測方程式 | |
# y = C * x + v, v ~ N(0,R) | |
C = np.mat([[1,0], [0,1]]) | |
R = np.mat([[2,0], [0,2]]) | |
# 観測のテストデータの生成 | |
T = 10 # 観測数 | |
x = np.mat([[0],[0]]) # 初期位置 | |
X = [x] # 状態列 | |
Y = [x] # 観測列 | |
u = np.mat([[2],[2]]) # 入力(一定) | |
U = [u] # 入力列 | |
for i in range(T): | |
x = A * x + B * u + np.random.multivariate_normal([0, 0], Q, 1).T | |
X.append(x) | |
y = C * x + np.random.multivariate_normal([0, 0], R, 1).T | |
Y.append(y) | |
U.append(u) | |
# LKF | |
mu0 = np.mat([[0],[0]]) # 初期状態推定値 | |
Sigma0 = np.mat([[0,0],[0,0]]) # 初期誤差共分散行列 | |
M = lkf(T, Y, U, mu0, Sigma0, A, B, C, Q, R) | |
# 描画 | |
a, b = np.array(np.concatenate(X,axis=1)) | |
plt.plot(a,b,'rs-') | |
a, b = np.array(np.concatenate(Y,axis=1)) | |
plt.plot(a,b,'g^-') | |
a, b = np.array(np.concatenate(M,axis=1)) | |
plt.plot(a,b,'bo-') | |
plt.axis('equal') | |
plt.show() | |
if __name__ == '__main__': | |
main() |
実行結果は下図。
観測に対してカルマンフィルタによる推定値が、実際の軌跡に近いことがわかります。
No comments:
Post a Comment