从零开始激光slam定位【1】- 激光里程计

写在前面:本文是参考知乎任乾大佬的帖子完成,增加了自己的注释以及一些其他功能,如果有理解不到位的地方,请大家指正~

里程计流程

1、如果当前帧是第一帧,那么将其看做关键帧,进行关键帧更新
2、关键帧更新都做了什么?更新local_map队列,更新local_map,更新ndt的target_cloud。
3、如果不是第一帧,那么将其与local_map进行ndt配准,配准的所需要的guess_pose是根据匀速运动模型进行计算的。
4、配准得到当前帧的pose,利用当前帧和上一阵的pose,结合匀速运动模型得到下一次配准的guess_pose;如果当前帧的pose相对于上一关键帧的pose距离较大,则更新关键帧。

front_end_node.cpp的注释

/*
 * @Descripttion: 对任乾大佬的tag4.0增加map--->lidar的tf变换,并可视化最原始的变换后的点云
 * @vision: 
 * @Author: suyunzzz
 * @Date: 2020-08-20 23:40:44
 * @LastEditors: Please set LastEditors
 * @LastEditTime: 2020-08-26 23:20:21
 */
/*
 * @Description: 
 * @Author: Ren Qian
 * @Date: 2020-02-05 02:56:27
 */

#include <ros/ros.h>
#include <pcl/common/transforms.h>

#include "glog/logging.h"

#include "lidar_localization/global_defination/global_defination.h"
#include "lidar_localization/subscriber/cloud_subscriber.hpp"
#include "lidar_localization/subscriber/imu_subscriber.hpp"
#include "lidar_localization/subscriber/gnss_subscriber.hpp"
#include "lidar_localization/tf_listener/tf_listener.hpp"
#include "lidar_localization/publisher/cloud_publisher.hpp"
#include "lidar_localization/publisher/odometry_publisher.hpp"
#include "lidar_localization/front_end/front_end.hpp"           // 前端相关的头文件

#include <tf/transform_broadcaster.h>
#include <tf/transform_datatypes.h>

using namespace lidar_localization;

