udacity 扩展卡尔曼滤波(EKF)-python版本

本程序用于处理毫米波雷达数据。参考udacity资料以及网络资料。供大家参考。

一、关键公式推导

二、正态分布数据生成

import numpy as np
import matplotlib.pyplot as plt
import math

# 设置随机种子以获得可复现的结果
np.random.seed(0)

# 设置正态分布的参数
mean, std = 0, 3

# 生成正态分布的随机数
data = np.random.normal(mean, std, size = 10000)

plt.xlim(-10, 10)  # x轴的范围设置为-3到3
plt.ylim(0, 0.2)  # y轴的范围设置为0到30

# 计算直方图
plt.hist(data, bins=300, density=True, alpha=0.5, color='red', edgecolor='black')

# 设置x和y轴的标签
plt.xlabel('Value')
plt.ylabel('Probability')

# 设置图表标题
plt.title('Histogram of Normal Distribution')

# 显示图表
plt.show()

三、正态分布数据的非线性变换

data = np.random.normal(mean, std, size = 10000)

new_data = []
for i in data:
    new_data.append(math.atan(i)) #non linear transform

非线性变换后的数据分布图(非正态分布)

四、正态分布数据的线性变换

# 生成正态分布的随机数
data = np.random.normal(mean, std, size = 10000)

new_data = []
new_data2 = []
for i in data:
    new_data2.append(1.2*i)   #linear transform

线性变换后的数据分布图(近似正态分布)

五、初始化变量

x = matrix([[0.], [0.], [0.], [0.]]) # initial state (location and velocity)
P = matrix([[1., 0, 0, 0.],[0, 1, 0, 0],[0, 0, 1000, 0], [0, 0, 0., 1000.]]) # initial uncertainty
u = matrix([[0.], [0.]]) # external motion
F = matrix([[1., 0., 1., 0.], [0., 1., 0., 1.],[0., 0., 1., 0.],[0., 0., 0., 1.]]) # next state function
H = matrix([[1., 0., 0, 0],[0, 1, 0, 0],[0, 0, 1, 0]]) # measurement function
R = matrix([[0.09, 0,  0],[0, 0.0009, 0],[0, 0, 0.09]]) # measurement uncertainty
Q = matrix([[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0],[0, 0, 0, 0]])
I = matrix([[1., 0., 0, 0], [0., 1., 0, 0],[0, 0, 1, 0],[0, 0, 0, 1]]) # identity matrix
noise_ax = 9
noise_ay = 9

六、雅可比变换

def calculate_jacobian(x):
    Hj = matrix([[0., 0., 0., 0.], [0., 0., 0., 0.],[0., 0., 0., 0.]])

    px = x.value[0][0]
    py = x.value[1][0]
    vx = x.value[2][0]
    vy = x.value[3][0]

    c1 = px * px + py * py
    c2 = sqrt(c1)
    c3 = c1 * c2

    if(fabs(c1) < 0.0001):
        print("error")
        return Hj

    Hj = matrix([[px/c2, py/c2, 0, 0],[-py/c1, px/c1, 0, 0],[ py * (vx * py - vy * px) / c3, px * (px * vy - py * vx) / c3, px / c2, py / c2]])
    return Hj

七、坐标变换

def RadarCartesianToPolar(x):
    px = x.value[0][0]
    py = x.value[1][0]
    vx = x.value[2][0]
    vy = x.value[3][0]

    rho = sqrt(px * px + py * py)
    phi = atan2(py, px)

    if (rho < 0.000001):
        rho = 0.000001

    rho_dot = (px * vx + py * vy) / rho

    z_pred = matrix([[rho], [phi], [rho_dot]])
    return z_pred

八、2D EKF

def extend_kalman_filter_2D(measurement):

    global x
    global P
    # predict
    x = F * x
    P = F * P * F.transpose() + Q

    # measurement update   
    Z_pred = RadarCartesianToPolar(x)
    Z = measurement
    y = Z - Z_pred

    while (y.value[1][0] > 3.14159265):
        y.value[1][0] = y.value[1][0] - 3.14159265 * 2

    while (y.value[1][0] < -3.14159265):
        y.value[1][0] = y.value[1][0] + 3.14159265 * 2

    H = calculate_jacobian(x)
    S = H * P * H.transpose() + R
    K = P * H.transpose() * S.inverse()

    x = x + (K * y)
    P = (I - (K * H)) * P

    return x, P

九、数据处理

以下程序用于处理雷达数据。

time = 0.
b_init = 0

pre_t = 0.
dt = 0.
dt2 = 0.
dt3 = 0.
dt4 = 0.

meas = matrix([[0.], [0.], [0.]]) # initial measurement

file1 = open('obj_pose-laser-radar-synthetic-input.txt', 'r')
file2 = open('obj_pose-laser-radar-synthetic-output.txt', 'w+')

data = file1.readlines()

