Source code for filterpy.leastsq.least_squares
# -*- coding: utf-8 -*-
# pylint: disable=C0103, R0913, R0902, C0326, R0914
# disable snake_case warning, too many arguments, too many attributes,
# one space before assignment, too many local variables
"""Copyright 2015 Roger R Labbe Jr.
FilterPy library.
http://github.com/rlabbe/filterpy
Documentation at:
https://filterpy.readthedocs.org
Supporting book at:
https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python
This is licensed under an MIT license. See the readme.MD file
for more information.
"""
from __future__ import absolute_import, division
from math import sqrt
import numpy as np
from filterpy.kalman import pretty_str
[docs]class LeastSquaresFilter(object):
"""Implements a Least Squares recursive filter. Formulation is per
Zarchan [1]_.
Filter may be of order 0 to 2. Order 0 assumes the value being tracked is
a constant, order 1 assumes that it moves in a line, and order 2 assumes
that it is tracking a second order polynomial.
Parameters
----------
dt : float
time step per update
order : int
order of filter 0..2
noise_sigma : float
sigma (std dev) in x. This allows us to calculate the error of
the filter, it does not influence the filter output.
Attributes
----------
n : int
step in the recursion. 0 prior to first call, 1 after the first call,
etc.
K : np.array
Gains for the filter. K[0] for all orders, K[1] for orders 0 and 1, and
K[2] for order 2
x: np.array (order + 1, 1)
estimate(s) of the output. It is a vector containing the estimate x
and the derivatives of x: [x x' x''].T. It contains as many
derivatives as the order allows. That is, a zero order filter has
no derivatives, a first order has one derivative, and a second order
has two.
y : float
residual (difference between measurement projection of previous
estimate to current time).
Examples
--------
.. code-block:: Python
from filterpy.leastsq import LeastSquaresFilter
lsq = LeastSquaresFilter(dt=0.1, order=1, noise_sigma=2.3)
while True:
z = sensor_reading() # get a measurement
x = lsq.update(z) # get the filtered estimate.
print('error: {}, velocity error: {}'.format(
lsq.error, lsq.derror))
References
----------
.. [1] Zarchan and Musoff. "Fundamentals of Kalman Filtering: A Practical
Approach." Third Edition. AIAA, 2009.
"""
[docs] def __init__(self, dt, order, noise_sigma=0.):
if order < 0 or order > 2:
raise ValueError('order must be between 0 and 2')
self.dt = dt
self.sigma = noise_sigma
self._order = order
self.reset()
[docs] def reset(self):
""" reset filter back to state at time of construction"""
self.n = 0 # nth step in the recursion
self.x = np.zeros(self._order + 1)
self.K = np.zeros(self._order + 1)
self.y = 0 # residual
[docs] def update(self, z):
""" Update filter with new measurement `z`
Returns
-------
x : np.array
estimate for this time step (same as self.x)
"""
self.n += 1
# rename for readability
n = self.n
dt = self.dt
x = self.x
K = self.K
y = self.y
if self._order == 0:
K[0] = 1. / n
y = z - x
x[0] += K[0] * y
elif self._order == 1:
K[0] = 2. * (2*n - 1) / (n*(n + 1))
K[1] = 6. / (n*(n + 1)*dt)
y = z - x[0] - (dt * x[1])
x[0] += (K[0] * y) + (dt * x[1])
x[1] += (K[1] * y)
else:
den = n * (n+1) * (n+2)
K[0] = 3. * (3*n**2 - 3*n + 2) / den
K[1] = 18. * (2*n-1) / (den*dt)
K[2] = 60. / (den*dt**2)
y = z - x[0] - (dt * x[1]) - (0.5 * dt**2 * x[2])
x[0] += (K[0] * y) + (x[1] * dt) + (.5 * dt**2 * x[2])
x[1] += (K[1] * y) + (x[2] * dt)
x[2] += (K[2] * y)
return self.x
[docs] def errors(self):
"""
Computes and returns the error and standard deviation of the
filter at this time step.
Returns
-------
error : np.array size 1xorder+1
std : np.array size 1xorder+1
"""
n = self.n
dt = self.dt
order = self._order
sigma = self.sigma
error = np.zeros(order + 1)
std = np.zeros(order + 1)
if n == 0:
return (error, std)
if order == 0:
error[0] = sigma/sqrt(n)
std[0] = sigma/sqrt(n)
elif order == 1:
if n > 1:
error[0] = sigma * sqrt(2*(2*n-1) / (n*(n+1)))
error[1] = sigma * sqrt(12. / (n*(n*n-1)*dt*dt))
std[0] = sigma * sqrt((2*(2*n-1)) / (n*(n+1)))
std[1] = (sigma/dt) * sqrt(12. / (n*(n*n-1)))
elif order == 2:
dt2 = dt * dt
if n >= 3:
error[0] = sigma * sqrt(3*(3*n*n-3*n+2) / (n*(n+1)*(n+2)))
error[1] = sigma * sqrt(12*(16*n*n-30*n+11) /
(n*(n*n-1)*(n*n-4)*dt2))
error[2] = sigma * sqrt(720/(n*(n*n-1)*(n*n-4)*dt2*dt2))
std[0] = sigma * sqrt((3*(3*n*n - 3*n + 2)) / (n*(n+1)*(n+2)))
std[1] = (sigma/dt) * sqrt((12*(16*n*n - 30*n + 11)) /
(n*(n*n - 1)*(n*n - 4)))
std[2] = (sigma/dt2) * sqrt(720 / (n*(n*n-1)*(n*n-4)))
return error, std
def __repr__(self):
return '\n'.join([
'LeastSquaresFilter object',
pretty_str('dt', self.dt),
pretty_str('sigma', self.sigma),
pretty_str('_order', self._order),
pretty_str('x', self.x),
pretty_str('K', self.K)
])