欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

传感器融合-Lidar and Radar Sensorfusion源码解读

程序员文章站 2022-03-07 12:50:42
...

1.完整代码地址:
链接:https://pan.baidu.com/s/1bV7ImmrRmee7KBQRKdQX6Q
提取码:x8hn
2.理论部分:
传感器融合-Lidar and Radar Sensorfusion源码解读
3.代码部分
(1)初始化(initialization)
使用Eigen库中非定长的数据结构,下图中的VerctorXd表示X维的列矩阵,其中的元素数据类型为double。

#include "Eigen/Dense"

class KalmanFilter {
public:

    KalmanFilter();
    ~KalmanFilter();
    void Initialization(Eigen::VectorXd x_in);
    bool IsInitialized();
    void SetF(Eigen::MatrixXd F_in);
    void SetP(Eigen::MatrixXd P_in);
    void SetQ(Eigen::MatrixXd Q_in);
    void SetH(Eigen::MatrixXd H_in);
    void SetR(Eigen::MatrixXd R_in);
    void Prediction();
    void KFUpdate(Eigen::VectorXd z);
    void EKFUpdate(Eigen::VectorXd z);
    Eigen::VectorXd GetX();
private:
    void CalculateJacobianMatrix();
    // flag of initialization
    bool is_initialized_;
    // state vector
    Eigen::VectorXd x_;
    // state covariance matrix
    Eigen::MatrixXd P_;
    // state transistion matrix
    Eigen::MatrixXd F_;
    // process covariance matrix
    Eigen::MatrixXd Q_;
    // measurement matrix
    Eigen::MatrixXd H_;
    // measurement covariance matrix
    Eigen::MatrixXd R_;
};

在这里,我们新建了一个KalmanFilter类,其中定义了一个叫做x_的变量,表示这个卡尔曼滤波器的状态向量。
(2)预测(Prediction)
完成初始化后,我们开始写Prediction部分的代码。首先是公式
传感器融合-Lidar and Radar Sensorfusion源码解读

这里的x为状态向量,通过左乘一个矩阵F,再加上外部的影响u,得到预测的状态向量x’。这里的F成为状态转移矩阵(state transistion matrix)。以2维的匀速运动为例,这里的x为
传感器融合-Lidar and Radar Sensorfusion源码解读

对于匀速运动模型,根据中学物理课本中的公式s1 = s0 + vt,经过时间△t后的预测状态向量应该是
传感器融合-Lidar and Radar Sensorfusion源码解读

由于假设当前运动为匀速运动,加速度为0,加速度不会对预测造成影响,即传感器融合-Lidar and Radar Sensorfusion源码解读

如果换成加速或减速运动模型,就可以引入加速度a_x和a_y,根据s1 = s0 + vt + at^2/2这里的u会变成:
传感器融合-Lidar and Radar Sensorfusion源码解读

作为入门课程,这里不讨论太复杂的模型,因此公式
传感器融合-Lidar and Radar Sensorfusion源码解读

最终将写成
传感器融合-Lidar and Radar Sensorfusion源码解读

由于每次做预测时,△t的大小不固定,因此我们专门写一个函数SetF()。

void KalmanFilter::SetF(Eigen::MatrixXd F_in)
{
    F_ = F_in;
}

再看预测模块的第二个公式
传感器融合-Lidar and Radar Sensorfusion源码解读

该公式中P表示系统的不确定程度,这个不确定程度,在卡尔曼滤波器初始化时会很大,随着越来越多的数据注入滤波器中,不确定程度会变小,P的专业术语叫状态协方差矩阵(state covariance matrix);这里的Q表示过程噪声(process covariance matrix),即无法用x’=Fx+u表示的噪声,比如车辆运动时突然到了上坡,这个影响是无法用之前的状态转移估计的。

以激光雷达为例。激光雷达只能测量点的位置,无法测量点的速度,因此对于激光雷达的协方差矩阵来说,对于位置信息,其测量位置较准,不确定度较低;对于速度信息,不确定度较高。因此可以认为这里的P为:
传感器融合-Lidar and Radar Sensorfusion源码解读

由于Q对整个系统存在影响,但又不能太确定对系统的影响有多大。工程上,我们一般将Q设置为单位矩阵参与运算,即
传感器融合-Lidar and Radar Sensorfusion源码解读

根据以上内容和公式
传感器融合-Lidar and Radar Sensorfusion源码解读

