本系列的前4篇文章主要介绍了深度学习技术在无人驾驶环境感知中的应用,包括交通标志识别图像与点云3D目标检测。关于环境感知部分的介绍本系列暂且告一段落,后续如有需要再进行补充。
现在我们开启新的篇章,在本文中将会介绍无人驾驶的定位部分,我们将使用仿真的RadarLiDAR数据对自行车进行追踪。这里使用到了两种传感器数据,因此我们需要进行数据融合,同时由于两种传感器工作原理不同,我们需要分别应用卡尔曼滤波扩展卡尔曼滤波技术。
本文主要参考了Udacity《无人驾驶工程师》课程相关项目,同时也参考了知乎作者陈光的分享,在此一同表示感谢。

文章目录

  • 1. 毫米波雷达简介
  • 2. 激光雷达简介
  • 3. 卡尔曼滤波直观理解
    • 3.1 预测
    • 3.2 更新
  • 4. 传感器数据融合
    • 4.1 线性卡尔曼滤波实现
      • (1)初始化
      • (2)预测
      • (3)更新
    • 4.2 非线性卡尔曼滤波实现
      • (1)预测
      • (2)更新
    • 4.3 数据融合

1. 毫米波雷达简介

毫米波雷达是自动驾驶汽车常用的传感器之一,目前市场上常用的毫米波雷达有大陆ARS408雷达,测距范围最远能达到250米。

毫米波雷达的工作原理是多普勒效应,输出值是极坐标下的测量结果。

如下图所示,毫米波雷达能够测量障碍物在极坐标下的距离ρ\rhoρ,方向夹角 φ\varphiφ,以及径向速度ρ˙\dot{\rho}ρ˙​。


2. 激光雷达简介

激光雷达与毫米波雷达不同,它的测量原理是光沿直线传播

能直接获得障碍物在笛卡尔坐标系下xxx方向、yyy方向和zzz方向上的距离。目前市场上常用的激光雷达有Velodyne激光雷达,国内有Robosense激光雷达以及大疆公司旗下的激光雷达。激光雷达根据线束的多少,分为16线,32线,64线,以及128线激光雷达。


3. 卡尔曼滤波直观理解

网上介绍卡尔曼滤波原理的书和资料有很多,这里从直观上来对其进行介绍,帮助大家理解,具体数学公式推导可查阅相关论文。

先一句话概括卡尔曼滤波的思想:根据上一时刻的状态xt−1x_{t-1}xt−1​,预测当前时刻的状态xprex_{pre}xpre​,将预测的状态xprex_{pre}xpre​与当前时刻的测量值ztz_tzt​进行加权更新更新后的结果为最终的追踪结果xtx_txt​。

以一个常见的小车运动为例来介绍。如下图所示:有辆小车在道路上水平向右侧匀速运动,在左侧ooo点安装了传感器,传感器每隔1秒测量一次小车的位置sss和运动速度vvv。

这里用向量xtx_txt​来表示小车在ttt时刻的运动状态,该向量xtx_txt​也是最终的输出结果,被称作状态向量(state vector),则状态向量为:
xt=[stvt]x_t=\begin{bmatrix}s_t \\ v_t\end{bmatrix}xt​=[st​vt​​]

由于传感器自身测量误差的存在,无法直接获取小车的真实位置,只能获取在真值附近的一个近似值,可以假设测量值在真值附近服从高斯分布,如下图所示,橙色部分为测量值。

在初始的时候,由于不存在上一时刻的状态,我们设初始的状态向量为测量值,因此有:

x1=z1=[s1v1]x_1=z_1=\begin{bmatrix}s_1 \\ v_1\end{bmatrix}x1​=z1​=[s1​v1​​]


3.1 预测

卡尔曼滤波大致分为两步,第一步为状态预测(prediction)。现在我们已经有了小车第1秒的状态x1x_1x1​,可以预测小车在第2秒的状态,小车所处位置假设如下图所示。

由于小车是做匀速运动,因此小车在第2秒时的预测状态为:
xpre=[s1+v1v1]x_{pre}=\begin{bmatrix}s_1 + v_1\\ v_1\end{bmatrix}xpre​=[s1​+v1​v1​​]


3.2 更新

现在我们已经预测了小车在第2秒的状态,同时传感器也测量出小车在第2秒时的位置,测量结果用z2z_2z2​表示,则:
z2=[s2v2]z_2=\begin{bmatrix}s_2 \\ v_2\end{bmatrix}z2​=[s2​v2​​]
由于传感器本身存在着测量噪声,测量结果存在很大不确定性,将小车预测位置与测量值进行比较,如下图所示。

不难发现,小车的真实位置应该处于测量值与预测值之间。

