Path: blob/master/experiments/RobotLocalizationParticleFilter.py
1700 views
# -*- coding: utf-8 -*-12"""Copyright 2015 Roger R Labbe Jr.345Code supporting the book67Kalman and Bayesian Filters in Python8https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Python91011This is licensed under an MIT license. See the LICENSE.txt file12for more information.13"""1415from __future__ import (absolute_import, division, print_function,16unicode_literals)1718import numpy as np1920from numpy.random import randn, random, uniform21import scipy.stats222324class RobotLocalizationParticleFilter(object):2526def __init__(self, N, x_dim, y_dim, landmarks, measure_std_error):27self.particles = np.empty((N, 3)) # x, y, heading28self.N = N29self.x_dim = x_dim30self.y_dim = y_dim31self.landmarks = landmarks32self.R = measure_std_error3334# distribute particles randomly with uniform weight35self.weights = np.empty(N)36#self.weights.fill(1./N)37'''self.particles[:, 0] = uniform(0, x_dim, size=N)38self.particles[:, 1] = uniform(0, y_dim, size=N)39self.particles[:, 2] = uniform(0, 2*np.pi, size=N)'''404142def create_uniform_particles(self, x_range, y_range, hdg_range):43self.particles[:, 0] = uniform(x_range[0], x_range[1], size=N)44self.particles[:, 1] = uniform(y_range[0], y_range[1], size=N)45self.particles[:, 2] = uniform(hdg_range[0], hdg_range[1], size=N)46self.particles[:, 2] %= 2 * np.pi4748def create_gaussian_particles(self, mean, var):49self.particles[:, 0] = mean[0] + randn(self.N)*var[0]50self.particles[:, 1] = mean[1] + randn(self.N)*var[1]51self.particles[:, 2] = mean[2] + randn(self.N)*var[2]52self.particles[:, 2] %= 2 * np.pi535455def predict(self, u, std, dt=1.):56""" move according to control input u (heading change, velocity)57with noise std"""5859self.particles[:, 2] += u[0] + randn(self.N) * std[0]60self.particles[:, 2] %= 2 * np.pi6162d = u[1]*dt + randn(self.N) * std[1]63self.particles[:, 0] += np.cos(self.particles[:, 2]) * d64self.particles[:, 1] += np.sin(self.particles[:, 2]) * d656667def update(self, z):68self.weights.fill(1.)69for i, landmark in enumerate(self.landmarks):70distance = np.linalg.norm(self.particles[:, 0:2] - landmark, axis=1)71self.weights *= scipy.stats.norm(distance, self.R).pdf(z[i])72#self.weights *= Gaussian(distance, self.R, z[i])7374self.weights += 1.e-30075self.weights /= sum(self.weights) # normalize767778def neff(self):79return 1. / np.sum(np.square(self.weights))808182def resample(self):83cumulative_sum = np.cumsum(self.weights)84cumulative_sum[-1] = 1. # avoid round-off error85indexes = np.searchsorted(cumulative_sum, random(self.N))8687# resample according to indexes88self.particles = self.particles[indexes]89self.weights = self.weights[indexes]90self.weights /= np.sum(self.weights) # normalize919293def resample_from_index(self, indexes):94assert len(indexes) == self.N9596self.particles = self.particles[indexes]97self.weights = self.weights[indexes]98self.weights /= np.sum(self.weights)99100101def estimate(self):102""" returns mean and variance """103pos = self.particles[:, 0:2]104mu = np.average(pos, weights=self.weights, axis=0)105var = np.average((pos - mu)**2, weights=self.weights, axis=0)106107return mu, var108109def mean(self):110""" returns weighted mean position"""111return np.average(self.particles[:, 0:2], weights=self.weights, axis=0)112113114115def residual_resample(w):116117N = len(w)118119w_ints = np.floor(N*w).astype(int)120residual = w - w_ints121residual /= sum(residual)122123indexes = np.zeros(N, 'i')124k = 0125for i in range(N):126for j in range(w_ints[i]):127indexes[k] = i128k += 1129cumsum = np.cumsum(residual)130cumsum[N-1] = 1.131for j in range(k, N):132indexes[j] = np.searchsorted(cumsum, random())133134return indexes135136137138def residual_resample2(w):139140N = len(w)141142w_ints =np.floor(N*w).astype(int)143144R = np.sum(w_ints)145m_rdn = N - R146147Ws = (N*w - w_ints)/ m_rdn148indexes = np.zeros(N, 'i')149i = 0150for j in range(N):151for k in range(w_ints[j]):152indexes[i] = j153i += 1154cumsum = np.cumsum(Ws)155cumsum[N-1] = 1 # just in case156157for j in range(i, N):158indexes[j] = np.searchsorted(cumsum, random())159160return indexes161162163164def systemic_resample(w):165N = len(w)166Q = np.cumsum(w)167indexes = np.zeros(N, 'int')168t = np.linspace(0, 1-1/N, N) + random()/N169170i, j = 0, 0171while i < N and j < N:172while Q[j] < t[i]:173j += 1174indexes[i] = j175i += 1176177return indexes178179180181182183def Gaussian(mu, sigma, x):184185# calculates the probability of x for 1-dim Gaussian with mean mu and var. sigma186g = (np.exp(-((mu - x) ** 2) / (sigma ** 2) / 2.0) /187np.sqrt(2.0 * np.pi * (sigma ** 2)))188for i in range(len(g)):189g[i] = max(g[i], 1.e-229)190return g191192193def test_pf():194195#seed(1234)196N = 10000197R = .2198landmarks = [[-1, 2], [20,4], [10,30], [18,25]]199#landmarks = [[-1, 2], [2,4]]200201pf = RobotLocalizationParticleFilter(N, 20, 20, landmarks, R)202203plot_pf(pf, 20, 20, weights=False)204205dt = .01206plt.pause(dt)207208for x in range(18):209210zs = []211pos=(x+3, x+3)212213for landmark in landmarks:214d = np.sqrt((landmark[0]-pos[0])**2 + (landmark[1]-pos[1])**2)215zs.append(d + randn()*R)216217pf.predict((0.01, 1.414), (.2, .05))218pf.update(z=zs)219pf.resample()220#print(x, np.array(list(zip(pf.particles, pf.weights))))221222mu, var = pf.estimate()223plot_pf(pf, 20, 20, weights=False)224plt.plot(pos[0], pos[1], marker='*', color='r', ms=10)225plt.scatter(mu[0], mu[1], color='g', s=100)226plt.tight_layout()227plt.pause(dt)228229230def test_pf2():231N = 1000232sensor_std_err = .2233landmarks = [[-1, 2], [20,4], [-20,6], [18,25]]234235pf = RobotLocalizationParticleFilter(N, 20, 20, landmarks, sensor_std_err)236237xs = []238for x in range(18):239zs = []240pos=(x+1, x+1)241242for landmark in landmarks:243d = np.sqrt((landmark[0]-pos[0])**2 + (landmark[1]-pos[1])**2)244zs.append(d + randn()*sensor_std_err)245246# move diagonally forward to (x+1, x+1)247pf.predict((0.00, 1.414), (.2, .05))248pf.update(z=zs)249pf.resample()250251mu, var = pf.estimate()252xs.append(mu)253254xs = np.array(xs)255plt.plot(xs[:, 0], xs[:, 1])256plt.show()257258259260if __name__ == '__main__':261262DO_PLOT_PARTICLES = False263from numpy.random import seed264import matplotlib.pyplot as plt265266#plt.figure()267268seed(5)269for count in range(10):270print()271print(count)272#numpy.random.set_state(fail_state)273#if count == 12:274# #fail_state = numpy.random.get_state()275# DO_PLOT_PARTICLES = True276277N = 4000278sensor_std_err = .1279landmarks = np.array([[-1, 2], [2,4], [10,6], [18,25]])280NL = len(landmarks)281282#landmarks = [[-1, 2], [2,4]]283284pf = RobotLocalizationParticleFilter(N, 20, 20, landmarks, sensor_std_err)285#pf.create_gaussian_particles([3, 2, 0], [5, 5, 2])286pf.create_uniform_particles((0,20), (0,20), (0, 6.28))287288if DO_PLOT_PARTICLES:289plt.scatter(pf.particles[:, 0], pf.particles[:, 1], alpha=.2, color='g')290291xs = []292for x in range(18):293zs = []294pos=(x+1, x+1)295296for landmark in landmarks:297d = np.sqrt((landmark[0]-pos[0])**2 + (landmark[1]-pos[1])**2)298zs.append(d + randn()*sensor_std_err)299300301zs = np.linalg.norm(landmarks - pos, axis=1) + randn(NL)*sensor_std_err302303304# move diagonally forward to (x+1, x+1)305pf.predict((0.00, 1.414), (.2, .05))306307pf.update(z=zs)308if x == 0:309print(max(pf.weights))310#while abs(pf.neff() -N) < .1:311# print('neffing')312# pf.create_uniform_particles((0,20), (0,20), (0, 6.28))313# pf.update(z=zs)314#print(pf.neff())315#indexes = residual_resample2(pf.weights)316indexes = systemic_resample(pf.weights)317318pf.resample_from_index(indexes)319#pf.resample()320321mu, var = pf.estimate()322xs.append(mu)323if DO_PLOT_PARTICLES:324plt.scatter(pf.particles[:, 0], pf.particles[:, 1], alpha=.2)325plt.scatter(pos[0], pos[1], marker='*', color='r')326plt.scatter(mu[0], mu[1], marker='s', color='r')327plt.pause(.01)328329xs = np.array(xs)330plt.plot(xs[:, 0], xs[:, 1])331plt.show()332333334