for line in data:
    line = line.split('\t')
    sensor = line[0]
    if(sensor == 'R'):
        meas.value[0][0] = float(line[1]) #rho
        meas.value[1][0] = float(line[2]) #phi
        meas.value[2][0] = float(line[3]) #rho-dot

        time = float(line[4])

        if(b_init == 0):
            b_init = 1
            pre_t = time
            x.value[0][0] = meas.value[0][0] * cos(meas.value[1][0]) #px = rho * cos(phi)
            x.value[1][0] = meas.value[0][0] * sin(meas.value[1][0]) #py = roh * sin(phi)
            x.value[2][0] = 0.
            x.value[3][0] = 0.

            file2.write(str(x.value[0][0]) + ' ' + str(x.value[1][0]) + ' ' + str(x.value[2][0]) + ' ' + str(x.value[3][0]) + '\n')
        else:
            dt = (time - pre_t) / 1000000.0
            pre_t = time

            dt2 = dt * dt
            dt3 = dt2 * dt
            dt4 = dt3 * dt

            F.value[0][2] = dt
            F.value[1][3] = dt


            Q.value[0][0] = dt4 * noise_ax / 4
            Q.value[0][2] = dt3 * noise_ax / 2
            Q.value[1][1] = dt4 * noise_ay / 4
            Q.value[1][3] = dt3 * noise_ay / 2
            Q.value[2][0] = dt3 * noise_ax / 2
            Q.value[2][2] = dt2 * noise_ax
            Q.value[3][1] = dt3 * noise_ay / 2
            Q.value[3][3] = dt2 * noise_ay


            extend_kalman_filter_2D(meas)
            file2.write(str(x.value[0][0]) + ' ' + str(x.value[1][0]) + ' ' + str(x.value[2][0]) + ' ' + str(x.value[3][0]) + '\n')

file1.close()
file2.close()

十、结果对比

本程序参考的C++代码:

GitHub - JunshengFu/tracking-with-Extended-Kalman-Filter: Object (e.g Pedestrian, vehicles) tracking by Extended Kalman Filter (EKF), with fused data from both lidar and radar sensors.

本程序得到的输出结果与参考的C++代码一致。

十一、关于速度的初始化

网上有不同的C++版本对速度进行初始化

(1)版本1

 float ro     = measurement_pack.raw_measurements_(0);
 float phi    = measurement_pack.raw_measurements_(1);
 float ro_dot = measurement_pack.raw_measurements_(2);
 ekf_.x_(0) = ro     * cos(phi);
 ekf_.x_(1) = ro     * sin(phi);      
 ekf_.x_(2) = ro_dot * cos(phi);
 ekf_.x_(3) = ro_dot * sin(phi);

(2)版本2(参考的C++代码)

 // phi is not the direction of the speed, it is better to set vx and vy to 0
 ekf_.x_ << rho * cos(phi), rho * sin(phi), 0, 0;  // x, y, vx, vy

个人认为版本2是对的。

十二、关于角度值的标准化处理

Normalizing Angles
In C++, atan2() returns values between -pi and pi. When calculating phi in y = z - h(x) for radar measurements,the resulting angle phi in the y vector should be adjusted so that it is between -pi and pi. The Kalman filter is expecting small angle values between the range -pi and pi.

有些版本的C++代码没有做角度的标准化处理,得到的轨迹预测值是不正确的。

不同的版本处理也不一样。

(1)版本1

  // Assume this is radar measurement,/ y(1) is an angle (phi), it shoulde be normalized
   if (y.size() == 3) y(1) = atan2(sin(y(1)), cos(y(1)));

(2)版本2

 if(y(1) > PI){
   y(1) = y(1) - 2*PI;
   }
 else if(y(1) < -PI){
   y(1) = y(1) + 2*PI;
   }

(3)版本3(参考的C++版本)

 // normalize the angle between -pi to pi
 while(y(1) > M_PI){
   y(1) -= PI2;
 }

 while(y(1) < -M_PI){
   y(1) += PI2;
 }

个人认为版本3是对的。

十三、Radar测量值、预测值与真值对比

(1)测量值与真值的RMSE

备注:这里测量值只包含位置的测量值,没有速度的测量值;导致速度的RMSE较大。

(2)预测值与真值的RMSE

备注:预测值能够得到速度的预测值

(3)小结

采用卡尔曼滤波后降低了RMSE

十四、Lidar的RMSE与Radar的RMSE对比

(1)Lidar的RMSE

(2)Radar的RMSE

(3)小结

Lidar对于目标轨迹预测误差小于Radar;

Radar对于目标速度预测误差小于Lidar;

十五、目标位置&速度的真值、测量值、预测值对比

只采用Radar传感器对目标位置以及速度进行预测,如下图:

(1)x方向预测对比

(2)y方向预测对比

(3)目标位置轨迹以及速度分布对比

(4)结论:

无。

十六、各种RMSE对比

(1)RMSE:X方向轨迹预测值与真值比较

(2)RMSE:Y方向轨迹预测值与真值比较

(3)RMSE:X方向速度预测值与真值比较

(4)RMSE:Y方向速度预测值与真值比较

(5)RMSE:所有RMSE比较

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值