对测量值与预测值进行加权,加权后的结果如下图所示,绿色部分即为小车真值分布区域。

这样,根据第2秒的预测值和测量值,我们就能得到第2秒的状态向量x2x_2x2​。同理,按照上述预测、更新的过程,我们就能得到第3秒,第4秒…第n秒的状态向量xnx_nxn​。

上面是卡尔曼滤波的直观理解,下面给出其数学公式。这里留个伏笔,在下一节部分=结合代码再介绍其中每个字母的含义。


4. 传感器数据融合

不知道大家有没有想过这样一个问题?既然毫米波雷达与激光雷达都能测量障碍物的位置,为什么还需要对传感器数据进行融合操作呢。

这是因为在自动驾驶中,使用单一传感器进行障碍物跟踪,往往都有着很大的局限性。激光雷达测量更精准,但是其无法得到速度信息;毫米波雷达能够得到障碍物速度信息,但是其位置测量精度不如激光雷达。如果能将二者有效结合,就能精准得到障碍物位置和速度信息,因此数据融合技术孕育而生。

在毫米波雷达与激光雷达进行数据融合时,因为两个传感器工作原理的不同,其相应的技术处理细节也不同。激光雷达可直接使用线性卡尔曼来进行障碍物跟踪;而毫米波雷达则需要使用非线性卡尔曼来进行物体跟踪。

这里分为三个小节来实现,一是线性卡尔曼滤波的实现;二是非线性卡尔曼滤波的实现;三是对二者进行数据融合。


4.1 线性卡尔曼滤波实现

在正式写代码处理问题时,我们先熟悉下要处理的传感器数据,这里要处理的是激光雷达与毫米波雷达数据。

下面是激光雷达与毫米波雷达交替发出的前20行数据,其中L代表激光雷达,R代表毫米波雷达。

L    0   0   1477010443349642    0   0   0   0
R   0   0   0   1477010443349642    0   0   0   0
L   1.559445e+00   -1.385015e-01   1477010444349642    2.098967e+00   5.222280e-02    2.195949e+00   1.093391e-01
R   1.812711e+00   2.619727e-02    2.305732e+00   1477010444349642    2.098967e+00   5.222280e-02    2.195949e+00   1.093391e-01
L   3.890927e+00   -1.341657e-01   1477010445349642    4.291359e+00   2.153118e-01    2.284336e+00   2.263225e-01
R   4.200967e+00   5.202598e-02    2.418127e+00   1477010445349642    4.291359e+00   2.153118e-01    2.284336e+00   2.263225e-01
L   6.863517e+00   4.168175e-01    1477010446349642    6.569422e+00   4.960956e-01    2.363816e+00   3.488471e-01
R   6.456604e+00   7.330529e-02    2.438466e+00   1477010446349642    6.569422e+00   4.960956e-01    2.363816e+00   3.488471e-01
L   9.077331e+00   5.932112e-01    1477010447349642    8.924371e+00   8.992403e-01    2.433593e+00   4.745258e-01
R   8.941596e+00   9.936074e-02    2.529122e+00   1477010447349642    8.924371e+00   8.992403e-01    2.433593e+00   4.745258e-01
L   1.157555e+01   1.666900e+00   1477010448349642    1.134677e+01   1.426997e+00   2.493282e+00   6.007812e-01
R   1.153365e+01   1.242681e-01    2.500995e+00   1477010448349642    1.134677e+01   1.426997e+00   2.493282e+00   6.007812e-01
L   1.359209e+01   2.311915e+00   1477010449349642    1.382695e+01   2.079045e+00   2.542900e+00   7.249468e-01
R   1.380271e+01   1.453491e-01    2.539724e+00   1477010449349642    1.382695e+01   2.079045e+00   2.542900e+00   7.249468e-01
L   1.664559e+01   2.902999e+00   1477010450349642    1.635537e+01   2.852433e+00   2.582842e+00   8.443653e-01
R   1.652890e+01   1.730753e-01    2.728349e+00   1477010450349642    1.635537e+01   2.852433e+00   2.582842e+00   8.443653e-01
L   1.901058e+01   3.705553e+00   1477010451349642    1.892299e+01   3.741610e+00   2.613820e+00   9.564796e-01
R   1.974624e+01   1.973267e-01    2.502012e+00   1477010451349642    1.892299e+01   3.741610e+00   2.613820e+00   9.564796e-01
L   2.133124e+01   4.732053e+00   1477010452349642    2.152152e+01   4.738551e+00   2.636790e+00   1.058912e+00
R   2.213645e+01   2.161499e-01    2.798219e+00   1477010452349642    2.152152e+01   4.738551e+00   2.636790e+00   1.058912e+00

