复现结果

B站视频链接:aloam ros2 视频演示,Debug版本,延迟明显

rviz2

在这里插入图片描述
在这里插入图片描述

ros2 node list

在这里插入图片描述

rqt — Node_Graph

在这里插入图片描述

代码结构

关键文件

scanRegistration.cpp

// This is an advanced implementation of the algorithm described in the following paper:
//   J. Zhang and S. Singh. LOAM: Lidar Odometry and Mapping in Real-time.
//     Robotics: Science and Systems Conference (RSS). Berkeley, CA, July 2014. 

// Modifier: Tong Qin               qintonguav@gmail.com
// 	         Shaozu Cao 		    saozu.cao@connect.ust.hk

// Copyright 2013, Ji Zhang, Carnegie Mellon University
// Further contributions copyright (c) 2016, Southwest Research Institute
// All rights reserved.

#include <cmath>
#include <vector>
#include <string>
#include "aloam_velodyne_ros2/common.h"
#include "aloam_velodyne_ros2/tic_toc.h"
#include <opencv2/core.hpp>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/imu.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <tf2/transform_datatypes.h>
#include <tf2_ros/transform_broadcaster.h>

using std::atan2;
using std::cos;
using std::sin;

const double scanPeriod = 0.1;
const int systemDelay = 0; 
int systemInitCount = 0;
bool systemInited = false;
int N_SCANS = 0;
float cloudCurvature[400000];
int cloudSortInd[400000];
int cloudNeighborPicked[400000];
int cloudLabel[400000];

bool comp (int i,int j) { return (cloudCurvature[i]<cloudCurvature[j]); }

bool PUB_EACH_LINE = false;
double MINIMUM_RANGE = 0.1; 

template <typename PointT>
void removeClosedPointCloud(const pcl::PointCloud<PointT> &cloud_in,
                              pcl::PointCloud<PointT> &cloud_out, float thres)
{
    if (&cloud_in != &cloud_out)
    {
        cloud_out.header = cloud_in.header;
        cloud_out.points.resize(cloud_in.points.size());
    }

    size_t j = 0;
    for (size_t i = 0; i < cloud_in.points.size(); ++i)
    {
        if (cloud_in.points[i].x * cloud_in.points[i].x + 
            cloud_in.points[i].y * cloud_in.points[i].y + 
            cloud_in.points[i].z * cloud_in.points[i].z < thres * thres)
            continue;
        cloud_out.points[j] = cloud_in.points[i];
        j++;
    }
    if (j != cloud_in.points.size())
        cloud_out.points.resize(j);

    cloud_out.height = 1;
    cloud_out.width = static_cast<uint32_t>(j);
    cloud_out.is_dense = true;
}

class ScanRegistration : public rclcpp::Node
{
public:
    ScanRegistration() : Node("scanRegistration")
    {
        // 读取参数
        this->declare_parameter<int>("scan_line", 16);
        this->declare_parameter<double>("minimum_range", 0.1);
        this->get_parameter("scan_line", N_SCANS);
        this->get_parameter("minimum_range", MINIMUM_RANGE);

        RCLCPP_INFO(this->get_logger(), "scan line number: %d", N_SCANS);

        if(N_SCANS != 16 && N_SCANS != 32 && N_SCANS != 64)
        {
            RCLCPP_ERROR(this->get_logger(), "only support velodyne with 16, 32 or 64 scan line!");
            rclcpp::shutdown();
            return;
        }

        // 订阅
        subLaserCloud_ = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/velodyne_points", 100,
            std::bind(&ScanRegistration::laserCloudHandler, this, std::placeholders::_1));

        // 发布
        pubLaserCloud = this->create_publisher<sensor_msgs::msg::PointCloud2>("/velodyne_cloud_2", 100);
        pubCornerPointsSharp = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_sharp", 100);
        pubCornerPointsLessSharp = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_less_sharp", 100);
        pubSurfPointsFlat = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_flat", 100);
        pubSurfPointsLessFlat = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_less_flat", 100);
        pubRemovePoints = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_remove_points", 100);

        if(PUB_EACH_LINE)
        {
            for(int i = 0; i < N_SCANS; i++)
            {
                auto tmp = this->create_publisher<sensor_msgs::msg::PointCloud2>(
                    "/laser_scanid_" + std::to_string(i), 100);
                pubEachScan.push_back(tmp);
            }
        }
    }

private:
    void laserCloudHandler(const sensor_msgs::msg::PointCloud2::SharedPtr laserCloudMsg)
    {
        if (!systemInited)
        { 
            systemInitCount++;
            if (systemInitCount >= systemDelay)
                systemInited = true;
            else
                return;
        }

        TicToc t_whole;
        TicToc t_prepare;
        std::vector<int> scanStartInd(N_SCANS, 0);
        std::vector<int> scanEndInd(N_SCANS, 0);

        pcl::PointCloud<pcl::PointXYZ> laserCloudIn;
        pcl::fromROSMsg(*laserCloudMsg, laserCloudIn);
        std::vector<int> indices;

        pcl::removeNaNFromPointCloud(laserCloudIn, laserCloudIn, indices);
        removeClosedPointCloud(laserCloudIn, laserCloudIn, MINIMUM_RANGE);

        int cloudSize = laserCloudIn.points.size();
        float startOri = -atan2(laserCloudIn.points[0].y, laserCloudIn.points[0].x);
        float endOri = -atan2(laserCloudIn.points[cloudSize - 1].y,
                              laserCloudIn.points[cloudSize - 1].x) + 2 * M_PI;

        if (endOri - startOri > 3 * M_PI)
            endOri -= 2 * M_PI;
        else if (endOri - startOri < M_PI)
            endOri += 2 * M_PI;

        bool halfPassed = false;
        int count = cloudSize;
        PointType point;
        std::vector<pcl::PointCloud<PointType>> laserCloudScans(N_SCANS);

        for (int i = 0; i < cloudSize; i++)
        {
            point.x = laserCloudIn.points[i].x;
            point.y = laserCloudIn.points[i].y;
            point.z = laserCloudIn.points[i].z;

            float angle = atan(point.z / sqrt(point.x * point.x + point.y * point.y)) * 180 / M_PI;
            int scanID = 0;

            if (N_SCANS == 16)
            {
                scanID = int((angle + 15) / 2 + 0.5);
                if (scanID > (N_SCANS - 1) || scanID < 0)
                { count--; continue; }
            }
            else if (N_SCANS == 32)
            {
                scanID = int((angle + 92.0/3.0) * 3.0 / 4.0);
                if (scanID > (N_SCANS - 1) || scanID < 0)
                { count--; continue; }
            }
            else if (N_SCANS == 64)
            {   
                if (angle >= -8.83)
                    scanID = int((2 - angle) * 3.0 + 0.5);
                else
                    scanID = N_SCANS / 2 + int((-8.83 - angle) * 2.0 + 0.5);

                if (angle > 2 || angle < -24.33 || scanID > 50 || scanID < 0)
                { count--; continue; }
            }

            float ori = -atan2(point.y, point.x);
            if (!halfPassed)
            { 
                if (ori < startOri - M_PI / 2)
                    ori += 2 * M_PI;
                else if (ori > startOri + M_PI * 3 / 2)
                    ori -= 2 * M_PI;

                if (ori - startOri > M_PI)
                    halfPassed = true;
            }
            else
            {
                ori += 2 * M_PI;
                if (ori < endOri - M_PI * 3 / 2)
                    ori += 2 * M_PI;
                else if (ori > endOri + M_PI / 2)
                    ori -= 2 * M_PI;
            }

            float relTime = (ori - startOri) / (endOri - startOri);
            point.intensity = scanID + scanPeriod * relTime;
            laserCloudScans[scanID].push_back(point); 
        }

        cloudSize = count;
        RCLCPP_INFO(this->get_logger(), "points size %d", cloudSize);

        pcl::PointCloud<PointType>::Ptr laserCloud(new pcl::PointCloud<PointType>());
        for (int i = 0; i < N_SCANS; i++)//16-dim to 1-dim 
        { 
            scanStartInd[i] = laserCloud->size() + 5;
            *laserCloud += laserCloudScans[i];
            scanEndInd[i] = laserCloud->size() - 6;
        }

        RCLCPP_INFO(this->get_logger(), "prepare time %f", t_prepare.toc());

        for (int i = 5; i < cloudSize - 5; i++)
        { 
            float diffX = laserCloud->points[i - 5].x + laserCloud->points[i - 4].x + 
                          laserCloud->points[i - 3].x + laserCloud->points[i - 2].x + 
                          laserCloud->points[i - 1].x - 10 * laserCloud->points[i].x + 
                          laserCloud->points[i + 1].x + laserCloud->points[i + 2].x + 
                          laserCloud->points[i + 3].x + laserCloud->points[i + 4].x + 
                          laserCloud->points[i + 5].x;

            float diffY = laserCloud->points[i - 5].y + laserCloud->points[i - 4].y + 
                          laserCloud->points[i - 3].y + laserCloud->points[i - 2].y + 
                          laserCloud->points[i - 1].y - 10 * laserCloud->points[i].y + 
                          laserCloud->points[i + 1].y + laserCloud->points[i + 2].y + 
                          laserCloud->points[i + 3].y + laserCloud->points[i + 4].y + 
                          laserCloud->points[i + 5].y;

            float diffZ = laserCloud->points[i - 5].z + laserCloud->points[i - 4].z + 
                          laserCloud->points[i - 3].z + laserCloud->points[i - 2].z + 
                          laserCloud->points[i - 1].z - 10 * laserCloud->points[i].z + 
                          laserCloud->points[i + 1].z + laserCloud->points[i + 2].z + 
                          laserCloud->points[i + 3].z + laserCloud->points[i + 4].z + 
                          laserCloud->points[i + 5].z;

            cloudCurvature[i] = diffX * diffX + diffY * diffY + diffZ * diffZ;
            cloudSortInd[i] = i;
            cloudNeighborPicked[i] = 0;
            cloudLabel[i] = 0;
        }

        TicToc t_pts;
        pcl::PointCloud<PointType> cornerPointsSharp;
        pcl::PointCloud<PointType> cornerPointsLessSharp;//diff level 
        pcl::PointCloud<PointType> surfPointsFlat;
        pcl::PointCloud<PointType> surfPointsLessFlat;

        float t_q_sort = 0;
        for (int i = 0; i < N_SCANS; i++)
        {
            if( scanEndInd[i] - scanStartInd[i] < 6)
                continue;

            pcl::PointCloud<PointType>::Ptr surfPointsLessFlatScan(new pcl::PointCloud<PointType>);
            for (int j = 0; j < 6; j++)//each line divided into 6 sections
            {
                int sp = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * j / 6; 
                int ep = scanStartInd[i] + (scanEndInd[i] - scanStartInd[i]) * (j + 1) / 6 - 1;

                TicToc t_tmp;
                std::sort (cloudSortInd + sp, cloudSortInd + ep + 1, comp);
                t_q_sort += t_tmp.toc();

                int largestPickedNum = 0;
                for (int k = ep; k >= sp; k--)
                {
                    int ind = cloudSortInd[k]; 
                    if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] > 0.1)
                    {
                        largestPickedNum++;
                        if (largestPickedNum <= 2)
                        {                        
                            cloudLabel[ind] = 2;
                            cornerPointsSharp.push_back(laserCloud->points[ind]);
                            cornerPointsLessSharp.push_back(laserCloud->points[ind]);
                        }
                        else if (largestPickedNum <= 20)
                        {                        
                            cloudLabel[ind] = 1; 
                            cornerPointsLessSharp.push_back(laserCloud->points[ind]);
                        }
                        else
                            break;

                        cloudNeighborPicked[ind] = 1; 

                        for (int l = 1; l <= 5; l++)
                        {
                            float dx = laserCloud->points[ind + l].x - laserCloud->points[ind + l - 1].x;
                            float dy = laserCloud->points[ind + l].y - laserCloud->points[ind + l - 1].y;
                            float dz = laserCloud->points[ind + l].z - laserCloud->points[ind + l - 1].z;
                            if (dx*dx + dy*dy + dz*dz > 0.05) break;
                            cloudNeighborPicked[ind + l] = 1;
                        }
                        for (int l = -1; l >= -5; l--)
                        {
                            float dx = laserCloud->points[ind + l].x - laserCloud->points[ind + l + 1].x;
                            float dy = laserCloud->points[ind + l].y - laserCloud->points[ind + l + 1].y;
                            float dz = laserCloud->points[ind + l].z - laserCloud->points[ind + l + 1].z;
                            if (dx*dx + dy*dy + dz*dz > 0.05) break;
                            cloudNeighborPicked[ind + l] = 1;
                        }
                    }
                }

                int smallestPickedNum = 0;
                for (int k = sp; k <= ep; k++)
                {
                    int ind = cloudSortInd[k];
                    if (cloudNeighborPicked[ind] == 0 && cloudCurvature[ind] < 0.1)
                    {
                        cloudLabel[ind] = -1; 
                        surfPointsFlat.push_back(laserCloud->points[ind]);
                        smallestPickedNum++;
                        if (smallestPickedNum >= 4)
                            break;

                        cloudNeighborPicked[ind] = 1;
                        for (int l = 1; l <= 5; l++)
                        {
                            float dx = laserCloud->points[ind + l].x - laserCloud->points[ind + l - 1].x;
                            float dy = laserCloud->points[ind + l].y - laserCloud->points[ind + l - 1].y;
                            float dz = laserCloud->points[ind + l].z - laserCloud->points[ind + l - 1].z;
                            if (dx*dx + dy*dy + dz*dz > 0.05) break;
                            cloudNeighborPicked[ind + l] = 1;
                        }
                        for (int l = -1; l >= -5; l--)
                        {
                            float dx = laserCloud->points[ind + l].x - laserCloud->points[ind + l + 1].x;
                            float dy = laserCloud->points[ind + l].y - laserCloud->points[ind + l + 1].y;
                            float dz = laserCloud->points[ind + l].z - laserCloud->points[ind + l + 1].z;
                            if (dx*dx + dy*dy + dz*dz > 0.05) break;
                            cloudNeighborPicked[ind + l] = 1;
                        }
                    }
                }

                for (int k = sp; k <= ep; k++)
                {
                    if (cloudLabel[k] <= 0)
                        surfPointsLessFlatScan->push_back(laserCloud->points[k]);
                }
            }

            pcl::PointCloud<PointType> surfPointsLessFlatScanDS;
            pcl::VoxelGrid<PointType> downSizeFilter;
            downSizeFilter.setInputCloud(surfPointsLessFlatScan);
            downSizeFilter.setLeafSize(0.2, 0.2, 0.2);
            downSizeFilter.filter(surfPointsLessFlatScanDS);
            surfPointsLessFlat += surfPointsLessFlatScanDS;
        }

        RCLCPP_INFO(this->get_logger(), "sort q time %f", t_q_sort);
        RCLCPP_INFO(this->get_logger(), "seperate points time %f", t_pts.toc());

        // 发布点云
        sensor_msgs::msg::PointCloud2 laserCloudOutMsg;
        pcl::toROSMsg(*laserCloud, laserCloudOutMsg);
        laserCloudOutMsg.header = laserCloudMsg->header;
        laserCloudOutMsg.header.frame_id = "camera_init";
        pubLaserCloud->publish(laserCloudOutMsg);

        sensor_msgs::msg::PointCloud2 cornerSharpMsg;
        pcl::toROSMsg(cornerPointsSharp, cornerSharpMsg);
        cornerSharpMsg.header = laserCloudMsg->header;
        cornerSharpMsg.header.frame_id = "camera_init";
        pubCornerPointsSharp->publish(cornerSharpMsg);

        sensor_msgs::msg::PointCloud2 cornerLessSharpMsg;
        pcl::toROSMsg(cornerPointsLessSharp, cornerLessSharpMsg);
        cornerLessSharpMsg.header = laserCloudMsg->header;
        cornerLessSharpMsg.header.frame_id = "camera_init";
        pubCornerPointsLessSharp->publish(cornerLessSharpMsg);

        sensor_msgs::msg::PointCloud2 surfFlatMsg;
        pcl::toROSMsg(surfPointsFlat, surfFlatMsg);
        surfFlatMsg.header = laserCloudMsg->header;
        surfFlatMsg.header.frame_id = "camera_init";
        pubSurfPointsFlat->publish(surfFlatMsg);

        sensor_msgs::msg::PointCloud2 surfLessFlatMsg;
        pcl::toROSMsg(surfPointsLessFlat, surfLessFlatMsg);
        surfLessFlatMsg.header = laserCloudMsg->header;
        surfLessFlatMsg.header.frame_id = "camera_init";
        pubSurfPointsLessFlat->publish(surfLessFlatMsg);

        if(PUB_EACH_LINE)
        {
            for(int i = 0; i< N_SCANS; i++)
            {
                sensor_msgs::msg::PointCloud2 scanMsg;
                pcl::toROSMsg(laserCloudScans[i], scanMsg);
                scanMsg.header = laserCloudMsg->header;
                scanMsg.header.frame_id = "camera_init";
                pubEachScan[i]->publish(scanMsg);
            }
        }

        if(t_whole.toc() > 100)
            RCLCPP_WARN(this->get_logger(), "scan registration over 100ms");
    }

    // 订阅器 & 发布器
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subLaserCloud_;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloud;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubCornerPointsSharp;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubCornerPointsLessSharp;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubSurfPointsFlat;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubSurfPointsLessFlat;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubRemovePoints;
    std::vector<rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr> pubEachScan;
};

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<ScanRegistration>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

