Pythonでカルマンフィルタを使ってみた

はじめに
この記事では、Pythonでカルマンフィルタを使った実験を紹介する。pykalmanライブラリを使って、ノイズの混じった正弦波にカルマンフィルタをかけてみた。また、FIRローパスフィルタとの比較も行った。
なお、動作環境は次の通りである。
項目 | バージョン |
---|---|
PC | macOS Big Sur version 11.0.1 |
Python | 3.7.3 |
pykalmanのインストール
今回は、pykalman
というカルマンフィルタライブラリを使うので次の通りインストールしておく。また、scipy
も必要とされるので同時にインストールしておく。
$ pip3 install pykalman
$ pip3 install scipy
https://pykalman.github.io/
Pythonでカルマンフィルタ
今回の実験では、下のプログラムを使った。正弦波にノイズをかけたデータを用意し、自作のローパスフィルタ(FIR)とカルマンフィルタを通してグラフで比較するプログラムである。
from matplotlib import pyplot as plt
from random import randint
import math
from pykalman import KalmanFilter
import numpy as np
def sine_wave(samplerate, frequency):
= list(range(samplerate))
x = [math.sin(i*2*math.pi/samplerate*frequency) for i in x]
y return x, y
def add_noise(values):
= [randint(-100, 100) * 0.01 for _ in values]
noise = [w + n for (w, n) in zip(values, noise)]
y return y
def filtered_kalman(values):
= KalmanFilter(transition_matrices=np.array([[1, 1], [0, 1]]),
kf =0.0001 * np.eye(2)) # np.eyeは単位行列
transition_covariance= kf.em(values).smooth(values)[0]
smoothed = kf.em(values).filter(values)[0]
filtered return smoothed, filtered
def filtered_lowpass(values):
= [0 for _ in values]
res = 0.2
k = 0
i for a in values:
try:
= res[i - 1]
b except:
= 0
b = k * a + (1-k) * b
res[i] += 1
i return res
if __name__ == '__main__':
= sine_wave(500, 5)
x, sine_y = add_noise(sine_y)
noised_y = filtered_kalman(noised_y)
smoothed, filtered = filtered_lowpass(noised_y)
lowpass_y
=(16, 9), dpi=80)
plt.figure(figsize='Original')
plt.plot(x, sine_y, label='Noised')
plt.plot(x, noised_y, label='FIR LPF')
plt.plot(x, lowpass_y, label0], label='Kalman Smoothed')
plt.plot(x, smoothed[:, # plt.plot(x, filtered[:, 0], label='Filtered')
plt.legend() plt.show()
実験結果
正弦波にノイズを足したグラフ
正弦波(青)にノイズを足したもの(オレンジ)が、フィルタの対象となるデータである。
自作ローパスフィルタを通したグラフ
ローパスフィルタだと、ノイズは完全に除去できない。強めにかけると振幅が小さくなってしまったり、また位相もズレてしまう。
カルマンフィルタを通したグラフ
今回はじめてカルマンフィルタを使ってみたが、原型の正弦波にかなり近い結果になり驚いた。
カルマンフィルタとローパスフィルタの比較
このように、ローパスフィルタよりもカルマンフィルタの方が原型に近い正弦波を取り出すことができる。