计算两帧雷达数据之间的变换矩阵

文章目录

package.xml

bash 复制代码
<?xml version="1.0"?>
<package format="2">
  <name>point_cloud_registration</name>
  <version>0.0.0</version>
  <description>The point_cloud_registration package</description>

  <maintainer email="xiaoqiuslam@qq.com">xiaqiuslam</maintainer>

  <license>TODO</license>

  <buildtool_depend>catkin</buildtool_depend>
  
  <build_depend>pcl_conversions</build_depend>
  <build_depend>pcl_ros</build_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>sensor_msgs</build_depend>

  <build_export_depend>pcl_conversions</build_export_depend>
  <build_export_depend>pcl_ros</build_export_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>sensor_msgs</build_export_depend>
  
  <exec_depend>pcl_conversions</exec_depend>
  <exec_depend>pcl_ros</exec_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>sensor_msgs</exec_depend>

  <export>
  </export>
</package>

CMakeLists.txt

bash 复制代码
cmake_minimum_required(VERSION 3.0.2)

project(point_cloud_registration)

add_compile_options(-std=c++11)

find_package(catkin REQUIRED COMPONENTS
  pcl_conversions
  pcl_ros
  roscpp
  sensor_msgs
)

find_package(PCL REQUIRED QUIET)

catkin_package()

include_directories(
include
  ${catkin_INCLUDE_DIRS}
  ${PCL_INCLUDE_DIRS}
)

add_executable(point_cloud_registration src/point_cloud_registration.cc)
    
target_link_libraries(point_cloud_registration ${catkin_LIBRARIES})

point_cloud_registration.cc

cpp 复制代码
#include <chrono>
#include <ros/ros.h>
#include <sensor_msgs/LaserScan.h>
#include <pcl_ros/point_cloud.h>
#include <pcl/point_cloud.h>
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>

void ScanCallback(const sensor_msgs::LaserScan::ConstPtr &scan_msg);
void ConvertScan2PointCloud(const sensor_msgs::LaserScan::ConstPtr &scan_msg);
void ScanMatchWithICP(const sensor_msgs::LaserScan::ConstPtr &scan_msg);

bool is_first_scan_ = true;
pcl::PointCloud<pcl::PointXYZ>::Ptr current_pointcloud_; 
pcl::PointCloud<pcl::PointXYZ>::Ptr last_pointcloud_; 
pcl::IterativeClosestPoint<pcl::PointXYZ, pcl::PointXYZ> icp_;


void ScanCallback(const sensor_msgs::LaserScan::ConstPtr &scan_msg)
{
    // 第一帧雷达数据
    if (is_first_scan_ == true)
    {
        // 转换数据类型 并保存到current_pointcloud_
        ConvertScan2PointCloud(scan_msg);
        is_first_scan_ = false;
    }
    // 第二帧雷达数据
    else
    {
        // 数据类型转换
        std::chrono::steady_clock::time_point start_time = std::chrono::steady_clock::now();

        // 将current_pointcloud_赋值到last_pointcloud_进行保存
        *last_pointcloud_ = *current_pointcloud_;   

        // 数据类型转换 
        ConvertScan2PointCloud(scan_msg);

        // 调用ICP进行计算 雷达前后两帧间的坐标变换
        ScanMatchWithICP(scan_msg);
    }
}


void ConvertScan2PointCloud(const sensor_msgs::LaserScan::ConstPtr &scan_msg)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_msg = boost::shared_ptr<pcl::PointCloud<pcl::PointXYZ>>(new pcl::PointCloud<pcl::PointXYZ>());
    cloud_msg->points.resize(scan_msg->ranges.size());

    for (unsigned int i = 0; i < scan_msg->ranges.size(); ++i)
    {
        pcl::PointXYZ &point_tmp = cloud_msg->points[i];
        float range = scan_msg->ranges[i];

        if (!std::isfinite(range))
            continue;

        if (range > scan_msg->range_min && range < scan_msg->range_max)
        {
            float angle = scan_msg->angle_min + i * scan_msg->angle_increment;
            point_tmp.x = range * cos(angle);
            point_tmp.y = range * sin(angle);
            point_tmp.z = 0.0;
        }
    }

    cloud_msg->width = scan_msg->ranges.size();
    cloud_msg->height = 1;
    cloud_msg->is_dense = true;

    pcl_conversions::toPCL(scan_msg->header, cloud_msg->header);

    *current_pointcloud_ = *cloud_msg;
}


void ScanMatchWithICP(const sensor_msgs::LaserScan::ConstPtr &scan_msg)
{
    icp_.setInputSource(last_pointcloud_);
    icp_.setInputTarget(current_pointcloud_);

    pcl::PointCloud<pcl::PointXYZ> unused_result;
    icp_.align(unused_result);

    if (icp_.hasConverged() == false)
    {
        return;
    }
    else
    {
        Eigen::Affine3f transfrom;
        transfrom = icp_.getFinalTransformation();

        float x, y, z, roll, pitch, yaw;
        pcl::getTranslationAndEulerAngles(transfrom, x, y, z, roll, pitch, yaw);
        std::cout << "transfrom: (x: " << x << ", y: " << y << ", yaw: " << yaw * 180 / M_PI << ")" << std::endl;
    }
}


int main(int argc, char **argv)
{

    ros::init(argc, argv, "point_cloud_registration");

    ros::NodeHandle node_handle_; 

    ros::Subscriber laser_scan_subscriber_;

    laser_scan_subscriber_ = node_handle_.subscribe("laser_scan", 1, &ScanCallback);

    current_pointcloud_ = boost::shared_ptr<pcl::PointCloud<pcl::PointXYZ>>(new pcl::PointCloud<pcl::PointXYZ>());
    
    last_pointcloud_ = boost::shared_ptr<pcl::PointCloud<pcl::PointXYZ>>(new pcl::PointCloud<pcl::PointXYZ>());

    ros::spin();
    
    return 0;
}

运行结果

bash 复制代码
roscore 
source devel/setup.bash && rosrun point_cloud_registration point_cloud_registration
rosbag play 1.bag 
相关推荐
莱茶荼菜2 天前
【slam公式】
人工智能·线性代数·机器学习
吴名氏.2 天前
Java转AI Agent教程(第九周)错误矩阵
java·人工智能·矩阵·ai agent
伯奥克客服3 天前
2026年视频矩阵厂家技术评估:从EDID到控制协议开放度的六维筛选法
线性代数·矩阵·音视频
释厄6234 天前
三角基底律——矩阵3x6 是所有三角形的基底·3D中高就是边
线性代数·3d·矩阵
桑年5 天前
四、Pandas数据处理
人工智能·python·线性代数·pandas·数据可视化·数据处理
Kobebryant-Manba6 天前
IEKT向量矩阵流程&评估
人工智能·算法·矩阵
学习论之费曼学习法8 天前
无人值守AI内容矩阵实战(10):Windows 上让 Python 进程 7×24 活着
人工智能·windows·python·矩阵·powershell·进程守护
zhongben_keji8 天前
家政小程序开发全解:经营模式、品类矩阵、派单约束与信用核验
线性代数·矩阵·apache
silvamux_8 天前
微头条爆款体平台适配实测:把交付要求变成可计算的指标
java·线性代数·算法·决策树·矩阵
zyseo88 天前
谷歌SEO 关键词研究完整方法论:从种子词到优先级矩阵
数据库·人工智能·矩阵