我们就可以写出预测模块的代码了

void KalmanFilter::SetF(Eigen::MatrixXd F_in)
{
    F_ = F_in;
}
void KalmanFilter::SetP(Eigen::MatrixXd P_in)
{
    P_ = P_in;
}
void KalmanFilter::SetQ(Eigen::MatrixXd Q_in)
{
    Q_ = Q_in;
}
void KalmanFilter::SetH(Eigen::MatrixXd H_in)
{
    H_ = H_in;
}
void KalmanFilter::SetR(Eigen::MatrixXd R_in)
{
    R_ = R_in;
}
void KalmanFilter::Prediction()
{
    x_ = F_ * x_;
    Eigen::MatrixXd Ft = F_.transpose();
    P_ = F_ * P_ * Ft + Q_;
}

际编程时x’及P’不需要申请新的内存去存储,使用原有的x和P代替即可。

(3)观测(Measurement)
观测的第一个公式是
传感器融合-Lidar and Radar Sensorfusion源码解读
这个公式计算的是实际观测到的测量值z与预测值x’之间差值y。不同传感器的测量值一般不同,比如激光雷达测量的位置信号为x方向和y方向上的距离,毫米波雷达测量的是位置和角度信息。因此需要将状态向量左乘一个矩阵H,才能与测量值进行相应的运算,这个H被称为测量矩阵(Measurement Matrix)。

激光雷达的测量值为
传感器融合-Lidar and Radar Sensorfusion源码解读

其中xm和ym分别表示x方向上的测量(measurement)值。
由于x’是一个41的列向量,如果要与一个21的列向量z进行减运算,需要左乘一个2*4的矩阵才行,因此整个公式最终要写成:
传感器融合-Lidar and Radar Sensorfusion源码解读

即,对于激光雷达来说,这里的测量矩阵H为:
传感器融合-Lidar and Radar Sensorfusion源码解读
求得y值后,对y值乘以一个加权量,再加到原来的预测量上去,就可以得到一个既考虑了测量值,又考虑了预测模型的位置的状态向量了。
那么y的这个权值该如何取呢?
再看接下里的两个公式
传感器融合-Lidar and Radar Sensorfusion源码解读
传感器融合-Lidar and Radar Sensorfusion源码解读
这两个公式求的是卡尔曼滤波器中一个很重要的量——卡尔曼增益K(Kalman Gain),用人话讲就是求y值的权值。
公式中的R是测量噪声矩阵(measurement covariance matrix),这个表示的是测量值与真值之间的差值。一般情况下,传感器的厂家会提供该值。S只是为了简化公式,写的一个临时变量,不要太在意。
看最后两个公式
传感器融合-Lidar and Radar Sensorfusion源码解读
传感器融合-Lidar and Radar Sensorfusion源码解读
这两个公式,实际上完成了卡尔曼滤波器的闭环,第一个公式是完成了当前状态向量x的更新,不仅考虑了上一时刻的预测值,也考虑了测量值,和整个系统的噪声,第二个公式根据卡尔曼增益,更新了系统的不确定度P,用于下一个周期的运算,该公式中的I为与状态向量同维度的单位矩阵。
将以上五个公式写成代码如下:

void KalmanFilter::KFUpdate(Eigen::VectorXd z)
{
    Eigen::VectorXd y = z - H_ * x_;
    Eigen::MatrixXd Ht = H_.transpose();
    Eigen::MatrixXd S = H_ * P_ * Ht + R_;
    Eigen::MatrixXd Si = S.inverse();
    Eigen::MatrixXd K =  P_ * Ht * Si;
    x_ = x_ + (K * y);
    int x_size = x_.size();
    Eigen::MatrixXd I = Eigen::MatrixXd::Identity(x_size, x_size);
    P_ = (I - K * H_) * P_;
}

主函数main.cpp:
需要用到的自定义数据类型