laserOdometry.cpp

// This is an advanced implementation of the algorithm described in the following paper:
//   J. Zhang and S. Zhang. LOAM: Lidar Odometry and Mapping in Real-time.
//     Robotics: Science and Systems Conference (RSS). Berkeley, CA, July 2014.

// Modifier: Tong Qin               qintonguav@gmail.com
// 	         Shaozu Cao 		    saozu.cao@connect.ust.hk

// Copyright 2013, Ji Zhang, Carnegie Mellon University
// Further contributions copyright (c) 2016, Southwest Research Institute
// All rights reserved.

#include <cmath>
#include <geometry_msgs/msg/pose_stamped.hpp>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl_conversions/pcl_conversions.h>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/imu.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <tf2/transform_datatypes.h>
#include <tf2_ros/transform_broadcaster.h>
#include <eigen3/Eigen/Dense>
#include <mutex>
#include <queue>

#include "aloam_velodyne_ros2/common.h"
#include "aloam_velodyne_ros2/tic_toc.h"
#include "lidarFactor.hpp"

#define DISTORTION 0

int corner_correspondence = 0, plane_correspondence = 0;

constexpr double SCAN_PERIOD = 0.1;
constexpr double DISTANCE_SQ_THRESHOLD = 25;
constexpr double NEARBY_SCAN = 2.5;

int skipFrameNum = 5;
bool systemInited = false;

double timeCornerPointsSharp = 0;
double timeCornerPointsLessSharp = 0;
double timeSurfPointsFlat = 0;
double timeSurfPointsLessFlat = 0;
double timeLaserCloudFullRes = 0;

pcl::KdTreeFLANN<pcl::PointXYZI>::Ptr kdtreeCornerLast(new pcl::KdTreeFLANN<pcl::PointXYZI>());
pcl::KdTreeFLANN<pcl::PointXYZI>::Ptr kdtreeSurfLast(new pcl::KdTreeFLANN<pcl::PointXYZI>());