激光雷达每一帧有7个数据,依次是:

  • 障碍物在X方向上的测量值(单位:米);
  • Y方向上的测量值(单位:米);
  • 测量时刻的时间戳(单位:微秒);
  • 障碍物位置在X方向上的真值(单位:米);
  • 障碍物位置在Y方向上的真值(单位:米);
  • 障碍物速度在X方向上的真值(单位:米/秒);
  • 障碍物速度在Y方向上的真值(单位:米/秒)。

毫米波雷达每一帧有8个数据,依次是:

  • 障碍物在极坐标系下的距离(单位:米);
  • 角度(单位:弧度) ;
  • 径向速度(单位:米/秒);
  • 测量时刻的时间戳(单位:微秒);
  • 障碍物位置在X方向上的真值(单位:米);
  • 障碍物位置在Y方向上的真值(单位:米);
  • 障碍物速度在X方向上的真值(单位:米/秒);
  • 障碍物速度在Y方向上的真值(单位:米/秒)。

对数据有了一定了解后,现在我们开始实现线性卡尔曼滤波。

(1)初始化

首先是初始化部分,在这里要初始化卡尔曼滤波的各个变量。

这里使用Eigen库进行初始化,这里我们定义了一个KalmanFilter类,表示为卡尔曼滤波追踪器,其成员函数包括初始化,预测,线性卡尔曼更新,扩展卡尔曼更新等,这在面向对象编程中是常使用的一种方法。

#ifndef KALMAN_FILTER_H_
#define KALMAN_FILTER_H_
#include "Eigen/Dense"class KalmanFilter {public:// state vectorEigen::VectorXd x_;// state covariance matrixEigen::MatrixXd P_;// state transistion matrixEigen::MatrixXd F_;// process covariance matrixEigen::MatrixXd Q_;// measurement matrixEigen::MatrixXd H_;// measurement covariance matrixEigen::MatrixXd R_;/*** Constructor*/KalmanFilter();/*** Destructor*/virtual ~KalmanFilter();/*** Init Initializes Kalman filter* @param x_in Initial state* @param P_in Initial state covariance* @param F_in Transition matrix* @param H_in Measurement matrix* @param R_in Measurement covariance matrix* @param Q_in Process covariance matrix*/void Init(Eigen::VectorXd &x_in, Eigen::MatrixXd &P_in, Eigen::MatrixXd &F_in,Eigen::MatrixXd &H_in, Eigen::MatrixXd &R_in, Eigen::MatrixXd &Q_in);/*** Prediction Predicts the state and the state covariance* using the process model* @param delta_T Time between k and k+1 in s*/void Predict();/*** Updates the state by using standard Kalman Filter equations* @param z The measurement at k+1*/void Update(const Eigen::VectorXd &z);/*** Updates the state by using Extended Kalman Filter equations* @param z The measurement at k+1*/void UpdateEKF(const Eigen::VectorXd &z);/*** General kalman filter update operations* @param y the update prediction error*/void UpdateRoutine(const Eigen::VectorXd &y);};#endif /* KALMAN_FILTER_H_ */

初始化时,需要初始化状态向量xxx,状态转移矩阵FFF,状态协方差矩阵PPP,测量矩阵HHH,过程协方差矩阵QQQ,测量协方差矩阵RRR,代码如下:

void KalmanFilter::Init(VectorXd &x_in, MatrixXd &P_in, MatrixXd &F_in,MatrixXd &H_in, MatrixXd &R_in, MatrixXd &Q_in) {x_ = x_in;P_ = P_in;F_ = F_in;H_ = H_in;R_ = R_in;Q_ = Q_in;
}

(2)预测

然后是预测部分,根据卡尔曼滤波原理,预测公式为:
x′=Fx+ux'=Fx+ux′=Fx+u
这里的xxx为状态向量,其乘以状态转移矩阵FFF,再加上外部影响uuu,即可得到预测状态向量x′x'x′。

