Skip to content

Commit b8afdb1

Browse files
authored
Using scipy.spatial.rotation matrix (AtsushiSakai#335)
1 parent d1bde58 commit b8afdb1

File tree

26 files changed

+1003
-932
lines changed

26 files changed

+1003
-932
lines changed

.travis.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,9 @@ before_install:
1818
- conda update -q conda
1919
# Useful for debugging any issues with conda
2020
- conda info -a
21-
# - conda install python==3.6.8
2221

2322
install:
24-
- conda install numpy==1.15
23+
- conda install numpy
2524
- conda install scipy
2625
- conda install matplotlib
2726
- conda install pandas

Localization/ensemble_kalman_filter/ensemble_kalman_filter.py

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@
55
author: Ryohei Sasaki(rsasaki0109)
66
77
Ref:
8-
- [Ensemble Kalman filtering](https://rmets.onlinelibrary.wiley.com/doi/10.1256/qj.05.135)
8+
Ensemble Kalman filtering
9+
(https://rmets.onlinelibrary.wiley.com/doi/10.1256/qj.05.135)
910
1011
"""
1112

1213
import math
1314

1415
import matplotlib.pyplot as plt
1516
import numpy as np
17+
from scipy.spatial.transform import Rotation as Rot
1618

1719
# Simulation parameter
1820
Q_sim = np.diag([0.2, np.deg2rad(1.0)]) ** 2
@@ -48,8 +50,8 @@ def observation(xTrue, xd, u, RFID):
4850
angle = pi_2_pi(math.atan2(dy, dx) - xTrue[2, 0])
4951
if d <= MAX_RANGE:
5052
dn = d + np.random.randn() * Q_sim[0, 0] ** 0.5 # add noise
51-
anglen = angle + np.random.randn() * Q_sim[1, 1] ** 0.5 # add noise
52-
zi = np.array([dn, anglen, RFID[i, 0], RFID[i, 1]])
53+
angle_with_noise = angle + np.random.randn() * Q_sim[1, 1] ** 0.5
54+
zi = np.array([dn, angle_with_noise, RFID[i, 0], RFID[i, 1]])
5355
z = np.vstack((z, zi))
5456

5557
# add noise to input
@@ -79,10 +81,12 @@ def motion_model(x, u):
7981
def observe_landmark_position(x, landmarks):
8082
landmarks_pos = np.zeros((2 * landmarks.shape[0], 1))
8183
for (i, lm) in enumerate(landmarks):
82-
landmarks_pos[2 * i] = x[0, 0] + lm[0] * math.cos(x[2, 0] + lm[1]) + np.random.randn() * Q_sim[
83-
0, 0] ** 0.5 / np.sqrt(2)
84-
landmarks_pos[2 * i + 1] = x[1, 0] + lm[0] * math.sin(x[2, 0] + lm[1]) + np.random.randn() * Q_sim[
85-
0, 0] ** 0.5 / np.sqrt(2)
84+
index = 2 * i
85+
q = Q_sim[0, 0] ** 0.5
86+
landmarks_pos[index] = x[0, 0] + lm[0] * math.cos(
87+
x[2, 0] + lm[1]) + np.random.randn() * q / np.sqrt(2)
88+
landmarks_pos[index + 1] = x[1, 0] + lm[0] * math.sin(
89+
x[2, 0] + lm[1]) + np.random.randn() * q / np.sqrt(2)
8690
return landmarks_pos
8791

8892

@@ -148,8 +152,9 @@ def plot_covariance_ellipse(xEst, PEst): # pragma: no cover
148152

149153
t = np.arange(0, 2 * math.pi + 0.1, 0.1)
150154

151-
# eig_val[big_ind] or eiq_val[small_ind] were occasionally negative numbers extremely
152-
# close to 0 (~10^-20), catch these cases and set the respective variable to 0
155+
# eig_val[big_ind] or eiq_val[small_ind] were occasionally negative
156+
# numbers extremely close to 0 (~10^-20), catch these cases and set
157+
# the respective variable to 0
153158
try:
154159
a = math.sqrt(eig_val[big_ind])
155160
except ValueError:
@@ -163,11 +168,11 @@ def plot_covariance_ellipse(xEst, PEst): # pragma: no cover
163168
x = [a * math.cos(it) for it in t]
164169
y = [b * math.sin(it) for it in t]
165170
angle = math.atan2(eig_vec[big_ind, 1], eig_vec[big_ind, 0])
166-
R = np.array([[math.cos(angle), math.sin(angle)],
167-
[-math.sin(angle), math.cos(angle)]])
168-
fx = R.dot(np.array([[x, y]]))
169-
px = np.array(fx[0, :] + xEst[0, 0]).flatten()
170-
py = np.array(fx[1, :] + xEst[1, 0]).flatten()
171+
rot = Rot.from_euler('z', angle).as_matrix()[0:2, 0:2]
172+
fx = np.stack([x, y]).T @ rot
173+
174+
px = np.array(fx[:, 0] + xEst[0, 0]).flatten()
175+
py = np.array(fx[:, 1] + xEst[1, 0]).flatten()
171176
plt.plot(px, py, "--r")
172177

173178

@@ -214,8 +219,9 @@ def main():
214219
if show_animation:
215220
plt.cla()
216221
# for stopping simulation with the esc key.
217-
plt.gcf().canvas.mpl_connect('key_release_event',
218-
lambda event: [exit(0) if event.key == 'escape' else None])
222+
plt.gcf().canvas.mpl_connect(
223+
'key_release_event',
224+
lambda event: [exit(0) if event.key == 'escape' else None])
219225

220226
for i in range(len(z[:, 0])):
221227
plt.plot([xTrue[0, 0], z[i, 2]], [xTrue[1, 0], z[i, 3]], "-k")
@@ -227,7 +233,7 @@ def main():
227233
np.array(hxDR[1, :]).flatten(), "-k")
228234
plt.plot(np.array(hxEst[0, :]).flatten(),
229235
np.array(hxEst[1, :]).flatten(), "-r")
230-
# plot_covariance_ellipse(xEst, PEst)
236+
plot_covariance_ellipse(xEst, PEst)
231237
plt.axis("equal")
232238
plt.grid(True)
233239
plt.pause(0.001)

Localization/histogram_filter/histogram_filter.py

Lines changed: 65 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
RANGE_STD = 3.0 # standard deviation for observation gaussian distribution
2929

3030
# grid map param
31-
XY_RESO = 0.5 # xy grid resolution
32-
MINX = -15.0
33-
MINY = -5.0
34-
MAXX = 15.0
35-
MAXY = 25.0
31+
XY_RESOLUTION = 0.5 # xy grid resolution
32+
MIN_X = -15.0
33+
MIN_Y = -5.0
34+
MAX_X = 15.0
35+
MAX_Y = 25.0
3636

3737
# simulation parameters
3838
NOISE_RANGE = 2.0 # [m] 1σ range noise parameter
@@ -41,17 +41,17 @@
4141
show_animation = True
4242

4343

44-
class GridMap():
44+
class GridMap:
4545

4646
def __init__(self):
4747
self.data = None
48-
self.xy_reso = None
49-
self.minx = None
50-
self.miny = None
51-
self.maxx = None
52-
self.maxx = None
53-
self.xw = None
54-
self.yw = None
48+
self.xy_resolution = None
49+
self.min_x = None
50+
self.min_y = None
51+
self.max_x = None
52+
self.max_y = None
53+
self.x_w = None
54+
self.y_w = None
5555
self.dx = 0.0 # movement distance
5656
self.dy = 0.0 # movement distance
5757

@@ -64,10 +64,10 @@ def histogram_filter_localization(grid_map, u, z, yaw):
6464
return grid_map
6565

6666

67-
def calc_gaussian_observation_pdf(gmap, z, iz, ix, iy, std):
67+
def calc_gaussian_observation_pdf(grid_map, z, iz, ix, iy, std):
6868
# predicted range
69-
x = ix * gmap.xy_reso + gmap.minx
70-
y = iy * gmap.xy_reso + gmap.miny
69+
x = ix * grid_map.xy_resolution + grid_map.min_x
70+
y = iy * grid_map.xy_resolution + grid_map.min_y
7171
d = math.hypot(x - z[iz, 1], y - z[iz, 2])
7272

7373
# likelihood
@@ -76,16 +76,16 @@ def calc_gaussian_observation_pdf(gmap, z, iz, ix, iy, std):
7676
return pdf
7777

7878

79-
def observation_update(gmap, z, std):
79+
def observation_update(grid_map, z, std):
8080
for iz in range(z.shape[0]):
81-
for ix in range(gmap.xw):
82-
for iy in range(gmap.yw):
83-
gmap.data[ix][iy] *= calc_gaussian_observation_pdf(
84-
gmap, z, iz, ix, iy, std)
81+
for ix in range(grid_map.x_w):
82+
for iy in range(grid_map.y_w):
83+
grid_map.data[ix][iy] *= calc_gaussian_observation_pdf(
84+
grid_map, z, iz, ix, iy, std)
8585

86-
gmap = normalize_probability(gmap)
86+
grid_map = normalize_probability(grid_map)
8787

88-
return gmap
88+
return grid_map
8989

9090

9191
def calc_input():
@@ -112,8 +112,8 @@ def motion_model(x, u):
112112

113113

114114
def draw_heat_map(data, mx, my):
115-
maxp = max([max(igmap) for igmap in data])
116-
plt.pcolor(mx, my, data, vmax=maxp, cmap=plt.cm.get_cmap("Blues"))
115+
max_value = max([max(i_data) for i_data in data])
116+
plt.pcolor(mx, my, data, vmax=max_value, cmap=plt.cm.get_cmap("Blues"))
117117
plt.axis("equal")
118118

119119

@@ -140,43 +140,47 @@ def observation(xTrue, u, RFID):
140140
return xTrue, z, ud
141141

142142

143-
def normalize_probability(gmap):
144-
sump = sum([sum(igmap) for igmap in gmap.data])
143+
def normalize_probability(grid_map):
144+
sump = sum([sum(i_data) for i_data in grid_map.data])
145145

146-
for ix in range(gmap.xw):
147-
for iy in range(gmap.yw):
148-
gmap.data[ix][iy] /= sump
146+
for ix in range(grid_map.x_w):
147+
for iy in range(grid_map.y_w):
148+
grid_map.data[ix][iy] /= sump
149149

150-
return gmap
150+
return grid_map
151151

152152

153-
def init_gmap(xy_reso, minx, miny, maxx, maxy):
153+
def init_grid_map(xy_resolution, min_x, min_y, max_x, max_y):
154154
grid_map = GridMap()
155155

156-
grid_map.xy_reso = xy_reso
157-
grid_map.minx = minx
158-
grid_map.miny = miny
159-
grid_map.maxx = maxx
160-
grid_map.maxy = maxy
161-
grid_map.xw = int(round((grid_map.maxx - grid_map.minx) / grid_map.xy_reso))
162-
grid_map.yw = int(round((grid_map.maxy - grid_map.miny) / grid_map.xy_reso))
163-
164-
grid_map.data = [[1.0 for _ in range(grid_map.yw)] for _ in range(grid_map.xw)]
156+
grid_map.xy_resolution = xy_resolution
157+
grid_map.min_x = min_x
158+
grid_map.min_y = min_y
159+
grid_map.max_x = max_x
160+
grid_map.max_y = max_y
161+
grid_map.x_w = int(round((grid_map.max_x - grid_map.min_x)
162+
/ grid_map.xy_resolution))
163+
grid_map.y_w = int(round((grid_map.max_y - grid_map.min_y)
164+
/ grid_map.xy_resolution))
165+
166+
grid_map.data = [[1.0 for _ in range(grid_map.y_w)]
167+
for _ in range(grid_map.x_w)]
165168
grid_map = normalize_probability(grid_map)
166169

167170
return grid_map
168171

169172

170173
def map_shift(grid_map, x_shift, y_shift):
171-
tgmap = copy.deepcopy(grid_map.data)
174+
tmp_grid_map = copy.deepcopy(grid_map.data)
172175

173-
for ix in range(grid_map.xw):
174-
for iy in range(grid_map.yw):
176+
for ix in range(grid_map.x_w):
177+
for iy in range(grid_map.y_w):
175178
nix = ix + x_shift
176179
niy = iy + y_shift
177180

178-
if 0 <= nix < grid_map.xw and 0 <= niy < grid_map.yw:
179-
grid_map.data[ix + x_shift][iy + y_shift] = tgmap[ix][iy]
181+
if 0 <= nix < grid_map.x_w and 0 <= niy < grid_map.y_w:
182+
grid_map.data[ix + x_shift][iy + y_shift] =\
183+
tmp_grid_map[ix][iy]
180184

181185
return grid_map
182186

@@ -185,22 +189,26 @@ def motion_update(grid_map, u, yaw):
185189
grid_map.dx += DT * math.cos(yaw) * u[0]
186190
grid_map.dy += DT * math.sin(yaw) * u[0]
187191

188-
x_shift = grid_map.dx // grid_map.xy_reso
189-
y_shift = grid_map.dy // grid_map.xy_reso
192+
x_shift = grid_map.dx // grid_map.xy_resolution
193+
y_shift = grid_map.dy // grid_map.xy_resolution
190194

191195
if abs(x_shift) >= 1.0 or abs(y_shift) >= 1.0: # map should be shifted
192196
grid_map = map_shift(grid_map, int(x_shift), int(y_shift))
193-
grid_map.dx -= x_shift * grid_map.xy_reso
194-
grid_map.dy -= y_shift * grid_map.xy_reso
197+
grid_map.dx -= x_shift * grid_map.xy_resolution
198+
grid_map.dy -= y_shift * grid_map.xy_resolution
195199

196200
grid_map.data = gaussian_filter(grid_map.data, sigma=MOTION_STD)
197201

198202
return grid_map
199203

200204

201-
def calc_grid_index(gmap):
202-
mx, my = np.mgrid[slice(gmap.minx - gmap.xy_reso / 2.0, gmap.maxx + gmap.xy_reso / 2.0, gmap.xy_reso),
203-
slice(gmap.miny - gmap.xy_reso / 2.0, gmap.maxy + gmap.xy_reso / 2.0, gmap.xy_reso)]
205+
def calc_grid_index(grid_map):
206+
mx, my = np.mgrid[slice(grid_map.min_x - grid_map.xy_resolution / 2.0,
207+
grid_map.max_x + grid_map.xy_resolution / 2.0,
208+
grid_map.xy_resolution),
209+
slice(grid_map.min_y - grid_map.xy_resolution / 2.0,
210+
grid_map.max_y + grid_map.xy_resolution / 2.0,
211+
grid_map.xy_resolution)]
204212

205213
return mx, my
206214

@@ -217,7 +225,7 @@ def main():
217225
time = 0.0
218226

219227
xTrue = np.zeros((4, 1))
220-
grid_map = init_gmap(XY_RESO, MINX, MINY, MAXX, MAXY)
228+
grid_map = init_grid_map(XY_RESOLUTION, MIN_X, MIN_Y, MAX_X, MAX_Y)
221229
mx, my = calc_grid_index(grid_map) # for grid map visualization
222230

223231
while SIM_TIME >= time:
@@ -234,8 +242,9 @@ def main():
234242
if show_animation:
235243
plt.cla()
236244
# for stopping simulation with the esc key.
237-
plt.gcf().canvas.mpl_connect('key_release_event',
238-
lambda event: [exit(0) if event.key == 'escape' else None])
245+
plt.gcf().canvas.mpl_connect(
246+
'key_release_event',
247+
lambda event: [exit(0) if event.key == 'escape' else None])
239248
draw_heat_map(grid_map.data, mx, my)
240249
plt.plot(xTrue[0, :], xTrue[1, :], "xr")
241250
plt.plot(RF_ID[:, 0], RF_ID[:, 1], ".k")

Localization/particle_filter/particle_filter.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import matplotlib.pyplot as plt
1212
import numpy as np
13+
from scipy.spatial.transform import Rotation as Rot
1314

1415
# Estimation parameter of PF
1516
Q = np.diag([0.2]) ** 2 # range error
@@ -172,8 +173,9 @@ def plot_covariance_ellipse(x_est, p_est): # pragma: no cover
172173

173174
t = np.arange(0, 2 * math.pi + 0.1, 0.1)
174175

175-
# eig_val[big_ind] or eiq_val[small_ind] were occasionally negative numbers extremely
176-
# close to 0 (~10^-20), catch these cases and set the respective variable to 0
176+
# eig_val[big_ind] or eiq_val[small_ind] were occasionally negative
177+
# numbers extremely close to 0 (~10^-20), catch these cases and set the
178+
# respective variable to 0
177179
try:
178180
a = math.sqrt(eig_val[big_ind])
179181
except ValueError:
@@ -187,9 +189,8 @@ def plot_covariance_ellipse(x_est, p_est): # pragma: no cover
187189
x = [a * math.cos(it) for it in t]
188190
y = [b * math.sin(it) for it in t]
189191
angle = math.atan2(eig_vec[big_ind, 1], eig_vec[big_ind, 0])
190-
Rot = np.array([[math.cos(angle), -math.sin(angle)],
191-
[math.sin(angle), math.cos(angle)]])
192-
fx = Rot.dot(np.array([[x, y]]))
192+
rot = Rot.from_euler('z', angle).as_matrix()[0:2, 0:2]
193+
fx = rot.dot(np.array([[x, y]]))
193194
px = np.array(fx[0, :] + x_est[0, 0]).flatten()
194195
py = np.array(fx[1, :] + x_est[1, 0]).flatten()
195196
plt.plot(px, py, "--r")
@@ -235,8 +236,9 @@ def main():
235236
if show_animation:
236237
plt.cla()
237238
# for stopping simulation with the esc key.
238-
plt.gcf().canvas.mpl_connect('key_release_event',
239-
lambda event: [exit(0) if event.key == 'escape' else None])
239+
plt.gcf().canvas.mpl_connect(
240+
'key_release_event',
241+
lambda event: [exit(0) if event.key == 'escape' else None])
240242

241243
for i in range(len(z[:, 0])):
242244
plt.plot([x_true[0, 0], z[i, 1]], [x_true[1, 0], z[i, 2]], "-k")

0 commit comments

Comments
 (0)