PCL点云配准官方教程

这篇文章是PCL官网关于两个点云配准的一个demo,我翻译了一下并做了一点小修改。使用的是ICP算法,该程序将加载点云并对其施加刚性变换。之后,ICP算法会将变换后的点云与原始对齐。每次用户按下“空格”,都会进行一次ICP迭代,并刷新查看器。
原文链接:交互式ICP点云配准
为了能让小白更直观的理解,我直接说重点(上来就能运行输出结果):
1.输入:在这里插入图片描述
此处输入两个ply格式的点云,可以是同一个模型,也不一定是不同姿态的两个模型,其中cloud_in是目标点云模型(固定不动,显示为白色),cloud_icp是迭代点云模型(显示为红色),第一次会先复制给点云模型cloud_tr(cloud_icp做一次姿态变换,因为如果cloud_in和cloud_icp是同一个模型时还配准个毛,先让他翻转跳跃一下🙃,显示为绿色)
输出:
在这里插入图片描述
前面输入两个点云环境搭建没错之后直接F5运行编译,白色的是cloud_in(始终固定不动),绿色的是cloud_tr(cloud_icp做一次姿态变换后复制给他),右边的红色是cloud_icp(每按一次空格键就会迭代一次,我手动迭代了24次之后基本和白色就完全重合了)。
在这里插入图片描述
输出端会显示两个点云的数量,初始姿态变换矩阵,后面每迭代一次就会输出一个旋转矩阵和位移向量,其实点云配准不就是为了得到两个点云的空间位置矩阵的关西嘛。

另外,要获取相机参数,只需在查看器中按“ C”即可将显示相机的位置/方向/焦点
在这里插入图片描述
总结:1、两个点云模型重叠区域越大效果越好
2、只能手动迭代比较蛋疼,最终配准的效果全凭目测(毕竟教程名字就是迭代式ICP)
3、最好多输入几个不同的点云模型迭代试试加深理解
4、此配准过程只是点云刚性变换,不涉及点云融合,输出的只是空间姿态关系(旋转平移)
为了方便大家练习,斯坦福兔子模型下载请参考rabbit.ply
在这里插入图片描述
这是人脸的模型(左右两边两个不太一样的模型),我上传了一下face.ply

#include <iostream>
#include <string>
#include <pcl/io/ply_io.h> //ply格式读取
#include <pcl/io/pcd_io.h> //pcd格式读取
#include <pcl/point_types.h> //PCL中支持的点类型头文件
#include <pcl/registration/icp.h>
#include <pcl/visualization/pcl_visualizer.h>  //可视化头文件
#include <pcl/console/time.h>   // TicToc 计时

typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

bool next_iteration = false;

void
print4x4Matrix(const Eigen::Matrix4d& matrix)
{
    printf("Rotation matrix :\n");
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), matrix(0, 2));
    printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), matrix(1, 2));
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), matrix(2, 2));
    printf("Translation vector :\n");
    printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), matrix(2, 3));
}

void
keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event,
    void* nothing)//使用空格键来增加迭代次数,并更新显示
{
    if (event.getKeySym() == "space" && event.keyDown())
        next_iteration = true;
}

