More math material for KF.
Added better explanation of P = FPF' + Q. Moved conversion of multivariate equations to univariate eqs. to the math chapter. Moved the walkthrough of KalmanFilter to an appendix.
This commit is contained in:
@@ -1,297 +1,340 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu May 1 16:56:49 2014
|
||||
|
||||
@author: rlabbe
|
||||
"""
|
||||
import numpy as np
|
||||
from matplotlib.patches import Ellipse
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cm
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from numpy.random import multivariate_normal
|
||||
import stats
|
||||
|
||||
def show_residual_chart():
|
||||
plt.xlim([0.9,2.5])
|
||||
plt.ylim([1.5,3.5])
|
||||
|
||||
plt.scatter ([1,2,2],[2,3,2.3])
|
||||
plt.scatter ([2],[2.8],marker='o')
|
||||
ax = plt.axes()
|
||||
ax.annotate('', xy=(2,3), xytext=(1,2),
|
||||
arrowprops=dict(arrowstyle='->', ec='#004080',
|
||||
lw=2,
|
||||
shrinkA=3, shrinkB=4))
|
||||
ax.annotate('prediction', xy=(2.04,3.), color='#004080')
|
||||
ax.annotate('measurement', xy=(2.05, 2.28))
|
||||
ax.annotate('prior estimate', xy=(1, 1.9))
|
||||
ax.annotate('residual', xy=(2.04,2.6), color='#e24a33')
|
||||
ax.annotate('new estimate', xy=(2,2.8),xytext=(2.1,2.8),
|
||||
arrowprops=dict(arrowstyle='->', ec="k", shrinkA=3, shrinkB=4))
|
||||
ax.annotate('', xy=(2,3), xytext=(2,2.3),
|
||||
arrowprops=dict(arrowstyle="-",
|
||||
ec="#e24a33",
|
||||
lw=2,
|
||||
shrinkA=5, shrinkB=5))
|
||||
plt.title("Kalman Filter Predict and Update")
|
||||
plt.axis('equal')
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_position_chart():
|
||||
""" Displays 3 measurements at t=1,2,3, with x=1,2,3"""
|
||||
|
||||
plt.scatter ([1,2,3], [1,2,3], s=128, color='#004080')
|
||||
plt.xlim([0,4]);
|
||||
plt.ylim([0,4])
|
||||
|
||||
plt.annotate('t=1', xy=(1,1), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=2', xy=(2,2), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=3', xy=(3,3), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.xlabel("X")
|
||||
plt.ylabel("Y")
|
||||
|
||||
plt.xticks(np.arange(1,4,1))
|
||||
plt.yticks(np.arange(1,4,1))
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_position_prediction_chart():
|
||||
""" displays 3 measurements, with the next position predicted"""
|
||||
|
||||
plt.scatter ([1,2,3], [1,2,3], s=128, color='#004080')
|
||||
|
||||
plt.annotate('t=1', xy=(1,1), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=2', xy=(2,2), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=3', xy=(3,3), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.xlim([0,5])
|
||||
plt.ylim([0,5])
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Time")
|
||||
|
||||
plt.xticks(np.arange(1,5,1))
|
||||
plt.yticks(np.arange(1,5,1))
|
||||
|
||||
plt.scatter ([4], [4], c='g',s=128, color='#8EBA42')
|
||||
ax = plt.axes()
|
||||
ax.annotate('', xy=(4,4), xytext=(3,3),
|
||||
arrowprops=dict(arrowstyle='->',
|
||||
ec='g',
|
||||
shrinkA=6, shrinkB=5,
|
||||
lw=3))
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_x_error_chart(count):
|
||||
""" displays x=123 with covariances showing error"""
|
||||
|
||||
plt.cla()
|
||||
plt.gca().autoscale(tight=True)
|
||||
|
||||
cov = np.array([[0.03,0], [0,8]])
|
||||
e = stats.covariance_ellipse (cov)
|
||||
|
||||
cov2 = np.array([[0.03,0], [0,4]])
|
||||
e2 = stats.covariance_ellipse (cov2)
|
||||
|
||||
cov3 = np.array([[12,11.95], [11.95,12]])
|
||||
e3 = stats.covariance_ellipse (cov3)
|
||||
|
||||
|
||||
sigma=[1, 4, 9]
|
||||
|
||||
if count >= 1:
|
||||
stats.plot_covariance_ellipse ((0,0), ellipse=e, variance=sigma)
|
||||
|
||||
if count == 2 or count == 3:
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e, variance=sigma)
|
||||
|
||||
if count == 3:
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e3, variance=sigma,
|
||||
edgecolor='r')
|
||||
|
||||
if count == 4:
|
||||
M1 = np.array([[5, 5]]).T
|
||||
m4, cov4 = stats.multivariate_multiply(M1, cov2, M1, cov3)
|
||||
e4 = stats.covariance_ellipse (cov4)
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e, variance=sigma,
|
||||
alpha=0.25)
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e3, variance=sigma,
|
||||
edgecolor='r', alpha=0.25)
|
||||
stats.plot_covariance_ellipse (m4[:,0], ellipse=e4, variance=sigma)
|
||||
|
||||
#plt.ylim([0,11])
|
||||
#plt.xticks(np.arange(1,4,1))
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Velocity")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_x_with_unobserved():
|
||||
""" shows x=1,2,3 with velocity superimposed on top """
|
||||
|
||||
# plot velocity
|
||||
sigma=[0.5,1.,1.5,2]
|
||||
cov = np.array([[1,1],[1,1.1]])
|
||||
stats.plot_covariance_ellipse ((2,2), cov=cov, variance=sigma, axis_equal=False)
|
||||
|
||||
# plot positions
|
||||
cov = np.array([[0.003,0], [0,12]])
|
||||
sigma=[0.5,1.,1.5,2]
|
||||
e = stats.covariance_ellipse (cov)
|
||||
|
||||
stats.plot_covariance_ellipse ((1,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
stats.plot_covariance_ellipse ((2,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
stats.plot_covariance_ellipse ((3,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
|
||||
# plot intersection cirle
|
||||
isct = Ellipse(xy=(2,2), width=.2, height=1.2, edgecolor='r', fc='None', lw=4)
|
||||
plt.gca().add_artist(isct)
|
||||
|
||||
plt.ylim([0,11])
|
||||
plt.xlim([0,4])
|
||||
plt.xticks(np.arange(1,4,1))
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Time")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
def plot_3d_covariance(mean, cov):
|
||||
""" plots a 2x2 covariance matrix positioned at mean. mean will be plotted
|
||||
in x and y, and the probability in the z axis.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mean : 2x1 tuple-like object
|
||||
mean for x and y coordinates. For example (2.3, 7.5)
|
||||
|
||||
cov : 2x2 nd.array
|
||||
the covariance matrix
|
||||
|
||||
"""
|
||||
|
||||
# compute width and height of covariance ellipse so we can choose
|
||||
# appropriate ranges for x and y
|
||||
o,w,h = stats.covariance_ellipse(cov,3)
|
||||
# rotate width and height to x,y axis
|
||||
wx = abs(w*np.cos(o) + h*np.sin(o))*1.2
|
||||
wy = abs(h*np.cos(o) - w*np.sin(o))*1.2
|
||||
|
||||
|
||||
# ensure axis are of the same size so everything is plotted with the same
|
||||
# scale
|
||||
if wx > wy:
|
||||
w = wx
|
||||
else:
|
||||
w = wy
|
||||
|
||||
minx = mean[0] - w
|
||||
maxx = mean[0] + w
|
||||
miny = mean[1] - w
|
||||
maxy = mean[1] + w
|
||||
|
||||
xs = np.arange(minx, maxx, (maxx-minx)/40.)
|
||||
ys = np.arange(miny, maxy, (maxy-miny)/40.)
|
||||
xv, yv = np.meshgrid (xs, ys)
|
||||
|
||||
zs = np.array([100.* stats.multivariate_gaussian(np.array([x,y]),mean,cov) \
|
||||
for x,y in zip(np.ravel(xv), np.ravel(yv))])
|
||||
zv = zs.reshape(xv.shape)
|
||||
|
||||
ax = plt.figure().add_subplot(111, projection='3d')
|
||||
ax.plot_surface(xv, yv, zv, rstride=1, cstride=1, cmap=cm.autumn)
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
|
||||
ax.contour(xv, yv, zv, zdir='x', offset=minx-1, cmap=cm.autumn)
|
||||
ax.contour(xv, yv, zv, zdir='y', offset=maxy, cmap=cm.BuGn)
|
||||
|
||||
|
||||
def plot_3d_sampled_covariance(mean, cov):
|
||||
""" plots a 2x2 covariance matrix positioned at mean. mean will be plotted
|
||||
in x and y, and the probability in the z axis.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mean : 2x1 tuple-like object
|
||||
mean for x and y coordinates. For example (2.3, 7.5)
|
||||
|
||||
cov : 2x2 nd.array
|
||||
the covariance matrix
|
||||
|
||||
"""
|
||||
|
||||
# compute width and height of covariance ellipse so we can choose
|
||||
# appropriate ranges for x and y
|
||||
o,w,h = stats.covariance_ellipse(cov,3)
|
||||
# rotate width and height to x,y axis
|
||||
wx = abs(w*np.cos(o) + h*np.sin(o))*1.2
|
||||
wy = abs(h*np.cos(o) - w*np.sin(o))*1.2
|
||||
|
||||
|
||||
# ensure axis are of the same size so everything is plotted with the same
|
||||
# scale
|
||||
if wx > wy:
|
||||
w = wx
|
||||
else:
|
||||
w = wy
|
||||
|
||||
minx = mean[0] - w
|
||||
maxx = mean[0] + w
|
||||
miny = mean[1] - w
|
||||
maxy = mean[1] + w
|
||||
|
||||
count = 1000
|
||||
x,y = multivariate_normal(mean=mean, cov=cov, size=count).T
|
||||
|
||||
xs = np.arange(minx, maxx, (maxx-minx)/40.)
|
||||
ys = np.arange(miny, maxy, (maxy-miny)/40.)
|
||||
xv, yv = np.meshgrid (xs, ys)
|
||||
|
||||
zs = np.array([100.* stats.multivariate_gaussian(np.array([xx,yy]),mean,cov) \
|
||||
for xx,yy in zip(np.ravel(xv), np.ravel(yv))])
|
||||
zv = zs.reshape(xv.shape)
|
||||
|
||||
ax = plt.figure().add_subplot(111, projection='3d')
|
||||
ax.scatter(x,y, [0]*count, marker='.')
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
|
||||
ax.contour(xv, yv, zv, zdir='x', offset=minx-1, cmap=cm.autumn)
|
||||
ax.contour(xv, yv, zv, zdir='y', offset=maxy, cmap=cm.BuGn)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#show_position_chart()
|
||||
#plot_3d_covariance((2,7), np.array([[8.,0],[0,4.]]))
|
||||
#plot_3d_sampled_covariance([2,7], [[8.,0],[0,4.]])
|
||||
#show_residual_chart()
|
||||
|
||||
#show_position_chart()
|
||||
show_x_error_chart(4)
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Thu May 1 16:56:49 2014
|
||||
|
||||
@author: rlabbe
|
||||
"""
|
||||
import numpy as np
|
||||
from matplotlib.patches import Ellipse
|
||||
import matplotlib.pyplot as plt
|
||||
from matplotlib import cm
|
||||
from mpl_toolkits.mplot3d import Axes3D
|
||||
from numpy.random import multivariate_normal
|
||||
import stats
|
||||
|
||||
def show_residual_chart():
|
||||
plt.xlim([0.9,2.5])
|
||||
plt.ylim([1.5,3.5])
|
||||
|
||||
plt.scatter ([1,2,2],[2,3,2.3])
|
||||
plt.scatter ([2],[2.8],marker='o')
|
||||
ax = plt.axes()
|
||||
ax.annotate('', xy=(2,3), xytext=(1,2),
|
||||
arrowprops=dict(arrowstyle='->', ec='#004080',
|
||||
lw=2,
|
||||
shrinkA=3, shrinkB=4))
|
||||
ax.annotate('prediction', xy=(2.04,3.), color='#004080')
|
||||
ax.annotate('measurement', xy=(2.05, 2.28))
|
||||
ax.annotate('prior estimate', xy=(1, 1.9))
|
||||
ax.annotate('residual', xy=(2.04,2.6), color='#e24a33')
|
||||
ax.annotate('new estimate', xy=(2,2.8),xytext=(2.1,2.8),
|
||||
arrowprops=dict(arrowstyle='->', ec="k", shrinkA=3, shrinkB=4))
|
||||
ax.annotate('', xy=(2,3), xytext=(2,2.3),
|
||||
arrowprops=dict(arrowstyle="-",
|
||||
ec="#e24a33",
|
||||
lw=2,
|
||||
shrinkA=5, shrinkB=5))
|
||||
plt.title("Kalman Filter Predict and Update")
|
||||
plt.axis('equal')
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_position_chart():
|
||||
""" Displays 3 measurements at t=1,2,3, with x=1,2,3"""
|
||||
|
||||
plt.scatter ([1,2,3], [1,2,3], s=128, color='#004080')
|
||||
plt.xlim([0,4]);
|
||||
plt.ylim([0,4])
|
||||
|
||||
plt.annotate('t=1', xy=(1,1), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=2', xy=(2,2), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=3', xy=(3,3), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.xlabel("X")
|
||||
plt.ylabel("Y")
|
||||
|
||||
plt.xticks(np.arange(1,4,1))
|
||||
plt.yticks(np.arange(1,4,1))
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_position_prediction_chart():
|
||||
""" displays 3 measurements, with the next position predicted"""
|
||||
|
||||
plt.scatter ([1,2,3], [1,2,3], s=128, color='#004080')
|
||||
|
||||
plt.annotate('t=1', xy=(1,1), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=2', xy=(2,2), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.annotate('t=3', xy=(3,3), xytext=(0,-10),
|
||||
textcoords='offset points', ha='center', va='top')
|
||||
|
||||
plt.xlim([0,5])
|
||||
plt.ylim([0,5])
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Time")
|
||||
|
||||
plt.xticks(np.arange(1,5,1))
|
||||
plt.yticks(np.arange(1,5,1))
|
||||
|
||||
plt.scatter ([4], [4], c='g',s=128, color='#8EBA42')
|
||||
ax = plt.axes()
|
||||
ax.annotate('', xy=(4,4), xytext=(3,3),
|
||||
arrowprops=dict(arrowstyle='->',
|
||||
ec='g',
|
||||
shrinkA=6, shrinkB=5,
|
||||
lw=3))
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_x_error_chart(count):
|
||||
""" displays x=123 with covariances showing error"""
|
||||
|
||||
plt.cla()
|
||||
plt.gca().autoscale(tight=True)
|
||||
|
||||
cov = np.array([[0.03,0], [0,8]])
|
||||
e = stats.covariance_ellipse (cov)
|
||||
|
||||
cov2 = np.array([[0.03,0], [0,4]])
|
||||
e2 = stats.covariance_ellipse (cov2)
|
||||
|
||||
cov3 = np.array([[12,11.95], [11.95,12]])
|
||||
e3 = stats.covariance_ellipse (cov3)
|
||||
|
||||
|
||||
sigma=[1, 4, 9]
|
||||
|
||||
if count >= 1:
|
||||
stats.plot_covariance_ellipse ((0,0), ellipse=e, variance=sigma)
|
||||
|
||||
if count == 2 or count == 3:
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e, variance=sigma)
|
||||
|
||||
if count == 3:
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e3, variance=sigma,
|
||||
edgecolor='r')
|
||||
|
||||
if count == 4:
|
||||
M1 = np.array([[5, 5]]).T
|
||||
m4, cov4 = stats.multivariate_multiply(M1, cov2, M1, cov3)
|
||||
e4 = stats.covariance_ellipse (cov4)
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e, variance=sigma,
|
||||
alpha=0.25)
|
||||
|
||||
stats.plot_covariance_ellipse ((5,5), ellipse=e3, variance=sigma,
|
||||
edgecolor='r', alpha=0.25)
|
||||
stats.plot_covariance_ellipse (m4[:,0], ellipse=e4, variance=sigma)
|
||||
|
||||
#plt.ylim([0,11])
|
||||
#plt.xticks(np.arange(1,4,1))
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Velocity")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
def show_x_with_unobserved():
|
||||
""" shows x=1,2,3 with velocity superimposed on top """
|
||||
|
||||
# plot velocity
|
||||
sigma=[0.5,1.,1.5,2]
|
||||
cov = np.array([[1,1],[1,1.1]])
|
||||
stats.plot_covariance_ellipse ((2,2), cov=cov, variance=sigma, axis_equal=False)
|
||||
|
||||
# plot positions
|
||||
cov = np.array([[0.003,0], [0,12]])
|
||||
sigma=[0.5,1.,1.5,2]
|
||||
e = stats.covariance_ellipse (cov)
|
||||
|
||||
stats.plot_covariance_ellipse ((1,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
stats.plot_covariance_ellipse ((2,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
stats.plot_covariance_ellipse ((3,1), ellipse=e, variance=sigma, axis_equal=False)
|
||||
|
||||
# plot intersection cirle
|
||||
isct = Ellipse(xy=(2,2), width=.2, height=1.2, edgecolor='r', fc='None', lw=4)
|
||||
plt.gca().add_artist(isct)
|
||||
|
||||
plt.ylim([0,11])
|
||||
plt.xlim([0,4])
|
||||
plt.xticks(np.arange(1,4,1))
|
||||
|
||||
plt.xlabel("Position")
|
||||
plt.ylabel("Time")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
|
||||
def plot_3d_covariance(mean, cov):
|
||||
""" plots a 2x2 covariance matrix positioned at mean. mean will be plotted
|
||||
in x and y, and the probability in the z axis.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mean : 2x1 tuple-like object
|
||||
mean for x and y coordinates. For example (2.3, 7.5)
|
||||
|
||||
cov : 2x2 nd.array
|
||||
the covariance matrix
|
||||
|
||||
"""
|
||||
|
||||
# compute width and height of covariance ellipse so we can choose
|
||||
# appropriate ranges for x and y
|
||||
o,w,h = stats.covariance_ellipse(cov,3)
|
||||
# rotate width and height to x,y axis
|
||||
wx = abs(w*np.cos(o) + h*np.sin(o))*1.2
|
||||
wy = abs(h*np.cos(o) - w*np.sin(o))*1.2
|
||||
|
||||
|
||||
# ensure axis are of the same size so everything is plotted with the same
|
||||
# scale
|
||||
if wx > wy:
|
||||
w = wx
|
||||
else:
|
||||
w = wy
|
||||
|
||||
minx = mean[0] - w
|
||||
maxx = mean[0] + w
|
||||
miny = mean[1] - w
|
||||
maxy = mean[1] + w
|
||||
|
||||
xs = np.arange(minx, maxx, (maxx-minx)/40.)
|
||||
ys = np.arange(miny, maxy, (maxy-miny)/40.)
|
||||
xv, yv = np.meshgrid (xs, ys)
|
||||
|
||||
zs = np.array([100.* stats.multivariate_gaussian(np.array([x,y]),mean,cov) \
|
||||
for x,y in zip(np.ravel(xv), np.ravel(yv))])
|
||||
zv = zs.reshape(xv.shape)
|
||||
|
||||
ax = plt.figure().add_subplot(111, projection='3d')
|
||||
ax.plot_surface(xv, yv, zv, rstride=1, cstride=1, cmap=cm.autumn)
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
|
||||
ax.contour(xv, yv, zv, zdir='x', offset=minx-1, cmap=cm.autumn)
|
||||
ax.contour(xv, yv, zv, zdir='y', offset=maxy, cmap=cm.BuGn)
|
||||
|
||||
|
||||
def plot_3d_sampled_covariance(mean, cov):
|
||||
""" plots a 2x2 covariance matrix positioned at mean. mean will be plotted
|
||||
in x and y, and the probability in the z axis.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
mean : 2x1 tuple-like object
|
||||
mean for x and y coordinates. For example (2.3, 7.5)
|
||||
|
||||
cov : 2x2 nd.array
|
||||
the covariance matrix
|
||||
|
||||
"""
|
||||
|
||||
# compute width and height of covariance ellipse so we can choose
|
||||
# appropriate ranges for x and y
|
||||
o,w,h = stats.covariance_ellipse(cov,3)
|
||||
# rotate width and height to x,y axis
|
||||
wx = abs(w*np.cos(o) + h*np.sin(o))*1.2
|
||||
wy = abs(h*np.cos(o) - w*np.sin(o))*1.2
|
||||
|
||||
|
||||
# ensure axis are of the same size so everything is plotted with the same
|
||||
# scale
|
||||
if wx > wy:
|
||||
w = wx
|
||||
else:
|
||||
w = wy
|
||||
|
||||
minx = mean[0] - w
|
||||
maxx = mean[0] + w
|
||||
miny = mean[1] - w
|
||||
maxy = mean[1] + w
|
||||
|
||||
count = 1000
|
||||
x,y = multivariate_normal(mean=mean, cov=cov, size=count).T
|
||||
|
||||
xs = np.arange(minx, maxx, (maxx-minx)/40.)
|
||||
ys = np.arange(miny, maxy, (maxy-miny)/40.)
|
||||
xv, yv = np.meshgrid (xs, ys)
|
||||
|
||||
zs = np.array([100.* stats.multivariate_gaussian(np.array([xx,yy]),mean,cov) \
|
||||
for xx,yy in zip(np.ravel(xv), np.ravel(yv))])
|
||||
zv = zs.reshape(xv.shape)
|
||||
|
||||
ax = plt.figure().add_subplot(111, projection='3d')
|
||||
ax.scatter(x,y, [0]*count, marker='.')
|
||||
|
||||
ax.set_xlabel('X')
|
||||
ax.set_ylabel('Y')
|
||||
|
||||
ax.contour(xv, yv, zv, zdir='x', offset=minx-1, cmap=cm.autumn)
|
||||
ax.contour(xv, yv, zv, zdir='y', offset=maxy, cmap=cm.BuGn)
|
||||
|
||||
|
||||
from filterpy.common import plot_covariance_ellipse
|
||||
def plot_3_covariances():
|
||||
|
||||
P = [[2, 0], [0, 2]]
|
||||
plt.subplot(131)
|
||||
plot_covariance_ellipse((2, 7), cov=P, facecolor='g', alpha=0.2,
|
||||
title='|2 0|\n|0 2|', axis_equal=False)
|
||||
plt.ylim((4, 10))
|
||||
plt.gca().set_aspect('equal', adjustable='box')
|
||||
|
||||
plt.subplot(132)
|
||||
P = [[2, 0], [0, 9]]
|
||||
plt.ylim((4, 10))
|
||||
plt.gca().set_aspect('equal', adjustable='box')
|
||||
plot_covariance_ellipse((2, 7), P, facecolor='g', alpha=0.2,
|
||||
axis_equal=False, title='|2 0|\n|0 9|')
|
||||
|
||||
plt.subplot(133)
|
||||
P = [[2, 1.2], [1.2, 2]]
|
||||
plt.ylim((4, 10))
|
||||
plt.gca().set_aspect('equal', adjustable='box')
|
||||
plot_covariance_ellipse((2, 7), P, facecolor='g', alpha=0.2,
|
||||
axis_equal=False,
|
||||
title='|2 1.2|\n|1.2 2|')
|
||||
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_correlation_covariance():
|
||||
P = [[4, 3.9], [3.9, 4]]
|
||||
plot_covariance_ellipse((5, 10), P, edgecolor='k',
|
||||
variance=[1, 2**2, 3**2])
|
||||
plt.xlabel('X')
|
||||
plt.ylabel('Y')
|
||||
plt.gca().autoscale(tight=True)
|
||||
plt.axvline(7.5, ls='--', lw=1)
|
||||
plt.axhline(12.5, ls='--', lw=1)
|
||||
plt.scatter(7.5, 12.5, s=2000, alpha=0.5)
|
||||
plt.title('|4.0 3.9|\n|3.9 4.0|')
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
#show_position_chart()
|
||||
#plot_3d_covariance((2,7), np.array([[8.,0],[0,4.]]))
|
||||
#plot_3d_sampled_covariance([2,7], [[8.,0],[0,4.]])
|
||||
#show_residual_chart()
|
||||
|
||||
#show_position_chart()
|
||||
show_x_error_chart(4)
|
||||
|
||||
|
||||
183
code/particle_filter.py
Normal file
183
code/particle_filter.py
Normal file
@@ -0,0 +1,183 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Created on Sat May 2 09:46:06 2015
|
||||
|
||||
@author: Roger
|
||||
"""
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
from numpy.random import uniform
|
||||
from numpy.random import randn
|
||||
import scipy.stats
|
||||
import matplotlib.pyplot as plt
|
||||
import random
|
||||
|
||||
|
||||
class ParticleFilter(object):
|
||||
|
||||
def __init__(self, N, x_range, y_range):
|
||||
self.particles = np.zeros((N, 4))
|
||||
self.N = N
|
||||
self.x_range = x_range
|
||||
self.y_range = y_range
|
||||
|
||||
# assign
|
||||
self.weights = np.array([1./N] * N)
|
||||
self.particles[:, 0] = uniform(0, x_range, size=N)
|
||||
self.particles[:, 1] = uniform(0, y_range, size=N)
|
||||
self.particles[:, 3] = uniform(0, 2*np.pi, size=N)
|
||||
|
||||
|
||||
|
||||
def create_particles(self, mu, var):
|
||||
self.particles[:, 0] = mu[0] + randn(self.N)* np.sqrt(var)
|
||||
self.particles[:, 1] = mu[1] + randn(self.N)* np.sqrt(var)
|
||||
|
||||
def create_particle(self):
|
||||
return [uniform(0, self.x_range), uniform(0, self.y_range), 0, 0]
|
||||
|
||||
|
||||
def assign_speed_by_gaussian(self, speed, var):
|
||||
""" move every particle by the specified speed (assuming time=1.)
|
||||
with the specified variance, assuming Gaussian distribution. """
|
||||
|
||||
self.particles[:, 2] = np.random.normal(speed, var, self.N)
|
||||
|
||||
def control(self, dx):
|
||||
self.particles[:, 0] += dx[0]
|
||||
self.particles[:, 1] += dx[1]
|
||||
|
||||
|
||||
def move(self, h, v, t=1.):
|
||||
""" move the particles according to their speed and direction for the
|
||||
specified time duration t"""
|
||||
h = math.atan2(h[1], h[0])
|
||||
h = randn(self.N) * .4 + h
|
||||
vs = v + randn(self.N) * 0.1
|
||||
vx = v * np.cos(h)
|
||||
vy = v * np.sin(h)
|
||||
|
||||
|
||||
#vx = self.particles[:, 2] * np.cos(self.particles[:, 3]) + randn(self.N)*0.5
|
||||
#vy = self.particles[:, 2] * np.sin(self.particles[:, 3]) + randn(self.N)*0.5
|
||||
self.particles[:, 0] = (self.particles[:, 0] + vx*t)
|
||||
self.particles[:, 1] = (self.particles[:, 1] + vy*t)
|
||||
|
||||
|
||||
def move2(self, u):
|
||||
dx = u[0] + randn(self.N) * 1.9
|
||||
dy = u[1] + randn(self.N) * 1.9
|
||||
self.particles[:, 0] = (self.particles[:, 0] + dx)
|
||||
self.particles[:, 1] = (self.particles[:, 1] + dy)
|
||||
|
||||
|
||||
def weight(self, z, var):
|
||||
dist = np.sqrt((self.particles[:, 0] - z[0])**2 +
|
||||
(self.particles[:, 1] - z[1])**2)
|
||||
|
||||
# simplification assumes variance is invariant to world projection
|
||||
n = scipy.stats.norm(0, np.sqrt(var))
|
||||
prob = n.pdf(dist)
|
||||
|
||||
# particles far from a measurement will give us 0.0 for a probability
|
||||
# due to floating point limits. Once we hit zero we can never recover,
|
||||
# so add some small nonzero value to all points.
|
||||
prob += 1.e-12
|
||||
self.weights *= prob
|
||||
self.weights /= sum(self.weights) # normalize
|
||||
|
||||
def neff(self):
|
||||
return 1. / np.sum(np.square(self.weights))
|
||||
|
||||
def resample(self):
|
||||
|
||||
p = np.zeros((self.N, 4))
|
||||
w = np.zeros(self.N)
|
||||
|
||||
cumsum = np.cumsum(self.weights)
|
||||
for i in range(self.N):
|
||||
index = np.searchsorted(cumsum, random.random())
|
||||
p[i] = self.particles[index]
|
||||
w[i] = self.weights[index]
|
||||
|
||||
self.particles = p
|
||||
self.weights = w / np.sum(w)
|
||||
|
||||
def estimate(self):
|
||||
""" returns mean and variance """
|
||||
pos = self.particles[:, 0:2]
|
||||
mu = np.average(pos, weights=self.weights, axis=0)
|
||||
var = np.average((pos - mu)**2, weights=self.weights, axis=0)
|
||||
|
||||
return mu, var
|
||||
|
||||
|
||||
def plot(pf, xlim=100, ylim=100, weights=True):
|
||||
|
||||
if weights:
|
||||
a = plt.subplot(221)
|
||||
a.cla()
|
||||
plt.xlim(0, ylim)
|
||||
plt.ylim(0, 1)
|
||||
plt.scatter(pf.particles[:, 0], pf.weights, marker='.', s=1)
|
||||
a = plt.subplot(224)
|
||||
a.cla()
|
||||
plt.scatter(pf.weights, pf.particles[:, 1], marker='.', s=1)
|
||||
plt.ylim(0, xlim)
|
||||
plt.xlim(0, 1)
|
||||
|
||||
a = plt.subplot(223)
|
||||
a.cla()
|
||||
else:
|
||||
plt.cla()
|
||||
plt.scatter(pf.particles[:, 0], pf.particles[:, 1], marker='.', s=1)
|
||||
plt.xlim(0, xlim)
|
||||
plt.ylim(0, ylim)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pf = ParticleFilter(5000, 100, 100)
|
||||
pf.particles[:,3] = np.random.randn(pf.N)*np.radians(10) + np.radians(45)
|
||||
|
||||
z = np.array([20, 20])
|
||||
pf.create_particles(z, 40)
|
||||
|
||||
mu0 = np.array([0., 0.])
|
||||
|
||||
for x in range(60):
|
||||
|
||||
z[0] += 1.0 + randn()*0.3
|
||||
z[1] += 1.0 + randn()*0.3
|
||||
|
||||
|
||||
pf.move2((1,1))
|
||||
pf.weight(z, 5.2)
|
||||
# pf.weight((z[0] + randn()*0.2, z[1] + randn()*0.2), 5.2)
|
||||
pf.resample()
|
||||
mu, var = pf.estimate()
|
||||
if x == 0:
|
||||
mu0 = mu
|
||||
print(mu - z)
|
||||
print('neff', pf.neff())
|
||||
#print(var)
|
||||
|
||||
plot(pf, weights=False)
|
||||
plt.scatter(z[0], z[1], c='r', s=40)
|
||||
|
||||
plt.scatter(mu[0], mu[1], c='g', s=100)#,
|
||||
#s=min(500, abs((1./np.sum(var)))*20), alpha=0.5)
|
||||
plt.tight_layout()
|
||||
plt.pause(.02)
|
||||
|
||||
#pf.assign_speed_by_gaussian(1, 1.5)
|
||||
#pf.move(h=[1,1], v=1.4, t=1)
|
||||
#pf.control(mu-mu0)
|
||||
mu0 = mu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user