int main(int argc, char *argv[]) {
    google::InitGoogleLogging(argv[0]);
    FLAGS_log_dir = WORK_SPACE_PATH + "/Log";
    FLAGS_alsologtostderr = 1;

    // 初始化节点
    ros::init(argc, argv, "front_end_node");
    ros::NodeHandle nh;

    std::shared_ptr<CloudSubscriber> cloud_sub_ptr = std::make_shared<CloudSubscriber>(nh, "/kitti/velo/pointcloud", 100000);
    std::shared_ptr<IMUSubscriber> imu_sub_ptr = std::make_shared<IMUSubscriber>(nh, "/kitti/oxts/imu", 1000000);
    std::shared_ptr<GNSSSubscriber> gnss_sub_ptr = std::make_shared<GNSSSubscriber>(nh, "/kitti/oxts/gps/fix", 1000000);

    // TF监听。lidar-->imu
    std::shared_ptr<TFListener> lidar_to_imu_ptr = std::make_shared<TFListener>(nh, "velo_link", "imu_link");   

    // 发布者,发布到frame_id="/map"
    std::shared_ptr<CloudPublisher> cloud_pub_ptr = std::make_shared<CloudPublisher>(nh, "current_scan", 100, "/map");  
    std::shared_ptr<CloudPublisher> raw_cloud_pub_ptr = std::make_shared<CloudPublisher>(nh,"raw_current_scan",100,"/map");
    std::shared_ptr<CloudPublisher> local_map_pub_ptr = std::make_shared<CloudPublisher>(nh, "local_map", 100, "/map");
    std::shared_ptr<CloudPublisher> global_map_pub_ptr = std::make_shared<CloudPublisher>(nh, "global_map", 100, "/map");

    // 发布odom信息,topic_name,base_frame_id,child_frame_id
    std::shared_ptr<OdometryPublisher> laser_odom_pub_ptr = std::make_shared<OdometryPublisher>(nh, "laser_odom", "map", "lidar", 100);
    std::shared_ptr<OdometryPublisher> gnss_pub_ptr = std::make_shared<OdometryPublisher>(nh, "gnss", "map", "lidar", 100);

    // 创建一个前端对象
    std::shared_ptr<FrontEnd> front_end_ptr = std::make_shared<FrontEnd>();

    // 数据队列
    std::deque<CloudData> cloud_data_buff;
    std::deque<IMUData> imu_data_buff;
    std::deque<GNSSData> gnss_data_buff;
    Eigen::Matrix4f lidar_to_imu = Eigen::Matrix4f::Identity();
    bool transform_received = false;
    bool gnss_origin_position_inited = false;
    bool front_end_pose_inited = false;             // true 前端pose未初始化

    // 局部地图,全局地图,当前帧点云
    CloudData::CLOUD_PTR local_map_ptr(new CloudData::CLOUD());     
    CloudData::CLOUD_PTR global_map_ptr(new CloudData::CLOUD());
    CloudData::CLOUD_PTR current_scan_ptr(new CloudData::CLOUD());
    CloudData::CLOUD_PTR current_scan_ptr_raw(new CloudData::CLOUD());  // 当前帧变换后的结果,

    /****************************************************/
    // 以下为我新增的tf变换,map--->lidar
    // 发布tf变换 map--->lidar
    //static tf::TransformBroadcaster br;
    tf::TransformBroadcaster br_;          
    tf::Transform transform;
    tf::Quaternion q;
    struct pose{double x,y,z;
                double roll,pitch,yaw;};
    struct pose current_pose_;
    /***************************************************/
    
    double run_time = 0.0;
    double init_time = 0.0;
    bool time_inited = false;
    bool has_global_map_published = false; 

    ros::Rate rate(100);
    while (ros::ok()) {
        ros::spinOnce();

        // 数据读入
        cloud_sub_ptr->ParseData(cloud_data_buff);
        imu_sub_ptr->ParseData(imu_data_buff);
        gnss_sub_ptr->ParseData(gnss_data_buff);

        // 如果没有得到变换
        if (!transform_received) {
            if (lidar_to_imu_ptr->LookupData(lidar_to_imu)) {
                transform_received = true;
            }
        } else {
            while (cloud_data_buff.size() > 0 && imu_data_buff.size() > 0 && gnss_data_buff.size() > 0) {
                CloudData cloud_data = cloud_data_buff.front();
                IMUData imu_data = imu_data_buff.front();
                GNSSData gnss_data = gnss_data_buff.front();

                if (!time_inited) {
                    time_inited = true;
                    init_time = cloud_data.time;
                } else {
                    run_time = cloud_data.time - init_time;
                }

                double d_time = cloud_data.time - imu_data.time;
                if (d_time < -0.05) {       // cloud慢
                    cloud_data_buff.pop_front();
                } else if (d_time > 0.05) {     // cloud快
                    imu_data_buff.pop_front();
                    gnss_data_buff.pop_front();
                } else {                                    // 开始
                    cloud_data_buff.pop_front();            // 将使用的数据在队列中删除
                    imu_data_buff.pop_front();
                    gnss_data_buff.pop_front();

                    Eigen::Matrix4f odometry_matrix = Eigen::Matrix4f::Identity();

                    if (!gnss_origin_position_inited) {
                        gnss_data.InitOriginPosition();
                        gnss_origin_position_inited = true;
                    }
                    gnss_data.UpdateXYZ();
                    odometry_matrix(0,3) = gnss_data.local_E;
                    odometry_matrix(1,3) = gnss_data.local_N;
                    odometry_matrix(2,3) = gnss_data.local_U;
                    odometry_matrix.block<3,3>(0,0) = imu_data.GetOrientationMatrix();
                    odometry_matrix *= lidar_to_imu;
                    gnss_pub_ptr->Publish(odometry_matrix);             // gnss发布里程计信息

                    if (!front_end_pose_inited) {       // 如果未初始化前端,将gnss的pose作为初始pose
                        front_end_pose_inited = true;
                        front_end_ptr->SetInitPose(odometry_matrix);        
                    }
                    front_end_ptr->SetPredictPose(odometry_matrix);                 // gnss的里程计作为预测(没有用到)
                    Eigen::Matrix4f laser_matrix = front_end_ptr->Update(cloud_data);               // 更新当前帧,返回当前帧的pose
                    laser_odom_pub_ptr->Publish(laser_matrix);                  // lidar发布里程计信息(当前帧的pose)

                    /****************************************************/
                    // 以下为我新增的tf变换,map--->lidar
                    // 发布tf变换map--->lidar
                    // current_pose_写入transform
                    transform.setOrigin(tf::Vector3(laser_matrix(0,3), laser_matrix(1,3), laser_matrix(2,3)));   // transform 设置平移
                    tf::Matrix3x3 mat_b;
                    mat_b.setValue(static_cast<double>(laser_matrix(0, 0)), static_cast<double>(laser_matrix(0, 1)),
                                    static_cast<double>(laser_matrix(0, 2)), static_cast<double>(laser_matrix(1, 0)),
                                    static_cast<double>(laser_matrix(1, 1)), static_cast<double>(laser_matrix(1, 2)),
                                    static_cast<double>(laser_matrix(2, 0)), static_cast<double>(laser_matrix(2, 1)),
                                    static_cast<double>(laser_matrix(2, 2)));                    
                    mat_b.getRPY(current_pose_.roll, current_pose_.pitch, current_pose_.yaw, 1);//mat2rpy  旋转
                    q.setRPY(current_pose_.roll, current_pose_.pitch, current_pose_.yaw); //q from rpy
                    transform.setRotation(q);//trans from q
                    br_.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "map", "lidar"));  // map--->scan的transform
                    /****************************************************/
                    
                    // 当前帧的点云
                    front_end_ptr->GetCurrentScan(current_scan_ptr);            // 变换到global下的当前帧点云
                    cloud_pub_ptr->Publish(current_scan_ptr);                   // 发布当前帧点云
                    front_end_ptr->GetCurrentScanRaw(current_scan_ptr_raw);        // 发布变换后的原始点云,未下采样
                    raw_cloud_pub_ptr->Publish(current_scan_ptr_raw);


                    if (front_end_ptr->GetNewLocalMap(local_map_ptr))           // 发布局部地图,只有遇到关键帧才能更新局部地图
                        local_map_pub_ptr->Publish(local_map_ptr);
                }
                if (run_time > 460.0 && !has_global_map_published) {            // 全局地图,只能发布一次,并且有时间限制
                    if (front_end_ptr->GetNewGlobalMap(global_map_ptr)) {    //  每100个关键帧,才更新一次全局地图
                        global_map_pub_ptr->Publish(global_map_ptr);
                        has_global_map_published = true;
                    }
                }
            }
        }

        rate.sleep();
    }

    return 0;
}

可视化

在这里插入图片描述黄色的代表gnss的里程计,红色的代表激光里程计,大的Axis的坐标系是lidar,小的坐标系是map。

debug

typename boost::detail::sp_dereference<T>::type boost::shared_ptr<T>::operator*() const [with T = pcl::PointCloud<pcl::PointXYZ>; typename boost::detail::sp_dereference<T>::type = pcl::PointCloud<pcl::PointXYZ>&]: Assertion `px != 0' failed

在这里插入图片描述
解决:
这个问题的原因是创建pcl点云指针的时候没有初始化,应该在构造函数中进行初始化。

参考

从零开始做自动驾驶定位(四): 前端里程计之初试