42

My input is 2d (x,y) time series of a dot moving on a screen for a tracker software. It has some noise I want to remove using Kalman filter. Does someone can point me for a python code for Kalman 2d filter? In scipy cookbook I found only a 1d example: http://www.scipy.org/Cookbook/KalmanFiltering I saw there is implementation for Kalman filter in OpenCV, but couldn't find code examples. Thanks!

Marco Cerliani
  • 14,870
  • 3
  • 25
  • 35
Noam Peled
  • 3,978
  • 3
  • 39
  • 46
  • 1
    I'm certainly not an expert on this topic, but in class we always applied filters to 2d space with applying it on each row and column seperately. Did you try that? Maybe it will improve your results already. – erikbwork Dec 16 '12 at 14:14
  • 2
    I guess you will have to generalize the 1d example if you. Since you have not specfied the it should be in python using numpy (I am just guessing this) Otherwise I would just point you to OSS-like matlab code http://www.mathworks.com/matlabcentral/fileexchange/14243-2d-target-tracking-using-kalman-filter which can be adopted for scipy. In the same link you have provided the was another link in the source that brings one to all sort of infos collected on Klaman filtering by the author of the snippet http://www.cs.unc.edu/~welch/kalman/ – Yauhen Yakimovich Dec 16 '12 at 14:20
  • 1
    erikb85 - I did try to apply it on each row and column separately, and my results were improved. Thanks! I'm very new to Kalman filtering, and not sure that it the right way to do it. Yauhen Yakimovich - Thanks, I downloaded Matlab code for 2d tracking from: http://www.cs.ubc.ca/~murphyk/Software/Kalman/kalman.html. It has also a nice EM learner for the filter parameters. I'm not sure if I'll move to Matlab or translate all the code to Python... – Noam Peled Dec 16 '12 at 16:20
  • 2
    The easiest way I found here to learn about the use of the Kalman filter. https://stackoverflow.com/a/53017661/7060530 – Sayed Mohsin Reza Oct 27 '18 at 00:08

2 Answers2

64

Here is my implementation of the Kalman filter based on the equations given on wikipedia. Please be aware that my understanding of Kalman filters is very rudimentary so there are most likely ways to improve this code. (For example, it suffers from the numerical instability problem discussed here. As I understand it, this only affects the numerical stability when Q, the motion noise, is very small. In real life, the noise is usually not small, so fortunately (at least for my implementation) in practice the numerical instability does not show up.)

In the example below, kalman_xy assumes the state vector is a 4-tuple: 2 numbers for the location, and 2 numbers for the velocity. The F and H matrices have been defined specifically for this state vector: If x is a 4-tuple state, then

new_x = F * x
position = H * x

It then calls kalman, which is the generalized Kalman filter. It is general in the sense it is still useful if you wish to define a different state vector -- perhaps a 6-tuple representing location, velocity and acceleration. You just have to define the equations of motion by supplying the appropriate F and H.

import numpy as np
import matplotlib.pyplot as plt

def kalman_xy(x, P, measurement, R,
              motion = np.matrix('0. 0. 0. 0.').T,
              Q = np.matrix(np.eye(4))):
    """
    Parameters:    
    x: initial state 4-tuple of location and velocity: (x0, x1, x0_dot, x1_dot)
    P: initial uncertainty convariance matrix
    measurement: observed position
    R: measurement noise 
    motion: external motion added to state vector x
    Q: motion noise (same shape as P)
    """
    return kalman(x, P, measurement, R, motion, Q,
                  F = np.matrix('''
                      1. 0. 1. 0.;
                      0. 1. 0. 1.;
                      0. 0. 1. 0.;
                      0. 0. 0. 1.
                      '''),
                  H = np.matrix('''
                      1. 0. 0. 0.;
                      0. 1. 0. 0.'''))

