Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rlabbe
GitHub Repository: rlabbe/Kalman-and-Bayesian-Filters-in-Python
Path: blob/master/experiments/slam.py
1700 views
1
# -*- coding: utf-8 -*-
2
"""
3
Created on Mon Oct 3 07:59:40 2016
4
5
@author: rlabbe
6
"""
7
8
from filterpy.kalman import UnscentedKalmanFilter as UKF, MerweScaledSigmaPoints
9
from math import tan, sin, cos, sqrt, atan2
10
import numpy as np
11
from numpy.random import randn
12
import matplotlib.pyplot as plt
13
from filterpy.stats import plot_covariance_ellipse
14
15
16
17
def move(x, u, dt, wheelbase):
18
hdg = x[2]
19
vel = u[0]
20
steering_angle = u[1]
21
dist = vel * dt
22
23
if abs(steering_angle) > 0.001: # is robot turning?
24
beta = (dist / wheelbase) * tan(steering_angle)
25
r = wheelbase / tan(steering_angle) # radius
26
27
sinh, sinhb = sin(hdg), sin(hdg + beta)
28
cosh, coshb = cos(hdg), cos(hdg + beta)
29
return x + np.array([-r*sinh + r*sinhb,
30
r*cosh - r*coshb, beta])
31
else: # moving in straight line
32
return x + np.array([dist*cos(hdg), dist*sin(hdg), 0])
33
34
35
def fx(x, dt, u):
36
return move(x, u, dt, wheelbase)
37
38
39
def normalize_angle(x):
40
x = x % (2 * np.pi) # force in range [0, 2 pi)
41
if x > np.pi: # move to [-pi, pi)
42
x -= 2 * np.pi
43
return x
44
45
46
def residual_h(a, b):
47
y = a - b
48
# data in format [dist_1, bearing_1, dist_2, bearing_2,...]
49
for i in range(0, len(y), 2):
50
y[i + 1] = normalize_angle(y[i + 1])
51
return y
52
53
54
def residual_x(a, b):
55
y = a - b
56
y[2] = normalize_angle(y[2])
57
return y
58
59
60
def aa(x, y):
61
if y is not None:
62
return x % y
63
else:
64
return x
65
66
def bb(x,y):
67
try:
68
return x % y
69
except:
70
return x
71
72
73
74
def Hx(x, landmarks):
75
""" takes a state variable and returns the measurement
76
that would correspond to that state. """
77
hx = []
78
for lmark in landmarks:
79
px, py = lmark
80
dist = sqrt((px - x[0])**2 + (py - x[1])**2)
81
angle = atan2(py - x[1], px - x[0])
82
hx.extend([dist, normalize_angle(angle - x[2])])
83
return np.array(hx)
84
85
86
def state_mean(sigmas, Wm):
87
x = np.zeros(3)
88
89
sum_sin = np.sum(np.dot(np.sin(sigmas[:, 2]), Wm))
90
sum_cos = np.sum(np.dot(np.cos(sigmas[:, 2]), Wm))
91
x[0] = np.sum(np.dot(sigmas[:, 0], Wm))
92
x[1] = np.sum(np.dot(sigmas[:, 1], Wm))
93
x[2] = atan2(sum_sin, sum_cos)
94
return x
95
96
97
def z_mean(sigmas, Wm):
98
z_count = sigmas.shape[1]
99
x = np.zeros(z_count)
100
101
for z in range(0, z_count, 2):
102
sum_sin = np.sum(np.dot(np.sin(sigmas[:, z+1]), Wm))
103
sum_cos = np.sum(np.dot(np.cos(sigmas[:, z+1]), Wm))
104
105
x[z] = np.sum(np.dot(sigmas[:,z], Wm))
106
x[z+1] = atan2(sum_sin, sum_cos)
107
return x
108
109
110
dt = 1.0
111
wheelbase = 0.5
112
113
def run_localization(
114
cmds, landmarks, sigma_vel, sigma_steer, sigma_range,
115
sigma_bearing, ellipse_step=1, step=10):
116
117
plt.figure()
118
points = MerweScaledSigmaPoints(n=3, alpha=.00001, beta=2, kappa=0,
119
subtract=residual_x)
120
ukf = UKF(dim_x=3, dim_z=2*len(landmarks), fx=fx, hx=Hx,
121
dt=dt, points=points, x_mean_fn=state_mean,
122
z_mean_fn=z_mean, residual_x=residual_x,
123
residual_z=residual_h)
124
125
ukf.x = np.array([2, 6, .3])
126
ukf.P = np.diag([.1, .1, .05])
127
ukf.R = np.diag([sigma_range**2,
128
sigma_bearing**2]*len(landmarks))
129
ukf.Q = np.eye(3)*0.0001
130
131
sim_pos = ukf.x.copy()
132
133
# plot landmarks
134
if len(landmarks) > 0:
135
plt.scatter(landmarks[:, 0], landmarks[:, 1],
136
marker='s', s=60)
137
138
track = []
139
for i, u in enumerate(cmds):
140
sim_pos = move(sim_pos, u, dt/step, wheelbase)
141
track.append(sim_pos)
142
143
if i % step == 0:
144
ukf.predict(fx_args=u)
145
146
if i % ellipse_step == 0:
147
plot_covariance_ellipse(
148
(ukf.x[0], ukf.x[1]), ukf.P[0:2, 0:2], std=6,
149
facecolor='k', alpha=0.3)
150
151
x, y = sim_pos[0], sim_pos[1]
152
z = []
153
for lmark in landmarks:
154
dx, dy = lmark[0] - x, lmark[1] - y
155
d = sqrt(dx**2 + dy**2) + randn()*sigma_range
156
bearing = atan2(lmark[1] - y, lmark[0] - x)
157
a = (normalize_angle(bearing - sim_pos[2] +
158
randn()*sigma_bearing))
159
z.extend([d, a])
160
ukf.update(z, hx_args=(landmarks,))
161
162
if i % ellipse_step == 0:
163
plot_covariance_ellipse(
164
(ukf.x[0], ukf.x[1]), ukf.P[0:2, 0:2], std=6,
165
facecolor='g', alpha=0.8)
166
track = np.array(track)
167
plt.plot(track[:, 0], track[:,1], color='k', lw=2)
168
plt.axis('equal')
169
plt.title("UKF Robot localization")
170
plt.show()
171
return ukf
172
173
174
landmarks = np.array([[5, 10], [10, 5], [15, 15]])
175
cmds = [np.array([1.1, .01])] * 200
176
ukf = run_localization(
177
cmds, landmarks, sigma_vel=0.1, sigma_steer=np.radians(1),
178
sigma_range=0.3, sigma_bearing=0.1)
179
print('Final P:', ukf.P.diagonal())
180