Skip to content

Commit d82c40c

Browse files
committed
final version of kf with reference solution
1 parent 016dc8b commit d82c40c

3 files changed

Lines changed: 128 additions & 107 deletions

File tree

‎KalmanFilter/kf.py‎

Lines changed: 128 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from numpy.linalg import inv as mat_inv # matrix inverse
88
import matplotlib.pyplot as plt
99
from scipy.io import loadmat
10+
import pdb
1011

1112
class Plotter():
1213
def __init__(self, times):
@@ -35,6 +36,28 @@ def __init__(self, times):
3536
self.k_v = []
3637
self.k_x = []
3738

39+
# plotting the covariance in position after each measurement and prediction
40+
self.x_cov_times = []
41+
self.x_cov_vals = []
42+
43+
def save_iteration_data(self, model, timestep, k_gains=None):
44+
"""
45+
@type model: Model
46+
"""
47+
self.v_sigma_pos.append(np.sqrt(model.sigma[0 , 0]) * 2)
48+
self.v_sigma_neg.append(np.sqrt(model.sigma[0 , 0]) * 2 * -1)
49+
self.v_error.append(model.vtr[0 , timestep] - model.mu[0 , 0])
50+
self.x_sigma_pos.append(np.sqrt(model.sigma[1 , 1]) * 2)
51+
self.x_sigma_neg.append(np.sqrt(model.sigma[1 , 1]) * 2 * -1)
52+
self.x_error.append(model.xtr[0 , timestep] - model.mu[1 , 0])
53+
self.v_true.append(model.vtr[0 , timestep])
54+
self.x_true.append(model.xtr[0 , timestep])
55+
self.v_pred.append(model.mu[0 , 0])
56+
self.x_pred.append(model.mu[1 , 0])
57+
if k_gains is not None:
58+
self.k_v.append(k_gains[0 , 0])
59+
self.k_x.append(k_gains[1 , 0])
60+
3861
def plot(self):
3962
# increase vertical spacing between subplots
4063
# https://matplotlib.org/3.1.1/api/_as_gen/matplotlib.pyplot.subplots_adjust.html
@@ -45,54 +68,51 @@ def plot(self):
4568
# error covariance plots
4669
p1 = plt.figure(1)
4770
plt.subplot(211)
48-
plt.plot(times, self.v_sigma_neg, 'r', label="Error Covariance")
49-
plt.plot(times, self.v_sigma_pos, 'r')
50-
plt.plot(times, self.v_error, 'b', alpha=.5, label="Velocity Estimation Error")
71+
plt.plot(self.times, self.v_sigma_neg, 'r', label="Error Covariance")
72+
plt.plot(self.times, self.v_sigma_pos, 'r')
73+
plt.plot(self.times, self.v_error, 'b', label="Velocity Estimation Error")
5174
plt.title("Estimation error and error covariance vs time")
5275
plt.ylabel("Error (velocity)")
5376
plt.legend()
5477
plt.subplot(212)
55-
plt.plot(times, self.x_sigma_neg, 'r', label="Error Covariance")
56-
plt.plot(times, self.x_sigma_pos, 'r')
57-
plt.plot(times, self.x_error, 'b', alpha=.5, label="Position Estimation Error")
78+
plt.plot(self.times, self.x_sigma_neg, 'r', label="Error Covariance")
79+
plt.plot(self.times, self.x_sigma_pos, 'r')
80+
plt.plot(self.times, self.x_error, 'b', label="Position Estimation Error")
5881
plt.ylabel("Error (position)")
5982
plt.xlabel(x_label_str)
6083
plt.legend()
6184
p1.show()
6285

6386
# state estimates vs ground truth plots
64-
true_opacity = .6
65-
pred_opacity = .75
6687
p2 = plt.figure(2)
67-
plt.subplot(211)
68-
plt.plot(times, self.v_true, alpha=true_opacity, label="True Velocity")
69-
plt.plot(times, self.v_pred, alpha=pred_opacity, label="Predicted Velocity")
88+
plt.plot(self.times, self.v_true, label="True Vel")
89+
plt.plot(self.times, self.v_pred, label="Predicted Vel")
90+
plt.plot(self.times, self.x_true, label="True Pos")
91+
plt.plot(self.times, self.x_pred, label="Predicted Pos")
7092
plt.title("State estimates and true states vs time")
71-
plt.ylabel("Velocity")
72-
plt.legend()
73-
plt.subplot(212)
74-
plt.plot(times, self.x_true, alpha=true_opacity, label="True Position")
75-
plt.plot(times, self.x_pred, alpha=pred_opacity, label="Predicted Prosition")
76-
plt.ylabel("Postion")
7793
plt.xlabel(x_label_str)
94+
plt.ylabel("Position (m) and Velocity (m / s^2)")
7895
plt.legend()
7996
p2.show()
8097

