Skip to content

Commit a452213

Browse files
Chipe1norvig
authored andcommitted
Added Monte Carlo localization (aimacode#602)
1 parent 92c98f9 commit a452213

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

probability.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,3 +649,69 @@ def particle_filtering(e, N, HMM):
649649
s = weighted_sample_with_replacement(N, s, w)
650650

651651
return s
652+
653+
# _________________________________________________________________________
654+
## TODO: Implement continous map for MonteCarlo similar to Fig25.10 from the book
655+
656+
class MCLmap:
657+
"""Map which provides probability distributions and sensor readings.
658+
Consists of discrete cells which are either an obstacle or empty"""
659+
def __init__(self, m):
660+
self.m = m
661+
self.nrows = len(m)
662+
self.ncols = len(m[0])
663+
# list of empty spaces in the map
664+
self.empty = [[i, j] for i in range(self.nrows) for j in range(self.ncols) if not m[i][j]]
665+
666+
def sample(self):
667+
"""Returns a random kinematic state possible in the map"""
668+
pos = random.choice(self.empty)
669+
# 0N 1E 2S 3W
670+
orient = random.choice(range(4))
671+
kin_state = pos + [orient]
672+
return kin_state
673+
674+
def ray_cast(self, sensor_num, kin_state):
675+
"""Returns distace to nearest obstacle or map boundary in the direction of sensor"""
676+
pos = kin_state[:2]
677+
orient = kin_state[2]
678+
# sensor layout when orientation is 0 (towards North)
679+
# 0
680+
# 3R1
681+
# 2
682+
delta = [(sensor_num%2 == 0)*(sensor_num - 1), (sensor_num%2 == 1)*(2 - sensor_num)]
683+
# sensor direction changes based on orientation
684+
for _ in range(orient):
685+
delta = [delta[1], -delta[0]]
686+
range_count = 0
687+
while (0 <= pos[0] < self.nrows) and (0 <= pos[1] < self.nrows) and (not self.m[pos[0]][pos[1]]):
688+
pos = vector_add(pos, delta)
689+
range_count += 1
690+
return range_count
691+
692+
693+
def monte_carlo_localization(a, z, N, P_motion_sample, P_sensor, m, S=None):
694+
"""Monte Carlo localization algorithm from Fig 25.9"""
695+
696+
def ray_cast(sensor_num, kin_state, m):
697+
return m.ray_cast(sensor_num, kin_state)
698+
699+
M = len(z)
700+
W = [0]*N
701+
S_ = [0]*N
702+
W_ = [0]*N
703+
v = a['v']
704+
w = a['w']
705+
706+
if S is None:
707+
S = [m.sample() for _ in range(N)]
708+
709+
for i in range(N):
710+
S_[i] = P_motion_sample(S[i], v, w)
711+
W_[i] = 1
712+
for j in range(M):
713+
z_ = ray_cast(j, S_[i], m)
714+
W_[i] = W_[i] * P_sensor(z[j], z_)
715+
716+
S = weighted_sample_with_replacement(N, S_, W_)
717+
return S

tests/test_probability.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,68 @@ def test_particle_filtering():
168168
# XXX 'A' and 'B' are really arbitrary names, but I'm letting it stand for now
169169

170170

171+
def test_monte_carlo_localization():
172+
## TODO: Add tests for random motion/inaccurate sensors
173+
random.seed('aima-python')
174+
m = MCLmap([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0],
175+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
176+
[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0],
177+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
178+
[0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0],
179+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
180+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
181+
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0],
182+
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
183+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0],
184+
[0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0]])
185+
186+
def P_motion_sample(kin_state, v, w):
187+
"""Sample from possible kinematic states.
188+
Returns from a single element distribution (no uncertainity in motion)"""
189+
pos = kin_state[:2]
190+
orient = kin_state[2]
191+
192+
# for simplicity the robot first rotates and then moves
193+
orient = (orient + w)%4
194+
for _ in range(orient):
195+
v = [v[1], -v[0]]
196+
pos = list(vector_add(pos, v))
197+
return pos + [orient]
198+
199+
def P_sensor(x, y):
200+
"""Conditional probability for sensor reading"""
201+
# Need not be exact probability. Can use a scaled value.
202+
if x == y:
203+
return 0.8
204+
elif abs(x - y) <= 2:
205+
return 0.05
206+
else:
207+
return 0
208+
209+
from utils import print_table
210+
a = {'v': [0, 0], 'w': 0}
211+
z = [2, 4, 1, 6]
212+
S = monte_carlo_localization(a, z, 1000, P_motion_sample, P_sensor, m)
213+
grid = [[0]*17 for _ in range(11)]
214+
for x, y, _ in S:
215+
if 0 <= x < 11 and 0 <= y < 17:
216+
grid[x][y] += 1
217+
print("GRID:")
218+
print_table(grid)
219+
220+
a = {'v': [0, 1], 'w': 0}
221+
z = [2, 3, 5, 7]
222+
S = monte_carlo_localization(a, z, 1000, P_motion_sample, P_sensor, m, S)
223+
grid = [[0]*17 for _ in range(11)]
224+
for x, y, _ in S:
225+
if 0 <= x < 11 and 0 <= y < 17:
226+
grid[x][y] += 1
227+
print("GRID:")
228+
print_table(grid)
229+
230+
assert grid[6][7] > 700
231+
232+
171233
# The following should probably go in .ipynb:
172234

173235
"""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy