11"""
22
3- Extended Kalman Filter based SLAM example
3+ Extended Kalman Filter SLAM example
44
55author: Atsushi Sakai (@Atsushi_twi)
66
1414Cx = np .diag ([0.5 , 0.5 , math .radians (30.0 )])** 2
1515
1616# Simulation parameter
17- Qsim = np .diag ([0.2 ])** 2
17+ Qsim = np .diag ([0.2 , math . radians ( 1.0 ) ])** 2
1818Rsim = np .diag ([1.0 , math .radians (10.0 )])** 2
1919
2020DT = 0.1 # time tick [s]
2121SIM_TIME = 50.0 # simulation time [s]
2222MAX_RANGE = 20.0 # maximum observation range
23-
24- M_DIST_TH = 1.0 # Threshold of Mahalanobis distance for data association.
25-
23+ M_DIST_TH = 2.0 # Threshold of Mahalanobis distance for data association.
2624STATE_SIZE = 3 # State size [x,y,yaw]
2725LM_SIZE = 2 # LM srate size [x,y]
2826
2927show_animation = True
3028
3129
30+ def ekf_slam (xEst , PEst , u , z ):
31+
32+ # Predict
33+ S = STATE_SIZE
34+ xEst [0 :S ] = motion_model (xEst [0 :S ], u )
35+ G , Fx = jacob_motion (xEst [0 :S ], u )
36+ PEst [0 :S , 0 :S ] = G .T * PEst [0 :S , 0 :S ] * G + Fx .T * Cx * Fx
37+ initP = np .eye (2 )
38+
39+ # Update
40+ for iz in range (len (z [:, 0 ])): # for each observation
41+ minid = search_correspond_LM_ID (xEst , PEst , z [iz , 0 :2 ])
42+
43+ nLM = calc_n_LM (xEst )
44+ if minid == nLM :
45+ print ("New LM" )
46+ # Extend state and covariance matrix
47+ xAug = np .vstack ((xEst , calc_LM_Pos (xEst , z [iz , :])))
48+ PAug = np .vstack ((np .hstack ((PEst , np .zeros ((len (xEst ), LM_SIZE )))),
49+ np .hstack ((np .zeros ((LM_SIZE , len (xEst ))), initP ))))
50+ xEst = xAug
51+ PEst = PAug
52+
53+ lm = get_LM_Pos_from_state (xEst , minid )
54+ y , S , H = calc_innovation (lm , xEst , PEst , z [iz , 0 :2 ], minid )
55+
56+ K = PEst * H .T * np .linalg .inv (S )
57+ xEst = xEst + K * y
58+ PEst = (np .eye (len (xEst )) - K * H ) * PEst
59+
60+ xEst [2 ] = pi_2_pi (xEst [2 ])
61+
62+ return xEst , PEst
63+
64+
3265def calc_input ():
3366 v = 1.0 # [m/s]
3467 yawrate = 0.1 # [rad/s]
@@ -50,8 +83,9 @@ def observation(xTrue, xd, u, RFID):
5083 d = math .sqrt (dx ** 2 + dy ** 2 )
5184 angle = pi_2_pi (math .atan2 (dy , dx ))
5285 if d <= MAX_RANGE :
53- # dn = d + np.random.randn() * Qsim[0, 0] # add noise
54- zi = np .matrix ([d , angle , i ])
86+ dn = d + np .random .randn () * Qsim [0 , 0 ] # add noise
87+ anglen = angle + np .random .randn () * Qsim [1 , 1 ] # add noise
88+ zi = np .matrix ([dn , anglen , i ])
5589 z = np .vstack ((z , zi ))
5690
5791 # add noise to input
@@ -108,23 +142,32 @@ def calc_LM_Pos(x, z):
108142 return zp
109143
110144
111- def calc_mahalanobis_dist (xAug , PAug , z , iz ):
145+ def get_LM_Pos_from_state (x , ind ):
146+
147+ lm = x [STATE_SIZE + LM_SIZE * ind : STATE_SIZE + LM_SIZE * (ind + 1 ), :]
148+
149+ return lm
150+
151+
152+ def search_correspond_LM_ID (xAug , PAug , zi ):
153+ """
154+ Landmark association with Mahalanobis distance
155+ """
112156
113157 nLM = calc_n_LM (xAug )
114158
115159 mdist = []
116160
117161 for i in range (nLM ):
162+ lm = get_LM_Pos_from_state (xAug , i )
163+ y , S , H = calc_innovation (lm , xAug , PAug , zi , i )
164+ mdist .append (y .T * np .linalg .inv (S ) * y )
165+
166+ mdist .append (M_DIST_TH ) # new landmark
118167
119- if i == nLM - 1 :
120- mdist .append (M_DIST_TH )
121- else :
122- lm = xAug [STATE_SIZE + LM_SIZE *
123- i : STATE_SIZE + LM_SIZE * (i + 1 ), :]
124- y , S , H = calc_innovation (lm , xAug , PAug , z [iz , 0 :2 ], i )
125- mdist .append (y .T * np .linalg .inv (S ) * y )
168+ minid = mdist .index (min (mdist ))
126169
127- return mdist
170+ return minid
128171
129172
130173def calc_innovation (lm , xEst , PEst , z , LMid ):
@@ -133,6 +176,7 @@ def calc_innovation(lm, xEst, PEst, z, LMid):
133176 zangle = math .atan2 (delta [1 ], delta [0 ]) - xEst [2 ]
134177 zp = [math .sqrt (q ), pi_2_pi (zangle )]
135178 y = (z - zp ).T
179+ y [1 ] = pi_2_pi (y [1 ])
136180 H = jacobH (q , delta , xEst , LMid + 1 )
137181 S = H * PEst * H .T + Cx [0 :2 , 0 :2 ]
138182
@@ -167,56 +211,15 @@ def pi_2_pi(angle):
167211 return angle
168212
169213
170- def ekf_slam (xEst , PEst , u , z ):
171- # Predict
172- xEst [0 :STATE_SIZE ] = motion_model (xEst [0 :STATE_SIZE ], u )
173- G , Fx = jacob_motion (xEst [0 :STATE_SIZE ], u )
174- PEst [0 :STATE_SIZE , 0 :STATE_SIZE ] = G .T * \
175- PEst [0 :STATE_SIZE , 0 :STATE_SIZE ] * G + Fx .T * Cx * Fx
176- initP = np .eye (2 )
177-
178- # Update
179- for iz in range (len (z [:, 0 ])): # for each observation
180- zp = calc_LM_Pos (xEst , z [iz , :])
181-
182- # Extend state and covariance matrix
183- xAug = np .vstack ((xEst , zp ))
184- PAug = np .vstack ((np .hstack ((PEst , np .zeros ((len (xEst ), LM_SIZE )))),
185- np .hstack ((np .zeros ((LM_SIZE , len (xEst ))), initP ))))
186-
187- mah_dists = calc_mahalanobis_dist (xAug , PAug , z , iz )
188- minid = mah_dists .index (min (mah_dists ))
189-
190- nLM = calc_n_LM (xAug )
191- if minid == (nLM - 1 ):
192- print ("New LM" )
193- xEst = xAug
194- PEst = PAug
195- else :
196- print ("Old LM" )
197-
198- lm = xEst [STATE_SIZE + LM_SIZE *
199- minid : STATE_SIZE + LM_SIZE * (minid + 1 ), :]
200- y , S , H = calc_innovation (lm , xEst , PEst , z [iz , 0 :2 ], minid )
201-
202- K = PEst * H .T * np .linalg .inv (S )
203- xEst = xEst + K * y
204- PEst = (np .eye (len (xEst )) - K * H ) * PEst
205-
206- xEst [2 ] = pi_2_pi (xEst [2 ])
207-
208- return xEst , PEst
209-
210-
211214def main ():
212215 print (__file__ + " start!!" )
213216
214217 time = 0.0
215218
216219 # RFID positions [x, y]
217- RFID = np .array ([[10.0 , 0 .0 ],
218- [10 .0 , 10.0 ],
219- [0 .0 , 15.0 ],
220+ RFID = np .array ([[10.0 , - 2 .0 ],
221+ [15 .0 , 10.0 ],
222+ [3 .0 , 15.0 ],
220223 [- 5.0 , 20.0 ]])
221224
222225 # State Vector [x y yaw v]'
@@ -246,8 +249,6 @@ def main():
246249 hxDR = np .hstack ((hxDR , xDR ))
247250 hxTrue = np .hstack ((hxTrue , xTrue ))
248251
249- # print(xEst)
250-
251252 if show_animation :
252253 plt .cla ()
253254
@@ -268,7 +269,6 @@ def main():
268269 plt .axis ("equal" )
269270 plt .grid (True )
270271 plt .pause (0.001 )
271- # input()
272272
273273
274274if __name__ == '__main__' :
0 commit comments