Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
96131 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
from __future__ import (absolute_import, division, print_function,
18
unicode_literals)
19
import numpy as np
20
import scipy.linalg as linalg
21
from numpy import dot, zeros, eye
22
from filterpy.common import setter, setter_scalar, dot3
23
24
25
class HInfinityFilter(object):
26
27
def __init__(self, dim_x, dim_z, dim_u, gamma):
28
""" Create an H-Infinity filter. You are responsible for setting the
29
various state variables to reasonable values; the defaults below will
30
not give you a functional filter.
31
32
**Parameters**
33
34
dim_x : int
35
Number of state variables for the Kalman filter. For example, if
36
you are tracking the position and velocity of an object in two
37
dimensions, dim_x would be 4.
38
39
This is used to set the default size of P, Q, and u
40
41
dim_z : int
42
Number of of measurement inputs. For example, if the sensor
43
provides you with position in (x,y), dim_z would be 2.
44
45
dim_u : int
46
Number of control inputs for the Gu part of the prediction step.
47
"""
48
49
self.dim_x = dim_x
50
self.dim_z = dim_z
51
self.dim_u = dim_u
52
self.gamma = gamma
53
54
self.x = zeros((dim_x,1)) # state
55
56
self._G = 0 # control transistion matrx
57
self._F = 0 # state transition matrix
58
self._H = 0 # Measurement function
59
60
self._P = eye(dim_x) # uncertainty covariance
61
self._V_inv = zeros((dim_z, dim_z))
62
self._W = zeros((dim_x, dim_x))
63
self._Q = eye(dim_x) # process uncertainty
64
65
# gain and residual are computed during the innovation step. We
66
# save them so that in case you want to inspect them for various
67
# purposes
68
self.K = 0 # kalman gain
69
self.residual = zeros((dim_z, 1))
70
71
# identity matrix. Do not alter this.
72
self._I = np.eye(dim_x)
73
74
75
def update(self, Z):
76
"""
77
Add a new measurement (Z) to the kalman filter. If Z is None, nothing
78
is changed.
79
80
**Parameters**
81
82
Z : np.array
83
measurement for this update.
84
"""
85
86
if Z is None:
87
return
88
89
# rename for readability and a tiny extra bit of speed
90
I = self._I
91
gamma = self.gamma
92
Q = self._Q
93
H = self._H
94
P = self._P
95
x = self._x
96
V_inv = self._V_inv
97
F = self._F
98
W = self._W
99
100
# common subexpression H.T * V^-1
101
HTVI = dot(H.T, V_inv)
102
103
L = linalg.inv(I - gamma*dot(Q, P) + dot3(HTVI, H, P))
104
105
#common subexpression P*L
106
PL = dot(P,L)
107
108
K = dot3(F, PL, HTVI)
109
110
self.residual = Z - dot(H, x)
111
112
# x = x + Ky
113
# predict new x with residual scaled by the kalman gain
114
self._x = self._x + dot(K, self.residual)
115
self._P = dot3(F, PL, F.T) + W
116
117
# force P to be symmetric
118
self._P = (self._P + self._P.T) / 2
119
120
121
'''def update_safe(self, Z):
122
""" same as update(), except we perform a check to ensure that the
123
eigenvalues are < 1. An exception is thrown if not. """
124
125
update(Z)
126
evalue = linalg.eig(self.P)'
127
'''
128
129
130
def predict(self, u=0):
131
""" Predict next position.
132
133
**Parameters**
134
135
u : np.array
136
Optional control vector. If non-zero, it is multiplied by G
137
to create the control input into the system.
138
"""
139
140
# x = Fx + Gu
141
self._x = dot(self._F, self._x) + dot(self._G, u)
142
143
144
def batch_filter(self, Zs, Rs=None, update_first=False):
145
""" Batch processes a sequences of measurements.
146
147
**Parameters**
148
149
Zs : list-like
150
list of measurements at each time step `self.dt` Missing
151
measurements must be represented by 'None'.
152
153
Rs : list-like, optional
154
optional list of values to use for the measurement error
155
covariance; a value of None in any position will cause the filter
156
to use `self.R` for that time step.
157
158
update_first : bool, optional,
159
controls whether the order of operations is update followed by
160
predict, or predict followed by update. Default is predict->update.
161
162
**Returns**
163
164
means: np.array((n,dim_x,1))
165
array of the state for each time step. Each entry is an np.array.
166
In other words `means[k,:]` is the state at step `k`.
167
168
covariance: np.array((n,dim_x,dim_x))
169
array of the covariances for each time step. In other words
170
`covariance[k,:,:]` is the covariance at step `k`.
171
"""
172
173
n = np.size(Zs,0)
174
if Rs is None:
175
Rs = [None]*n
176
177
# mean estimates from Kalman Filter
178
means = zeros((n,self.dim_x,1))
179
180
# state covariances from Kalman Filter
181
covariances = zeros((n,self.dim_x,self.dim_x))
182
183
if update_first:
184
for i,(z,r) in enumerate(zip(Zs,Rs)):
185
self.update(z,r)
186
means[i,:] = self.x
187
covariances[i,:,:] = self.P
188
self.predict()
189
else:
190
for i,(z,r) in enumerate(zip(Zs,Rs)):
191
self.predict()
192
self.update(z,r)
193
194
means[i,:] = self.x
195
covariances[i,:,:] = self.P
196
197
return (means, covariances)
198
199
200
def get_prediction(self, u=0):
201
""" Predicts the next state of the filter and returns it. Does not
202
alter the state of the filter.
203
204
**Parameters**
205
206
u : np.array
207
optional control input
208
209
**Returns**
210
211
x : numpy.ndarray
212
State vecto of the prediction.
213
"""
214
215
x = dot(self._F, self._x) + dot(self._G, u)
216
return x
217
218
219
def residual_of(self, z):
220
""" returns the residual for the given measurement (z). Does not alter
221
the state of the filter.
222
"""
223
return z - dot(self._H, self._x)
224
225
226
def measurement_of_state(self, x):
227
""" Helper function that converts a state into a measurement.
228
229
**Parameters**
230
231
x : np.array
232
kalman state vector
233
234
**Returns**
235
236
z : np.array
237
measurement corresponding to the given state
238
"""
239
return dot(self._H, x)
240
241
242
@property
243
def x(self):
244
""" state vector property"""
245
return self._x
246
247
248
@x.setter
249
def x(self, value):
250
self._x = setter(value, self.dim_x, 1)
251
252
253
@property
254
def G(self):
255
return self._G
256
257
258
@G.setter
259
def G(self, value):
260
self._G = setter(self.dim_x, 1)
261
262
263
@property
264
def P(self):
265
""" covariance matrix property"""
266
return self._P
267
268
269
@P.setter
270
def P(self, value):
271
self._P = setter_scalar(value, self.dim_x)
272
273
274
@property
275
def F(self):
276
return self._F
277
278
279
@F.setter
280
def F(self, value):
281
self._F = setter(value, self.dim_x, self.dim_x)
282
283
284
@property
285
def G(self):
286
return self._G
287
288
289
@G.setter
290
def G(self, value):
291
self._G = setter(value, self.dim_x, self.dim_u)
292
293
294
@property
295
def H(self):
296
return self._H
297
298
299
@H.setter
300
def H(self, value):
301
self._H = setter(value, self.dim_z, self.dim_x)
302
303
304
@property
305
def V(self):
306
return self._V
307
308
309
@V.setter
310
def V(self, value):
311
self._V = setter_scalar(value, self.dim_z)
312
self._V_inv = linalg.inv(self.V)
313
314
315
@property
316
def W(self):
317
return self._W
318
319
320
@W.setter
321
def W(self, value):
322
self._W = setter_scalar(value, self.dim_x)
323
324
325
@property
326
def Q(self):
327
return self._Q
328
329
@Q.setter
330
def Q(self, value):
331
self._Q = setter_scalar(value, self.dim_x)
332
333