8198
# kalman gain plots
82-
gain = "Gain"
99+
# no kalman gain at the first timestep since filtering starts at second timestep
83100
p3 = plt.figure(3)
84-
plt.subplot(211)
85-
plt.plot(times, self.k_v, label="Velocity")
101+
plt.plot(self.times[1:], self.k_v, label="Velocity")
102+
plt.plot(self.times[1:], self.k_x, label="Position")
86103
plt.title("Kalman gain vs time")
87-
plt.ylabel(gain)
88-
plt.legend()
89-
plt.subplot(212)
90-
plt.plot(times, self.k_x, label="Position")
91-
plt.ylabel(gain)
104+
plt.ylabel("Gain")
92105
plt.xlabel(x_label_str)
93106
plt.legend()
94107
p3.show()
95108

109+
p4 = plt.figure(4)
110+
plt.plot(self.x_cov_times, self.x_cov_vals)
111+
plt.title("Position covariance between prediction and measurement update")
112+
plt.ylabel("Covariance")
113+
plt.xlabel(x_label_str)
114+
p4.show()
115+
96116
# keep the plots open until user enters Ctrl+D to terminal (EOF)
97117
try:
98118
input()
@@ -117,6 +137,8 @@ def __init__(self, times, A, B, C, D, R, Q,
117137
"""
118138
np.random.seed(seed) # for debugging (removes randomness from noise)
119139

140+
self.times = times
141+
120142
# state space
121143
self.A = A
122144
self.B = B
@@ -155,18 +177,23 @@ def load_matlab_data(self):
155177
# try on the matlab data (for comparison)
156178
self.mu = mu0
157179
self.sigma = Sig0
180+
self.Q = Q
181+
self.R = R
158182
self.control_inputs = u
159183
self.vtr = vtr
160184
self.xtr = xtr
161185
self.measurements = z
162186

163187
def get_data(self):
188+
# initial condition is v=0 and x=0
189+
curr_state = np.array([[0] , [0]])
190+
# input and measurement at start at timestep 1 since KF starts at timestep 1
164191
vtr = np.zeros((1,self.control_inputs.size))
165192
xtr = np.zeros((1,self.control_inputs.size))
166193
measurements = np.zeros((1,self.control_inputs.size))
167194

168-
curr_state = np.array(self.mu)
169-
for i in range(self.control_inputs.size):
195+
# updates begin at timestep 1 since timestep 0 is the initial state
196+
for i in range(1, self.control_inputs.size):
170197
c_input = self.control_inputs[0 , i]
171198
c_input = np.reshape(c_input, (1,1))
172199
# get the next state from the control input
@@ -197,17 +224,22 @@ def make_measurement_noise(self):
197224

198225
def kalman_filter(self):
199226
# for plotting
200-
self.plotter = Plotter(times)
227+
self.plotter = Plotter(self.times)
228+
229+
self.plotter.save_iteration_data(self, 0, None)
230+
self.plotter.x_cov_times.append(0)
231+
self.plotter.x_cov_vals.append(self.sigma[1 , 1])
232+
for timestep in range(1,self.control_inputs.size):
201233

202-
for timestep in range(self.control_inputs.size):
203234
c_input = self.control_inputs[0 , timestep]
204235
c_input = np.reshape(c_input, (1,1))
205236
z = self.measurements[0 , timestep]
206237

207238
# prediction
208239
mu_bar = mm(self.A, self.mu) + mm(self.B, c_input)
209240
sigma_bar = mm(self.A, mm(self.sigma, np.transpose(self.A))) + self.R
210-
241+
self.plotter.x_cov_times.append(self.times[timestep])
242+
self.plotter.x_cov_vals.append(sigma_bar[1 , 1])
211243
# correction
212244
c_transpose = np.transpose(self.C)
213245
matrix_one = mm(sigma_bar, c_transpose)
@@ -220,85 +252,74 @@ def kalman_filter(self):
220252
self.mu = mu
221253
self.sigma = sigma
222254

223-
# save info for plotting
224-
# error covariance and estimation error plots
225-
self.plotter.v_sigma_pos.append(np.sqrt(sigma[0 , 0]) * 2)
226-
self.plotter.v_sigma_neg.append(np.sqrt(sigma[0 , 0]) * 2 * -1)
227-
self.plotter.v_error.append(self.vtr[0 , timestep] - mu[0 , 0])
228-
self.plotter.x_sigma_pos.append(np.sqrt(sigma[1 , 1]) * 2)
229-
self.plotter.x_sigma_neg.append(np.sqrt(sigma[1 , 1]) * 2 * -1)
230-
self.plotter.x_error.append(self.xtr[0 , timestep] - mu[1 , 0])
231-
# state estimation plots
232-
self.plotter.v_pred.append(mu[0 , 0])
233-
self.plotter.v_true.append(self.vtr[0 , timestep])
234-
self.plotter.x_pred.append(mu[1 , 0])
235-
self.plotter.x_true.append(self.xtr[0 , timestep])
236-
# kalman gain plots
237-
self.plotter.k_v.append(k[0 , 0])
238-
self.plotter.k_x.append(k[1 , 0])
255+
self.plotter.save_iteration_data(self, timestep, k)
256+
self.plotter.x_cov_times.append(self.times[timestep])
257+
self.plotter.x_cov_vals.append(sigma[1 , 1])
239258

240259
def plot_results(self):
241260
self.plotter.plot()
242261

243262

244-
########################################################################################
245-
############################## DEFINE PARAMETERS HERE ##################################
246-
########################################################################################
247-
# use the data from the matlab file, or make our own?
248-
use_file_data = False
249-
# eliminate randomness in noise generation? (For debugging)
250-
rand_seed = None
251-
# measurement covariance (default = .001)
252-
z_noise = .001
253-
# process noise associated with velocity state (default = .01)
254-
v_noise = .01
255-
# process noise associated with position state (default = .0001)
256-
x_noise = .0001
257-
# timestep (seconds)
258-
dt = .05
259-
# initial belief (initial condition):
260-
# starting v and x, [[v] , [x]]
261-
mu = np.array([[0] , [0]])
262-
# starting covariance, [[v , 0] , [0 , x]]
263-
sigma = np.array([[1 , 0] , [0 , 1]])
264-
########################################################################################
265-
########################################################################################
266-
267-
268-
# robot model parameters
269-
b = 20 # drag coefficient
270-
m = 100 # mass
271-
272-
# define continuous state space model
273-
A = np.array([[(-b/m) , 0] , [1 , 0]])
274-
B = np.array([[(1/m)] , [0]])
275-
C = np.array([0, 1])
276-
D = np.array([0])
277-
dt = .05
278-
279-
# get a discrete state space model
280-
sys = control.ss(A, B, C, D)
281-
sys_d = control.c2d(sys, dt)
282-
sys_d.A = np.array(sys_d.A)
283-
sys_d.B = np.array(sys_d.B)
284-
sys_d.C = np.array(sys_d.C)
285-
sys_d.D = np.array(sys_d.D)
286-
287-
# get the system/measurement noise
288-
R = np.array([[v_noise , 0] , [0 , x_noise]])
289-
Q = np.array([[z_noise]])
290-
291-
# set the ground truth control inputs
292-
times = np.arange(0, 50+dt, dt)
293-
control_inputs = np.zeros((1, times.size))
294-
control_inputs[0 , 0:100] = 50
295-
control_inputs[0 , 100:500] = 0
296-
control_inputs[0 , 500:600] = -50
297-
control_inputs[0 , 600:] = 0
298-
299-
# make the robot model
300-
uuv = Model(times, sys_d.A, sys_d.B, sys_d.C, sys_d.D, R, Q,
301-
mu, sigma, control_inputs, rand_seed, use_file_data)
302-
303-
uuv.kalman_filter()
304-
uuv.plot_results()
263+
264+
if __name__ == "__main__":
265+
########################################################################################
266+
############################## DEFINE PARAMETERS HERE ##################################
267+
########################################################################################
268+
# use the data from the matlab file, or make our own?
269+
use_file_data = True
270+
# eliminate randomness in noise generation? (For debugging)
271+
rand_seed = None
272+
# measurement covariance (default = .001)
273+
z_noise = .001
274+
# process noise associated with velocity state (default = .01)
275+
v_noise = .01
276+
# process noise associated with position state (default = .0001)
277+
x_noise = .0001
278+
# timestep (seconds)
279+
dt = .05
280+
# initial belief (initial condition):
281+
# starting v and x: [[v] , [x]]
282+
mu = np.array([[0] , [0]])
283+
# starting covariance: [[v , 0] , [0 , x]]
284+
sigma = np.array([[.75 , 0] , [0 , .05]])
285+
########################################################################################
286+
########################################################################################
287+
288+
289+
# robot model parameters
290+
b = 20 # drag coefficient
291+
m = 100 # mass
292+
293+
# define continuous state space model
294+
A = np.array([[(-b/m) , 0] , [1 , 0]])
295+
B = np.array([[(1/m)] , [0]])
296+
C = np.array([0, 1])
297+
D = np.array([0])
298+
dt = .05
299+
300+
# get a discrete state space model
301+
sys = control.ss(A, B, C, D)
302+
sys_d = control.c2d(sys, dt)
303+
sys_d.A = np.array(sys_d.A)
304+
sys_d.B = np.array(sys_d.B)
305+
sys_d.C = np.array(sys_d.C)
306+
sys_d.D = np.array(sys_d.D)
307+
308+
# get the system/measurement noise
309+
R = np.array([[v_noise , 0] , [0 , x_noise]])
310+
Q = np.array([[z_noise]])
311+
312+
# set the ground truth control inputs
313+
input_times = np.arange(0, 50+dt, dt)
314+
control_inputs = np.zeros((1, input_times.size))
315+
control_inputs[0 , 0:100] = 50
316+
control_inputs[0 , 100:500] = 0
317+
control_inputs[0 , 500:600] = -50
318+
control_inputs[0 , 600:] = 0
319+
320+
# make the robot model
321+
uuv = Model(input_times, sys_d.A, sys_d.B, sys_d.C, sys_d.D, R, Q,
322+
mu, sigma, control_inputs, rand_seed, use_file_data)
323+
324+
uuv.kalman_filter()
325+
uuv.plot_results()
860 KB
Binary file not shown.

‎KalmanFilter/problem_specs.pdf‎

31.6 KB
Binary file not shown.

0 commit comments

Comments
 (0)