pcl::PointCloud<PointType>::Ptr cornerPointsSharp(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr cornerPointsLessSharp(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr surfPointsFlat(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr surfPointsLessFlat(new pcl::PointCloud<PointType>());

pcl::PointCloud<PointType>::Ptr laserCloudCornerLast(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudSurfLast(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudFullRes(new pcl::PointCloud<PointType>());

int laserCloudCornerLastNum = 0;
int laserCloudSurfLastNum = 0;

Eigen::Quaterniond q_w_curr(1, 0, 0, 0);
Eigen::Vector3d t_w_curr(0, 0, 0);

double para_q[4] = {0, 0, 0, 1};
double para_t[3] = {0, 0, 0};

Eigen::Map<Eigen::Quaterniond> q_last_curr(para_q);//
Eigen::Map<Eigen::Vector3d> t_last_curr(para_t);//

std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> cornerSharpBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> cornerLessSharpBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> surfFlatBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> surfLessFlatBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> fullPointsBuf;
std::mutex mBuf;

void TransformToStart(PointType const *const pi, PointType *const po)
{
    double s;
    if (DISTORTION)
        s = (pi->intensity - int(pi->intensity)) / SCAN_PERIOD;
    else
        s = 1.0;

    Eigen::Quaterniond q_point_last = Eigen::Quaterniond::Identity().slerp(s, q_last_curr);
    Eigen::Vector3d t_point_last = s * t_last_curr;
    Eigen::Vector3d point(pi->x, pi->y, pi->z);
    Eigen::Vector3d un_point = q_point_last * point + t_point_last;

    po->x = un_point.x();
    po->y = un_point.y();
    po->z = un_point.z();
    po->intensity = pi->intensity;
}

void TransformToEnd(PointType const *const pi, PointType *const po)
{
    pcl::PointXYZI un_point_tmp;
    TransformToStart(pi, &un_point_tmp);

    Eigen::Vector3d un_point(un_point_tmp.x, un_point_tmp.y, un_point_tmp.z);
    Eigen::Vector3d point_end = q_last_curr.inverse() * (un_point - t_last_curr);

    po->x = point_end.x();
    po->y = point_end.y();
    po->z = point_end.z();

    po->intensity = int(pi->intensity);
}

class LaserOdometry : public rclcpp::Node
{
public:
    LaserOdometry() : Node("laserOdometry")
    {
        this->declare_parameter<int>("mapping_skip_frame", 2);
        this->get_parameter("mapping_skip_frame", skipFrameNum);

        RCLCPP_INFO(this->get_logger(), "Mapping %d Hz", 10 / skipFrameNum);

        subCornerPointsSharp = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/laser_cloud_sharp", 100,
            std::bind(&LaserOdometry::laserCloudSharpHandler, this, std::placeholders::_1));

        subCornerPointsLessSharp = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/laser_cloud_less_sharp", 100,
            std::bind(&LaserOdometry::laserCloudLessSharpHandler, this, std::placeholders::_1));

        subSurfPointsFlat = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/laser_cloud_flat", 100,
            std::bind(&LaserOdometry::laserCloudFlatHandler, this, std::placeholders::_1));

        subSurfPointsLessFlat = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/laser_cloud_less_flat", 100,
            std::bind(&LaserOdometry::laserCloudLessFlatHandler, this, std::placeholders::_1));

        subLaserCloudFullRes = this->create_subscription<sensor_msgs::msg::PointCloud2>(
            "/velodyne_cloud_2", 100,
            std::bind(&LaserOdometry::laserCloudFullResHandler, this, std::placeholders::_1));

        pubLaserCloudCornerLast = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_corner_last", 100);
        pubLaserCloudSurfLast = this->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_surf_last", 100);
        pubLaserCloudFullRes = this->create_publisher<sensor_msgs::msg::PointCloud2>("/velodyne_cloud_3", 100);
        pubLaserOdometry = this->create_publisher<nav_msgs::msg::Odometry>("/laser_odom_to_init", 100);
        pubLaserPath = this->create_publisher<nav_msgs::msg::Path>("/laser_odom_path", 100);

        timer_ = this->create_wall_timer(std::chrono::milliseconds(10), std::bind(&LaserOdometry::runLoop, this));
    }

private:
    void laserCloudSharpHandler(const sensor_msgs::msg::PointCloud2::SharedPtr cornerPointsSharp2)
    {
        std::lock_guard<std::mutex> lock(mBuf);
        cornerSharpBuf.push(cornerPointsSharp2);
    }

    void laserCloudLessSharpHandler(const sensor_msgs::msg::PointCloud2::SharedPtr cornerPointsLessSharp2)
    {
        std::lock_guard<std::mutex> lock(mBuf);
        cornerLessSharpBuf.push(cornerPointsLessSharp2);
    }

    void laserCloudFlatHandler(const sensor_msgs::msg::PointCloud2::SharedPtr surfPointsFlat2)
    {
        std::lock_guard<std::mutex> lock(mBuf);
        surfFlatBuf.push(surfPointsFlat2);
    }

    void laserCloudLessFlatHandler(const sensor_msgs::msg::PointCloud2::SharedPtr surfPointsLessFlat2)
    {
        std::lock_guard<std::mutex> lock(mBuf);
        surfLessFlatBuf.push(surfPointsLessFlat2);
    }

    void laserCloudFullResHandler(const sensor_msgs::msg::PointCloud2::SharedPtr laserCloudFullRes2)
    {
        std::lock_guard<std::mutex> lock(mBuf);
        fullPointsBuf.push(laserCloudFullRes2);
    }

    void runLoop()
    {
        if (!cornerSharpBuf.empty() && !cornerLessSharpBuf.empty() &&
            !surfFlatBuf.empty() && !surfLessFlatBuf.empty() &&
            !fullPointsBuf.empty())
        {
            timeCornerPointsSharp = cornerSharpBuf.front()->header.stamp.sec;
            timeCornerPointsLessSharp = cornerLessSharpBuf.front()->header.stamp.sec;
            timeSurfPointsFlat = surfFlatBuf.front()->header.stamp.sec;
            timeSurfPointsLessFlat = surfLessFlatBuf.front()->header.stamp.sec;
            timeLaserCloudFullRes = fullPointsBuf.front()->header.stamp.sec;

            if (timeCornerPointsSharp != timeLaserCloudFullRes ||
                timeCornerPointsLessSharp != timeLaserCloudFullRes ||
                timeSurfPointsFlat != timeLaserCloudFullRes ||
                timeSurfPointsLessFlat != timeLaserCloudFullRes)
            {
                RCLCPP_ERROR(this->get_logger(), "unsync message!");
                rclcpp::shutdown();
                return;
            }

            mBuf.lock();
            cornerPointsSharp->clear();
            pcl::fromROSMsg(*cornerSharpBuf.front(), *cornerPointsSharp);
            cornerSharpBuf.pop();

            cornerPointsLessSharp->clear();
            pcl::fromROSMsg(*cornerLessSharpBuf.front(), *cornerPointsLessSharp);
            cornerLessSharpBuf.pop();

            surfPointsFlat->clear();
            pcl::fromROSMsg(*surfFlatBuf.front(), *surfPointsFlat);
            surfFlatBuf.pop();

            surfPointsLessFlat->clear();
            pcl::fromROSMsg(*surfLessFlatBuf.front(), *surfPointsLessFlat);
            surfLessFlatBuf.pop();

            laserCloudFullRes->clear();
            pcl::fromROSMsg(*fullPointsBuf.front(), *laserCloudFullRes);
            fullPointsBuf.pop();
            mBuf.unlock();

            TicToc t_whole;

            if (!systemInited)
            {
                systemInited = true;
                RCLCPP_INFO(this->get_logger(), "Initialization finished");
            }
            else
            {//基于特征匹配的迭代最近点(ICP)优化,用于计算相邻两帧激光雷达之间的相对运动(位姿变换)
                int cornerPointsSharpNum = cornerPointsSharp->points.size();
                int surfPointsFlatNum = surfPointsFlat->points.size();

                TicToc t_opt;
                //双次迭代优化循环
                for (size_t opti_counter = 0; opti_counter < 2; ++opti_counter)
                {
                    corner_correspondence = 0;
                    plane_correspondence = 0;

                    /*Huber损失函数:鲁棒性优化,减少外点(误匹配点)的影响
                      四元数参数化:确保旋转矩阵的正交性约束
                      优化变量:
                        para_q:4维,旋转四元数 (qx, qy, qz, qw)
                        para_t:3维,平移向量 (x, y, z)*/
                    ceres::LossFunction *loss_function = new ceres::HuberLoss(0.1);
                    ceres::LocalParameterization *q_parameterization =
                        new ceres::EigenQuaternionParameterization();
                    ceres::Problem::Options problem_options;

                    ceres::Problem problem(problem_options);
                    problem.AddParameterBlock(para_q, 4, q_parameterization);
                    problem.AddParameterBlock(para_t, 3);

                    pcl::PointXYZI pointSel;
                    std::vector<int> pointSearchInd;
                    std::vector<float> pointSearchSqDis;

                    TicToc t_data;
                    for (int i = 0; i < cornerPointsSharpNum; ++i)
                    {
                        // 1. 变换到扫描起始时刻(去畸变)
                        TransformToStart(&(cornerPointsSharp->points[i]), &pointSel);
                        // 2. KD树搜索最近邻点
                        kdtreeCornerLast->nearestKSearch(pointSel, 1, pointSearchInd, pointSearchSqDis);

                        int closestPointInd = -1, minPointInd2 = -1;
                        // 3. 在同一扫描线或相邻扫描线中寻找第二个最近邻点
                        //    构成一条直线(A-B),用于点到线的距离计算
                        /*当前角点 → 在上一帧中找最近点A → 
                                向前搜索 → 找同一条线上的点(同scan ID)
                                向后搜索 → 找同一条线上的点(同scan ID)
                            形成直线 A-B*/
                        if (pointSearchSqDis[0] < DISTANCE_SQ_THRESHOLD)
                        {
                            closestPointInd = pointSearchInd[0];
                            int closestPointScanID = int(laserCloudCornerLast->points[closestPointInd].intensity);

                            double minPointSqDis2 = DISTANCE_SQ_THRESHOLD;
                            for (int j = closestPointInd + 1; j < (int)laserCloudCornerLast->points.size(); ++j)
                            {
                                if (int(laserCloudCornerLast->points[j].intensity) <= closestPointScanID)
                                    continue;
                                if (int(laserCloudCornerLast->points[j].intensity) > (closestPointScanID + NEARBY_SCAN))
                                    break;

                                double pointSqDis = (laserCloudCornerLast->points[j].x - pointSel.x) *
                                                        (laserCloudCornerLast->points[j].x - pointSel.x) +
                                                    (laserCloudCornerLast->points[j].y - pointSel.y) *
                                                        (laserCloudCornerLast->points[j].y - pointSel.y) +
                                                    (laserCloudCornerLast->points[j].z - pointSel.z) *
                                                        (laserCloudCornerLast->points[j].z - pointSel.z);

                                if (pointSqDis < minPointSqDis2)
                                {
                                    minPointSqDis2 = pointSqDis;
                                    minPointInd2 = j;
                                }
                            }
                            for (int j = closestPointInd - 1; j >= 0; --j)
                            {
                                if (int(laserCloudCornerLast->points[j].intensity) >= closestPointScanID)//intensity字段存储了相对时间(在0-1之间)
                                    continue;
                                if (int(laserCloudCornerLast->points[j].intensity) < (closestPointScanID - NEARBY_SCAN))
                                    break;

                                double pointSqDis = (laserCloudCornerLast->points[j].x - pointSel.x) *
                                                        (laserCloudCornerLast->points[j].x - pointSel.x) +
                                                    (laserCloudCornerLast->points[j].y - pointSel.y) *
                                                        (laserCloudCornerLast->points[j].y - pointSel.y) +
                                                    (laserCloudCornerLast->points[j].z - pointSel.z) *
                                                        (laserCloudCornerLast->points[j].z - pointSel.z);

                                if (pointSqDis < minPointSqDis2)
                                {
                                    minPointSqDis2 = pointSqDis;
                                    minPointInd2 = j;
                                }
                            }
                        }
                        if (minPointInd2 >= 0)
                        {
                            Eigen::Vector3d curr_point(cornerPointsSharp->points[i].x,
                                                       cornerPointsSharp->points[i].y,
                                                       cornerPointsSharp->points[i].z);
                            Eigen::Vector3d last_point_a(laserCloudCornerLast->points[closestPointInd].x,
                                                         laserCloudCornerLast->points[closestPointInd].y,
                                                         laserCloudCornerLast->points[closestPointInd].z);
                            Eigen::Vector3d last_point_b(laserCloudCornerLast->points[minPointInd2].x,
                                                         laserCloudCornerLast->points[minPointInd2].y,
                                                         laserCloudCornerLast->points[minPointInd2].z);

                            //当DISTORTION启用时,根据点在扫描中的时间戳进行线性插值
                            //s:归一化的时间比例(0表示扫描开始,1表示扫描结束)
                            double s = DISTORTION ? (cornerPointsSharp->points[i].intensity -
                                                     int(cornerPointsSharp->points[i].intensity)) / SCAN_PERIOD : 1.0;

                            // 角点:点到线距离
                            ceres::CostFunction *cost_function = LidarEdgeFactor::Create(curr_point, last_point_a, last_point_b, s);
                            problem.AddResidualBlock(cost_function, loss_function, para_q, para_t);
                            corner_correspondence++;
                        }
                    }

                    for (int i = 0; i < surfPointsFlatNum; ++i)
                    {
                        TransformToStart(&(surfPointsFlat->points[i]), &pointSel);
                        kdtreeSurfLast->nearestKSearch(pointSel, 1, pointSearchInd, pointSearchSqDis);

                        int closestPointInd = -1, minPointInd2 = -1, minPointInd3 = -1;
                        if (pointSearchSqDis[0] < DISTANCE_SQ_THRESHOLD)
                        {
                            closestPointInd = pointSearchInd[0];
                            int closestPointScanID = int(laserCloudSurfLast->points[closestPointInd].intensity);
                            double minPointSqDis2 = DISTANCE_SQ_THRESHOLD, minPointSqDis3 = DISTANCE_SQ_THRESHOLD;

                            for (int j = closestPointInd + 1; j < (int)laserCloudSurfLast->points.size(); ++j)
                            {
                                if (int(laserCloudSurfLast->points[j].intensity) > (closestPointScanID + NEARBY_SCAN))
                                    break;

                                double pointSqDis = (laserCloudSurfLast->points[j].x - pointSel.x) *
                                                        (laserCloudSurfLast->points[j].x - pointSel.x) +
                                                    (laserCloudSurfLast->points[j].y - pointSel.y) *
                                                        (laserCloudSurfLast->points[j].y - pointSel.y) +
                                                    (laserCloudSurfLast->points[j].z - pointSel.z) *
                                                        (laserCloudSurfLast->points[j].z - pointSel.z);

                                if (int(laserCloudSurfLast->points[j].intensity) <= closestPointScanID && pointSqDis < minPointSqDis2)
                                {
                                    minPointSqDis2 = pointSqDis;
                                    minPointInd2 = j;
                                }
                                else if (int(laserCloudSurfLast->points[j].intensity) > closestPointScanID && pointSqDis < minPointSqDis3)
                                {
                                    minPointSqDis3 = pointSqDis;
                                    minPointInd3 = j;
                                }
                            }
                            for (int j = closestPointInd - 1; j >= 0; --j)
                            {
                                if (int(laserCloudSurfLast->points[j].intensity) < (closestPointScanID - NEARBY_SCAN))
                                    break;

                                double pointSqDis = (laserCloudSurfLast->points[j].x - pointSel.x) *
                                                        (laserCloudSurfLast->points[j].x - pointSel.x) +
                                                    (laserCloudSurfLast->points[j].y - pointSel.y) *
                                                        (laserCloudSurfLast->points[j].y - pointSel.y) +
                                                    (laserCloudSurfLast->points[j].z - pointSel.z) *
                                                        (laserCloudSurfLast->points[j].z - pointSel.z);

                                if (int(laserCloudSurfLast->points[j].intensity) >= closestPointScanID && pointSqDis < minPointSqDis2)
                                {
                                    minPointSqDis2 = pointSqDis;
                                    minPointInd2 = j;
                                }
                                else if (int(laserCloudSurfLast->points[j].intensity) < closestPointScanID && pointSqDis < minPointSqDis3)
                                {
                                    minPointSqDis3 = pointSqDis;
                                    minPointInd3 = j;
                                }
                            }
                        }
                        if (minPointInd2 >= 0 && minPointInd3 >= 0)
                        {
                            Eigen::Vector3d curr_point(surfPointsFlat->points[i].x,
                                                        surfPointsFlat->points[i].y,
                                                        surfPointsFlat->points[i].z);
                            Eigen::Vector3d last_point_a(laserCloudSurfLast->points[closestPointInd].x,
                                                            laserCloudSurfLast->points[closestPointInd].y,
                                                            laserCloudSurfLast->points[closestPointInd].z);
                            Eigen::Vector3d last_point_b(laserCloudSurfLast->points[minPointInd2].x,
                                                            laserCloudSurfLast->points[minPointInd2].y,
                                                            laserCloudSurfLast->points[minPointInd2].z);
                            Eigen::Vector3d last_point_c(laserCloudSurfLast->points[minPointInd3].x,
                                                            laserCloudSurfLast->points[minPointInd3].y,
                                                            laserCloudSurfLast->points[minPointInd3].z);

                            double s = DISTORTION ? (surfPointsFlat->points[i].intensity -
                                                     int(surfPointsFlat->points[i].intensity)) / SCAN_PERIOD : 1.0;

                            // 平面点:点到面距离
                            ceres::CostFunction *cost_function = LidarPlaneFactor::Create(curr_point, last_point_a, last_point_b, last_point_c, s);
                            problem.AddResidualBlock(cost_function, loss_function, para_q, para_t);
                            plane_correspondence++;
                        }
                    }

                    RCLCPP_INFO(this->get_logger(), "data association time %f ms", t_data.toc());

                    if ((corner_correspondence + plane_correspondence) < 10)
                    {
                        RCLCPP_WARN(this->get_logger(), "less correspondence!");
                    }

                    TicToc t_solver;
                    ceres::Solver::Options options;
                    options.linear_solver_type = ceres::DENSE_QR; // 稠密QR分解
                    options.max_num_iterations = 4;               // 最大迭代次数 (平衡精度和计算效率)
                    options.minimizer_progress_to_stdout = false; // 不输出迭代信息
                    ceres::Solver::Summary summary;
                    ceres::Solve(options, &problem, &summary);
                    RCLCPP_INFO(this->get_logger(), "solver time %f ms", t_solver.toc());
                }
                RCLCPP_INFO(this->get_logger(), "optimization twice time %f", t_opt.toc());

                // 注意平移变换需要先用当前旋转对位移进行旋转
                t_w_curr = t_w_curr + q_w_curr * t_last_curr;//平移
                q_w_curr = q_w_curr * q_last_curr;//旋转
            }

            TicToc t_pub;

            nav_msgs::msg::Odometry laserOdometry;
            laserOdometry.header.frame_id = "camera_init";
            laserOdometry.child_frame_id = "laser_odom";
            laserOdometry.header.stamp = rclcpp::Time(static_cast<int64_t>(timeSurfPointsLessFlat*1e9));
            laserOdometry.pose.pose.orientation.x = q_w_curr.x();
            laserOdometry.pose.pose.orientation.y = q_w_curr.y();
            laserOdometry.pose.pose.orientation.z = q_w_curr.z();
            laserOdometry.pose.pose.orientation.w = q_w_curr.w();
            laserOdometry.pose.pose.position.x = t_w_curr.x();
            laserOdometry.pose.pose.position.y = t_w_curr.y();
            laserOdometry.pose.pose.position.z = t_w_curr.z();
            pubLaserOdometry->publish(laserOdometry);

            geometry_msgs::msg::PoseStamped laserPose;
            laserPose.header = laserOdometry.header;
            laserPose.pose = laserOdometry.pose.pose;
            laserPath.header.stamp = laserOdometry.header.stamp;
            laserPath.poses.push_back(laserPose);
            laserPath.header.frame_id = "camera_init";
            pubLaserPath->publish(laserPath);

            // 交换点云指针
            pcl::PointCloud<PointType>::Ptr laserCloudTemp = cornerPointsLessSharp;
            cornerPointsLessSharp = laserCloudCornerLast;
            laserCloudCornerLast = laserCloudTemp;

            laserCloudTemp = surfPointsLessFlat;
            surfPointsLessFlat = laserCloudSurfLast;
            laserCloudSurfLast = laserCloudTemp;

            laserCloudCornerLastNum = laserCloudCornerLast->points.size();
            laserCloudSurfLastNum = laserCloudSurfLast->points.size();

            std::cout << "the size of corner last is " << laserCloudCornerLastNum << ", and the size of surf last is " << laserCloudSurfLastNum << '\n';

            // 更新KD树
            kdtreeCornerLast->setInputCloud(laserCloudCornerLast);
            kdtreeSurfLast->setInputCloud(laserCloudSurfLast);

            if (frameCount % skipFrameNum == 0)
            {
                frameCount = 0;

                sensor_msgs::msg::PointCloud2 laserCloudCornerLast2;
                pcl::toROSMsg(*laserCloudCornerLast, laserCloudCornerLast2);
                laserCloudCornerLast2.header.stamp = rclcpp::Time(static_cast<int64_t>(timeSurfPointsLessFlat*1e9));
                laserCloudCornerLast2.header.frame_id = "camera";
                pubLaserCloudCornerLast->publish(laserCloudCornerLast2);

                sensor_msgs::msg::PointCloud2 laserCloudSurfLast2;
                pcl::toROSMsg(*laserCloudSurfLast, laserCloudSurfLast2);
                laserCloudSurfLast2.header.stamp = rclcpp::Time(static_cast<int64_t>(timeSurfPointsLessFlat*1e9));
                laserCloudSurfLast2.header.frame_id = "camera";
                pubLaserCloudSurfLast->publish(laserCloudSurfLast2);

                sensor_msgs::msg::PointCloud2 laserCloudFullRes3;
                pcl::toROSMsg(*laserCloudFullRes, laserCloudFullRes3);
                laserCloudFullRes3.header.stamp = rclcpp::Time(static_cast<int64_t>(timeSurfPointsLessFlat*1e9));
                laserCloudFullRes3.header.frame_id = "camera";
                pubLaserCloudFullRes->publish(laserCloudFullRes3);
            }

            RCLCPP_INFO(this->get_logger(), "publication time %f ms", t_pub.toc());
            RCLCPP_INFO(this->get_logger(), "whole laserOdometry time %f ms\n", t_whole.toc());

            if(t_whole.toc() > 100)
                RCLCPP_WARN(this->get_logger(), "odometry process over 100ms");

            frameCount++;
        }
    }

    rclcpp::TimerBase::SharedPtr timer_;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subCornerPointsSharp;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subCornerPointsLessSharp;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subSurfPointsFlat;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subSurfPointsLessFlat;
    rclcpp::Subscription<sensor_msgs::msg::PointCloud2>::SharedPtr subLaserCloudFullRes;

    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudCornerLast;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudSurfLast;
    rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudFullRes;
    rclcpp::Publisher<nav_msgs::msg::Odometry>::SharedPtr pubLaserOdometry;
    rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr pubLaserPath;

    nav_msgs::msg::Path laserPath;
    int frameCount = 0;
};

int main(int argc, char **argv)
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<LaserOdometry>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

laserMapping.cpp

// This is an advanced implementation of the algorithm described in the following paper:
//   J. Zhang and S. Zhang. LOAM: Lidar Odometry and Mapping in Real-time.
//     Robotics: Science and Systems Conference (RSS). Berkeley, CA, July 2014.

// Modifier: Tong Qin               qintonguav@gmail.com
// 	         Shaozu Cao 		    saozu.cao@connect.ust.hk

// Copyright 2013, Ji Zhang, Carnegie Mellon University
// Further contributions copyright (c) 2016, Southwest Research Institute
// All rights reserved.

/*
!!! Debug 模式需要降低延时
1.1 增加下采样参数
	# 修改参数文件
	mapping_line_resolution: 0.8    # 从0.4改为0.8
	mapping_plane_resolution: 1.2   # 从0.8改为1.2
1.2 减少局部地图范围
	// 在laserMapping.cpp中
	// 将提取范围从±2改为±1
	for (int i = centerCubeI-1; i <= centerCubeI+1; i++)
	for (int j = centerCubeJ-1; j <= centerCubeJ+1; j++) 
	for (int k = centerCubeK-1; k <= centerCubeK+1; k++)
	从5×5×3=75个体素 → 3×3×3=27个体素
	特征点数量减少约2/3
1.3 降低优化迭代次数
	// 修改建图优化
	for (int iterCount = 0; iterCount < 1; iterCount++)  // 从2改为1
	ceres::Solver::Options options;
	options.max_num_iterations = 2;  // 从4改为2
*/

#include <math.h>
#include <vector>
#include <nav_msgs/msg/odometry.hpp>
#include <nav_msgs/msg/path.hpp>
#include <geometry_msgs/msg/pose_stamped.hpp>
#include <geometry_msgs/msg/transform_stamped.hpp>
#include <pcl_conversions/pcl_conversions.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/imu.hpp>
#include <sensor_msgs/msg/point_cloud2.hpp>
#include <tf2/transform_datatypes.h>
#include <tf2_ros/transform_broadcaster.h>
#include <eigen3/Eigen/Dense>
#include <ceres/ceres.h>
#include <mutex>
#include <queue>
#include <thread>
#include <iostream>
#include <string>

#include "lidarFactor.hpp"
#include "aloam_velodyne_ros2/common.h"
#include "aloam_velodyne_ros2/tic_toc.h"

int frameCount = 0;

double timeLaserCloudCornerLast = 0;
double timeLaserCloudSurfLast = 0;
double timeLaserCloudFullRes = 0;
double timeLaserOdometry = 0;

/*每个网格(cube)大小:50×50×50米  地图网格范围 1050m 以上
总网格数:21×21×11 = 4851个
坐标原点在中心网格中心,不是网格边界

21×21×11网格是全局地图的存储结构:
	存储所有历史特征点的累积
	通过滑动窗口管理内存
	局部地图从中提取用于匹配
*/
// 中心网格索引
int laserCloudCenWidth = 10;// 中心那个网格的x索引
int laserCloudCenHeight = 10;
int laserCloudCenDepth = 5;

int SkipMapFrame = 5;//及时清理缓存
// 网格维度
const int laserCloudWidth = 21;// x轴方向网格数
const int laserCloudHeight = 21;
const int laserCloudDepth = 11;
const int laserCloudNum = laserCloudWidth * laserCloudHeight * laserCloudDepth; //4851

int laserCloudValidInd[125];
int laserCloudSurroundInd[125];

pcl::PointCloud<PointType>::Ptr laserCloudCornerLast(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudSurfLast(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudSurround(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudCornerFromMap(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudSurfFromMap(new pcl::PointCloud<PointType>());
pcl::PointCloud<PointType>::Ptr laserCloudFullRes(new pcl::PointCloud<PointType>());

pcl::PointCloud<PointType>::Ptr laserCloudCornerArray[laserCloudNum];
pcl::PointCloud<PointType>::Ptr laserCloudSurfArray[laserCloudNum];

pcl::KdTreeFLANN<PointType>::Ptr kdtreeCornerFromMap(new pcl::KdTreeFLANN<PointType>());
pcl::KdTreeFLANN<PointType>::Ptr kdtreeSurfFromMap(new pcl::KdTreeFLANN<PointType>());

double parameters[7] = {0, 0, 0, 1, 0, 0, 0};
Eigen::Map<Eigen::Quaterniond> q_w_curr(parameters);
Eigen::Map<Eigen::Vector3d> t_w_curr(parameters + 4);

Eigen::Quaterniond q_wmap_wodom(1, 0, 0, 0);
Eigen::Vector3d t_wmap_wodom(0, 0, 0);
Eigen::Quaterniond q_wodom_curr(1, 0, 0, 0);
Eigen::Vector3d t_wodom_curr(0, 0, 0);

std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> cornerLastBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> surfLastBuf;
std::queue<sensor_msgs::msg::PointCloud2::SharedPtr> fullResBuf;
std::queue<nav_msgs::msg::Odometry::SharedPtr> odometryBuf;
std::mutex mBuf;

pcl::VoxelGrid<PointType> downSizeFilterCorner;
pcl::VoxelGrid<PointType> downSizeFilterSurf;

std::vector<int> pointSearchInd;
std::vector<float> pointSearchSqDis;
PointType pointOri, pointSel;

nav_msgs::msg::Path laserAfterMappedPath;

std::unique_ptr<tf2_ros::TransformBroadcaster> tf_broadcaster_;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudSurround;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudMap;
rclcpp::Publisher<sensor_msgs::msg::PointCloud2>::SharedPtr pubLaserCloudFullRes;
rclcpp::Publisher<nav_msgs::msg::Odometry>::SharedPtr pubOdomAftMapped;
rclcpp::Publisher<nav_msgs::msg::Odometry>::SharedPtr pubOdomAftMappedHighFrec;
rclcpp::Publisher<nav_msgs::msg::Path>::SharedPtr pubLaserAfterMappedPath;

rclcpp::Node::SharedPtr node_;

void transformAssociateToMap()
{
	q_w_curr = q_wmap_wodom * q_wodom_curr;
	t_w_curr = q_wmap_wodom * t_wodom_curr + t_wmap_wodom;
}

void transformUpdate()
{
	q_wmap_wodom = q_w_curr * q_wodom_curr.inverse();
	t_wmap_wodom = t_w_curr - q_wmap_wodom * t_wodom_curr;
}

void pointAssociateToMap(PointType const *const pi, PointType *const po)
{
	Eigen::Vector3d point_curr(pi->x, pi->y, pi->z);
	Eigen::Vector3d point_w = q_w_curr * point_curr + t_w_curr;
	po->x = point_w.x();
	po->y = point_w.y();
	po->z = point_w.z();
	po->intensity = pi->intensity;
}

void pointAssociateTobeMapped(PointType const *const pi, PointType *const po)
{
	Eigen::Vector3d point_w(pi->x, pi->y, pi->z);
	Eigen::Vector3d point_curr = q_w_curr.inverse() * (point_w - t_w_curr);
	po->x = point_curr.x();
	po->y = point_curr.y();
	po->z = point_curr.z();
	po->intensity = pi->intensity;
}

void laserCloudCornerLastHandler(const sensor_msgs::msg::PointCloud2::SharedPtr laserCloudCornerLast2)
{
	mBuf.lock();
	cornerLastBuf.push(laserCloudCornerLast2);
	mBuf.unlock();
}

void laserCloudSurfLastHandler(const sensor_msgs::msg::PointCloud2::SharedPtr laserCloudSurfLast2)
{
	mBuf.lock();
	surfLastBuf.push(laserCloudSurfLast2);
	mBuf.unlock();
}

void laserCloudFullResHandler(const sensor_msgs::msg::PointCloud2::SharedPtr laserCloudFullRes2)
{
	mBuf.lock();
	fullResBuf.push(laserCloudFullRes2);
	mBuf.unlock();
}

void laserOdometryHandler(const nav_msgs::msg::Odometry::SharedPtr laserOdometry)
{
	mBuf.lock();
	odometryBuf.push(laserOdometry);
	mBuf.unlock();

	Eigen::Quaterniond q_wodom_curr;
	Eigen::Vector3d t_wodom_curr;
	q_wodom_curr.x() = laserOdometry->pose.pose.orientation.x;
	q_wodom_curr.y() = laserOdometry->pose.pose.orientation.y;
	q_wodom_curr.z() = laserOdometry->pose.pose.orientation.z;
	q_wodom_curr.w() = laserOdometry->pose.pose.orientation.w;
	t_wodom_curr.x() = laserOdometry->pose.pose.position.x;
	t_wodom_curr.y() = laserOdometry->pose.pose.position.y;
	t_wodom_curr.z() = laserOdometry->pose.pose.position.z;

	Eigen::Quaterniond q_w_curr = q_wmap_wodom * q_wodom_curr;
	Eigen::Vector3d t_w_curr = q_wmap_wodom * t_wodom_curr + t_wmap_wodom;

	nav_msgs::msg::Odometry odomAftMapped;
	odomAftMapped.header.frame_id = "camera_init";
	odomAftMapped.child_frame_id = "aft_mapped";
	odomAftMapped.header.stamp = laserOdometry->header.stamp;
	odomAftMapped.pose.pose.orientation.x = q_w_curr.x();
	odomAftMapped.pose.pose.orientation.y = q_w_curr.y();
	odomAftMapped.pose.pose.orientation.z = q_w_curr.z();
	odomAftMapped.pose.pose.orientation.w = q_w_curr.w();
	odomAftMapped.pose.pose.position.x = t_w_curr.x();
	odomAftMapped.pose.pose.position.y = t_w_curr.y();
	odomAftMapped.pose.pose.position.z = t_w_curr.z();
	pubOdomAftMappedHighFrec->publish(odomAftMapped);
}

void process()
{
	// static int skip_counter = 0;//Debug 模式

	while(rclcpp::ok())
	{	
		
		// if (skip_counter % SkipMapFrame == 0) {  // 每3帧处理1帧
		// 	// 清空队列,避免堆积
		// 	mBuf.lock();
		// 	while (!cornerLastBuf.empty()) cornerLastBuf.pop();
		// 	while (!surfLastBuf.empty()) surfLastBuf.pop();
		// 	while (!fullResBuf.empty()) fullResBuf.pop();
		// 	while (!odometryBuf.empty()) odometryBuf.pop();
		// 	mBuf.unlock();
		// 	skip_counter = 0;
		// }

		// skip_counter++;

		while (!cornerLastBuf.empty() && !surfLastBuf.empty() &&
			!fullResBuf.empty() && !odometryBuf.empty())
		{
			mBuf.lock();
			while (!odometryBuf.empty() && (odometryBuf.front()->header.stamp.sec) < (cornerLastBuf.front()->header.stamp.sec))
				odometryBuf.pop();
			if (odometryBuf.empty()) { mBuf.unlock(); break; }

			while (!surfLastBuf.empty() && (surfLastBuf.front()->header.stamp.sec) < (cornerLastBuf.front()->header.stamp.sec))
				surfLastBuf.pop();
			if (surfLastBuf.empty()) { mBuf.unlock(); break; }

			while (!fullResBuf.empty() && (fullResBuf.front()->header.stamp.sec) < (cornerLastBuf.front()->header.stamp.sec))
				fullResBuf.pop();
			if (fullResBuf.empty()) { mBuf.unlock(); break; }

			timeLaserCloudCornerLast = cornerLastBuf.front()->header.stamp.sec;
			timeLaserCloudSurfLast = surfLastBuf.front()->header.stamp.sec;
			timeLaserCloudFullRes = fullResBuf.front()->header.stamp.sec;
			timeLaserOdometry = odometryBuf.front()->header.stamp.sec;

			if (timeLaserCloudCornerLast != timeLaserOdometry ||
				timeLaserCloudSurfLast != timeLaserOdometry ||
				timeLaserCloudFullRes != timeLaserOdometry)
			{
				RCLCPP_ERROR(node_->get_logger(), "unsync message!");
				mBuf.unlock();
				break;
			}

			laserCloudCornerLast->clear();
			pcl::fromROSMsg(*cornerLastBuf.front(), *laserCloudCornerLast);
			cornerLastBuf.pop();

			laserCloudSurfLast->clear();
			pcl::fromROSMsg(*surfLastBuf.front(), *laserCloudSurfLast);
			surfLastBuf.pop();

			laserCloudFullRes->clear();
			pcl::fromROSMsg(*fullResBuf.front(), *laserCloudFullRes);
			fullResBuf.pop();

			q_wodom_curr.x() = odometryBuf.front()->pose.pose.orientation.x;
			q_wodom_curr.y() = odometryBuf.front()->pose.pose.orientation.y;
			q_wodom_curr.z() = odometryBuf.front()->pose.pose.orientation.z;
			q_wodom_curr.w() = odometryBuf.front()->pose.pose.orientation.w;
			t_wodom_curr.x() = odometryBuf.front()->pose.pose.position.x;
			t_wodom_curr.y() = odometryBuf.front()->pose.pose.position.y;
			t_wodom_curr.z() = odometryBuf.front()->pose.pose.position.z;
			odometryBuf.pop();

			while(!cornerLastBuf.empty()) {cornerLastBuf.pop();}
			mBuf.unlock();

			TicToc t_whole;
			transformAssociateToMap();

			
			TicToc t_shift;
			// 当前位姿所在的网格索引计算
			/*分解计算:
				1.t_w_curr.x() + 25.0:将世界坐标系平移到网格中心
					网格范围:[-25, 25],中心是0
					加25后范围:[0, 50]
				2.(x+25)/50.0:计算归一化网格坐标
					结果范围:[0, 1.0)
				3. int():取整得到相对于中心网格的偏移(中心网格索引为10: -25~25范围都是)
					例如:当前位置x=12.5 → (12.5+25)/50=0.75 → int=0
					当前位置x=-12.5 → (-12.5+25)/50=0.25 → int=0
				4. + laserCloudCenWidth:转换为绝对网格索引*/
			int centerCubeI = int((t_w_curr.x() + 25.0) / 50.0) + laserCloudCenWidth;
			int centerCubeJ = int((t_w_curr.y() + 25.0) / 50.0) + laserCloudCenHeight;
			int centerCubeK = int((t_w_curr.z() + 25.0) / 50.0) + laserCloudCenDepth;
			// 处理int()函数对负数的截断问题 int(-1.5)=-1 应该是 -2
			if (t_w_curr.x() + 25.0 < 0) centerCubeI--;
			if (t_w_curr.y() + 25.0 < 0) centerCubeJ--;
			if (t_w_curr.z() + 25.0 < 0) centerCubeK--;

			// cube shift logic (unchanged)
			/*为什么要滑动?
				机器人不断移动,但地图存储空间有限
				需要保持机器人位于地图中心区域
				当机器人接近地图边界时,移动地图而不是扩展地图
				// 注意:laserCloudWidth=21,索引0-20,边界是索引3和18
			*/
			while (centerCubeI < 3) // 机器人太靠近左边界
			{
				for (int j = 0; j < laserCloudHeight; j++) for (int k = 0; k < laserCloudDepth; k++) 
				{
					int i = laserCloudWidth - 1;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; i >= 1; i--) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
												laserCloudCornerArray[i-1 + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
												laserCloudSurfArray[i-1 + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeI++; 
				laserCloudCenWidth++;
			}
			while (centerCubeI >= laserCloudWidth - 3) //18 机器人太靠近右边界
			{
				for (int j = 0; j < laserCloudHeight; j++) for (int k = 0; k < laserCloudDepth; k++) 
				{
					int i = 0;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; i < laserCloudWidth-1; i++) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudCornerArray[i+1 + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudSurfArray[i+1 + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeI--; 
				laserCloudCenWidth--;
			}
			while (centerCubeJ < 3) 
			{
				for (int i = 0; i < laserCloudWidth; i++) for (int k = 0; k < laserCloudDepth; k++) 
				{
					int j = laserCloudHeight-1;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; j >=1; j--) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudCornerArray[i + laserCloudWidth*(j-1) + laserCloudWidth*laserCloudHeight*k];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudSurfArray[i + laserCloudWidth*(j-1) + laserCloudWidth*laserCloudHeight*k];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeJ++; 
				laserCloudCenHeight++;
			}
			while (centerCubeJ >= laserCloudHeight-3) 
			{
				for (int i = 0; i < laserCloudWidth; i++) for (int k = 0; k < laserCloudDepth; k++) 
				{
					int j = 0;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; j < laserCloudHeight-1; j++) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudCornerArray[i + laserCloudWidth*(j+1) + laserCloudWidth*laserCloudHeight*k];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudSurfArray[i + laserCloudWidth*(j+1) + laserCloudWidth*laserCloudHeight*k];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeJ--; 
				laserCloudCenHeight--;
			}
			while (centerCubeK < 3) 
			{
				for (int i = 0; i < laserCloudWidth; i++) for (int j = 0; j < laserCloudHeight; j++) 
				{
					int k = laserCloudDepth-1;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; k >=1; k--) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*(k-1)];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*(k-1)];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeK++; 
				laserCloudCenDepth++;
			}
			while (centerCubeK >= laserCloudDepth-3) 
			{
				for (int i = 0; i < laserCloudWidth; i++) for (int j = 0; j < laserCloudHeight; j++) 
				{
					int k = 0;
					auto c = laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					auto s = laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k];
					for (; k < laserCloudDepth-1; k++) {
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*(k+1)];
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] =
						laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*(k+1)];
					}
					laserCloudCornerArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = c;
					laserCloudSurfArray[i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k] = s;
					c->clear(); 
					s->clear();
				}
				centerCubeK--; 
				laserCloudCenDepth--;
			}

			// 局部地图提取 源代码512行
			/*x,y方向:±2个网格(共5×5)
			  z方向:±1个网格(共3层)
			  总网格数:5×5×3=75个(最大125个,边界时更少)*/
			int laserCloudValidNum = 0, laserCloudSurroundNum = 0;
			for (int i = centerCubeI-2; i <= centerCubeI+2; i++)
			for (int j = centerCubeJ-2; j <= centerCubeJ+2; j++)
			for (int k = centerCubeK-1; k <= centerCubeK+1; k++)
			{
				if (i>=0&&i<laserCloudWidth && 
					j>=0&&j<laserCloudHeight && 
					k>=0&&k<laserCloudDepth) 
				{
					laserCloudValidInd[laserCloudValidNum++] = i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k;
					laserCloudSurroundInd[laserCloudSurroundNum++] = i + laserCloudWidth*j + laserCloudWidth*laserCloudHeight*k;
				}
			}

			// 清空之前的局部地图
			laserCloudCornerFromMap->clear(); 
			laserCloudSurfFromMap->clear();
			// 收集有效区域的地图点
			/*laserCloudValidInd:之前计算的75个有效体素索引
				将机器人周围的有效体素中的点云合并到局部地图
				局部地图范围:±2网格(xy方向),±1网格(z方向)*/
			for (int i=0; i<laserCloudValidNum; i++) 
			{
				*laserCloudCornerFromMap += *laserCloudCornerArray[laserCloudValidInd[i]];
				*laserCloudSurfFromMap += *laserCloudSurfArray[laserCloudValidInd[i]];
			}

			
			//角点特征下采样
			pcl::PointCloud<PointType>::Ptr laserCloudCornerStack(new pcl::PointCloud<PointType>());
			downSizeFilterCorner.setInputCloud(laserCloudCornerLast);
			downSizeFilterCorner.filter(*laserCloudCornerStack);

			//平面点特征下采样
			pcl::PointCloud<PointType>::Ptr laserCloudSurfStack(new pcl::PointCloud<PointType>());
			downSizeFilterSurf.setInputCloud(laserCloudSurfLast);
			downSizeFilterSurf.filter(*laserCloudSurfStack);

			printf("map prepare time %f ms\n", t_shift.toc());
			printf("map corner num %d  surf num %d \n", laserCloudCornerFromMap->size(), laserCloudSurfFromMap->size());

			/*需要足够的地图特征点才能进行优化
			角点至少10个,平面点至少50个*/
			if (laserCloudCornerFromMap->size() > 10 && laserCloudSurfFromMap->size() > 50)
			{
				TicToc t_opt;
				TicToc t_tree;

				kdtreeCornerFromMap->setInputCloud(laserCloudCornerFromMap);
				kdtreeSurfFromMap->setInputCloud(laserCloudSurfFromMap);

				for (int iter=0; iter<2; iter++)
				{
					ceres::LossFunction *loss = new ceres::HuberLoss(0.1);
					ceres::LocalParameterization *q_param = new ceres::EigenQuaternionParameterization();
					ceres::Problem problem;
					problem.AddParameterBlock(parameters, 4, q_param);
					problem.AddParameterBlock(parameters+4, 3);

					TicToc t_data;

					for (auto &p : laserCloudCornerStack->points) {
						pointOri = p;
						// 转换到地图坐标系(使用当前估计的位姿)
						pointAssociateToMap(&p, &pointSel);
						// 搜索最近5个地图角点
						kdtreeCornerFromMap->nearestKSearch(pointSel,5,pointSearchInd,pointSearchSqDis);
						// 线特征判断 // 最大距离小于1米  确保匹配点足够近
						if (pointSearchSqDis[4] < 1.0) 
						{
							std::vector<Eigen::Vector3d> ns;
							Eigen::Vector3d cen(0,0,0);
							// 计算5个点的中心
							for (int j=0;j<5;j++) 
							{
								auto &pp = laserCloudCornerFromMap->points[pointSearchInd[j]];
								Eigen::Vector3d tmp(pp.x,pp.y,pp.z);
								cen += tmp; ns.push_back(tmp);
							}
							cen /=5;

							// 计算协方差矩阵
							Eigen::Matrix3d cov = Eigen::Matrix3d::Zero();
							for (auto &n : ns) 
							{ 
								auto t = n - cen; 
								cov += t*t.transpose(); 
							}
							// 特征值分解
							Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> saes(cov);
							// 线特征判断条件(构建点到线的距离残差)
							if (saes.eigenvalues()[2] > 3*saes.eigenvalues()[1]) 
							{
								// 线的方向向量
								auto dir = saes.eigenvectors().col(2);
								// 线上两点
								Eigen::Vector3d curr(p.x,p.y,p.z);
								Eigen::Vector3d a = cen + 0.1*dir;
								Eigen::Vector3d b = cen - 0.1*dir;
								// 构建残差
								auto cost = LidarEdgeFactor::Create(curr,a,b,1.0);
								problem.AddResidualBlock(cost, loss, parameters, parameters+4);
							}
						}
					}

					for (auto &p : laserCloudSurfStack->points) {
						pointOri = p;
						pointAssociateToMap(&p, &pointSel);
						kdtreeSurfFromMap->nearestKSearch(pointSel,5,pointSearchInd,pointSearchSqDis);
						if (pointSearchSqDis[4] <1.0) {
							// 拟合平面( 构建线性方程组:n·x + d = 0)
							Eigen::Matrix<double,5,3> A;
							Eigen::Matrix<double,5,1> b = -Eigen::Matrix<double,5,1>::Ones();
							for (int j=0;j<5;j++) 
							{
								auto &pp = laserCloudSurfFromMap->points[pointSearchInd[j]];
								A(j,0)=pp.x; 
								A(j,1)=pp.y; 
								A(j,2)=pp.z;
							}
							// 求解平面法向量 n
							Eigen::Vector3d n = A.colPivHouseholderQr().solve(b);
							double inv = 1/n.norm(); // d = -1/|n|
							n.normalize(); // 单位化法向量
							// 平面拟合质量检查:
							bool ok = true;
							for (int j=0;j<5;j++) {
								auto &pp = laserCloudSurfFromMap->points[pointSearchInd[j]];
								if (fabs(n.x()*pp.x + n.y()*pp.y + n.z()*pp.z + inv) >0.2) {
									ok=false; break;
								}
							}
							// 构建点到面的距离残差:
							if (ok) {
								Eigen::Vector3d curr(p.x,p.y,p.z);
								auto cost = LidarPlaneNormFactor::Create(curr,n,inv);
								problem.AddResidualBlock(cost, loss, parameters, parameters+4);
							}
						}
					}
					printf("mapping data assosiation time %f ms \n", t_data.toc());

					//优化求解
					TicToc t_solver;
					ceres::Solver::Options opt;
					opt.linear_solver_type=ceres::DENSE_QR;
					opt.max_num_iterations=4;
					ceres::Solver::Summary sum;
					ceres::Solve(opt,&problem,&sum);
					printf("mapping solver time %f ms \n", t_solver.toc());
				}
				printf("mapping optimization time %f \n", t_opt.toc());
			}

			transformUpdate();

			TicToc t_add;
			// 将当前帧加入地图
			// 将优化后的角点加入地图
			for (auto &p : laserCloudCornerStack->points) {
				pointAssociateToMap(&p, &pointSel);// 用优化后的位姿转换
				// 计算体素索引并加入对应体素
				int ci = int((pointSel.x+25)/50) + laserCloudCenWidth;
				int cj = int((pointSel.y+25)/50) + laserCloudCenHeight;
				int ck = int((pointSel.z+25)/50) + laserCloudCenDepth;
				if (pointSel.x+25<0) ci--;
				if (pointSel.y+25<0) cj--;
				if (pointSel.z+25<0) ck--;
				// 索引计算和边界检查
				if (ci>=0&&ci<laserCloudWidth &&
					cj>=0&&cj<laserCloudHeight &&
					ck>=0&&ck<laserCloudDepth) 
				{
					int idx = ci + laserCloudWidth*cj + laserCloudWidth*laserCloudHeight*ck;
					laserCloudCornerArray[idx]->push_back(pointSel);
				}
			}
			for (auto &p : laserCloudSurfStack->points) {
				pointAssociateToMap(&p, &pointSel);
				int ci = int((pointSel.x+25)/50) + laserCloudCenWidth;
				int cj = int((pointSel.y+25)/50) + laserCloudCenHeight;
				int ck = int((pointSel.z+25)/50) + laserCloudCenDepth;
				if (pointSel.x+25<0) ci--;
				if (pointSel.y+25<0) cj--;
				if (pointSel.z+25<0) ck--;
				if (ci>=0&&ci<laserCloudWidth &&
					cj>=0&&cj<laserCloudHeight &&
					ck>=0&&ck<laserCloudDepth) {
					int idx = ci + laserCloudWidth*cj + laserCloudWidth*laserCloudHeight*ck;
					laserCloudSurfArray[idx]->push_back(pointSel);
				}
			}
			printf("add points time %f ms\n", t_add.toc());

			TicToc t_filter;
			// 地图维护与下采样
			for (int i=0; i<laserCloudValidNum; i++) 
			{
				int idx = laserCloudValidInd[i];

				pcl::PointCloud<PointType>::Ptr tmp(new pcl::PointCloud<PointType>());
				downSizeFilterCorner.setInputCloud(laserCloudCornerArray[idx]);
				downSizeFilterCorner.filter(*tmp);
				laserCloudCornerArray[idx] = tmp;

				tmp.reset(new pcl::PointCloud<PointType>());
				downSizeFilterSurf.setInputCloud(laserCloudSurfArray[idx]);
				downSizeFilterSurf.filter(*tmp);
				laserCloudSurfArray[idx] = tmp;
			}
			printf("filter time %f ms \n", t_filter.toc());

			//局部地图发布(每5帧)
			TicToc t_pub;
			if (frameCount %5 ==0) {
				laserCloudSurround->clear();
				for (int i=0; i<laserCloudSurroundNum; i++) 
				{
					int idx = laserCloudSurroundInd[i];
					*laserCloudSurround += *laserCloudCornerArray[idx];
					*laserCloudSurround += *laserCloudSurfArray[idx];
				}
				sensor_msgs::msg::PointCloud2 msg;
				pcl::toROSMsg(*laserCloudSurround, msg);
				msg.header.stamp = rclcpp::Time(static_cast<int64_t>(timeLaserOdometry*1e9));
				msg.header.frame_id = "camera_init";
				pubLaserCloudSurround->publish(msg);
			}

			//全局地图发布(每20帧)
			if (frameCount%20 ==0) {
				pcl::PointCloud<PointType> map;
				for (int i=0; i<laserCloudNum; i++) 
				{
					map += *laserCloudCornerArray[i]; 
					map += *laserCloudSurfArray[i];
				}
				sensor_msgs::msg::PointCloud2 msg;
				pcl::toROSMsg(map, msg);
				msg.header.stamp = rclcpp::Time(static_cast<int64_t>(timeLaserOdometry*1e9));
				msg.header.frame_id = "camera_init";
				pubLaserCloudMap->publish(msg);
			}

			for (auto &p : laserCloudFullRes->points) pointAssociateToMap(&p, &p);


			sensor_msgs::msg::PointCloud2 msg_full;
			pcl::toROSMsg(*laserCloudFullRes, msg_full);
			msg_full.header.stamp = rclcpp::Time(static_cast<int64_t>(timeLaserOdometry*1e9));
			msg_full.header.frame_id = "camera_init";
			pubLaserCloudFullRes->publish(msg_full);

			printf("mapping pub time %f ms \n", t_pub.toc());

			printf("whole mapping time %f ms +++++\n", t_whole.toc());

			nav_msgs::msg::Odometry odom;
			odom.header.frame_id = "camera_init";
			odom.child_frame_id = "aft_mapped";
			odom.header.stamp = rclcpp::Time(static_cast<int64_t>(timeLaserOdometry*1e9));
			odom.pose.pose.orientation.x = q_w_curr.x();
			odom.pose.pose.orientation.y = q_w_curr.y();
			odom.pose.pose.orientation.z = q_w_curr.z();
			odom.pose.pose.orientation.w = q_w_curr.w();
			odom.pose.pose.position.x = t_w_curr.x();
			odom.pose.pose.position.y = t_w_curr.y();
			odom.pose.pose.position.z = t_w_curr.z();
			pubOdomAftMapped->publish(odom);

			geometry_msgs::msg::PoseStamped pose;
			pose.header = odom.header;
			pose.pose = odom.pose.pose;
			laserAfterMappedPath.header.stamp = odom.header.stamp;
			laserAfterMappedPath.header.frame_id = "camera_init";
			laserAfterMappedPath.poses.push_back(pose);
			pubLaserAfterMappedPath->publish(laserAfterMappedPath);

			geometry_msgs::msg::TransformStamped tf;
			tf.header.stamp = odom.header.stamp;
			tf.header.frame_id = "camera_init";
			tf.child_frame_id = "aft_mapped";
			tf.transform.translation.x = t_w_curr.x();
			tf.transform.translation.y = t_w_curr.y();
			tf.transform.translation.z = t_w_curr.z();
			tf.transform.rotation.x = q_w_curr.x();
			tf.transform.rotation.y = q_w_curr.y();
			tf.transform.rotation.z = q_w_curr.z();
			tf.transform.rotation.w = q_w_curr.w();
			tf_broadcaster_->sendTransform(tf);

			frameCount++;
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(2));
	}
}

int main(int argc, char **argv)
{
	rclcpp::init(argc, argv);
	node_ = std::make_shared<rclcpp::Node>("laserMapping");

	tf_broadcaster_ = std::make_unique<tf2_ros::TransformBroadcaster>(*node_);

	//增加下采样参数(去launch文件修改)
	float lineRes = 0.4; 
	float planeRes = 1.2; 
	node_->declare_parameter<float>("mapping_line_resolution", lineRes);
	node_->declare_parameter<float>("mapping_plane_resolution", planeRes);
	node_->get_parameter("mapping_line_resolution", lineRes);
	node_->get_parameter("mapping_plane_resolution", planeRes);
	RCLCPP_INFO(node_->get_logger(), "line %.2f plane %.2f", lineRes, planeRes);
	downSizeFilterCorner.setLeafSize(lineRes,lineRes,lineRes);
	downSizeFilterSurf.setLeafSize(planeRes,planeRes,planeRes);

	auto sub1 = node_->create_subscription<sensor_msgs::msg::PointCloud2>("/laser_cloud_corner_last",100,laserCloudCornerLastHandler);
	auto sub2 = node_->create_subscription<sensor_msgs::msg::PointCloud2>("/laser_cloud_surf_last",100,laserCloudSurfLastHandler);
	auto sub3 = node_->create_subscription<nav_msgs::msg::Odometry>("/laser_odom_to_init",100,laserOdometryHandler);
	auto sub4 = node_->create_subscription<sensor_msgs::msg::PointCloud2>("/velodyne_cloud_3",100,laserCloudFullResHandler);

	pubLaserCloudSurround = node_->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_surround",100);
	pubLaserCloudMap = node_->create_publisher<sensor_msgs::msg::PointCloud2>("/laser_cloud_map",100);
	pubLaserCloudFullRes = node_->create_publisher<sensor_msgs::msg::PointCloud2>("/velodyne_cloud_registered",100);
	pubOdomAftMapped = node_->create_publisher<nav_msgs::msg::Odometry>("/aft_mapped_to_init",100);
	pubOdomAftMappedHighFrec = node_->create_publisher<nav_msgs::msg::Odometry>("/aft_mapped_to_init_high_frec",100);//直接从里程计获取位姿
	pubLaserAfterMappedPath = node_->create_publisher<nav_msgs::msg::Path>("/aft_mapped_path",100);

	for (int i=0; i<laserCloudNum; i++) {
		laserCloudCornerArray[i].reset(new pcl::PointCloud<PointType>());
		laserCloudSurfArray[i].reset(new pcl::PointCloud<PointType>());
	}

	std::thread mapping_process{process};

	rclcpp::spin(node_);
	rclcpp::shutdown();
	return 0;
}

lidarFactor.hpp

// Author:   Tong Qin               qintonguav@gmail.com
// 	         Shaozu Cao 		    saozu.cao@connect.ust.hk

#pragma once

#include <ceres/ceres.h>
#include <ceres/rotation.h>
#include <Eigen/Dense>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/kdtree/kdtree_flann.h>
#include <pcl_conversions/pcl_conversions.h>

struct LidarEdgeFactor
{
	LidarEdgeFactor(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_a_,
					Eigen::Vector3d last_point_b_, double s_)
		: curr_point(curr_point_), last_point_a(last_point_a_), last_point_b(last_point_b_), s(s_) {}

	template <typename T>
	bool operator()(const T *q, const T *t, T *residual) const
	{

		Eigen::Matrix<T, 3, 1> cp{T(curr_point.x()), T(curr_point.y()), T(curr_point.z())};
		Eigen::Matrix<T, 3, 1> lpa{T(last_point_a.x()), T(last_point_a.y()), T(last_point_a.z())};
		Eigen::Matrix<T, 3, 1> lpb{T(last_point_b.x()), T(last_point_b.y()), T(last_point_b.z())};

		Eigen::Quaternion<T> q_last_curr{q[3], q[0], q[1], q[2]};
		Eigen::Quaternion<T> q_identity{T(1), T(0), T(0), T(0)};
		q_last_curr = q_identity.slerp(T(s), q_last_curr);
		Eigen::Matrix<T, 3, 1> t_last_curr{T(s) * t[0], T(s) * t[1], T(s) * t[2]};

		Eigen::Matrix<T, 3, 1> lp;
		lp = q_last_curr * cp + t_last_curr;

		Eigen::Matrix<T, 3, 1> nu = (lp - lpa).cross(lp - lpb);
		Eigen::Matrix<T, 3, 1> de = lpa - lpb;

		residual[0] = nu.x() / de.norm();
		residual[1] = nu.y() / de.norm();
		residual[2] = nu.z() / de.norm();

		return true;
	}

	static ceres::CostFunction *Create(const Eigen::Vector3d curr_point_, const Eigen::Vector3d last_point_a_,
									   const Eigen::Vector3d last_point_b_, const double s_)
	{
		return (new ceres::AutoDiffCostFunction<
				LidarEdgeFactor, 3, 4, 3>(
			new LidarEdgeFactor(curr_point_, last_point_a_, last_point_b_, s_)));
	}

	Eigen::Vector3d curr_point, last_point_a, last_point_b;
	double s;
};

struct LidarPlaneFactor
{
	LidarPlaneFactor(Eigen::Vector3d curr_point_, Eigen::Vector3d last_point_j_,
					 Eigen::Vector3d last_point_l_, Eigen::Vector3d last_point_m_, double s_)
		: curr_point(curr_point_), last_point_j(last_point_j_), last_point_l(last_point_l_),
		  last_point_m(last_point_m_), s(s_)
	{
		ljm_norm = (last_point_j - last_point_l).cross(last_point_j - last_point_m);
		ljm_norm.normalize();
	}

	template <typename T>
	bool operator()(const T *q, const T *t, T *residual) const
	{

		Eigen::Matrix<T, 3, 1> cp{T(curr_point.x()), T(curr_point.y()), T(curr_point.z())};
		Eigen::Matrix<T, 3, 1> lpj{T(last_point_j.x()), T(last_point_j.y()), T(last_point_j.z())};

		Eigen::Quaternion<T> q_last_curr{q[3], q[0], q[1], q[2]};
		Eigen::Quaternion<T> q_identity{T(1), T(0), T(0), T(0)};
		q_last_curr = q_identity.slerp(T(s), q_last_curr);
		Eigen::Matrix<T, 3, 1> t_last_curr{T(s) * t[0], T(s) * t[1], T(s) * t[2]};

		Eigen::Matrix<T, 3, 1> lp;
		lp = q_last_curr * cp + t_last_curr;

		residual[0] = (lp - lpj).dot(ljm_norm.cast<T>());

		return true;
	}

	static ceres::CostFunction *Create(const Eigen::Vector3d curr_point_, const Eigen::Vector3d last_point_j_,
									   const Eigen::Vector3d last_point_l_, const Eigen::Vector3d last_point_m_,
									   const double s_)
	{
		return (new ceres::AutoDiffCostFunction<
				LidarPlaneFactor, 1, 4, 3>(
			new LidarPlaneFactor(curr_point_, last_point_j_, last_point_l_, last_point_m_, s_)));
	}

	Eigen::Vector3d curr_point, last_point_j, last_point_l, last_point_m;
	Eigen::Vector3d ljm_norm;
	double s;
};

struct LidarPlaneNormFactor
{

	LidarPlaneNormFactor(Eigen::Vector3d curr_point_, Eigen::Vector3d plane_unit_norm_,
						 double negative_OA_dot_norm_) : curr_point(curr_point_), plane_unit_norm(plane_unit_norm_),
														 negative_OA_dot_norm(negative_OA_dot_norm_) {}

	template <typename T>
	bool operator()(const T *q, const T *t, T *residual) const
	{
		Eigen::Quaternion<T> q_w_curr{q[3], q[0], q[1], q[2]};
		Eigen::Matrix<T, 3, 1> t_w_curr{t[0], t[1], t[2]};
		Eigen::Matrix<T, 3, 1> cp{T(curr_point.x()), T(curr_point.y()), T(curr_point.z())};
		Eigen::Matrix<T, 3, 1> point_w;
		point_w = q_w_curr * cp + t_w_curr;

		Eigen::Matrix<T, 3, 1> norm(T(plane_unit_norm.x()), T(plane_unit_norm.y()), T(plane_unit_norm.z()));
		residual[0] = norm.dot(point_w) + T(negative_OA_dot_norm);
		return true;
	}

	static ceres::CostFunction *Create(const Eigen::Vector3d curr_point_, const Eigen::Vector3d plane_unit_norm_,
									   const double negative_OA_dot_norm_)
	{
		return (new ceres::AutoDiffCostFunction<
				LidarPlaneNormFactor, 1, 4, 3>(
			new LidarPlaneNormFactor(curr_point_, plane_unit_norm_, negative_OA_dot_norm_)));
	}

	Eigen::Vector3d curr_point;
	Eigen::Vector3d plane_unit_norm;
	double negative_OA_dot_norm;
};


struct LidarDistanceFactor
{

	LidarDistanceFactor(Eigen::Vector3d curr_point_, Eigen::Vector3d closed_point_) 
						: curr_point(curr_point_), closed_point(closed_point_) {}

	template <typename T>
	bool operator()(const T *q, const T *t, T *residual) const
	{
		Eigen::Quaternion<T> q_w_curr{q[3], q[0], q[1], q[2]};
		Eigen::Matrix<T, 3, 1> t_w_curr{t[0], t[1], t[2]};
		Eigen::Matrix<T, 3, 1> cp{T(curr_point.x()), T(curr_point.y()), T(curr_point.z())};
		Eigen::Matrix<T, 3, 1> point_w;
		point_w = q_w_curr * cp + t_w_curr;

		residual[0] = point_w.x() - T(closed_point.x());
		residual[1] = point_w.y() - T(closed_point.y());
		residual[2] = point_w.z() - T(closed_point.z());
		return true;
	}

	static ceres::CostFunction *Create(const Eigen::Vector3d curr_point_, const Eigen::Vector3d closed_point_)
	{
		return (new ceres::AutoDiffCostFunction<
				LidarDistanceFactor, 3, 4, 3>(
			new LidarDistanceFactor(curr_point_, closed_point_)));
	}

	Eigen::Vector3d curr_point;
	Eigen::Vector3d closed_point;
};

CMakeList.txt

cmake_minimum_required(VERSION 3.8)
project(aloam_velodyne_ros2)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

# 设置 C++ 标准
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# ROS2 依赖
find_package(ament_cmake REQUIRED)
find_package(glog REQUIRED)

find_package(rclcpp REQUIRED)
find_package(sensor_msgs REQUIRED)
find_package(nav_msgs REQUIRED)
find_package(geometry_msgs REQUIRED)
find_package(tf2_ros REQUIRED)
find_package(tf2_geometry_msgs REQUIRED)
find_package(pcl_conversions REQUIRED)
find_package(pcl_ros REQUIRED)
find_package(cv_bridge REQUIRED)
find_package(image_transport REQUIRED)
find_package(rosbag2_cpp REQUIRED)

# 第三方库
find_package(Eigen3 REQUIRED)
find_package(OpenCV REQUIRED)
find_package(Ceres REQUIRED)
find_package(PCL REQUIRED)

# 包含目录
include_directories(
  include
  ${EIGEN3_INCLUDE_DIR}
  ${OpenCV_INCLUDE_DIRS}
  ${CERES_INCLUDE_DIRS}
  ${PCL_INCLUDE_DIRS}
)

# ==============================================
# A-LOAM 核心节点
# ==============================================

# 1. 特征提取
add_executable(ascanRegistration src/scanRegistration.cpp)
ament_target_dependencies(ascanRegistration
  rclcpp
  sensor_msgs
  nav_msgs
  geometry_msgs
  tf2_ros
  tf2_geometry_msgs
  pcl_conversions
  pcl_ros
  Eigen3
)
target_link_libraries(ascanRegistration
  ${PCL_LIBRARIES}
  ${OpenCV_LIBRARIES}
)

# 2. 激光里程计
add_executable(alaserOdometry src/laserOdometry.cpp)
ament_target_dependencies(alaserOdometry
  rclcpp
  sensor_msgs
  nav_msgs
  geometry_msgs
  tf2_ros
  tf2_geometry_msgs
  pcl_conversions
  pcl_ros
  Eigen3
)
target_link_libraries(alaserOdometry
  ${PCL_LIBRARIES}
  ${OpenCV_LIBRARIES}
  ceres
  glog::glog
)

# 3. 激光建图
add_executable(alaserMapping src/laserMapping.cpp)
ament_target_dependencies(alaserMapping
  rclcpp
  sensor_msgs
  nav_msgs
  geometry_msgs
  tf2_ros
  tf2_geometry_msgs
  pcl_conversions
  pcl_ros
  Eigen3
)
target_link_libraries(alaserMapping
  ${PCL_LIBRARIES}
  ${OpenCV_LIBRARIES}
  ceres
  glog::glog
)

# ==============================================
# 安装目标
# ==============================================
install(TARGETS
  ascanRegistration
  alaserOdometry
  alaserMapping
  # atransformIntegration
  DESTINATION lib/${PROJECT_NAME}
)

# 安装头文件
install(DIRECTORY include/${PROJECT_NAME}/
  DESTINATION include/${PROJECT_NAME}
)

# 安装 launch 与 rviz 配置
install(DIRECTORY launch DESTINATION share/${PROJECT_NAME})
install(DIRECTORY rviz_cfg DESTINATION share/${PROJECT_NAME})

# 导出包含目录
ament_export_include_directories(include)
ament_export_dependencies(
  rclcpp
  sensor_msgs
  nav_msgs
  geometry_msgs
  tf2_ros
  tf2_geometry_msgs
  pcl_conversions
  pcl_ros
  cv_bridge
  image_transport
  rosbag2_cpp
  Eigen3
  OpenCV
  Ceres
  PCL
  glog
)

# 测试
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  ament_lint_auto_find_test_dependencies()
endif()

ament_package()

package.xml

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>aloam_velodyne_ros2</name>
  <version>0.0.0</version>
  <description>ROS2 Humble port of A-LOAM</description>
  <maintainer email="user@todo.todo">user</maintainer>
  <license>GPLv3</license>

  <buildtool_depend>ament_cmake</buildtool_depend>
  <depend>rclcpp</depend>
  <depend>sensor_msgs</depend>
  <depend>nav_msgs</depend>
  <depend>geometry_msgs</depend>
  <depend>tf2_ros</depend>
  <depend>tf2_geometry_msgs</depend>
  <depend>pcl_conversions</depend>
  <depend>pcl_ros</depend>
  <depend>eigen3</depend>
  <depend>opencv</depend>

  <depend>libpcl-dev</depend>
  <depend>libeigen3-dev</depend>
  <depend>libopencv-dev</depend>
  <depend>libceres-dev</depend>
  <depend>libgoogle-glog-dev</depend>

  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>

  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

启动文件

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.conditions import IfCondition
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare

def generate_launch_description():
    # 启动参数:是否打开 rviz
    rviz_arg = DeclareLaunchArgument(
        'rviz', default_value='true',
        description='Open RVIZ if true'
    )

    # 包路径
    package_name = 'aloam_velodyne_ros2'
    rviz_config_path = PathJoinSubstitution([
        FindPackageShare(package_name),
        'rviz_cfg',
        'aloam_velodyne_ros2.rviz'
    ])

    # ===================== 全局参数 =====================
    scan_line = 16
    mapping_skip_frame = 1 #2
    minimum_range = 1.0
    mapping_line_res =  0.8 #0.2
    mapping_plane_res = 1.2 #0.4

    # ===================== 三个 A-LOAM 核心节点 =====================
    ascanRegistration_node = Node(
        package='aloam_velodyne_ros2',
        executable='ascanRegistration',
        name='ascanRegistration',
        output='screen',
        parameters=[
            {'scan_line': scan_line},
            {'minimum_range': minimum_range}
        ]
    )

    alaserOdometry_node = Node(
        package='aloam_velodyne_ros2',
        executable='alaserOdometry',
        name='alaserOdometry',
        output='screen'
    )

    alaserMapping_node = Node(
        package='aloam_velodyne_ros2',
        executable='alaserMapping',
        name='alaserMapping',
        output='screen',
        parameters=[
            {'mapping_skip_frame': mapping_skip_frame},
            {'mapping_line_resolution': mapping_line_res},
            {'mapping_plane_resolution': mapping_plane_res}
        ]
    )


    transform_map = Node(
        package='tf2_ros',
        executable='static_transform_publisher',
        name='camera_init_to_map',
        # arguments=['0', '0', '0', '1.570795', '0', '1.570795', 'map', 'camera_init'],
        arguments=['0', '0', '0', '0', '0', '0', 'map', 'camera_init'],
    )

    # ===================== RVIZ =====================
    rviz_node = Node(
        package='rviz2',
        executable='rviz2',
        name='rviz2',
        arguments=['-d', rviz_config_path],
        condition=IfCondition(LaunchConfiguration('rviz'))
    )

    return LaunchDescription([
        rviz_arg,
        ascanRegistration_node,
        alaserOdometry_node,
        alaserMapping_node,
        rviz_node,
        transform_map
    ])
Logo

立足具身智能前沿赛道,致力于搭建全球化、开源化、全栈式技术交流与实践共创平台。

更多推荐