[关闭]
@iStarLee 2019-08-29T16:02:46.000000Z 字数 7643 阅读 339

EKF-SLAM Project Report


SLAM


1 EKF Filter design

In this simulation, the robot has a state vector includes 4 states at time .

x, y are a 2D x-y position, is orientation, and v is velocity. In the code, "xEst" means the state vector. And, is covariace matrix of the state, is covariance matrix of process noise, is covariance matrix of observation noise at time . The robot has a speed sensor and a gyro sensor. So, the input vecor can be used as each time step

Also, the robot has a GNSS sensor, it means that the robot can observe x-y position at each time.
The input and observation vector includes sensor noise.In the code, "observation" function generates the input and observation vector with noise

2 Motion Model Design

The robot model is


So, the motion model is
where

is a time interval.

Its Jacobian matrix is

 

3 Observation Model Design

The robot can get x-y position infomation from GPS. So GPS Observation model is


where

Its Jacobian matrix is

4 Extented Kalman Filter Summary

Localization process using Extendted Kalman Filter is

4.1 Predict Steps


4.2 Update Steps






5 Experiments

5.1 Parameter Settings

The robot is running at linear speed 1.0 m/s, twist speed is 0.1 rad/s. There are four already known landmark, which are located at (10,-2),(15,10),(3,15),(-5,20). The motion model noise and observation noise are gaussian noise, the mean is 0, the covariance is [0,1].

5.2 Results

In our EKF simulation system, we show the three kind of line results.