class MeasurementPackage {
public:
  long long timestamp_;
  enum SensorType{
    LASER,
    RADAR
  } sensor_type_;
  Eigen::VectorXd raw_measurements_;
};
class GroundTruthPackage {
public:
  long timestamp_;
  enum SensorType{
    LASER,
    RADAR
  } sensor_type_;
  Eigen::VectorXd gt_values_;
};
int main(int argc, char* argv[]) {

    // 设置毫米波雷达/激光雷达输入数据的路径
    // Set radar & lidar data file path
    std::string input_file_name = "../data/sample-laser-radar-measurement-data-2.txt";

    // 打开数据,若失败则输出失败信息,返回-1,并终止程序
    // Open file. if failed return -1 & end program
    std::ifstream input_file(input_file_name.c_str(), std::ifstream::in);
    if (!input_file.is_open()) {
        std::cout << "Failed to open file named : " << input_file_name << std::endl;
        return -1;
    }

    // 分配内存
    // measurement_pack_list:毫米波雷达/激光雷达实际测得的数据。数据包含测量值和时间戳,即融合算法的输入。
    // groundtruth_pack_list:每次测量时,障碍物位置的真值。对比融合算法输出和真值的差别,用于评估融合算法结果的好坏。
    std::vector<MeasurementPackage> measurement_pack_list;
    std::vector<GroundTruthPackage> groundtruth_pack_list;

    // 通过while循环将雷达测量值和真值全部读入内存,存入measurement_pack_list和groundtruth_pack_list中
    // Store radar & lidar data into memory
    std::string line;
    while (getline(input_file, line)) {
        std::string sensor_type;
        MeasurementPackage meas_package;
        GroundTruthPackage gt_package;
        std::istringstream iss(line);
        long long timestamp;

        // 读取当前行的第一个元素,L代表Lidar数据,R代表Radar数据
        // Reads first element from the current line. L stands for Lidar. R stands for Radar.
        iss >> sensor_type;
        if (sensor_type.compare("L") == 0) {
            // 激光雷达数据 Lidar data
            // 该行第二个元素为测量值x,第三个元素为测量值y,第四个元素为时间戳(纳秒)
            // 2nd element is x; 3rd element is y; 4th element is timestamp(nano second)
            meas_package.sensor_type_ = MeasurementPackage::LASER;
            meas_package.raw_measurements_ = Eigen::VectorXd(2);
            float x;
            float y;
            iss >> x;
            iss >> y;
            meas_package.raw_measurements_ << x, y;
            iss >> timestamp;
            meas_package.timestamp_ = timestamp;
            measurement_pack_list.push_back(meas_package);
        } else if (sensor_type.compare("R") == 0) {
            // 毫米波雷达数据 Radar data
            // 该行第二个元素为距离pho,第三个元素为角度phi,第四个元素为径向速度pho_dot,第五个元素为时间戳(纳秒)
            // 2nd element is pho; 3rd element is phi; 4th element is pho_dot; 5th element is timestamp(nano second)
            meas_package.sensor_type_ = MeasurementPackage::RADAR;
            meas_package.raw_measurements_ = Eigen::VectorXd(3);
            float rho;
            float phi;
            float rho_dot;
            iss >> rho;
            iss >> phi;
            iss >> rho_dot;
            meas_package.raw_measurements_ << rho, phi, rho_dot;
            iss >> timestamp;
            meas_package.timestamp_ = timestamp;
            measurement_pack_list.push_back(meas_package);
        }

        // 当前行的最后四个元素分别是x方向上的距离真值,y方向上的距离真值,x方向上的速度真值,y方向上的速度真值
        // read ground truth data to compare later
        float x_gt;
        float y_gt;
        float vx_gt;
        float vy_gt;
        iss >> x_gt;
        iss >> y_gt;
        iss >> vx_gt;
        iss >> vy_gt;
        gt_package.gt_values_ = Eigen::VectorXd(4);
        gt_package.gt_values_ << x_gt, y_gt, vx_gt, vy_gt;
        groundtruth_pack_list.push_back(gt_package);
    }

    std::cout << "Success to load data." << std::endl;

    // 开始部署跟踪算法
    SensorFusion fuser;

    for (size_t i = 0; i < measurement_pack_list.size(); ++i) {
        fuser.Process(measurement_pack_list[i]);
        Eigen::Vector4d x_out = fuser.kf_.GetX();

        std::cout << "x " << x_out(0)
                  << " y " << x_out(1)
                  << " vx " << x_out(2)
                  << " vy " << x_out(3) 
                  << std::endl;
    }
}

sensorfusion.cpp

