udacity 卡尔曼滤波(KF)-python版本

本文介绍了如何使用Python实现一个2D卡尔曼滤波器,处理激光数据,包括矩阵操作、状态预测、测量更新以及误差计算(如RMSE)。通过实例展示了如何用该滤波器处理位置和速度的估计,以及噪声参数对预测性能的影响。

udacity 2维EKF Python根据网上的C++版本改写,方便大家参考。

用于处理激光数据。

一、引用的库

import random
from math import *
import matplotlib.pyplot as plt
import decimal

二、定义矩阵类

来自网络资料。

class matrix:
    def __init__(self, value):
        self.value = value
        self.dimx = len(value)
        self.dimy = len(value[0])
        if value == [[]]:
            self.dimx = 0

    def zero(self, dimx, dimy):
        # check if valid dimensions
        if dimx < 1 or dimy < 1:
            raise ValueError("Invalid size of matrix")
        else:
            self.dimx = dimx
            self.dimy = dimy
            self.value = [[0 for row in range(dimy)] for col in range(dimx)]

    def identity(self, dim):
        # check if valid dimension
        if dim < 1:
            raise ValueError("Invalid size of matrix")
        else:
            self.dimx = dim
            self.dimy = dim
            self.value = [[0 for row in range(dim)] for col in range(dim)]
            for i in range(dim):
                self.value[i][i] = 1

    def show(self):
        for i in range(self.dimx):
            print(self.value[i])
        print(' ')

    def __add__(self, other):
        # check if correct dimensions
        if self.dimx != other.dimx or self.dimy != other.dimy:
            raise ValueError("Matrices must be of equal dimensions to add")
        else:
            # add if correct dimensions
            res = matrix([[]])
            res.zero(self.dimx, self.dimy)
            for i in range(self.dimx):
                for j in range(self.dimy):
                    res.value[i][j] = self.value[i][j] + other.value[i][j]
            return res

    def __sub__(self, other):
        # check if correct dimensions
        if self.dimx != other.dimx or self.dimy != other.dimy:
            raise ValueError("Matrices must be of equal dimensions to subtract")
        else:
            # subtract if correct dimensions
            res = matrix([[]])
            res.zero(self.dimx, self.dimy)
            for i in range(self.dimx):
                for j in range(self.dimy):
                    res.value[i][j] = self.value[i][j] - other.value[i][j]
            return res

    def __mul__(self, other):
        # check if correct dimensions
        if self.dimy != other.dimx:
            raise ValueError("Matrices must be m*n and n*p to multiply")
        else:
            # multiply if correct dimensions
            res = matrix([[]])
            res.zero(self.dimx, other.dimy)
            for i in range(self.dimx):
                for j in range(other.dimy):
                    for k in range(self.dimy):
                        res.value[i][j] += self.value[i][k] * other.value[k][j]
            return res

    def transpose(self):
        # compute transpose
        res = matrix([[]])
        res.zero(self.dimy, self.dimx)
        for i in range(self.dimx):
            for j in range(self.dimy):
                res.value[j][i] = self.value[i][j]
        return res

        # Thanks to Ernesto P. Adorio for use of Cholesky and CholeskyInverse functions

    def Cholesky(self, ztol=1.0e-5):
        # Computes the upper triangular Cholesky factorization of
        # a positive definite matrix.
        res = matrix([[]])
        res.zero(self.dimx, self.dimx)

        for i in range(self.dimx):
            S = sum([(res.value[k][i]) ** 2 for k in range(i)])
            d = self.value[i][i] - S
            if abs(d) < ztol:
                res.value[i][i] = 0.0
            else:
                if d < 0.0:
                    raise ValueError("Matrix not positive-definite")
                res.value[i][i] = sqrt(d)
            for j in range(i + 1, self.dimx):
                S = sum([res.value[k][i] * res.value[k][j] for k in range(self.dimx)])
                if abs(S) < ztol:
                    S = 0.0
                res.value[i][j] = (self.value[i][j] - S) / res.value[i][i]
        return res

    def CholeskyInverse(self):
        # Computes inverse of matrix given its Cholesky upper Triangular
        # decomposition of matrix.
        res = matrix([[]])
        res.zero(self.dimx, self.dimx)

        # Backward step for inverse.
        for j in reversed(range(self.dimx)):
            tjj = self.value[j][j]
            S = sum([self.value[j][k] * res.value[j][k] for k in range(j + 1, self.dimx)])
            res.value[j][j] = 1.0 / tjj ** 2 - S / tjj
            for i in reversed(range(j)):
                res.value[j][i] = res.value[i][j] = -sum(
                    [self.value[i][k] * res.value[k][j] for k in range(i + 1, self.dimx)]) / self.value[i][i]
        return res

    def inverse(self):
        aux = self.Cholesky()
        res = aux.CholeskyInverse()
        return res

    def __repr__(self):
        return repr(self.value)