对于激光雷达来说,其状态向量xxx为4维向量,x,yx,yx,y方向上的位置和速度:
x=[xyvxvy]x=\begin{bmatrix}x\\ y\\v_x\\v_y\end{bmatrix}x=⎣⎢⎢⎡​xyvx​vy​​⎦⎥⎥⎤​
假设障碍物做匀速运动,则在经过δt\delta{t}δt时间后,状态向量为:
x′=[x+vx∗δty+vy∗δtvxvy]x'=\begin{bmatrix}{x+v_x*\delta{t}} \\ {y+v_y*\delta{t}}\\v_x\\v_y\end{bmatrix}x′=⎣⎢⎢⎡​x+vx​∗δty+vy​∗δtvx​vy​​⎦⎥⎥⎤​
如果用矩阵表示的话,则状态转移公式为:
[x+vx∗δty+vy∗δtvxvy]=[10δt0010δt00100001]∗[xyvxvy]\begin{bmatrix}{x+v_x*\delta{t}} \\ {y+v_y*\delta{t}}\\v_x\\v_y\end{bmatrix}=\begin{bmatrix}1&0&\delta{t}&0\\0&1&0&\delta{t}\\0&0&1&0\\0&0&0&1\end{bmatrix}*\begin{bmatrix}x \\ y \\ v_x \\v_y \end{bmatrix}⎣⎢⎢⎡​x+vx​∗δty+vy​∗δtvx​vy​​⎦⎥⎥⎤​=⎣⎢⎢⎡​1000​0100​δt010​0δt01​⎦⎥⎥⎤​∗⎣⎢⎢⎡​xyvx​vy​​⎦⎥⎥⎤​
因此,状态转移矩阵为(4,4)(4,4)(4,4)的矩阵,我们可以先将δt\delta{t}δt定义为1,计算时再根据实际情况修改:

  // Initial transition matrix F_ekf_.F_ = MatrixXd(4, 4);ekf_.F_ << 1, 0, 1, 0,0, 1, 0, 1,0, 0, 1, 0,0, 0, 0, 1;

然后是预测部分的第二个公式:
P′=FPFT+QP'=FPF^T+QP′=FPFT+Q
在公式中,PPP为状态协方差矩阵,表示系统的不确定性,开始时系统的不确定性会很大,但随着输入的数据越来越多,系统会趋于稳定。这里还用到了过程协方差矩阵QQQ,这表示系统受外部环境影响的情况,如突然遇到爬坡或路面有凹坑的情况。

对于激光雷达来说,无法测量障碍物的速度,因此,测量位置的不确定性要小于速度不确定性。因此这里的PPP可以初始化为:

// Initialize state covariance matrix Pekf_.P_ = MatrixXd(4, 4);ekf_.P_ << 1, 0,  0,   0,0,   1,  0,   0,0,   0, 1000, 0,0,   0, 0,   1000;

QQQ代码如下,这里也是可以调整的。

 // Initialize process noise covariance matrixekf_.Q_ = MatrixXd(4, 4); ekf_.Q_ << 0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0;

这样我们就完成了预测部分,最终我们写为一个函数Predict()代码如下:

void KalmanFilter::Predict() {// Predict the statex_ = F_ * x_;MatrixXd Ft = F_.transpose();P_ = F_ * P_ * Ft + Q_;
}

(3)更新

最后是卡尔曼滤波的更新部分,更新部分第一个公式为:
y=z−Hx′y=z-Hx'y=z−Hx′
上式计算了测量值zzz与预测值x′x'x′之间的差值yyy。

由于激光雷达测量值为(xm,ym)(x_m,y_m)(xm​,ym​),与状态向量维度不同。在与状态向量进行计算时,需要乘以一个测量矩阵HHH变为同维的状态量,上式用矩阵表示为:
[δxδy]=[xmym]−[10000100]∗[x+vx∗δty+vy∗δtvxvy]\begin{bmatrix}{\delta{x}} \\ \delta{y}\end{bmatrix}=\begin{bmatrix}x_m\\y_m\end{bmatrix}-\begin{bmatrix}1&0&0&0 \\0&1&0&0 \end{bmatrix}*\begin{bmatrix} {x+v_x*\delta{t}} \\ {y+v_y*\delta{t}}\\v_x\\v_y\end{bmatrix}[δxδy​]=[xm​ym​​]−[10​01​00​00​]∗⎣⎢⎢⎡​x+vx​∗δty+vy​∗δtvx​vy​​⎦⎥⎥⎤​
因此这里的测量矩阵HHH为:

 // Lidar - measurement matrixH_laser_ = MatrixXd(2, 4);H_laser_ << 1, 0, 0, 0,0, 1, 0, 0;

然后是下面两个公式:
S=HP′HT+RK=P′HTS−1S=HP'H^T+R\\K=P'H^TS^{-1}S=HP′HT+RK=P′HTS−1
这两个公式是求卡尔曼增益KKK,实际就是yyy的权重。

最后还有两个公式:
x=x′+KyP=(I−KH)P′x=x'+Ky\\P=(I-KH)P'x=x′+KyP=(I−KH)P′
这样就得到了当前帧的状态向量与状态协方差矩阵。然后根据新的状态向量xxx和协方差矩阵PPP可以计算下一帧的状态向量,如此反复。
这里定义测量协方差矩阵RRR为:

 // Initialize measurement covariance matrix - laserR_laser_ = MatrixXd(2, 2);R_laser_ << 0.0225, 0,0, 0.0225;

至此,卡尔曼滤波中五个重要矩阵F,P,Q,H,RF,P,Q,H,RF,P,Q,H,R就已经定义完了,更新部分代码为:

void KalmanFilter::Update(const VectorXd &z) {VectorXd z_pred = H_ * x_;VectorXd y = z - z_pred;UpdateRoutine(y);
}void KalmanFilter::UpdateRoutine(const VectorXd &y) {MatrixXd Ht = H_.transpose();MatrixXd S = H_ * P_ * Ht + R_;MatrixXd Si = S.inverse();// Compute Kalman gainMatrixXd K = P_ * Ht * Si;// Update estimatex_ = x_ + K * y;long x_size = x_.size();MatrixXd I = MatrixXd::Identity(x_size, x_size);P_ = (I - K * H_) * P_;
}

至此,线性卡尔曼滤波部分就已经完成了,下一节我们实现非线性卡尔曼滤波。


4.2 非线性卡尔曼滤波实现

非线性卡尔曼滤波是用于解决非线性问题,与线性卡尔曼滤波相同,非线性卡尔曼滤波也分为初始化、预测、更新三部分。

初始化与线性卡尔曼滤波相似,我们仍然使用KalmanFilter类构造一个追踪器。

(1)预测

这里再回顾下卡尔曼滤波预测中的两个公式:
x′=Fx+uP′=FPFT+Qx'=Fx+u\\P'=FPF^T+Qx′=Fx+uP′=FPFT+Q
这里仍然使用第一次测量值作为初始状态,状态转移矩阵为FFF,状态协方差矩阵为PPP,过程协方差矩阵为QQQ,与上一节的初始化相同。


(2)更新

回顾上一节,更新部分第一个公式为:
y=z−Hx′y=z-Hx'y=z−Hx′
但是这里我们使用的是毫米波雷达数据,测量得到的数据为:距离ρ\rhoρ,方向夹角 φ\varphiφ,以及径向速度ρ˙\dot{\rho}ρ˙​。

这里得到的信息为极坐标信息,需要转换为直角坐标距离。我们将上式写成矩阵形式为:
y=[ρφρ˙]−[ρx2+ρy2arctan(ρyρx)ρxvxρyvyρx2+ρy2]y=\begin{bmatrix}\rho\\\\\varphi\\\\\dot{\rho}\end{bmatrix}-\begin{bmatrix}\sqrt{\rho_x^2+\rho_y^2} \\\\arctan(\frac{\rho_y}{\rho_x})\\\\\frac{\rho_xv_x\rho_yv_y}{\sqrt{\rho_x^2+\rho_y^2} } \end{bmatrix}y=⎣⎢⎢⎢⎢⎡​ρφρ˙​​⎦⎥⎥⎥⎥⎤​−⎣⎢⎢⎢⎢⎢⎢⎡​ρx2​+ρy2​​arctan(ρx​ρy​​)ρx2​+ρy2​​ρx​vx​ρy​vy​​​⎦⎥⎥⎥⎥⎥⎥⎤​

其中,ρx,ρy\rho_x,\rho_yρx​,ρy​为预测后的位置,vx,vyv_x,v_yvx​,vy​为预测后的速度。这里的位置转换,速度转换为非线性转换

然后是卡尔曼滤波的后面两个公式:
S=HP′HT+RK=P′HTS−1S=HP'H^T+R\\K=P'H^TS^{-1}S=HP′HT+RK=P′HTS−1

这里在求卡尔曼增益时,需要知道测量矩阵HHH,因为我们测量数据为(3,1)(3,1)(3,1)的列向量,而状态向量为(4,1)(4,1)(4,1)的列向量。因此测量矩阵的维度为(3,4)(3,4)(3,4)。我们可以写成下面这个形式:
Hx′=[ρx2+ρy2arctan(ρyρx)ρxvxρyvyρx2+ρy2]=[H00H01H02H03H10H11H12H13H20H21H22H23]∗[ρxρyvxvy]Hx'=\begin{bmatrix}\sqrt{\rho_x^2+\rho_y^2} \\\\arctan(\frac{\rho_y}{\rho_x})\\\\\frac{\rho_xv_x\rho_yv_y}{\sqrt{\rho_x^2+\rho_y^2} } \end{bmatrix}=\begin{bmatrix}H_{00}&H_{01}&H_{02}&H_{03}\\H_{10}&H_{11}&H_{12}&H_{13}\\H_{20}&H_{21}&H_{22}&H_{23}\end{bmatrix}*\begin{bmatrix}\rho_x\\\rho_y\\v_x\\v_y\end{bmatrix}Hx′=⎣⎢⎢⎢⎢⎢⎢⎡​ρx2​+ρy2​​arctan(ρx​ρy​​)ρx2​+ρy2​​ρx​vx​ρy​vy​​​⎦⎥⎥⎥⎥⎥⎥⎤​=⎣⎡​H00​H10​H20​​H01​H11​H21​​H02​H12​H22​​H03​H13​H23​​⎦⎤​∗⎣⎢⎢⎡​ρx​ρy​vx​vy​​⎦⎥⎥⎤​
显然,上式两边转化为非线性转换,那么如何才能将非线性函数用近似线性函数表达呢?

