Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
96129 views
1
# -*- coding: utf-8 -*-
2
3
"""Copyright 2015 Roger R Labbe Jr.
4
5
FilterPy library.
6
http://github.com/rlabbe/filterpy
7
8
Documentation at:
9
https://filterpy.readthedocs.org
10
11
Supporting book at:
12
https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python
13
14
This is licensed under an MIT license. See the readme.MD file
15
for more information.
16
"""
17
18
19
from __future__ import (absolute_import, division, print_function,
20
unicode_literals)
21
import numpy as np
22
import scipy.linalg as linalg
23
from numpy import dot, zeros, eye, asarray
24
from filterpy.common import setter, setter_scalar, dot3
25
26
27
28
class FadingKalmanFilter(object):
29
30
def __init__(self, alpha, dim_x, dim_z, dim_u=0):
31
""" Create a Kalman filter. You are responsible for setting the
32
various state variables to reasonable values; the defaults below will
33
not give you a functional filter.
34
35
**Parameters**
36
37
alpha : float, >= 1
38
alpha controls how much you want the filter to forget past
39
measurements. alpha==1 yields identical performance to the
40
Kalman filter. A typical application might use 1.01
41
42
dim_x : int
43
Number of state variables for the Kalman filter. For example, if
44
you are tracking the position and velocity of an object in two
45
dimensions, dim_x would be 4.
46
47
This is used to set the default size of P, Q, and u
48
49
dim_z : int
50
Number of of measurement inputs. For example, if the sensor
51
provides you with position in (x,y), dim_z would be 2.
52
53
dim_u : int (optional)
54
size of the control input, if it is being used.
55
Default value of 0 indicates it is not used.
56
57
58
**Instance Variables**
59
60
You will have to assign reasonable values to all of these before
61
running the filter. All must have dtype of float
62
63
X : ndarray (dim_x, 1), default = [0,0,0...0]
64
state of the filter
65
66
P : ndarray (dim_x, dim_x), default identity matrix
67
covariance matrix
68
69
Q : ndarray (dim_x, dim_x), default identity matrix
70
Process uncertainty matrix
71
72
R : ndarray (dim_z, dim_z), default identity matrix
73
measurement uncertainty
74
75
H : ndarray (dim_z, dim_x)
76
measurement function
77
78
F : ndarray (dim_x, dim_x)
79
state transistion matrix
80
81
B : ndarray (dim_x, dim_u), default 0
82
control transition matrix
83
"""
84
85
assert alpha >= 1
86
assert dim_x > 0
87
assert dim_z > 0
88
assert dim_u >= 0
89
90
91
self.alpha_sq = alpha**2
92
self.dim_x = dim_x
93
self.dim_z = dim_z
94
self.dim_u = dim_u
95
96
self._X = zeros((dim_x,1)) # state
97
self._P = eye(dim_x) # uncertainty covariance
98
self._Q = eye(dim_x) # process uncertainty
99
self._B = 0 # control transition matrix
100
self._F = 0 # state transition matrix
101
self._H = 0 # Measurement function
102
self._R = eye(dim_z) # state uncertainty
103
104
# gain and residual are computed during the innovation step. We
105
# save them so that in case you want to inspect them for various
106
# purposes
107
self._K = 0 # kalman gain
108
self._y = zeros((dim_z, 1))
109
self._S = 0 # system uncertainty in measurement space
110
111
# identity matrix. Do not alter this.
112
self._I = np.eye(dim_x)
113
114
115
def update(self, z, R=None):
116
"""
117
Add a new measurement (z) to the kalman filter. If z is None, nothing
118
is changed.
119
120
**Parameters**
121
122
z : np.array
123
measurement for this update.
124
125
R : np.array, scalar, or None
126
Optionally provide R to override the measurement noise for this
127
one call, otherwise self.R will be used.
128
"""
129
130
if z is None:
131
return
132
133
if R is None:
134
R = self._R
135
elif np.isscalar(R):
136
R = eye(self.dim_z) * R
137
138
# rename for readability and a tiny extra bit of speed
139
H = self._H
140
P = self._P
141
x = self._X
142
143
# y = z - Hx
144
# error (residual) between measurement and prediction
145
self._y = z - dot(H, x)
146
147
# S = HPH' + R
148
# project system uncertainty into measurement space
149
S = dot3(H, P, H.T) + R
150
151
# K = PH'inv(S)
152
# map system uncertainty into kalman gain
153
K = dot3(P, H.T, linalg.inv(S))
154
155
# x = x + Ky
156
# predict new x with residual scaled by the kalman gain
157
self._X = x + dot(K, self._y)
158
159
# P = (I-KH)P(I-KH)' + KRK'
160
I_KH = self._I - dot(K, H)
161
self._P = dot3(I_KH, P, I_KH.T) + dot3(K, R, K.T)
162
163
self._S = S
164
self._K = K
165
166
167
def predict(self, u=0):
168
""" Predict next position.
169
170
**Parameters**
171
172
u : np.array
173
Optional control vector. If non-zero, it is multiplied by B
174
to create the control input into the system.
175
"""
176
177
# x = Fx + Bu
178
self._X = dot(self._F, self._X) + dot(self._B, u)
179
180
# P = FPF' + Q
181
self._P = self.alpha_sq*dot3(self._F, self._P, self._F.T) + self._Q
182
183
184
def batch_filter(self, zs, Rs=None, update_first=False):
185
""" Batch processes a sequences of measurements.
186
187
**Parameters**
188
189
zs : list-like
190
list of measurements at each time step `self.dt` Missing
191
measurements must be represented by 'None'.
192
193
Rs : list-like, optional
194
optional list of values to use for the measurement error
195
covariance; a value of None in any position will cause the filter
196
to use `self.R` for that time step.
197
198
update_first : bool, optional,
199
controls whether the order of operations is update followed by
200
predict, or predict followed by update. Default is predict->update.
201
202
**Returns**
203
204
means: np.array((n,dim_x,1))
205
array of the state for each time step after the update. Each entry
206
is an np.array. In other words `means[k,:]` is the state at step
207
`k`.
208
209
covariance: np.array((n,dim_x,dim_x))
210
array of the covariances for each time step after the update.
211
In other words `covariance[k,:,:]` is the covariance at step `k`.
212
213
means_predictions: np.array((n,dim_x,1))
214
array of the state for each time step after the predictions. Each
215
entry is an np.array. In other words `means[k,:]` is the state at
216
step `k`.
217
218
covariance_predictions: np.array((n,dim_x,dim_x))
219
array of the covariances for each time step after the prediction.
220
In other words `covariance[k,:,:]` is the covariance at step `k`.
221
"""
222
223
n = np.size(zs,0)
224
if Rs is None:
225
Rs = [None]*n
226
227
# mean estimates from Kalman Filter
228
means = zeros((n,self.dim_x,1))
229
means_p = zeros((n,self.dim_x,1))
230
231
# state covariances from Kalman Filter
232
covariances = zeros((n,self.dim_x,self.dim_x))
233
covariances_p = zeros((n,self.dim_x,self.dim_x))
234
235
if update_first:
236
for i,(z,r) in enumerate(zip(zs,Rs)):
237
self.update(z,r)
238
means[i,:] = self._X
239
covariances[i,:,:] = self._P
240
241
self.predict()
242
means_p[i,:] = self._X
243
covariances_p[i,:,:] = self._P
244
else:
245
for i,(z,r) in enumerate(zip(zs,Rs)):
246
self.predict()
247
means_p[i,:] = self._X
248
covariances_p[i,:,:] = self._P
249
250
self.update(z,r)
251
means[i,:] = self._X
252
covariances[i,:,:] = self._P
253
254
return (means, covariances, means_p, covariances_p)
255
256
257
def get_prediction(self, u=0):
258
""" Predicts the next state of the filter and returns it. Does not
259
alter the state of the filter.
260
261
**Parameters**
262
263
u : np.array
264
optional control input
265
266
**Returns**
267
268
(x, P)
269
State vector and covariance array of the prediction.
270
"""
271
272
x = dot(self._F, self._X) + dot(self._B, u)
273
P = self.alpha_sq*dot3(self._F, self._P, self._F.T) + self.Q
274
return (x, P)
275
276
277
def residual_of(self, z):
278
""" returns the residual for the given measurement (z). Does not alter
279
the state of the filter.
280
"""
281
return z - dot(self._H, self._X)
282
283
284
def measurement_of_state(self, x):
285
""" Helper function that converts a state into a measurement.
286
287
**Parameters**
288
289
x : np.array
290
kalman state vector
291
292
**Returns**
293
294
z : np.array
295
measurement corresponding to the given state
296
"""
297
return dot(self._H, x)
298
299
300
@property
301
def Q(self):
302
""" Process uncertainty"""
303
return self._Q
304
305
306
@Q.setter
307
def Q(self, value):
308
self._Q = setter_scalar(value, self.dim_x)
309
310
@property
311
def P(self):
312
""" covariance matrix"""
313
return self._P
314
315
316
@P.setter
317
def P(self, value):
318
self._P = setter_scalar(value, self.dim_x)
319
320
321
@property
322
def R(self):
323
""" measurement uncertainty"""
324
return self._R
325
326
327
@R.setter
328
def R(self, value):
329
self._R = setter_scalar(value, self.dim_z)
330
331
@property
332
def H(self):
333
""" Measurement function"""
334
return self._H
335
336
337
@H.setter
338
def H(self, value):
339
self._H = setter(value, self.dim_z, self.dim_x)
340
341
342
@property
343
def F(self):
344
""" state transition matrix"""
345
return self._F
346
347
348
@F.setter
349
def F(self, value):
350
self._F = setter(value, self.dim_x, self.dim_x)
351
352
@property
353
def B(self):
354
""" control transition matrix"""
355
return self._B
356
357
358
@B.setter
359
def B(self, value):
360
""" control transition matrix"""
361
self._B = setter (value, self.dim_x, self.dim_u)
362
363
364
@property
365
def X(self):
366
""" filter state vector."""
367
return self._X
368
369
370
@X.setter
371
def X(self, value):
372
self._X = setter(value, self.dim_x, 1)
373
374
375
@property
376
def x(self):
377
assert False
378
379
@x.setter
380
def x(self, value):
381
assert False
382
383
@property
384
def K(self):
385
""" Kalman gain """
386
return self._K
387
388
@property
389
def y(self):
390
""" measurement residual (innovation) """
391
return self._y
392
393
@property
394
def S(self):
395
""" system uncertainy in measurement space """
396
return self._S
397
398