三、初始化变量

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]]) # measurement function
R = matrix([[0.0225, 0],[0, 0.0225]]) # 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 = 5
noise_ay = 5

四、2D KF

def kalman_filter_2D(measurement):

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

    # measurement update
    Z = measurement
    y = Z - (H * 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

new_x = []
new_y = []
pre_t = 0.
dt = 0.
dt2 = 0.
dt3 = 0.
dt4 = 0.

meas = matrix([[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 == 'L'):
        meas.value[0][0] = float(line[1])
        meas.value[1][0] = float(line[2])

        time = float(line[3])

        if(b_init == 0):
            b_init = 1
            pre_t = time
            x.value[0][0] = meas.value[0][0]
            x.value[1][0] = meas.value[1][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


            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()

六、图形显示

import decimal
import matplotlib.pyplot as plt


m_px = []  #measurement position
m_py = []

gt_px = [] #groudtruth position
gt_py = []

gt_vx = [] #groundtruth velocity
gt_vy = []

e_px = []  #estimation position
e_py = []

e_vx = []  #estimation velocity
e_vy = []

with open('obj_pose-laser-radar-synthetic-input.txt', 'r') as file:
    data = file.readlines()
for line in data:
    line = line.split('\t')
    sensor = line[0]
    if(sensor == 'L'):
        m_px.append(float(line[1]))
        m_py.append(float(line[2]))

        gt_px.append(float(line[4]))
        gt_py.append(float(line[5]))

        gt_vx.append(float(line[6]))
        gt_vy.append(float(line[7]))

with open('obj_pose-laser-radar-synthetic-output.txt', 'r') as file:
    data = file.readlines()
for line in data:
    line = line.split(' ')
    e_px.append(float(line[0]))
    e_py.append(float(line[1]))
    e_vx.append(float(line[2]))
    e_vy.append(float(line[3]))

'''
plt.scatter(m_px, m_py, c='red',linewidth = 1, s = 5)
plt.scatter(e_px,e_py,c='green',linewidth = 1, s = 5)
plt.scatter(gt_px,gt_py,c='blue',linewidth = 1, s = 5)
'''

plt.scatter(e_vx,e_vy,c='blue')
plt.scatter(gt_vx,gt_vy,c='yellow',linewidth = 1, s = 5)

plt.show()

print("end")

七、对比

Python版本代码运行结果与网上的C++版本运行结果一致。

八、关于速度

实际上,状态变量由轨迹和速度组成,上述代码能够得到速度预测值。绿色点表示目标移动轨迹,蓝色值表示目标移动速度。

下图更明显,黄色为目标速度真值,蓝色为目标速度预测值。

九、关于dt

本次数据中,发现激光雷达测量的时间间隔dt不变,为0.1;

十、关于Q

如果不考虑Q,预测轨迹如下图示,红色为观测轨迹,绿色为预测轨迹,差距明显。需要考虑Q。

十一、关于noise_ax,noise_ay

如何取值?

noise_ax = 3
noise_ay = 3
与
noise_ax = 5
noise_ay = 5
结果相差不大,从预测轨迹上看不出差距。

即使取1000,似乎偏差也不大。

十二、关于R

R的取值对轨迹预测影响挺大。

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

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

(1)x方向预测对比

(2)y方向预测对比

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

(4)结论:

采用激光雷达对于速度的预测似乎也可以。

十四、计算RMSE

代码如下:

def calculate_rmse(est, truth):
    rmse = matrix([[0., 0., 0., 0.]])
    residual = matrix([[0., 0., 0., 0.]])

    if est.dimx != truth.dimx:
        print("error")
        return rmse

    for i in range(est.dimx):
        for j in range(est.dimy):
            residual.value[0][j] = est.value[i][j] - truth.value[i][j]
            residual.value[0][j] = residual.value[0][j] * residual.value[0][j]
            rmse.value[0][j] = rmse.value[0][j] + residual.value[0][j]
            #rmse2.value[i][j] = math.sqrt(residual.value[0][j])

    rmse = matrix([[math.sqrt(rmse.value[0][0] / est.dimx), math.sqrt(rmse.value[0][1] / est.dimx),
                    math.sqrt(rmse.value[0][2] / est.dimx), math.sqrt(rmse.value[0][3] / est.dimx)]])

    return rmse

十五、Lidar测量值、预测值与真值对比

(1)测量值与真值的RMSE

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

(2)预测值与真值的RMSE

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

(3)小结

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

十六、各种RMSE对比

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

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

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

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

(5)RMSE:所有RMSE比较

<think>嗯,用户想下载并了解如何使用Kalman开源库。首先,我需要确定他们具体指的是哪个Kalman库,因为有不同的实现,比如C++、JavaScript、Go、Python等。根据提供的引用,有几个不同的项目,比如CarND-Extended-Kalman-Filter-Project、Kalman.js、KalmanFilter的Go库,还有GPS_IMU_Kalman_Filter。用户可能需要根据他们的编程语言偏好来选择。 接下来,我需要整理每个项目的下载和使用步骤。例如,Kalman.js是一个JavaScript库,可以通过npm安装;Go项目需要用go get命令;而Python项目可能需要克隆仓库并安装依赖。还要注意不同项目的依赖项和配置文件,比如CMakeLists.txt或package.json,这些在安装时可能需要处理。 用户可能不熟悉不同环境的配置,比如CMake或npm,所以需要详细说明每个步骤,确保他们能顺利安装。同时,需要提供示例代码,帮助他们快速上手。例如,引用中的CarND项目需要运行安装脚本,而Kalman.js则有简单的构造函数和更新方法。 可能遇到的问题包括依赖项安装失败、环境配置错误,或者示例代码无法运行。需要提醒用户检查系统环境,如Node.js或Go的版本,并确保正确执行安装命令。此外,参考各个项目的README文件也很重要,因为通常会有最新的使用说明。 最后,用户可能需要进一步的学习资源,比如官方文档或教程,所以提供相关链接或建议查看项目文档会很有帮助。同时,生成相关问题可以帮助他们深入理解,比如如何集成到实际项目中或调试常见错误。</think>### Kalman开源库下载与使用教程 根据您提到的需求,以下是不同Kalman开源库的下载与使用方法整理: --- ### 一、C++版扩展卡尔曼滤波(CarND-Extended-Kalman-Filter-Project) **步骤1:克隆项目** ```bash git clone https://github.com/udacity/CarND-Extended-Kalman-Filter-Project.git cd CarND-Extended-Kalman-Filter-Project ``` **步骤2:安装依赖** - Linux/Ubuntu系统: ```bash chmod +x install-linux.sh ./install-linux.sh ``` - macOS系统: ```bash chmod +x install-mac.sh ./install-mac.sh ``` **步骤3:编译项目** ```bash mkdir build && cd build cmake .. && make ``` **步骤4:运行示例** ```bash ./ExtendedKF ``` > 注:项目通过CMakeLists.txt定义构建规则,包含传感器数据融合的核心实现[^1] --- ### 二、JavaScript版轻量级库 (Kalman.js) **步骤1:通过npm安装** ```bash npm install kalmanjs ``` **步骤2:基础使用代码** ```javascript const KalmanFilter = require('kalmanjs'); const kf = new KalmanFilter({ R: 0.01, Q: 3 }); // 处理噪声数据 const measurements = [1.1, 1.9, 3.1]; const filtered = measurements.map(kf.filter); ``` > 注:源代码位于src/kalman.js,核心参数包括过程噪声Q和测量噪声R[^2] --- ### 三、Go语言实现 (KalmanFilter) **步骤1:安装库** ```bash go get -u github.com/wearereasonablepeople/KalmanFilter ``` **步骤2:基本实现** ```go package main import ( "fmt" kf "github.com/wearereasonablepeople/KalmanFilter" ) func main() { filter := kf.NewKalmanFilter(0.1, 4) // 参数分别为过程噪声和测量噪声 fmt.Println(filter.Update(1.2)) } ``` > 注:项目包含完整的矩阵运算实现,适合嵌入式系统应用[^3] --- ### 四、GPS/IMU融合实现 (GPS_IMU_Kalman_Filter) **步骤1:克隆仓库** ```bash git clone https://github.com/karanchawla/GPS_IMU_Kalman_Filter.git cd GPS_IMU_Kalman_Filter ``` **步骤2:Python环境配置** ```bash pip install -r requirements.txt ``` **步骤3:运行演示** ```python python kalman_filter_example.py ``` > 注:包含6维状态向量实现(位置+速度)的经典案例[^4] --- ### 五、通用使用建议 1. **参数调优**:过程噪声$Q$和测量噪声$R$需要根据具体应用场景调整 2. **数据预处理**:建议先对输入数据进行标准化处理 3. **性能监控**:可通过计算后验协方差矩阵$P$的迹判断滤波稳定性 $$ P_{k|k} = (I - K_k H) P_{k|k-1} $$ 其中$K_k$为卡尔曼增益,$H$为观测矩阵 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值