5 Appendix: Python Code

  1. """
  2. Extended Kalman Filter SLAM example
  3. author: Atsushi Sakai (@Atsushi_twi)
  4. """
  5. import math
  6. import numpy as np
  7. import matplotlib.pyplot as plt
  8. # EKF state covariance
  9. Cx = np.diag([0.5, 0.5, np.deg2rad(30.0)])**2
  10. # Simulation parameter
  11. Qsim = np.diag([0.2, np.deg2rad(1.0)])**2
  12. Rsim = np.diag([1.0, np.deg2rad(10.0)])**2
  13. DT = 0.1 # time tick [s]
  14. SIM_TIME = 50.0 # simulation time [s]
  15. MAX_RANGE = 20.0 # maximum observation range
  16. M_DIST_TH = 2.0 # Threshold of Mahalanobis distance for data association.
  17. STATE_SIZE = 3 # State size [x,y,yaw]
  18. LM_SIZE = 2 # LM state size [x,y]
  19. show_animation = True
  20. def ekf_slam(xEst, PEst, u, z):
  21. # Predict
  22. S = STATE_SIZE
  23. xEst[0:S] = motion_model(xEst[0:S], u)
  24. G, Fx = jacob_motion(xEst[0:S], u)
  25. PEst[0:S, 0:S] = G.T * PEst[0:S, 0:S] * G + Fx.T * Cx * Fx
  26. initP = np.eye(2)
  27. # Update
  28. for iz in range(len(z[:, 0])): # for each observation
  29. minid = search_correspond_LM_ID(xEst, PEst, z[iz, 0:2])
  30. nLM = calc_n_LM(xEst)
  31. if minid == nLM:
  32. print("New LM")
  33. # Extend state and covariance matrix
  34. xAug = np.vstack((xEst, calc_LM_Pos(xEst, z[iz, :])))
  35. PAug = np.vstack((np.hstack((PEst, np.zeros((len(xEst), LM_SIZE)))),
  36. np.hstack((np.zeros((LM_SIZE, len(xEst))), initP))))
  37. xEst = xAug
  38. PEst = PAug
  39. lm = get_LM_Pos_from_state(xEst, minid)
  40. y, S, H = calc_innovation(lm, xEst, PEst, z[iz, 0:2], minid)
  41. K = (PEst @ H.T) @ np.linalg.inv(S)
  42. xEst = xEst + (K @ y)
  43. PEst = (np.eye(len(xEst)) - (K @ H)) @ PEst
  44. xEst[2] = pi_2_pi(xEst[2])
  45. return xEst, PEst
  46. def calc_input():
  47. v = 1.0 # [m/s]
  48. yawrate = 0.1 # [rad/s]
  49. u = np.array([[v, yawrate]]).T
  50. return u
  51. def observation(xTrue, xd, u, RFID):
  52. xTrue = motion_model(xTrue, u)
  53. # add noise to gps x-y
  54. z = np.zeros((0, 3))
  55. for i in range(len(RFID[:, 0])):
  56. dx = RFID[i, 0] - xTrue[0, 0]
  57. dy = RFID[i, 1] - xTrue[1, 0]
  58. d = math.sqrt(dx**2 + dy**2)
  59. angle = pi_2_pi(math.atan2(dy, dx) - xTrue[2, 0])
  60. if d <= MAX_RANGE:
  61. dn = d + np.random.randn() * Qsim[0, 0] # add noise
  62. anglen = angle + np.random.randn() * Qsim[1, 1] # add noise
  63. zi = np.array([dn, anglen, i])
  64. z = np.vstack((z, zi))
  65. # add noise to input
  66. ud = np.array([[
  67. u[0, 0] + np.random.randn() * Rsim[0, 0],
  68. u[1, 0] + np.random.randn() * Rsim[1, 1]]]).T
  69. xd = motion_model(xd, ud)
  70. return xTrue, z, xd, ud
  71. def motion_model(x, u):
  72. F = np.array([[1.0, 0, 0],
  73. [0, 1.0, 0],
  74. [0, 0, 1.0]])
  75. B = np.array([[DT * math.cos(x[2, 0]), 0],
  76. [DT * math.sin(x[2, 0]), 0],
  77. [0.0, DT]])
  78. x = (F @ x) + (B @ u)
  79. return x
  80. def calc_n_LM(x):
  81. n = int((len(x) - STATE_SIZE) / LM_SIZE)
  82. return n
  83. def jacob_motion(x, u):
  84. Fx = np.hstack((np.eye(STATE_SIZE), np.zeros(
  85. (STATE_SIZE, LM_SIZE * calc_n_LM(x)))))
  86. jF = np.array([[0.0, 0.0, -DT * u[0] * math.sin(x[2, 0])],
  87. [0.0, 0.0, DT * u[0] * math.cos(x[2, 0])],
  88. [0.0, 0.0, 0.0]])
  89. G = np.eye(STATE_SIZE) + Fx.T * jF * Fx
  90. return G, Fx,
  91. def calc_LM_Pos(x, z):
  92. zp = np.zeros((2, 1))
  93. zp[0, 0] = x[0, 0] + z[0] * math.cos(x[2, 0] + z[1])
  94. zp[1, 0] = x[1, 0] + z[0] * math.sin(x[2, 0] + z[1])
  95. #zp[0, 0] = x[0, 0] + z[0, 0] * math.cos(x[2, 0] + z[0, 1])
  96. #zp[1, 0] = x[1, 0] + z[0, 0] * math.sin(x[2, 0] + z[0, 1])
  97. return zp
  98. def get_LM_Pos_from_state(x, ind):
  99. lm = x[STATE_SIZE + LM_SIZE * ind: STATE_SIZE + LM_SIZE * (ind + 1), :]
  100. return lm
  101. def search_correspond_LM_ID(xAug, PAug, zi):
  102. """
  103. Landmark association with Mahalanobis distance
  104. """
  105. nLM = calc_n_LM(xAug)
  106. mdist = []
  107. for i in range(nLM):
  108. lm = get_LM_Pos_from_state(xAug, i)
  109. y, S, H = calc_innovation(lm, xAug, PAug, zi, i)
  110. mdist.append(y.T @ np.linalg.inv(S) @ y)
  111. mdist.append(M_DIST_TH) # new landmark
  112. minid = mdist.index(min(mdist))
  113. return minid
  114. def calc_innovation(lm, xEst, PEst, z, LMid):
  115. delta = lm - xEst[0:2]
  116. q = (delta.T @ delta)[0, 0]
  117. zangle = math.atan2(delta[1, 0], delta[0, 0]) - xEst[2, 0]
  118. zp = np.array([[math.sqrt(q), pi_2_pi(zangle)]])
  119. y = (z - zp).T
  120. y[1] = pi_2_pi(y[1])
  121. H = jacobH(q, delta, xEst, LMid + 1)
  122. S = H @ PEst @ H.T + Cx[0:2, 0:2]
  123. return y, S, H
  124. def jacobH(q, delta, x, i):
  125. sq = math.sqrt(q)
  126. G = np.array([[-sq * delta[0, 0], - sq * delta[1, 0], 0, sq * delta[0, 0], sq * delta[1, 0]],
  127. [delta[1, 0], - delta[0, 0], - 1.0, - delta[1, 0], delta[0, 0]]])
  128. G = G / q
  129. nLM = calc_n_LM(x)
  130. F1 = np.hstack((np.eye(3), np.zeros((3, 2 * nLM))))
  131. F2 = np.hstack((np.zeros((2, 3)), np.zeros((2, 2 * (i - 1))),
  132. np.eye(2), np.zeros((2, 2 * nLM - 2 * i))))
  133. F = np.vstack((F1, F2))
  134. H = G @ F
  135. return H
  136. def pi_2_pi(angle):
  137. return (angle + math.pi) % (2 * math.pi) - math.pi
  138. def main():
  139. print(__file__ + " start!!")
  140. time = 0.0
  141. # RFID positions [x, y]
  142. RFID = np.array([[10.0, -2.0],
  143. [15.0, 10.0],
  144. [3.0, 15.0],
  145. [-5.0, 20.0]])
  146. # State Vector [x y yaw v]'
  147. xEst = np.zeros((STATE_SIZE, 1))
  148. xTrue = np.zeros((STATE_SIZE, 1))
  149. PEst = np.eye(STATE_SIZE)
  150. xDR = np.zeros((STATE_SIZE, 1)) # Dead reckoning
  151. # history
  152. hxEst = xEst
  153. hxTrue = xTrue
  154. hxDR = xTrue
  155. while SIM_TIME >= time:
  156. time += DT
  157. u = calc_input()
  158. xTrue, z, xDR, ud = observation(xTrue, xDR, u, RFID)
  159. xEst, PEst = ekf_slam(xEst, PEst, ud, z)
  160. x_state = xEst[0:STATE_SIZE]
  161. # store data history
  162. hxEst = np.hstack((hxEst, x_state))
  163. hxDR = np.hstack((hxDR, xDR))
  164. hxTrue = np.hstack((hxTrue, xTrue))
  165. if show_animation: # pragma: no cover
  166. plt.cla()
  167. plt.plot(RFID[:, 0], RFID[:, 1], "*b", lw='3')
  168. plt.plot(xEst[0], xEst[1], ".r")
  169. # plot landmark
  170. for i in range(calc_n_LM(xEst)):
  171. plt.plot(xEst[STATE_SIZE + i * 2],
  172. xEst[STATE_SIZE + i * 2 + 1], "xg")
  173. gt,=plt.plot(hxTrue[0, :],
  174. hxTrue[1, :], "-g")
  175. dr,=plt.plot(hxDR[0, :],
  176. hxDR[1, :], "-k")
  177. est,=plt.plot(hxEst[0, :],
  178. hxEst[1, :], "-r")
  179. plt.axis("equal")
  180. plt.xlabel('x/m')
  181. plt.ylabel('y/m')
  182. plt.grid(True)
  183. plt.legend([gt,dr,est],['Ground True','Dead Reckoning', 'EKF SLAM Estimation'],loc='best')
  184. plt.pause(0.001)
  185. if __name__ == '__main__':
  186. main()
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注