def kalman(x, P, measurement, R, motion, Q, F, H):
    '''
    Parameters:
    x: initial state
    P: initial uncertainty convariance matrix
    measurement: observed position (same shape as H*x)
    R: measurement noise (same shape as H)
    motion: external motion added to state vector x
    Q: motion noise (same shape as P)
    F: next state function: x_prime = F*x
    H: measurement function: position = H*x

    Return: the updated and predicted new values for (x, P)

    See also http://en.wikipedia.org/wiki/Kalman_filter

    This version of kalman can be applied to many different situations by
    appropriately defining F and H 
    '''
    # UPDATE x, P based on measurement m    
    # distance between measured and current position-belief
    y = np.matrix(measurement).T - H * x
    S = H * P * H.T + R  # residual convariance
    K = P * H.T * S.I    # Kalman gain
    x = x + K*y
    I = np.matrix(np.eye(F.shape[0])) # identity matrix
    P = (I - K*H)*P

    # PREDICT x, P based on motion
    x = F*x + motion
    P = F*P*F.T + Q

    return x, P

def demo_kalman_xy():
    x = np.matrix('0. 0. 0. 0.').T 
    P = np.matrix(np.eye(4))*1000 # initial uncertainty

    N = 20
    true_x = np.linspace(0.0, 10.0, N)
    true_y = true_x**2
    observed_x = true_x + 0.05*np.random.random(N)*true_x
    observed_y = true_y + 0.05*np.random.random(N)*true_y
    plt.plot(observed_x, observed_y, 'ro')
    result = []
    R = 0.01**2
    for meas in zip(observed_x, observed_y):
        x, P = kalman_xy(x, P, meas, R)
        result.append((x[:2]).tolist())
    kalman_x, kalman_y = zip(*result)
    plt.plot(kalman_x, kalman_y, 'g-')
    plt.show()

demo_kalman_xy()

enter image description here

The red dots show the noisy position measurements, the green line shows the Kalman predicted positions.

unutbu
  • 711,858
  • 148
  • 1,594
  • 1,547
  • 2
    To calculate the initial uncertainty, as in `P = np.matrix(np.eye(4))*1000`. Why do you multiply by 1000? – Sophia Mar 16 '19 at 11:33
  • 1
    Shouldn't there be matrix multiplication in the many places where you use "*"? – Sandu Ursu Sep 05 '19 at 14:28
  • @SanduUrsu when the arguments to the * operator are of type np.matrix (as opposed to np.array) matrix multiplication is performed. To be safe though, the @ operator can be used to ensure matrix multiplication in either case. – hyperspasm Jan 03 '21 at 09:41
  • In the example `demo_kalman_xy()` R should really be a 2x2 (measurement noise covariance) matrix, for example `R = np.matrix([[r,0], [0,r]])`. The default value of Q in `kalman_xy()` is also probably too high to easily see the effects of adjusting R. – hyperspasm Jan 03 '21 at 10:19
0

For a project of mine, I needed to create intervals for time-series modeling, and to make the procedure more efficient I created tsmoothie: A python library for time-series smoothing and outlier detection in a vectorized way.

It provides different smoothing algorithms together with the possibility to computes intervals.

In the case of KalmanSmoother, you can operate a smoothing of a curve putting together different components: level, trend, seasonality, long seasonality

import numpy as np
import matplotlib.pyplot as plt
from tsmoothie.smoother import *
from tsmoothie.utils_func import sim_randomwalk

# generate 3 randomwalks timeseries of lenght 100
np.random.seed(123)
data = sim_randomwalk(n_series=3, timesteps=100, 
                      process_noise=10, measure_noise=30)

# operate smoothing
smoother = KalmanSmoother(component='level_trend', 
                          component_noise={'level':0.1, 'trend':0.1})
smoother.smooth(data)

# generate intervals
low, up = smoother.get_intervals('kalman_interval', confidence=0.05)

# plot the first smoothed timeseries with intervals
plt.figure(figsize=(11,6))
plt.plot(smoother.smooth_data[0], linewidth=3, color='blue')
plt.plot(smoother.data[0], '.k')
plt.fill_between(range(len(smoother.data[0])), low[0], up[0], alpha=0.3)

enter image description here

I point out also that tsmoothie can carry out the smoothing of multiple timeseries in a vectorized way

Marco Cerliani
  • 14,870
  • 3
  • 25
  • 35
  • your example is 1d only. the question asks specifically for 2d data – LudvigH Aug 25 '20 at 09:02
  • In my case, x is the time (assumed to be increasing) and y the values reached by the series, so 2D... it's the same case reported in the answer above where observed_x and observed_y are two increasing quantity – Marco Cerliani Aug 25 '20 at 09:38