From c315f2a5b475b96fecd61741fbba4e681774a64f Mon Sep 17 00:00:00 2001 From: Roger Labbe Date: Wed, 14 Sep 2016 20:07:50 -0700 Subject: [PATCH] Improved Sphinx documentation. Reduced number of errors generated by Sphinx. Added SimplexSigmaPoints. --- docs/conf.py | 2 ++ docs/kalman/UnscentedKalmanFilter.rst | 7 ++++++ filterpy/__init__.py | 2 +- filterpy/changelog.txt | 10 +++----- filterpy/common/discretization.py | 4 +-- filterpy/hinfinity/hinfinity_filter.py | 5 ++-- filterpy/kalman/EKF.py | 12 +++++++-- filterpy/kalman/fading_memory.py | 34 +++++++++----------------- filterpy/kalman/information_filter.py | 7 ++++-- filterpy/kalman/kalman_filter.py | 12 ++++++--- filterpy/kalman/sigma_points.py | 29 +++++++++++----------- filterpy/kalman/square_root.py | 12 +++++---- 12 files changed, 76 insertions(+), 60 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index f9c683a..ebcf61e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -49,6 +49,7 @@ # ones. extensions = [ 'sphinx.ext.autodoc', + 'sphinx.ext.autosummary', 'sphinx.ext.doctest', 'numpydoc', 'sphinx.ext.intersphinx', @@ -57,6 +58,7 @@ 'sphinx.ext.autodoc', 'numpydoc' ] +numpydoc_show_class_members = False # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/kalman/UnscentedKalmanFilter.rst b/docs/kalman/UnscentedKalmanFilter.rst index 179b8f4..d4c0abc 100644 --- a/docs/kalman/UnscentedKalmanFilter.rst +++ b/docs/kalman/UnscentedKalmanFilter.rst @@ -36,3 +36,10 @@ This implements the unscented Kalman filter. .. automethod:: __init__ +-------- + + +.. autoclass:: SimplexSigmaPoints + :members: + + .. automethod:: __init__ diff --git a/filterpy/__init__.py b/filterpy/__init__.py index ed93730..cd1ebcc 100644 --- a/filterpy/__init__.py +++ b/filterpy/__init__.py @@ -14,4 +14,4 @@ for more information. """ -__version__ = "0.1.3" +__version__ = "0.1.4" diff --git a/filterpy/changelog.txt b/filterpy/changelog.txt index 24d10e7..3fbdf92 100644 --- a/filterpy/changelog.txt +++ b/filterpy/changelog.txt @@ -1,13 +1,11 @@ -Version 0.1.5 -============= - -* Bug in Q_continuous_white_noise(). The first term in the matrix should be (dt**3)/3, not (dt**4)/3. - Version 0.1.4 ============= * Added Cubature Kalman filter. - +* Bug in Q_continuous_white_noise(). The first term in the matrix should be (dt**3)/3, not (dt**4)/3. +* Added log-likelihood computation to UKF. +* Added simplex points for UKF +* fixed bug in KF matrix size check Version 0.1.3 ============= diff --git a/filterpy/common/discretization.py b/filterpy/common/discretization.py index 55e1463..0072445 100644 --- a/filterpy/common/discretization.py +++ b/filterpy/common/discretization.py @@ -106,8 +106,8 @@ def van_loan_discretization(F, G, dt): Given y'' + y = 2u(t), we create the continuous state model of - x' = | 0 1| * x + |0|*u(t) - |-1 0| |2| + x' = [ 0 1] * x + [0]*u(t) + [-1 0] [2] and a time step of 0.1: diff --git a/filterpy/hinfinity/hinfinity_filter.py b/filterpy/hinfinity/hinfinity_filter.py index b34d818..d8134a6 100644 --- a/filterpy/hinfinity/hinfinity_filter.py +++ b/filterpy/hinfinity/hinfinity_filter.py @@ -250,7 +250,7 @@ def measurement_of_state(self, x): @property def x(self): - """ state vector property""" + """ state vector""" return self._x @@ -271,7 +271,7 @@ def G(self, value): @property def P(self): - """ covariance matrix property""" + """ covariance matrix""" return self._P @@ -282,6 +282,7 @@ def P(self, value): @property def F(self): + """ State transition matrix""" return self._F diff --git a/filterpy/kalman/EKF.py b/filterpy/kalman/EKF.py index 35abc3d..93e4a52 100644 --- a/filterpy/kalman/EKF.py +++ b/filterpy/kalman/EKF.py @@ -224,23 +224,25 @@ def predict(self, u=0): @property def Q(self): - """ Process uncertainty""" + """ Process uncertainty matrix""" return self._Q @Q.setter def Q(self, value): + """ Process uncertainty matrix""" self._Q = setter_scalar(value, self.dim_x) @property def P(self): - """ covariance matrix""" + """ state covariance matrix""" return self._P @P.setter def P(self, value): + """ state covariance matrix""" self._P = setter_scalar(value, self.dim_x) @@ -252,21 +254,25 @@ def R(self): @R.setter def R(self, value): + """ measurement uncertainty""" self._R = setter_scalar(value, self.dim_z) @property def F(self): + """State Transition matrix""" return self._F @F.setter def F(self, value): + """State Transition matrix""" self._F = setter(value, self.dim_x, self.dim_x) @property def B(self): + """ control transition matrix""" return self._B @@ -278,10 +284,12 @@ def B(self, value): @property def x(self): + """ state estimate vector """ return self._x @x.setter def x(self, value): + """ state estimate vector """ self._x = setter_1d(value, self.dim_x) @property diff --git a/filterpy/kalman/fading_memory.py b/filterpy/kalman/fading_memory.py index 5e869bc..bcee10f 100644 --- a/filterpy/kalman/fading_memory.py +++ b/filterpy/kalman/fading_memory.py @@ -60,7 +60,7 @@ def __init__(self, alpha, dim_x, dim_z, dim_u=0): You will have to assign reasonable values to all of these before running the filter. All must have dtype of float - X : ndarray (dim_x, 1), default = [0,0,0...0] + x : ndarray (dim_x, 1), default = [0,0,0...0] state of the filter P : ndarray (dim_x, dim_x), default identity matrix @@ -93,7 +93,7 @@ def __init__(self, alpha, dim_x, dim_z, dim_u=0): self.dim_z = dim_z self.dim_u = dim_u - self._X = zeros((dim_x,1)) # state + self._x = zeros((dim_x,1)) # state self._P = eye(dim_x) # uncertainty covariance self._Q = eye(dim_x) # process uncertainty self._B = 0 # control transition matrix @@ -139,7 +139,7 @@ def update(self, z, R=None): # rename for readability and a tiny extra bit of speed H = self._H P = self._P - x = self._X + x = self._x # y = z - Hx # error (residual) between measurement and prediction @@ -155,7 +155,7 @@ def update(self, z, R=None): # x = x + Ky # predict new x with residual scaled by the kalman gain - self._X = x + dot(K, self._y) + self._x = x + dot(K, self._y) # P = (I-KH)P(I-KH)' + KRK' I_KH = self._I - dot(K, H) @@ -177,7 +177,7 @@ def predict(self, u=0): """ # x = Fx + Bu - self._X = dot(self._F, self._X) + dot(self._B, u) + self._x = dot(self._F, self._x) + dot(self._B, u) # P = FPF' + Q self._P = self.alpha_sq*dot3(self._F, self._P, self._F.T) + self._Q @@ -239,20 +239,20 @@ def batch_filter(self, zs, Rs=None, update_first=False): if update_first: for i,(z,r) in enumerate(zip(zs,Rs)): self.update(z,r) - means[i,:] = self._X + means[i,:] = self._x covariances[i,:,:] = self._P self.predict() - means_p[i,:] = self._X + means_p[i,:] = self._x covariances_p[i,:,:] = self._P else: for i,(z,r) in enumerate(zip(zs,Rs)): self.predict() - means_p[i,:] = self._X + means_p[i,:] = self._x covariances_p[i,:,:] = self._P self.update(z,r) - means[i,:] = self._X + means[i,:] = self._x covariances[i,:,:] = self._P return (means, covariances, means_p, covariances_p) @@ -275,7 +275,7 @@ def get_prediction(self, u=0): State vector and covariance array of the prediction. """ - x = dot(self._F, self._X) + dot(self._B, u) + x = dot(self._F, self._x) + dot(self._B, u) P = self.alpha_sq*dot3(self._F, self._P, self._F.T) + self.Q return (x, P) @@ -284,7 +284,7 @@ def residual_of(self, z): """ returns the residual for the given measurement (z). Does not alter the state of the filter. """ - return z - dot(self._H, self._X) + return z - dot(self._H, self._x) def measurement_of_state(self, x): @@ -369,19 +369,9 @@ def B(self, value): self._B = setter (value, self.dim_x, self.dim_u) - @property - def X(self): - """ filter state vector.""" - return self._X - - - @X.setter - def X(self, value): - self._X = setter(value, self.dim_x, 1) - - @property def x(self): + """ state vector.""" assert False @x.setter diff --git a/filterpy/kalman/information_filter.py b/filterpy/kalman/information_filter.py index c5ed51d..6cbcf1f 100644 --- a/filterpy/kalman/information_filter.py +++ b/filterpy/kalman/information_filter.py @@ -284,7 +284,7 @@ def measurement_of_state(self, x): @property def Q(self): - """ Process uncertainty""" + """Process uncertainty""" return self._Q @@ -305,7 +305,7 @@ def P_inv(self, value): @property def R_inv(self): - """ measurement uncertainty""" + """inverse measurement uncertainty""" return self._R_inv @@ -315,6 +315,7 @@ def R_inv(self, value): @property def H(self): + """Measurement function""" return self._H @@ -325,6 +326,7 @@ def H(self, value): @property def F(self): + """State Transition matrix""" return self._F @@ -347,6 +349,7 @@ def B(self, value): @property def x(self): + """ State estimate vector """ return self._x diff --git a/filterpy/kalman/kalman_filter.py b/filterpy/kalman/kalman_filter.py index c4d411e..ac3a188 100644 --- a/filterpy/kalman/kalman_filter.py +++ b/filterpy/kalman/kalman_filter.py @@ -700,6 +700,7 @@ def alpha(self): @property def likelihood(self): + """ likelihood of measurement""" return math.exp(self.log_likelihood) @@ -712,22 +713,24 @@ def alpha(self, value): @property def Q(self): - """ Process uncertainty""" + """ Process uncertainty matrix""" return self._Q @Q.setter def Q(self, value): + """ Process uncertainty matrix""" self._Q = setter_scalar(value, self.dim_x) @property def P(self): - """ covariance matrix""" + """ state covariance matrix""" return self._P @P.setter def P(self, value): + """ state covariance matrix""" self._P = setter_scalar(value, self.dim_x) @@ -739,6 +742,7 @@ def F(self): @F.setter def F(self, value): + """ state transition matrix""" self._F = setter(value, self.dim_x, self.dim_x) @property @@ -749,7 +753,6 @@ def B(self): @B.setter def B(self, value): - self._B = value """ control transition matrix""" if np.isscalar(value): self._B = value @@ -759,12 +762,13 @@ def B(self, value): @property def x(self): - """ filter state vector.""" + """ state vector.""" return self._x @x.setter def x(self, value): + """ state vector.""" self._x = setter_1d(value, self.dim_x) diff --git a/filterpy/kalman/sigma_points.py b/filterpy/kalman/sigma_points.py index 5ee176c..8ceeef6 100644 --- a/filterpy/kalman/sigma_points.py +++ b/filterpy/kalman/sigma_points.py @@ -21,9 +21,8 @@ class MerweScaledSigmaPoints(object): def __init__(self, n, alpha, beta, kappa, sqrt_method=None, subtract=None): - """ - Generates sigma points and weights according to Van der Merwe's - 2004 dissertation [1]. It parametizes the sigma points using + """ Generates sigma points and weights according to Van der Merwe's + 2004 dissertation[1]. It parametizes the sigma points using alpha, beta, kappa terms, and is the version seen in most publications. Unless you know better, this should be your default choice. @@ -73,6 +72,7 @@ def __init__(self, n, alpha, beta, kappa, sqrt_method=None, subtract=None): .. [1] R. Van der Merwe "Sigma-Point Kalman Filters for Probabilitic Inference in Dynamic State-Space Models" (Doctoral dissertation) + """ self.n = n @@ -177,8 +177,8 @@ def weights(self): class JulierSigmaPoints(object): def __init__(self,n, kappa, sqrt_method=None, subtract=None): - """v Generates sigma points and weights according to Simon J. Julier - and Jeffery K. Uhlmann's original paper [1]. It parametizes the sigma + """ Generates sigma points and weights according to Simon J. Julier + and Jeffery K. Uhlmann's original paper []. It parametizes the sigma points using kappa. Parameters @@ -214,12 +214,12 @@ def __init__(self,n, kappa, sqrt_method=None, subtract=None): You will have to supply this if your state variable cannot support subtraction, such as angles (359-1 degreees is 2, not 358). x and y - References - ---------- + References + ---------- - .. [1] Julier, Simon J.; Uhlmann, Jeffrey "A New Extension of the Kalman - Filter to Nonlinear Systems". Proc. SPIE 3068, Signal Processing, - Sensor Fusion, and Target Recognition VI, 182 (July 28, 1997) + .. [1] Julier, Simon J.; Uhlmann, Jeffrey "A New Extension of the Kalman + Filter to Nonlinear Systems". Proc. SPIE 3068, Signal Processing, + Sensor Fusion, and Target Recognition VI, 182 (July 28, 1997) """ self.n = n @@ -278,6 +278,7 @@ def sigma_points(self, x, P): \chi[1..n] = &x + [\sqrt{(n+\kappa)P}]_k \\ \chi[n+1..2n] = &x - [\sqrt{(n+\kappa)P}]_k \end{eqnarray} + """ assert self.n == np.size(x) @@ -309,7 +310,7 @@ def weights(self): formulatyion the weights for the mean and covariance are the same. Returns - -------s + ------- Wm : ndarray[2n+1] weights for mean @@ -317,6 +318,7 @@ def weights(self): Wc : ndarray[2n+1] weights for the covariances """ + n = self.n k = self.kappa @@ -328,9 +330,8 @@ def weights(self): class SimplexSigmaPoints(object): def __init__(self, n, alpha=1, sqrt_method=None, subtract=None): - """ - Generates sigma points and weights according to the simplex method - presented in [1] DOI: 10.1051/cocv/2010006 + """ Generates sigma points and weights according to the simplex + method presented in [1] DOI: 10.1051/cocv/2010006 Parameters ---------- diff --git a/filterpy/kalman/square_root.py b/filterpy/kalman/square_root.py index b42d891..656568c 100644 --- a/filterpy/kalman/square_root.py +++ b/filterpy/kalman/square_root.py @@ -15,7 +15,6 @@ for more information. """ - from __future__ import (absolute_import, division, print_function, unicode_literals) import numpy as np @@ -24,9 +23,6 @@ from filterpy.common import setter, setter_scalar, dot3 - - - class SquareRootKalmanFilter(object): """ @@ -237,6 +233,7 @@ def Q1_2(self): @Q.setter def Q(self, value): + """ Process uncertainty""" self._Q = setter_scalar(value, self.dim_x) self._Q1_2 = cholesky (self._Q, lower=True) @@ -254,6 +251,7 @@ def P1_2(self): @P.setter def P(self, value): + """ covariance matrix""" self._P = setter_scalar(value, self.dim_x) self._P1_2 = cholesky(self._P, lower=True) @@ -271,17 +269,19 @@ def R1_2(self): @R.setter def R(self, value): + """ measurement uncertainty""" self._R = setter_scalar(value, self.dim_z) self._R1_2 = cholesky (self._R, lower=True) @property def H(self): - """ Measurement function""" + """Measurement function""" return self._H @H.setter def H(self, value): + """Measurement function""" self._H = setter(value, self.dim_z, self.dim_x) @@ -293,6 +293,7 @@ def F(self): @F.setter def F(self, value): + """ state transition matrix""" self._F = setter(value, self.dim_x, self.dim_x) @property @@ -315,6 +316,7 @@ def x(self): @x.setter def x(self, value): + """ filter state vector.""" self._x = setter(value, self.dim_x, 1) @property