手把手教你写扩展卡尔曼滤波器(C++)

手把手教你写扩展卡尔曼滤波器(C++)

正文

附赠自动驾驶最全的学习资料和量产经验:链接

源码在结尾

毫米波雷达的数据

毫米波雷达观察世界的方式与激光雷达有所不同。激光雷达测量的原理是光的直线传播,因此在测量时能直接获得障碍物在笛卡尔坐标系下x方向、y方向和z方向上的距离;而毫米波雷达的原理是多普勒效应,它所测量的数据都是在极坐标系下的。

如下图所示,毫米波雷达能够测量障碍物在极坐标下离雷达的距离ρ、方向角ϕ以及距离的变化率(径向速度)ρ’,如下图所示。

image

图片出处:优达学城(Udacity)无人驾驶工程师学位

扩展卡尔曼滤波器理论

我们再次祭出卡尔曼老先生给我们留下的宝贵财富,即状态估计时预测和测量值更新时所用到的7个公式,如下图所示。扩展卡尔曼滤波的理论和编程依旧需要使用到这些公式,相比于原生的卡尔曼滤波,只在个别地方有所不同。

image

图片出处:优达学城(Udacity)无人驾驶工程师学位

完成扩展卡尔曼滤波器C++代码的过程,实际上就是结合上面的公式,一步步完成初始化、预测、观测的过程。由于公式中涉及大量的矩阵转置和求逆运算,我们使用开源的矩阵运算库Eigen辅助我们代码的编写。

代码:初始化(Initialization)

扩展卡尔曼滤波的初始化,需要将各个变量进行设置,对于不同的运动模型,状态向量是不一样的。为了保证代码对不同状态向量的兼容性,我们使用Eigen库中非定长的数据结构。

如下所示,我们新建了一个ExtendedKalmanFilter类,定义了一个叫做x_的状态向量(state vector)。代码中的VerctorXd表示X维的列向量,元素的数据类型为double。

image

初始化扩展卡尔曼滤波器时需要输入一个初始的状态量x_in,用以表示障碍物最初的位置和速度信息,一般直接使用第一次的测量结果。

代码:预测(Prediction)

完成初始化后,我们开始写Prediction部分的代码。首先是公式

image

这里的x为状态向量,通过左乘一个矩阵F,再加上外部的影响u,得到预测的状态向量x’。这里的F被称作状态转移矩阵(

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用C++卡尔曼滤波来对雷达点云进行滤波的示例代码: ```c++ #include <iostream> #include <vector> #include <Eigen/Dense> using namespace std; using namespace Eigen; class KalmanFilter { public: KalmanFilter() {} MatrixXd F; // 状态转移矩阵 MatrixXd Q; // 状态噪声协方差矩阵 MatrixXd H; // 观测矩阵 MatrixXd R; // 观测噪声协方差矩阵 MatrixXd P; // 状态估计协方差矩阵 VectorXd x; // 状态向量 void init(int state_dim, int measure_dim) { F = MatrixXd::Identity(state_dim, state_dim); Q = MatrixXd::Identity(state_dim, state_dim); H = MatrixXd::Identity(measure_dim, state_dim); R = MatrixXd::Identity(measure_dim, measure_dim); P = MatrixXd::Identity(state_dim, state_dim); x = VectorXd::Zero(state_dim); } void predict() { x = F * x; P = F * P * F.transpose() + Q; } void update(const VectorXd &z) { VectorXd y = z - H * x; MatrixXd S = H * P * H.transpose() + R; MatrixXd K = P * H.transpose() * S.inverse(); x = x + K * y; P = (MatrixXd::Identity(P.rows(), P.cols()) - K * H) * P; } }; int main() { // 模拟雷达点云数据 vector<VectorXd> measurements; for (int i = 0; i < 100; ++i) { double x = sin(i * 0.1); double y = cos(i * 0.1); VectorXd z(2); z << x, y; measurements.push_back(z); } // 初始化卡尔曼滤波器 KalmanFilter kf; kf.init(4, 2); kf.F << 1, 0, 0.1, 0, 0, 1, 0, 0.1, 0, 0, 1, 0, 0, 0, 0, 1; kf.Q << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1; kf.H << 1, 0, 0, 0, 0, 1, 0, 0; kf.R << 0.1, 0, 0, 0.1; kf.P << 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 10, 0, 0, 0, 0, 10; // 对数据进行滤波 vector<VectorXd> filtered_measurements; for (int i = 0; i < measurements.size(); ++i) { kf.predict(); kf.update(measurements[i]); filtered_measurements.push_back(kf.x); } // 输出滤波结果 for (int i = 0; i < filtered_measurements.size(); ++i) { cout << filtered_measurements[i].transpose() << endl; } return 0; } ``` 在上述代码中,我们首先定义了一个KalmanFilter类,其中包含了状态转移矩阵F、状态噪声协方差矩阵Q、观测矩阵H、观测噪声协方差矩阵R、状态估计协方差矩阵P和状态向量x。然后,我们通过init函数对卡尔曼滤波器进行初始化,并使用predict函数进行预测和update函数进行更新。最后,我们对模拟的雷达点云数据进行滤波,并输出滤波结果。 需要注意的是,实际应用中,雷达点云数据通常是三维的,因此需要将KalmanFilter类中的向量和矩阵维度进行相应的修改。同时,还需要根据实际应用情况对卡尔曼滤波器的参数进行调整和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值