SensorFusion::SensorFusion()
{
    is_initialized_ = false;
    last_timestamp_ = 0.0;

    // 初始化激光雷达的测量矩阵 H_lidar_
    // Set Lidar's measurement matrix H_lidar_
    H_lidar_ = Eigen::MatrixXd(2, 4);
    H_lidar_ << 1, 0, 0, 0,
                0, 1, 0, 0;

    // 设置传感器的测量噪声矩阵,一般由传感器厂商提供,如不提供,也可通过有经验的工程师调试得到
    // Set R. R is provided by Sensor supplier, in sensor datasheet
    // set measurement covariance matrix
    R_lidar_ = Eigen::MatrixXd(2, 2);
    R_lidar_ << 0.0225, 0,
                0, 0.0225;

    // Measurement covariance matrix - radar
    R_radar_ = Eigen::MatrixXd(3, 3);
    R_radar_ << 0.09, 0, 0,
                0, 0.0009, 0,
                0, 0, 0.09;
}

SensorFusion::~SensorFusion()
{

}

void SensorFusion::Process(MeasurementPackage measurement_pack)
{
    // 第一帧数据用于初始化 Kalman 滤波器
    if (!is_initialized_) {
        Eigen::Vector4d x;
        if (measurement_pack.sensor_type_ == MeasurementPackage::LASER) {
            // 如果第一帧数据是激光雷达数据,没有速度信息,因此初始化时只能传入位置,速度设置为0
            x << measurement_pack.raw_measurements_[0], measurement_pack.raw_measurements_[1], 0, 0;
        } else if (measurement_pack.sensor_type_ == MeasurementPackage::RADAR) {
            // 如果第一帧数据是毫米波雷达,可以通过三角函数算出x-y坐标系下的位置和速度
            float rho = measurement_pack.raw_measurements_[0];
            float phi = measurement_pack.raw_measurements_[1];
            float rho_dot = measurement_pack.raw_measurements_[2];
            float position_x = rho * cos(phi);
            if (position_x < 0.0001) {
                position_x = 0.0001;
            }
            float position_y = rho * sin(phi);
            if (position_y < 0.0001) {
                position_y = 0.0001;
            }
            float velocity_x = rho_dot * cos(phi);
            float velocity_y = rho_dot * sin(phi);
            x << position_x, position_y, velocity_x , velocity_y;
        }
        
        // 避免运算时,0作为被除数
        if (fabs(x(0)) < 0.001) {
            x(0) = 0.001;
        }
        if (fabs(x(1)) < 0.001) {
            x(1) = 0.001;
        }
        // 初始化Kalman滤波器
        kf_.Initialization(x);

        // 设置协方差矩阵P
        Eigen::MatrixXd P = Eigen::MatrixXd(4, 4);
        P << 1.0, 0.0, 0.0, 0.0,
             0.0, 1.0, 0.0, 0.0,
             0.0, 0.0, 1000.0, 0.0,
             0.0, 0.0, 0.0, 1000.0;
        kf_.SetP(P);

        // 设置过程噪声Q
        Eigen::MatrixXd Q = Eigen::MatrixXd(4, 4);
        Q << 1.0, 0.0, 0.0, 0.0,
             0.0, 1.0, 0.0, 0.0,
             0.0, 0.0, 1.0, 0.0,
             0.0, 0.0, 0.0, 1.0;
        kf_.SetQ(Q);

        // 存储第一帧的时间戳,供下一帧数据使用
        last_timestamp_ = measurement_pack.timestamp_;
        is_initialized_ = true;
        return;
    }

    // 求前后两帧的时间差,数据包中的时间戳单位为微秒,处以1e6,转换为秒
    double delta_t = (measurement_pack.timestamp_ - last_timestamp_) / 1000000.0; // unit : s
    last_timestamp_ = measurement_pack.timestamp_;

    // 设置状态转移矩阵F
    Eigen::MatrixXd F = Eigen::MatrixXd(4, 4);
    F << 1.0, 0.0, delta_t, 0.0,
         0.0, 1.0, 0.0, delta_t,
         0.0, 0.0, 1.0, 0.0,
         0.0, 0.0, 0.0, 1.0;
    kf_.SetF(F);

    // 预测
    kf_.Prediction();

    // 更新
    if (measurement_pack.sensor_type_ == MeasurementPackage::LASER) {
        kf_.SetH(H_lidar_);
        kf_.SetR(R_lidar_);
        kf_.KFUpdate(measurement_pack.raw_measurements_);
    } else if (measurement_pack.sensor_type_ == MeasurementPackage::RADAR) {
        kf_.SetR(R_radar_);
        // Jocobian矩阵Hj的运算已包含在EKFUpdate中
        kf_.EKFUpdate(measurement_pack.raw_measurements_);
    }
}
相关标签: 传感器融合