int
main(int argc,
    char* argv[])
{
    // 将会出现的三个点云模型
    PointCloudT::Ptr cloud_in(new PointCloudT);  // (原始点云)Original point cloud
    PointCloudT::Ptr cloud_tr(new PointCloudT);  // (位姿变化点云)Transformed point cloud
    PointCloudT::Ptr cloud_icp(new PointCloudT);  // (ICP迭代输出点云)ICP output point cloud

    // Checking program arguments
    //if (argc < 2)
    //{
    //    printf("Usage :\n");
    //    printf("\t\t%s file.ply number_of_ICP_iterations\n", argv[0]);
    //    PCL_ERROR("Provide one ply file.\n");
    //    return (-3);
    //}

    int iterations = 1;  // // 默认的ICP迭代次数
    if (argc > 2)
    {
        // 如果用户将迭代次数作为参数传递
        iterations = atoi(argv[2]);
        if (iterations < 1)
        {
            PCL_ERROR("Number of initial iterations must be >= 1\n");
            return (-1);
        }
    }

    pcl::console::TicToc time;
    time.tic();

 /*   if (pcl::io::loadPLYFile(argv[1], *cloud_in) < 0)
    {
        PCL_ERROR("Error loading cloud %s.\n", argv[1]);
        return (-1);
    }
    std::cout << "\nLoaded file " << argv[1] << " (" << cloud_in->size() << " points) in " << time.toc() << " ms\n" << std::endl;*/

    	//读取ply文件
	if (pcl::io::loadPLYFile<pcl::PointXYZ>("C:\\Users\\fhlhc\\Desktop\\left.ply", *cloud_in) == -1)
	{
		PCL_ERROR("Couldn't read file1 \n");
		return (-2);
	}
	std::cout << "Loaded " << cloud_in->size() << " data points from file1" << std::endl;

    if (pcl::io::loadPLYFile<pcl::PointXYZ>("C:\\Users\\fhlhc\\Desktop\\right.ply", *cloud_icp) == -1)
    {
        PCL_ERROR("Couldn't read file1 \n");
        return (-2);
    }
    std::cout << "Loaded " << cloud_icp->size() << " data points from file1" << std::endl;

    // 定义旋转矩阵和平移向量Matrix4d是4*4的矩阵
    Eigen::Matrix4d transformation_matrix = Eigen::Matrix4d::Identity();

    // 旋转矩阵 (see https://en.wikipedia.org/wiki/Rotation_matrix)
    double theta = M_PI / 8;  // // 旋转的角度用弧度的表示方法
    transformation_matrix(0, 0) = std::cos(theta);
    transformation_matrix(0, 1) = -sin(theta);
    transformation_matrix(1, 0) = sin(theta);
    transformation_matrix(1, 1) = std::cos(theta);

    // 平移向量在X,Y,Z方向的位移 (meters)
    transformation_matrix(0, 3) = 1;
    transformation_matrix(1, 3) = 2;
    transformation_matrix(2, 3) = 4;

    // 在终端显示转换矩阵
    std::cout << "Applying this rigid transformation to: cloud_in -> cloud_icp" << std::endl;
    print4x4Matrix(transformation_matrix);

    // 执行点云转化
    pcl::transformPointCloud(*cloud_icp, *cloud_icp, transformation_matrix);
    *cloud_tr = *cloud_icp;  // 备份cloud_icp赋值给cloud_tr为后期使用

    // icp算法配准
    time.tic();
    pcl::IterativeClosestPoint<PointT, PointT> icp;
    icp.setMaximumIterations(iterations); //设置要执行的初始迭代次数(默认值为1)
    icp.setInputSource(cloud_icp);//设置输入点云
    icp.setInputTarget(cloud_in); //设置目标点云(输入点云进行仿射变换,得到目标点云)
    icp.align(*cloud_icp);//匹配后源点云
    icp.setMaximumIterations(1);  // 设置为1以便下次调用
    std::cout << "Applied " << iterations << " ICP iteration(s) in " << time.toc() << " ms" << std::endl;

    if (icp.hasConverged())   //输出变换矩阵的适合性评估,检查ICP算法是否收敛;否则退出程序。如果成功,我们将转换矩阵存储在4x4矩阵中,然后打印刚性矩阵转换
    {
        std::cout << "\nICP has converged, score is " << icp.getFitnessScore() << std::endl;
        std::cout << "\nICP transformation " << iterations << " : cloud_icp -> cloud_in" << std::endl;
        transformation_matrix = icp.getFinalTransformation().cast<double>();
        print4x4Matrix(transformation_matrix);
    }
    else
    {
        PCL_ERROR("\nICP has not converged.\n");
        return (-1);
    }

    // //可视化
    pcl::visualization::PCLVisualizer viewer("ICP demo");
    // 创建两个独立垂直观察视点
    int v1(0);

    int v2(1);
    viewer.createViewPort(0.0, 0.0, 0.5, 1.0, v1);
    viewer.createViewPort(0.5, 0.0, 1.0, 1.0, v2);

    // 定义显示的颜色信息
    float bckgr_gray_level = 0.0;  // 黑色
    float txt_gray_lvl = 1.0 - bckgr_gray_level;

    	// 原始的点云设置为白色的
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_in_color_h(cloud_in, (int)255 * txt_gray_lvl, (int)255 * txt_gray_lvl,
        (int)255 * txt_gray_lvl);
    viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v1", v1);
    viewer.addPointCloud(cloud_in, cloud_in_color_h, "cloud_in_v2", v2);

    //  转换后的点云显示为绿色
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_tr_color_h(cloud_tr, 20, 180, 20);
    viewer.addPointCloud(cloud_tr, cloud_tr_color_h, "cloud_tr_v1", v1);

    //  ICP配准后的点云为红色
    pcl::visualization::PointCloudColorHandlerCustom<PointT> cloud_icp_color_h(cloud_icp, 180, 20, 20);
    viewer.addPointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2", v2);

    // 加入文本的描述在各自的视口界面
    viewer.addText("White: Original point cloud\nGreen: Matrix transformed point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_1", v1);
    viewer.addText("White: Original point cloud\nRed: ICP aligned point cloud", 10, 15, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "icp_info_2", v2);

    std::stringstream ss;  //需要使用字符串流ss来将整数迭代转换为字符串,在终端显示迭代次数
    ss << iterations;
    std::string iterations_cnt = "ICP iterations = " + ss.str();
    viewer.addText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt", v2);

     设置背景颜色
    viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v1);
    viewer.setBackgroundColor(bckgr_gray_level, bckgr_gray_level, bckgr_gray_level, v2);

    //  设置相机的坐标和方向(在终端初始观察的位置)
    viewer.setCameraPosition(-3.68332, 2.94092, 5.71266, 0.289847, 0.921947, -0.256907, 0);
    viewer.setSize(1280, 1024);  //可视化窗口的大小

    // 配准按键回调函数 :
    viewer.registerKeyboardCallback(&keyboardEventOccurred, (void*)NULL);

    //显示可视化终端
    while (!viewer.wasStopped())
    {
        viewer.spinOnce();

        // 按下空格键 :
        if (next_iteration)
        {
            // The Iterative Closest Point algorithm(ICP算法)
            time.tic();
            icp.align(*cloud_icp);
            std::cout << "Applied 1 ICP iteration in " << time.toc() << " ms" << std::endl;

            if (icp.hasConverged())
            {
                printf("\033[11A");  // Go up 11 lines in terminal output.
                printf("\nICP has converged, score is %+.0e\n", icp.getFitnessScore());
                std::cout << "\nICP transformation " << ++iterations << " : cloud_icp -> cloud_in" << std::endl;
                transformation_matrix *= icp.getFinalTransformation().cast<double>();  // WARNING /!\ This is not accurate! For "educational" purpose only!
                print4x4Matrix(transformation_matrix);  // // 打印原始位置与现在位置的矩阵变换

                ss.str("");
                ss << iterations;
                std::string iterations_cnt = "ICP iterations = " + ss.str();
                viewer.updateText(iterations_cnt, 10, 60, 16, txt_gray_lvl, txt_gray_lvl, txt_gray_lvl, "iterations_cnt");
                viewer.updatePointCloud(cloud_icp, cloud_icp_color_h, "cloud_icp_v2");
            }
            else
            {
                PCL_ERROR("\nICP has not converged.\n");
                return (-1);
            }
        }
        next_iteration = false;
    }
    return (0);
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

滑了丝的螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值