Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
96129 views
1
# -*- coding: utf-8 -*-
2
"""Copyright 2015 Roger R Labbe Jr.
3
4
FilterPy library.
5
http://github.com/rlabbe/filterpy
6
7
Documentation at:
8
https://filterpy.readthedocs.org
9
10
Supporting book at:
11
https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python
12
13
This is licensed under an MIT license. See the readme.MD file
14
for more information.
15
"""
16
17
import numpy as np
18
from scipy.linalg import cholesky
19
20
21
class MerweScaledSigmaPoints(object):
22
23
def __init__(self, n, alpha, beta, kappa, sqrt_method=None, subtract=None):
24
"""
25
Generates sigma points and weights according to Van der Merwe's
26
2004 dissertation [1]. It parametizes the sigma points using
27
alpha, beta, kappa terms, and is the version seen in most publications.
28
29
Unless you know better, this should be your default choice.
30
31
**Parameters**
32
33
n : int
34
Dimensionality of the state. 2n+1 weights will be generated.
35
36
alpha : float
37
Determins the spread of the sigma points around the mean.
38
Usually a small positive value (1e-3) according to [3].
39
40
beta : float
41
Incorporates prior knowledge of the distribution of the mean. For
42
Gaussian x beta=2 is optimal, according to [3].
43
44
kappa : float, default=0.0
45
Secondary scaling parameter usually set to 0 according to [4],
46
or to 3-n according to [5].
47
48
sqrt_method : function(ndarray), default=scipy.linalg.cholesky
49
Defines how we compute the square root of a matrix, which has
50
no unique answer. Cholesky is the default choice due to its
51
speed. Typically your alternative choice will be
52
scipy.linalg.sqrtm. Different choices affect how the sigma points
53
are arranged relative to the eigenvectors of the covariance matrix.
54
Usually this will not matter to you; if so the default cholesky()
55
yields maximal performance. As of van der Merwe's dissertation of
56
2004 [6] this was not a well reseached area so I have no advice
57
to give you.
58
59
If your method returns a triangular matrix it must be upper
60
triangular. Do not use numpy.linalg.cholesky - for historical
61
reasons it returns a lower triangular matrix. The SciPy version
62
does the right thing.
63
64
subtract : callable (x, y), optional
65
Function that computes the difference between x and y.
66
You will have to supply this if your state variable cannot support
67
subtraction, such as angles (359-1 degreees is 2, not 358). x and y
68
are state vectors, not scalars.
69
70
**References**
71
72
.. [1] R. Van der Merwe "Sigma-Point Kalman Filters for Probabilitic
73
Inference in Dynamic State-Space Models" (Doctoral dissertation)
74
"""
75
76
self.n = n
77
self.alpha = alpha
78
self.beta = beta
79
self.kappa = kappa
80
if sqrt_method is None:
81
self.sqrt = cholesky
82
else:
83
self.sqrt = sqrt_method
84
85
if subtract is None:
86
self.subtract= np.subtract
87
else:
88
self.subtract = subtract
89
90
91
def sigma_points(self, x, P):
92
""" Computes the sigma points for an unscented Kalman filter
93
given the mean (x) and covariance(P) of the filter.
94
Returns tuple of the sigma points and weights.
95
96
Works with both scalar and array inputs:
97
sigma_points (5, 9, 2) # mean 5, covariance 9
98
sigma_points ([5, 2], 9*eye(2), 2) # means 5 and 2, covariance 9I
99
100
**Parameters**
101
102
X An array-like object of the means of length n
103
Can be a scalar if 1D.
104
examples: 1, [1,2], np.array([1,2])
105
106
P : scalar, or np.array
107
Covariance of the filter. If scalar, is treated as eye(n)*P.
108
109
**Returns**
110
111
sigmas : np.array, of size (n, 2n+1)
112
Two dimensional array of sigma points. Each column contains all of
113
the sigmas for one dimension in the problem space.
114
115
Ordered by Xi_0, Xi_{1..n}, Xi_{n+1..2n}
116
"""
117
118
assert self.n == np.size(x), "expected size {}, but size is {}".format(
119
self.n, np.size(x))
120
121
n = self.n
122
123
if np.isscalar(x):
124
x = np.asarray([x])
125
126
if np.isscalar(P):
127
P = np.eye(n)*P
128
129
lambda_ = self.alpha**2 * (n + self.kappa) - n
130
U = self.sqrt((lambda_ + n)*P)
131
132
sigmas = np.zeros((2*n+1, n))
133
sigmas[0] = x
134
for k in range(n):
135
sigmas[k+1] = self.subtract(x, -U[k])
136
sigmas[n+k+1] = self.subtract(x, U[k])
137
138
return sigmas
139
140
141
def weights(self):
142
""" Computes the weights for the scaled unscented Kalman filter.
143
144
**Returns**
145
146
Wm : ndarray[2n+1]
147
weights for mean
148
149
Wc : ndarray[2n+1]
150
weights for the covariances
151
"""
152
153
n = self.n
154
lambda_ = self.alpha**2 * (n +self.kappa) - n
155
156
c = .5 / (n + lambda_)
157
Wc = np.full(2*n + 1, c)
158
Wm = np.full(2*n + 1, c)
159
Wc[0] = lambda_ / (n + lambda_) + (1 - self.alpha**2 + self.beta)
160
Wm[0] = lambda_ / (n + lambda_)
161
162
return Wm, Wc
163
164
165
class JulierSigmaPoints(object):
166
167
def __init__(self,n, kappa, sqrt_method=None, subtract=None):
168
"""
169
Generates sigma points and weights according to Simon J. Julier
170
and Jeffery K. Uhlmann's original paper [1]. It parametizes the sigma
171
points using kappa.
172
173
**Parameters**
174
175
176
n : int
177
Dimensionality of the state. 2n+1 weights will be generated.
178
179
kappa : float, default=0.
180
Scaling factor that can reduce high order errors. kappa=0 gives
181
the standard unscented filter. According to [Julier], if you set
182
kappa to 3-dim_x for a Gaussian x you will minimize the fourth
183
order errors in x and P.
184
185
sqrt_method : function(ndarray), default=scipy.linalg.cholesky
186
Defines how we compute the square root of a matrix, which has
187
no unique answer. Cholesky is the default choice due to its
188
speed. Typically your alternative choice will be
189
scipy.linalg.sqrtm. Different choices affect how the sigma points
190
are arranged relative to the eigenvectors of the covariance matrix.
191
Usually this will not matter to you; if so the default cholesky()
192
yields maximal performance. As of van der Merwe's dissertation of
193
2004 [6] this was not a well reseached area so I have no advice
194
to give you.
195
196
If your method returns a triangular matrix it must be upper
197
triangular. Do not use numpy.linalg.cholesky - for historical
198
reasons it returns a lower triangular matrix. The SciPy version
199
does the right thing.
200
201
202
subtract : callable (x, y), optional
203
Function that computes the difference between x and y.
204
You will have to supply this if your state variable cannot support
205
subtraction, such as angles (359-1 degreees is 2, not 358). x and y
206
207
**References**
208
209
.. [1] Julier, Simon J.; Uhlmann, Jeffrey "A New Extension of the Kalman
210
Filter to Nonlinear Systems". Proc. SPIE 3068, Signal Processing,
211
Sensor Fusion, and Target Recognition VI, 182 (July 28, 1997)
212
"""
213
214
self.n = n
215
self.kappa = kappa
216
if sqrt_method is None:
217
self.sqrt = cholesky
218
else:
219
self.sqrt = sqrt_method
220
221
if subtract is None:
222
self.subtract= np.subtract
223
else:
224
self.subtract = subtract
225
226
227
def sigma_points(self, x, P):
228
r""" Computes the sigma points for an unscented Kalman filter
229
given the mean (x) and covariance(P) of the filter.
230
kappa is an arbitrary constant. Returns sigma points.
231
232
Works with both scalar and array inputs:
233
sigma_points (5, 9, 2) # mean 5, covariance 9
234
sigma_points ([5, 2], 9*eye(2), 2) # means 5 and 2, covariance 9I
235
236
**Parameters**
237
238
X : array-like object of the means of length n
239
Can be a scalar if 1D.
240
examples: 1, [1,2], np.array([1,2])
241
242
P : scalar, or np.array
243
Covariance of the filter. If scalar, is treated as eye(n)*P.
244
245
kappa : float
246
Scaling factor.
247
248
**Returns**
249
250
sigmas : np.array, of size (n, 2n+1)
251
2D array of sigma points :math:`\chi`. Each column contains all of
252
the sigmas for one dimension in the problem space. They
253
are ordered as:
254
255
.. math::
256
:nowrap:
257
258
\begin{eqnarray}
259
\chi[0] = &x \\
260
\chi[1..n] = &x + [\sqrt{(n+\kappa)P}]_k \\
261
\chi[n+1..2n] = &x - [\sqrt{(n+\kappa)P}]_k
262
\end{eqnarray}
263
"""
264
265
assert self.n == np.size(x)
266
n = self.n
267
268
if np.isscalar(x):
269
x = np.asarray([x])
270
271
n = np.size(x) # dimension of problem
272
273
if np.isscalar(P):
274
P = np.eye(n)*P
275
276
sigmas = np.zeros((2*n+1, n))
277
278
# implements U'*U = (n+kappa)*P. Returns lower triangular matrix.
279
# Take transpose so we can access with U[i]
280
U = self.sqrt((n + self.kappa) * P)
281
282
sigmas[0] = x
283
for k in range(n):
284
sigmas[k+1] = self.subtract(x, -U[k])
285
sigmas[n+k+1] = self.subtract(x, U[k])
286
return sigmas
287
288
289
def weights(self):
290
""" Computes the weights for the unscented Kalman filter. In this
291
formulatyion the weights for the mean and covariance are the same.
292
293
**Returns**
294
295
Wm : ndarray[2n+1]
296
weights for mean
297
298
Wc : ndarray[2n+1]
299
weights for the covariances
300
"""
301
n = self.n
302
k = self.kappa
303
304
W = np.full(2*n+1, .5 / (n + k))
305
W[0] = k / (n+k)
306
return W, W
307
308