Path: blob/master/experiments/slam.py
1700 views
# -*- coding: utf-8 -*-1"""2Created on Mon Oct 3 07:59:40 201634@author: rlabbe5"""67from filterpy.kalman import UnscentedKalmanFilter as UKF, MerweScaledSigmaPoints8from math import tan, sin, cos, sqrt, atan29import numpy as np10from numpy.random import randn11import matplotlib.pyplot as plt12from filterpy.stats import plot_covariance_ellipse13141516def move(x, u, dt, wheelbase):17hdg = x[2]18vel = u[0]19steering_angle = u[1]20dist = vel * dt2122if abs(steering_angle) > 0.001: # is robot turning?23beta = (dist / wheelbase) * tan(steering_angle)24r = wheelbase / tan(steering_angle) # radius2526sinh, sinhb = sin(hdg), sin(hdg + beta)27cosh, coshb = cos(hdg), cos(hdg + beta)28return x + np.array([-r*sinh + r*sinhb,29r*cosh - r*coshb, beta])30else: # moving in straight line31return x + np.array([dist*cos(hdg), dist*sin(hdg), 0])323334def fx(x, dt, u):35return move(x, u, dt, wheelbase)363738def normalize_angle(x):39x = x % (2 * np.pi) # force in range [0, 2 pi)40if x > np.pi: # move to [-pi, pi)41x -= 2 * np.pi42return x434445def residual_h(a, b):46y = a - b47# data in format [dist_1, bearing_1, dist_2, bearing_2,...]48for i in range(0, len(y), 2):49y[i + 1] = normalize_angle(y[i + 1])50return y515253def residual_x(a, b):54y = a - b55y[2] = normalize_angle(y[2])56return y575859def aa(x, y):60if y is not None:61return x % y62else:63return x6465def bb(x,y):66try:67return x % y68except:69return x70717273def Hx(x, landmarks):74""" takes a state variable and returns the measurement75that would correspond to that state. """76hx = []77for lmark in landmarks:78px, py = lmark79dist = sqrt((px - x[0])**2 + (py - x[1])**2)80angle = atan2(py - x[1], px - x[0])81hx.extend([dist, normalize_angle(angle - x[2])])82return np.array(hx)838485def state_mean(sigmas, Wm):86x = np.zeros(3)8788sum_sin = np.sum(np.dot(np.sin(sigmas[:, 2]), Wm))89sum_cos = np.sum(np.dot(np.cos(sigmas[:, 2]), Wm))90x[0] = np.sum(np.dot(sigmas[:, 0], Wm))91x[1] = np.sum(np.dot(sigmas[:, 1], Wm))92x[2] = atan2(sum_sin, sum_cos)93return x949596def z_mean(sigmas, Wm):97z_count = sigmas.shape[1]98x = np.zeros(z_count)99100for z in range(0, z_count, 2):101sum_sin = np.sum(np.dot(np.sin(sigmas[:, z+1]), Wm))102sum_cos = np.sum(np.dot(np.cos(sigmas[:, z+1]), Wm))103104x[z] = np.sum(np.dot(sigmas[:,z], Wm))105x[z+1] = atan2(sum_sin, sum_cos)106return x107108109dt = 1.0110wheelbase = 0.5111112def run_localization(113cmds, landmarks, sigma_vel, sigma_steer, sigma_range,114sigma_bearing, ellipse_step=1, step=10):115116plt.figure()117points = MerweScaledSigmaPoints(n=3, alpha=.00001, beta=2, kappa=0,118subtract=residual_x)119ukf = UKF(dim_x=3, dim_z=2*len(landmarks), fx=fx, hx=Hx,120dt=dt, points=points, x_mean_fn=state_mean,121z_mean_fn=z_mean, residual_x=residual_x,122residual_z=residual_h)123124ukf.x = np.array([2, 6, .3])125ukf.P = np.diag([.1, .1, .05])126ukf.R = np.diag([sigma_range**2,127sigma_bearing**2]*len(landmarks))128ukf.Q = np.eye(3)*0.0001129130sim_pos = ukf.x.copy()131132# plot landmarks133if len(landmarks) > 0:134plt.scatter(landmarks[:, 0], landmarks[:, 1],135marker='s', s=60)136137track = []138for i, u in enumerate(cmds):139sim_pos = move(sim_pos, u, dt/step, wheelbase)140track.append(sim_pos)141142if i % step == 0:143ukf.predict(fx_args=u)144145if i % ellipse_step == 0:146plot_covariance_ellipse(147(ukf.x[0], ukf.x[1]), ukf.P[0:2, 0:2], std=6,148facecolor='k', alpha=0.3)149150x, y = sim_pos[0], sim_pos[1]151z = []152for lmark in landmarks:153dx, dy = lmark[0] - x, lmark[1] - y154d = sqrt(dx**2 + dy**2) + randn()*sigma_range155bearing = atan2(lmark[1] - y, lmark[0] - x)156a = (normalize_angle(bearing - sim_pos[2] +157randn()*sigma_bearing))158z.extend([d, a])159ukf.update(z, hx_args=(landmarks,))160161if i % ellipse_step == 0:162plot_covariance_ellipse(163(ukf.x[0], ukf.x[1]), ukf.P[0:2, 0:2], std=6,164facecolor='g', alpha=0.8)165track = np.array(track)166plt.plot(track[:, 0], track[:,1], color='k', lw=2)167plt.axis('equal')168plt.title("UKF Robot localization")169plt.show()170return ukf171172173landmarks = np.array([[5, 10], [10, 5], [15, 15]])174cmds = [np.array([1.1, .01])] * 200175ukf = run_localization(176cmds, landmarks, sigma_vel=0.1, sigma_steer=np.radians(1),177sigma_range=0.3, sigma_bearing=0.1)178print('Final P:', ukf.P.diagonal())179180