这里我们使用泰勒公式来近似,近似后的泰勒公式为:
h(x)≈h(x0)+∂h(x0)∂x(x−x0)h(x)\approx h(x_0)+\frac{\partial h(x_0)}{\partial x}(x-x_0)h(x)≈h(x0​)+∂x∂h(x0​)​(x−x0​)
这里忽略了二次以上的高阶项。对xxx求导后的项为雅克比公式。这里,雅克比公式就是我们的测量矩阵HHH。

最终测量矩阵为:
H=[pxρx2+ρy2pyρx2+ρy200−pyρx2+ρy2−pxρx2+ρy200py(vxρy−vyρx)(ρx2+ρy2)3/2px(vyρx−vxρy)(ρx2+ρy2)3/2pxρx2+ρy2pyρx2+ρy2]H = \begin{bmatrix} \frac{p_x}{\sqrt{\rho_x^2+\rho_y^2}} & \frac{p_y}{\sqrt{\rho_x^2+\rho_y^2}} & 0& 0 \\\\ \frac{-p_y}{\rho_x^2+\rho_y^2} & \frac{-p_x}{\rho_x^2+\rho_y^2} & 0& 0 \\\\ \frac{p_y(v_x\rho_y-v_y\rho_x)}{(\rho_x^2+\rho_y^2)^{3/2}} & \frac{p_x(v_y\rho_x-v_x\rho_y)}{(\rho_x^2+\rho_y^2)^{3/2}} & \frac{p_x}{\sqrt{\rho_x^2+\rho_y^2}} & \frac{p_y}{\sqrt{\rho_x^2+\rho_y^2}} \end{bmatrix}H=⎣⎢⎢⎢⎢⎢⎢⎡​ρx2​+ρy2​​px​​ρx2​+ρy2​−py​​(ρx2​+ρy2​)3/2py​(vx​ρy​−vy​ρx​)​​ρx2​+ρy2​​py​​ρx2​+ρy2​−px​​(ρx2​+ρy2​)3/2px​(vy​ρx​−vx​ρy​)​​00ρx2​+ρy2​​px​​​00ρx2​+ρy2​​py​​​⎦⎥⎥⎥⎥⎥⎥⎤​
这里对测量矩阵进行初始化HHH:

  // Radar - jacobian matrixHj_ = MatrixXd(3, 4);Hj_ << 0, 0, 0, 0,0, 0, 0, 0,0, 0, 0, 0;

测量噪声协方差矩阵RRR为:

  //measurement covariance matrix - radarR_radar_ = MatrixXd(3, 3);R_radar_ << 0.09,  0,      0,0,    0.0009, 0,0,    0,      0.09;

下面是雅克比公式计算函数:

MatrixXd Tools::CalculateJacobian(const VectorXd& x_state) {MatrixXd Hj(3, 4);// Unroll state parametersfloat px = x_state(0);float py = x_state(1);float vx = x_state(2);float vy = x_state(3);// Pre-compute some term which recur in the Jacobianfloat c1 = px * px + py * py;float c2 = sqrt(c1);float c3 = c1 * c2;// Sanity check to avoid division by zeroif (std::abs(c1) < 0.0001) {std::cout << "Error in CalculateJacobian. Division by zero." << std::endl;return Hj;}// Actually compute Jacobian matrixHj << (px / c2),             (py / c2),                  0,          0,-(py / c1),                   (px / c1),                  0,          0,py * (vx*py - vy*px) / c3,    px * (vy*px - vx*py) / c3,  px / c2,    py / c2;return Hj;}

最后还有两个公式:
x=x′+KyP=(I−KH)P′x=x'+Ky\\P=(I-KH)P'x=x′+KyP=(I−KH)P′
最终得到更新后的状态向量xxx,以及新的状态协方差矩阵PPP。

至此,非线性卡尔曼滤波部分就已经完成了,重点是测量矩阵H的计算。


4.3 数据融合

完成了毫米波雷达与激光雷达的预测与更新,现在是对二者进行融合。

下图所示为毫米波雷达与激光雷达数据融合的整体框架。
首先是读入传感器数据,选择第一帧作为初始值。对于之后的帧,进行状态预测,然后根据传感器类型进行更新,最后得到跟踪后的状态向量。

这里定义了一个数据融合类FusionEKF

class FusionEKF {public:/* Constructor. */FusionEKF();/* Destructor. */virtual ~FusionEKF();/* Run the whole flow of the Kalman Filter from here. */void ProcessMeasurement(const MeasurementPackage &measurement_pack);/* Kalman Filter update and prediction math lives in here. */KalmanFilter ekf_;private:// check whether the tracking toolbox was initialized or not (first measurement)bool is_initialized_;// previous timestamplong long previous_timestamp_;// tool object used to compute Jacobian and RMSETools tools;Eigen::MatrixXd R_laser_;Eigen::MatrixXd R_radar_;Eigen::MatrixXd H_laser_;Eigen::MatrixXd Hj_;float noise_ax;float noise_ay;
};

上面有一个很重要的处理函数ProcessMeasurement(),是对上图数据融合的代码实现。

void FusionEKF::ProcessMeasurement(const MeasurementPackage &measurement_pack) {/******************************************************************************  Initialization****************************************************************************/if (!is_initialized_){if (measurement_pack.sensor_type_ == MeasurementPackage::RADAR) {// Extract values from measurementfloat rho     = measurement_pack.raw_measurements_(0);float phi      = measurement_pack.raw_measurements_(1);float rho_dot  = measurement_pack.raw_measurements_(2);// Convert from polar to cartesian coordinatesfloat px = rho * cos(phi);float py = rho * sin(phi);float vx = rho_dot * cos(phi);float vy = rho_dot * sin(phi);// Initialize stateekf_.x_ << px, py, vx, vy;}else if (measurement_pack.sensor_type_ == MeasurementPackage::LASER) {// Extract values from measurementfloat px = measurement_pack.raw_measurements_(0);float py = measurement_pack.raw_measurements_(1);// Initialize stateekf_.x_ << px, py, 0.0, 0.0;}// Update last measurementprevious_timestamp_ = measurement_pack.timestamp_;// Done initializing, no need to predict or updateis_initialized_ = true;return;}/******************************************************************************  Prediction****************************************************************************/// Compute elapsed time from last measurementfloat dt = (measurement_pack.timestamp_ - previous_timestamp_) / 1000000.0;// Update last measurementprevious_timestamp_ = measurement_pack.timestamp_;// Update state transition matrix F (according to elapsed time dt)ekf_.F_(0, 2) = dt;ekf_.F_(1, 3) = dt;// Compute process noise covariance matrixfloat dt_2 = dt * dt;float dt_3 = dt_2 * dt;float dt_4 = dt_3 * dt;ekf_.Q_   <<    dt_4 / 4 * noise_ax,    0,                      dt_3 / 2 * noise_ax,    0,0,                        dt_4 / 4 * noise_ay,    0,                      dt_3 / 2 * noise_ay,dt_3 / 2 * noise_ax,    0,                      dt_2 * noise_ax,        0,0,                        dt_3 / 2 * noise_ay,    0,                      dt_2 * noise_ay;ekf_.Predict();/******************************************************************************  Update****************************************************************************/if (measurement_pack.sensor_type_ == MeasurementPackage::RADAR) {// Radar updatesekf_.R_ = R_radar_;ekf_.H_ = tools.CalculateJacobian(ekf_.x_);ekf_.UpdateEKF(measurement_pack.raw_measurements_);} else {// Laser updatesekf_.R_ = R_laser_;ekf_.H_ = H_laser_;ekf_.Update(measurement_pack.raw_measurements_);}// print the outputcout << "x_ = " << ekf_.x_ << endl;cout << "P_ = " << ekf_.P_ << endl;
}

至此,激光雷达与毫米波雷达融合部分就已经完成了。

而在实际开发时,一辆ADAS汽车通常会安装多个毫米波雷达,包括前雷达,后雷达,角雷达;有的还会装有激光雷达;本文没有考虑相机,而相机是ADAS汽车最常使用的传感器。在对这些传感器进行数据融合时还需要考虑时间同步空间同步问题。

以上只是对汽车外部的障碍物进行定位,而如果汽车想对自身进行定位的话,通常需要安装惯性测量单元IMU和GPS传感器,在下一篇博客中将会进行介绍。

动手学无人驾驶(5):多传感器数据融合相关推荐

  1. 动手学无人驾驶(6):基于IMU和GPS数据融合的自车定位

    在上一篇博文<动手学无人驾驶(5):多传感器数据融合>介绍了如何使用Radar和LiDAR数据对自行车进行追踪,这是对汽车外界运动物体进行定位. 对于自动驾驶的汽车来说,有时也需要对自身进 ...

  2. 动手学无人驾驶(4):基于激光雷达点云数据3D目标检测

    上一篇文章<动手学无人驾驶(3):基于激光雷达3D多目标追踪>介绍了3D多目标追踪,多目标追踪里使用的传感器数据为激光雷达Lidar检测到的数据,本文就介绍如何基于激光雷达点云数据进行3D ...

  3. 重磅!国内首个面向自动驾驶领域的多传感器数据融合系统课程

    应用背景介绍 多传感器融合是一项结合多传感器数据的综合性前沿内容,主要包括Camera.激光雷达.IMU.毫米波雷达等传感器的融合,在自动驾驶.移动机器人的感知和定位领域中占有非常重要的地位: 随着A ...

  4. 多传感器数据融合技术如何应用在自动驾驶领域?

    多传感器融合是一项结合多传感器数据的综合性前沿内容,主要包括Camera.激光雷达.IMU.毫米波雷达等传感器的融合,在自动驾驶.移动机器人的感知和定位领域中占有非常重要的地位: 随着AI技术的大规模 ...

  5. 基于多传感器数据融合的全自动泊车系统研究与应用(开题报告)

    论 文 题 目 基于多传感器数据融合的全自动泊车系统研究与应用 论文选题来源 以某公司自动泊车项目为依托 本课题研究的目的.意义 随着工业4.0的到来,通讯技术.计算机技术.人工智能技术已经应用到汽车 ...

  6. 基于多传感器数据融合的全自动泊车系统研究与应用(文献综述)

    附:文献综述 基于多传感器数据融合的全自动泊车系统研究与应用 摘要: 随着科技的进步与城市车位空间的减小,汽车逐渐向智能化发展,如何安全快速地泊车成为驾驶员面临的难题.关于自动泊车辅助系统的研究,国内 ...

  7. 动手学无人驾驶(2):车辆检测

    上一篇博客介绍了无人驾驶中深度学习在交通标志识别中的应用(动手学无人驾驶(1):交通标志识别). 本文介绍如何使用深度学习进行车辆检测,使用到的模型是YOLO模型,关于YOLO模型的具体检测原理,可以 ...

  8. 动手学无人驾驶(1):交通标志识别

    今天主要介绍无人驾驶当中深度学习技术的应用. 本文是根据博客专家AdamShan的文章整理而来,在此表示感谢. 关于深度学习的图像分类技术,网上已有很多关于深度学习的课程(如吴恩达老师的深度学习专项课 ...

  9. 传感器数据融合及姿态估计总结

    传感器数据融合及姿态估计总结 一.准备工作 二.原始数据 三.传感器直接估算姿态 3.1 加速度计和磁力计解算姿态角 3.1.1 加速度计解算Roll和Pitch 3.1.2 磁力计估算Yaw角 3. ...

最新文章

  1. 记录一下提取文件夹中所有文件名字
  2. 系统架构领域的一些学习材料
  3. Java集合篇:Stack
  4. outlook 未安装信息服务器,Outlook Web Access 未初始化并且在客户端访问服务器上的应用程序日志中记录了事件 ID 64...
  5. 机器学习(三十八)——博弈论(1)
  6. Oracle Weblogic 11g(10.3.4)的小知识
  7. adminlte支持html5吗,spring boot:用adminlte做前端
  8. android sqlite assets,使用GreenDao加载assets下sqlite数据库的示例
  9. python之os.path.join
  10. Proxmark3教程2:用Pm3Gui_Pro V5.2 新功能 IC卡匠数据维护
  11. VBA学习笔记6:将多个工作表中满足条件的数据汇总到同一个工作表
  12. quicktime ogv_Windows的QuickTime已死,应卸载以确保安全
  13. 新西兰八大名校--新西兰公立大学
  14. 人件札记:团队的化学反应
  15. 近几年Linux TCP相关的漏洞被夸大了
  16. windows通知栏中显示 微信等应用软件 的通知
  17. 目标检测算法——GHM
  18. ORACLE子查询的多种用法
  19. 房产领域共享公司分析(共享经济专题)2/7
  20. 中国十大IT行业名校

热门文章

  1. linux c 读取摄像头,Linux下onvif客户端获取ipc摄像头 获取能力:GetCapabilities
  2. html字体变大自动换行,网页css中实现字符超出宽度自动换行和英语字符不断行的解决方法...
  3. mysql提供了表示日期和时间的数据类型_MySQL数据类型 - 日期和时间类型(1)
  4. python中restful接口开发实例_Python RESTful接口开发02
  5. Linux下的Shell编程之Helloworld.sh看过来
  6. 外部函数能修改闭包内的变量_Python函数式编程,Python闭包
  7. cisco服务器维修,面向终端的AMP控制台的思科维护的排除列表更改
  8. python tk protocol_Python Modbus_tk在树莓派上实现rtu master
  9. excel排名_Excel案例:比赛中,如何实时显示排名
  10. SliceProceduralMesh的使用