|
1 | 1 | """ |
2 | 2 |
|
3 | | -Extended kalman filter localization sample |
| 3 | +Extended kalman filter (EKF) localization sample |
4 | 4 |
|
5 | 5 | author: Atsushi Sakai (@Atsushi_twi) |
6 | 6 |
|
7 | 7 | """ |
8 | 8 |
|
| 9 | +import numpy as np |
| 10 | +import math |
| 11 | +import matplotlib.pyplot as plt |
| 12 | + |
| 13 | + |
| 14 | +# Covariance Matrix for motion |
| 15 | +# Q=diag([0.1 0.1 toRadian(1) 0.05]).^2; |
| 16 | + |
| 17 | +# % Covariance Matrix for observation |
| 18 | +# R=diag([1.5 1.5 toRadian(3) 0.05]).^2; |
| 19 | + |
| 20 | +# % Simulation parameter |
| 21 | +# global Qsigma |
| 22 | +# Qsigma=diag([0.1 toRadian(20)]).^2; %[v yawrate] |
| 23 | + |
| 24 | +# global Rsigma |
| 25 | +# Rsigma=diag([1.5 1.5 toRadian(3) 0.05]).^2;%[x y z yaw v] |
| 26 | + |
| 27 | +# PEst = eye(4); |
| 28 | + |
| 29 | +# u=doControl(time); |
| 30 | +# % Observation |
| 31 | +# [z,xTrue,xd,u]=Observation(xTrue, xd, u); |
| 32 | + |
| 33 | +# % ------ Kalman Filter -------- |
| 34 | +# % Predict |
| 35 | +# xPred = f(xEst, u); |
| 36 | +# F=jacobF(xPred, u); |
| 37 | +# PPred= F*PEst*F' + Q; |
| 38 | + |
| 39 | +# % Update |
| 40 | +# H=jacobH(xPred); |
| 41 | +# y = z - h(xPred); |
| 42 | +# S = H*PPred*H' + R; |
| 43 | +# K = PPred*H'*inv(S); |
| 44 | +# xEst = xPred + K*y; |
| 45 | +# PEst = (eye(size(xEst,1)) - K*H)*PPred; |
| 46 | + |
| 47 | +# % Simulation Result |
| 48 | +# result.time=[result.time; time]; |
| 49 | +# result.xTrue=[result.xTrue; xTrue']; |
| 50 | +# result.xd=[result.xd; xd']; |
| 51 | +# result.xEst=[result.xEst;xEst']; |
| 52 | +# result.z=[result.z; z']; |
| 53 | +# result.PEst=[result.PEst; diag(PEst)']; |
| 54 | +# result.u=[result.u; u']; |
| 55 | + |
| 56 | +# %Animation (remove some flames) |
| 57 | +# if rem(i,5)==0 |
| 58 | +# %hold off; |
| 59 | +# plot(result.xTrue(:,1),result.xTrue(:,2),'.b');hold on; |
| 60 | +# plot(result.z(:,1),result.z(:,2),'.g');hold on; |
| 61 | +# plot(result.xd(:,1),result.xd(:,2),'.k');hold on; |
| 62 | +# plot(result.xEst(:,1),result.xEst(:,2),'.r');hold on; |
| 63 | +# ShowErrorEllipse(xEst,PEst); |
| 64 | +# axis equal; |
| 65 | +# grid on; |
| 66 | +# drawnow; |
| 67 | +# %movcount=movcount+1; |
| 68 | +# %mov(movcount) = getframe(gcf);% アニメーションのフレームをゲットする |
| 69 | +# end |
| 70 | +# end |
| 71 | +# toc |
| 72 | +# %アニメーション保存 |
| 73 | +# %movie2avi(mov,'movie.avi'); |
| 74 | + |
| 75 | +# DrawGraph(result); |
| 76 | + |
| 77 | +# function ShowErrorEllipse(xEst,PEst) |
| 78 | +# %誤差分散円を計算し、表示する関数 |
| 79 | +# Pxy=PEst(1:2,1:2);%x,yの共分散を取得 |
| 80 | +# [eigvec, eigval]=eig(Pxy);%固有値と固有ベクトルの計算 |
| 81 | +# %固有値の大きい方のインデックスを探す |
| 82 | +# if eigval(1,1)>=eigval(2,2) |
| 83 | +# bigind=1; |
| 84 | +# smallind=2; |
| 85 | +# else |
| 86 | +# bigind=2; |
| 87 | +# smallind=1; |
| 88 | +# end |
| 89 | + |
| 90 | +# chi=9.21;%誤差楕円のカイの二乗分布値 99% |
| 91 | + |
| 92 | +# %楕円描写 |
| 93 | +# t=0:10:360; |
| 94 | +# a=sqrt(eigval(bigind,bigind)*chi); |
| 95 | +# b=sqrt(eigval(smallind,smallind)*chi); |
| 96 | +# x=[a*cosd(t); |
| 97 | +# b*sind(t)]; |
| 98 | +# %誤差楕円の角度を計算 |
| 99 | +# angle = atan2(eigvec(bigind,2),eigvec(bigind,1)); |
| 100 | +# if(angle < 0) |
| 101 | +# angle = angle + 2*pi; |
| 102 | +# end |
| 103 | + |
| 104 | +# %誤差楕円の回転 |
| 105 | +# R=[cos(angle) sin(angle); |
| 106 | +# -sin(angle) cos(angle)]; |
| 107 | +# x=R*x; |
| 108 | +# plot(x(1,:)+xEst(1),x(2,:)+xEst(2)) |
| 109 | + |
| 110 | + |
| 111 | +# function x = f(x, u) |
| 112 | +# % Motion Model |
| 113 | +# global dt; |
| 114 | + |
| 115 | +# F = [1 0 0 0 |
| 116 | +# 0 1 0 0 |
| 117 | +# 0 0 1 0 |
| 118 | +# 0 0 0 0]; |
| 119 | + |
| 120 | +# B = [ |
| 121 | +# dt*cos(x(3)) 0 |
| 122 | +# dt*sin(x(3)) 0 |
| 123 | +# 0 dt |
| 124 | +# 1 0]; |
| 125 | + |
| 126 | +# x= F*x+B*u; |
| 127 | + |
| 128 | +# function jF = jacobF(x, u) |
| 129 | +# % Jacobian of Motion Model |
| 130 | +# global dt; |
| 131 | + |
| 132 | +# jF=[ |
| 133 | +# 1 0 0 0 |
| 134 | +# 0 1 0 0 |
| 135 | +# -dt*u(1)*sin(x(3)) dt*u(1)*cos(x(3)) 1 0 |
| 136 | +# dt*cos(x(3)) dt*sin(x(3)) 0 1]; |
| 137 | + |
| 138 | +# function z = h(x) |
| 139 | +# %Observation Model |
| 140 | + |
| 141 | +# H = [1 0 0 0 |
| 142 | +# 0 1 0 0 |
| 143 | +# 0 0 1 0 |
| 144 | +# 0 0 0 1 ]; |
| 145 | + |
| 146 | +# z=H*x; |
| 147 | + |
| 148 | +# function jH = jacobH(x) |
| 149 | +# %Jacobian of Observation Model |
| 150 | + |
| 151 | +# jH =[1 0 0 0 |
| 152 | +# 0 1 0 0 |
| 153 | +# 0 0 1 0 |
| 154 | +# 0 0 0 1]; |
| 155 | + |
| 156 | + |
| 157 | +# function [z, x, xd, u] = Observation(x, xd, u) |
| 158 | +# %Calc Observation from noise prameter |
| 159 | +# global Qsigma; |
| 160 | +# global Rsigma; |
| 161 | + |
| 162 | +# x=f(x, u);% Ground Truth |
| 163 | +# u=u+Qsigma*randn(2,1);%add Process Noise |
| 164 | +# xd=f(xd, u);% Dead Reckoning |
| 165 | +# z=h(x+Rsigma*randn(4,1));%Simulate Observation |
| 166 | + |
| 167 | + |
| 168 | +# function []=DrawGraph(result) |
| 169 | +# %Plot Result |
| 170 | + |
| 171 | +# figure(1); |
| 172 | +# x=[ result.xTrue(:,1:2) result.xEst(:,1:2) result.z(:,1:2)]; |
| 173 | +# set(gca, 'fontsize', 16, 'fontname', 'times'); |
| 174 | +# plot(x(:,5), x(:,6),'.g','linewidth', 4); hold on; |
| 175 | +# plot(x(:,1), x(:,2),'-.b','linewidth', 4); hold on; |
| 176 | +# plot(x(:,3), x(:,4),'r','linewidth', 4); hold on; |
| 177 | +# plot(result.xd(:,1), result.xd(:,2),'--k','linewidth', 4); hold on; |
| 178 | + |
| 179 | +# title('EKF Localization Result', 'fontsize', 16, 'fontname', 'times'); |
| 180 | +# xlabel('X (m)', 'fontsize', 16, 'fontname', 'times'); |
| 181 | +# ylabel('Y (m)', 'fontsize', 16, 'fontname', 'times'); |
| 182 | +# legend('Ground Truth','GPS','Dead Reckoning','EKF','Error Ellipse'); |
| 183 | +# grid on; |
| 184 | +# axis equal; |
| 185 | + |
| 186 | +# function angle=Pi2Pi(angle) |
| 187 | +# %ロボットの角度を-pi~piの範囲に補正する関数 |
| 188 | +# angle = mod(angle, 2*pi); |
| 189 | + |
| 190 | +# i = find(angle>pi); |
| 191 | +# angle(i) = angle(i) - 2*pi; |
| 192 | + |
| 193 | +# i = find(angle<-pi); |
| 194 | +# angle(i) = angle(i) + 2*pi; |
| 195 | + |
| 196 | + |
| 197 | +# function radian = toRadian(degree) |
| 198 | +# % degree to radian |
| 199 | +# radian = degree/180*pi; |
| 200 | + |
| 201 | +# function degree = toDegree(radian) |
| 202 | +# % radian to degree |
| 203 | +# degree = radian/pi*180; |
| 204 | + |
| 205 | +DT = 0.1 # time tick [s] |
| 206 | +SIM_TIME = 60.0 # simulation time [s] |
| 207 | + |
| 208 | + |
| 209 | +def do_control(): |
| 210 | + v = 1.0 # [m/s] |
| 211 | + yawrate = 0.1 # [rad/s] |
| 212 | + |
| 213 | + u = np.matrix([v, yawrate]).T |
| 214 | + |
| 215 | + return u |
| 216 | + |
| 217 | + |
| 218 | +# z, xTrue, xd, u = Observation(xTrue, xd, u) |
| 219 | +def observation(xTrue, xd, u): |
| 220 | + |
| 221 | + xTrue = motion_model(xTrue, u) |
| 222 | + |
| 223 | + return xTrue |
| 224 | + |
| 225 | + |
| 226 | +def motion_model(x, u): |
| 227 | + |
| 228 | + F = np.matrix([[1.0, 0, 0, 0], |
| 229 | + [0, 1.0, 0, 0], |
| 230 | + [0, 0, 1.0, 0], |
| 231 | + [0, 0, 0, 0]]) |
| 232 | + |
| 233 | + B = np.matrix([[DT * math.cos(x[2, 0]), 0], |
| 234 | + [DT * math.sin(x[2, 0]), 0], |
| 235 | + [0.0, DT], |
| 236 | + [1.0, 0.0]]) |
| 237 | + |
| 238 | + x = F * x + B * u |
| 239 | + |
| 240 | + return x |
| 241 | + |
9 | 242 |
|
10 | 243 | def main(): |
11 | 244 | print(__file__ + " start!!") |
12 | 245 |
|
| 246 | + time = 0.0 |
| 247 | + # State Vector [x y yaw v]' |
| 248 | + # xEst = np.matrix(np.zeros((3, 1))) |
| 249 | + xTrue = np.matrix(np.zeros((4, 1))) |
| 250 | + |
| 251 | + # Dead Reckoning |
| 252 | + xDR = np.matrix(np.zeros((4, 1))) |
| 253 | + |
| 254 | + # Observation vector |
| 255 | + # z = np.matrix(np.zeros((2, 1))) |
| 256 | + |
| 257 | + while SIM_TIME >= time: |
| 258 | + # print(time) |
| 259 | + time += DT |
| 260 | + |
| 261 | + u = do_control() |
| 262 | + |
| 263 | + xTrue = observation(xTrue, xDR, u) |
| 264 | + |
| 265 | + plt.plot(xTrue[0, 0], xTrue[1, 0], ".r") |
| 266 | + plt.axis("equal") |
| 267 | + plt.grid(True) |
| 268 | + plt.pause(0.001) |
| 269 | + |
13 | 270 |
|
14 | 271 | if __name__ == '__main__': |
15 | 272 | main() |
0 commit comments