1.运动和测量模型
1.1汽车运动模型
其中,是目前汽车的2D位姿。和是速度和角速度里程计的度数。
1.2 观测模型
观测模型按照如下公式将LIDAR测量的距离和方位与汽车的位姿结合起来。
其中,和是陆标的真实坐标。和和是汽车的当前位姿。是汽车中心和LIDAR的距离。(已知)
2. 滤波方程
2.1 一步预测
2.2 状态更新
3.程序代码
3.1 获取数据
import pickle
import numpy as np
import math
import matplotlib.pyplot as plt
with open(‘data/data.pickle’, ‘rb’) as f:
data = pickle.load(f)
t = data[‘t’] # timestamps [s]
x_init = data[‘x_init’] # initial x position [m]
y_init = data[‘y_init’] # initial y position [m]
th_init = data[‘th_init’] # initial theta position [rad]
v = data[‘v’] # translational velocity input [m/s]
om = data[‘om’] # rotational velocity input [rad/s]
b = data[‘b’] # bearing to each landmarks center in the frame attached to the laser [rad]
r = data[‘r’] # range measurements [m]
l = data[‘l’] # x,y positions of landmarks [m]
d = data[‘d’] # distance between robot center and laser rangefinder [m]
3.2 参数初始化
v_var = 0.01 # translation velocity variance
om_var =1 # rotational velocity variance 待调参数
r_var = 0.001# range measurements variance待调参数
b_var = 0.001 # bearing measurement variance待调参数
Q_km = np.diag([v_var, om_var]) # input noise covariance
cov_y = np.diag([r_var, b_var]) # measurement noise covariance
x_est = np.zeros([len(v), 3]) # estimated states, x, y, and theta
P_est = np.zeros([len(v), 3, 3]) # state covariance matrices
x_est[0] = np.array([x_init, y_init, th_init]) # initial state
P_est[0] = np.diag([1, 1, 0.1]) # initial state covariance
x_check = x_est[0]
x_check = x_check.reshape(1,3)
P_check = P_est[0]
3.3 限定角度的函数
def wraptopi(x):
if x > np.pi:
x = x - (np.floor(x / (2 * np.pi)) + 1) * 2 * np.pi
elif x < -np.pi:
x = x + (np.floor(x / (-2 * np.pi)) + 1) * 2 * np.pi
return x
3.4 测量更新函数
def measurement_update(lk, rk, bk, P_check, x_check):
bk = wraptopi(bk)
==x_check[:,2]= wraptopi(x_check[:,2]) ==
A = lk[0] - x_check[:,0] - d * math.cos(x_check[:,2])
B = lk[1] - x_check[:,1] - d * math.sin(x_check[:,2])
# 1. Compute measurement Jacobian
M = np.identity(2)
H = np.array([[-A * (A ** 2 + B ** 2) ** (-0.5),-B * (A ** 2 + B ** 2) ** (-0.5),d * (A ** 2 + B ** 2) ** (-0.5) * (A * math.sin(x_check[:,2])- B * math.cos(x_check[:,2]))],
[(1 + (B/A) ** 2) ** (-1) * B / A ** 2,-(1 + (B/A) ** 2) ** (-1) / A, (1 + (B/A) ** 2) ** (-1)/ A ** 2 * (-d*math.cos(x_check[:,2]) * A - B * d * math.sin(x_check[:,2]))-1]])
H = H.reshape(2,3)
# 2. Compute Kalman Gain
K = P_check @ H.T @ np.linalg.inv(H @ P_check @ H.T + M @cov_y @ M.T )
# 3. Correct predicted state (remember to wrap the angles to [-pi,pi])
h = math.atan2(B,A) - x_check[:,2]
h = wraptopi(h)
ss = K @ np.array([rk-(A ** 2 + B ** 2) ** 0.5,bk-h])
x_check = x_check + ss.reshape(1,3)
x_check[:,2]= wraptopi(x_check[:,2])
# 4. Correct covariance
P_check = (np.identity(3)-K @ H) @ P_check
return x_check, P_check
3.5 滤波主程序
for k in range(1, len(t)): # start at 1 because we’ve set the initial prediciton
delta_t = t[k] - t[k - 1] # time step (difference between timestamps)
x_check[:,2]= wraptopi(x_check[:,2])
theta = x_check[:,2]
# 1. Update state with odometry readings (remember to wrap the angles to [-pi,pi])
cc = delta_t * np.array([[math.cos(theta),0],[math.sin(theta),0],[0,1]]) @ np.array([[v[k]],[om[k]]])
x_check = x_check + cc.T
x_check[:,2] = wraptopi(x_check[:,2])
# 2. Motion model jacobian with respect to last state
F_km = np.array([[1,0,-delta_t * v[k] math.sin(theta)],[0,1,delta_t * v[k] math.cos(theta)],[0,0,1]])
# 3. Motion model jacobian with respect to noise
L_km = delta_t * np.array([[math.cos(theta),0],[math.sin(theta),0],[0,1]])
# 4. Propagate uncertainty
P_check = F_km @ P_check @ F_km.T + L_km @ Q_km @L_km.T
# 5. Update state estimate using available landmark measurements
for i in range(len(r[k])):
x_check, P_check = measurement_update(l[i], r[k, i], b[k, i], P_check, x_check)
# Set final state predictions for timestep
x_est[k, 0] = x_check[:,0]
x_est[k, 1] = x_check[:,1]
x_est[k, 2] = x_check[:,2]
P_est[k, :, :] = P_check
3.6 作图
e_fig = plt.figure()
ax = e_fig.add_subplot(111)
ax.plot(x_est[:, 0], x_est[:, 1])
ax.set_xlabel(‘x [m]’)
ax.set_ylabel(‘y [m]’)
ax.set_title(‘Estimated trajectory’)
plt.show()
e_fig = plt.figure()
ax = e_fig.add_subplot(111)
ax.plot(t[:], x_est[:, 2])
ax.set_xlabel(‘Time [s]’)
ax.set_ylabel(‘theta [rad]’)
ax.set_title(‘Estimated trajectory’)
plt.show()
4.仿真结果
注释:这个EKF练习要求用python进行编程。我在编程的时候犯了两个错误,卡了很久。现在分享出来,希望读者在自己学习这门课的时候多加留意,避免这些坑。
- python做矩阵运算确实不如matlab方便,尤其是带向量的运算,一会儿是一维,一会儿是二维数组,傻傻分不清。要想要使能够按照矩阵来操作,必须把向量变成二维的。例如上面程序中有。这是把变成一个行向量。(当然,也可以变成列向量)。
- 文中把姿态角限定在了之间。编程中有五个地方需要进行限定。滤波主程序中有2处,分别是在状态一步递推的前后。更新子函数中有3处,一处是最开始对一步预测的结果进行限定,另外两处是对真实测量值和一步预测对应的测量计算值进行限定。(文中已经标黄)