6
$\begingroup$

I'm experimenting with the pykalman Python library to learn about Kalman Filters. In the code below, I'm generating a random walk where each step is the last step multiplied by 1 plus some noise: 1 + std_dev. In the first block, the covariance matrix of the KF (using a 1D signal/state, so it's really just one variance) appears to converge to about 0.62. Even if I set grow_std_dev=True, causing the noise to grow significantly over time, the covariance matrix still converges to this value.

import numpy as np
import matplotlib.pyplot as plt
from pykalman import KalmanFilter

def generate_random_walk(seed=1, n_steps=100, grow_std_dev=False):
    np.random.seed(seed)
    rand_walk = [1]
    std_devs = []

    for n in range(n_steps):
        if grow_std_dev:
            std_dev = np.random.normal(0,0.01 * (1 + n))
        else:
            std_dev = np.random.normal(0,0.01)
        val = rand_walk[-1] * (1 + std_dev)
        std_devs.append(std_dev)
        rand_walk.append(val)
      
    return np.array(rand_walk), np.array(std_devs)


kf = KalmanFilter(transition_matrices = [1],
    observation_matrices = [1],
    initial_state_mean = 1,
    initial_state_covariance = 1,
    observation_covariance=1,
    transition_covariance=1)

rand_walk, std_devs = generate_random_walk(0)
state_means, state_cov = kf.filter(rand_walk)


plt.plot(std_devs)
plt.show()
plt.plot(rand_walk_1)
plt.show()
plt.plot(state_cov.flatten())

The plots produced by the above code:

enter image description here enter image description here enter image description here

I read elsewhere that KFs can be used to perform recursive linear regression (I believe this is the correct term) between two variables by passing one variable for the observation_matrices argument and the other as the value to filter. So in the block below, I'm generating a second random walk with a different seed and passing that for observation_matrices rather than the fixed matrix of [1] as in the first block. Unlike the first example, this results in a covariance matrix that does not converge.

rand_walk_2, _ = generate_random_walk(1)

kf = KalmanFilter(transition_matrices = [1],
    observation_matrices = rand_walk_2[:,None,None],
    initial_state_mean = 1,
    initial_state_covariance = 1,
    observation_covariance=1,
    transition_covariance=1)


state_means, state_cov = kf.filter(rand_walk_1)

plt.plot(rand_walk_2)
plt.show()
plt.plot(state_cov.flatten())

enter image description here enter image description here

I'm really not as concerned with the second example other than to show that it's possible for the covariance matrix to not converge. In learning about Kalman Filters, my understanding was that part of the value is that the uncertainty of the estimate is quantified at each time step, i.e., that the covariance matrix is evolving with the state estimate. Again, by setting grow_std_dev=True, I would expect that the covariance matrix would need to change for the filter to make optimal predictions.

With that said, I'm wondering if I'm missing something about how KFs are supposed to work, missing an argument, or otherwise.

Any help is appreciated.

$\endgroup$
1
  • 1
    $\begingroup$ Most KFs don’t have time varying noise covariance, and so the state covariance converges. Sometimes people forgo the full Kalyan filter and just use the converged filter. I’ll have to dig a bit further into your case to see what is happening, but that will be tomorrow. $\endgroup$
    – Peter K.
    Commented Dec 13, 2021 at 3:59

1 Answer 1

3
$\begingroup$

For classic Kalman Filter, where $ {Q}_{k} = Q $ and $ {R}_{k} = R $, namely the process noise covariance and the measurement noise covariance (I'm using Wikipedia - Kalman Filter notations) the Posterior Covariance $ {P}_{k} $ is a deterministic matrix independent of the measurements themselves.

Since your code set the measurement and the process noise to be constant the Kalman Filter has no idea about the growing variance of the process you created.

In PyKalman, the initialization of the Kalman Filter with KalmanFilter() doesn't allow time changing matrices. But you have the filter_update() method which allows you to set the transition_covariance and observation_covariance per iteration which is what you need to do.

$\endgroup$
3
  • $\begingroup$ Thanks for your answer. I replicated something like this using randomly generated numbers: stackoverflow.com/questions/49885025/… and it seems to work just to test the concept, but I assume this means that the values of transition_covariance or observation_covariance will always be unique to the problem? And varying these at each time step would qualify as an "adaptive" filter? $\endgroup$ Commented Dec 13, 2021 at 6:15
  • $\begingroup$ Part of your above comment merits another question. Yes, the process noise (PyKalman's "transition covariance") and the measurement noise ("observation covariance") will always be unique to the problem. No, this doesn't qualify as an "adaptive filter". An adaptive filter is one that changes its properties on the fly to match the incoming data -- if you're changing the process and measurement noise on a fixed program, the filter isn't adaptive (and could, in fact, have the covariances and gains pre-computed, independent of an actual run). $\endgroup$
    – TimWescott
    Commented Dec 13, 2021 at 21:37
  • $\begingroup$ It wouldn't be a fixed program in practice - changing properties to match the data is what I was getting at. The confusion on my part was over whether this was part of the default behavior of KFs. I read a chunk of this book a while back: nbviewer.org/github/rlabbe/… (I'm just now coming back to KFs), and I think some of the solutions on this page might work. $\endgroup$ Commented Dec 14, 2021 at 0:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.