Ubuntu 20.04 安装 PCL 库

推荐安装方法

1
sudo apt install libpcl*

如果不用最新的PCL功能的话,正常点云处理显示等使用还是没问题的。

源码安装(建议使用cmake-gui)

下载PCL-1.10,一定要下载1.10,因为ROS安装的过程中会安装pcl-1.10的库,正常使用没问题,但是缺少一些新功能例如pcl/surface/on_nurbs,我想要使用这个库,因此能够从源码编译,如果使用其它版本安装,会出现冲突例如error: redefinition of

1
2
3
cd pcl-pcl-1.10
mkdir build && cd build
cmake-gui

选择source code为下载解压的文件夹pcl-pcl-1.10,第二个选择刚创建的build文件夹

点击Configure,点击Finish,出现一些初始化配置。

注意要选BUILD_surface_on_nurbs,不然会报错pcl/surface/on_nurbs/fitting_surface_tdm.h: 没有那个文件或目录,当然最好把能选的都选了,我这里除了默认的,还额外勾选了

  • BUILD_CUDA
  • BUILD_GPU
  • BUILD_examples
  • BUILD_simulation
  • BUILD_surface_on_nurbs
  • BUILD_kinfu_tools

image.png

直到没有红色区域之后,点击configure,配置完成后点击generate按钮,会在build文件夹下生成工程文件,然后关闭cmake-gui界面就可以了

1
2
3
make
# 或者使用make -j4,make -j8,后面的数字为同时使用的线程数,量力而行,线程过多可能会系统直接卡死
sudo make install

源码卸载

1
2
3
4
5
sudo updatedb
locate pcl-1.13 #查看pcl-1.13的位置
sudo rm -r /usr/local/include/pcl-1.13 /usr/local/share/pcl-1.13
sudo updatedb
locate pcl-1.13 #检查是否全部删除

PCL基本使用

1. 基本数据类型

  • 点:pcl::PointXYZpcl::PointXYZRGBpcl::PointXYZI
  • 点云:pcl::PointCloud
    • 宽高:PointCloud::widthPointCloud::height,都为int类型,如果是规律排列的,则分别代表点云的长宽,如果是无序的则高为1,宽为点数量
    • 点:PointCloud::points存放点的vector变量
    • 指针:PointCloud::Ptr:只想PointCloud的智能指针

2. 读写点云数据

读取点云

1
2
3
4
pcl::PCDReader pcd_reader;
pcd_reader.read("xxx.pcd", *cloud);
// 或
pcl::io::loadPCDFile<pcl::PointXYZ>("xxx.pcd", *cloud);

保存点云

1
2
3
pcd_writer.write<pcl::PointXYZ>("xxx.pcd", *cloud, false);  //false表示保存为ASCII文件
// 或
pcl::io::savePCDFileASCII("xxx.pcd", *cloud);

3. 点云滤波

直通滤波:直接对x,y,z三个方向上设置要保留的点的距离范围,滤除距离范围外的点

1
2
3
4
5
pcl::passThrough<pcl::PointXYZ> pass;
pass.setInputCloud(cloud);
pass.setFilterFieldName("x\y\z");
pass.setFileterLimits(0.0, 3.0);
pass.filter(*output_cloud);

体素滤波:将空间划分为一定体积的网格,用每个网格内所有的点的质心来代替所有的点,将每个网格内的点云压缩为一个质心点。

1
2
3
4
pcl::VoxelGrid<pcl::PointXYZ> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(0.01f, 0.01f, 0.01f); // 网格的长宽高
vg.filter(*output_cloud)

离群点滤波:统计每个点与周围最临近的若干个点的平均距离,若平均距离大于设定的阈值则判定为离群点而滤除。

1
2
3
4
5
pcl::StatisticalOutlierRemoval<pcl::PointXYZ> sor;
sor.setInputCloud(cloud);
sor.setMeanK(50); //设置参与计算平均距离的点数
sor.setStddevMulThresh(0.1); //设置平均距离的阈值(米)
sor.filter(*cloud_filtered);

半径滤波:对一定半径范围内点数少于设定值的点进行滤波。

1
2
3
4
5
pcl::RadiusOutlierRemoval<pcl::PointXYZ> outrem;
outrem.setInputCloud(cloud);
outrem.setRadiusSearch(0.8) //设置半径大小
outrem.setMinNeighborsInRadius(2); //设置点数阈值
outrem.filter(*output_cloud)

4. 点云聚类与分割

RANSAC:随机抽样一致性算法,解决了传统最小二乘法全数据参与不能排除错误数据干扰的问题,可以拟合出更精确的模型,算法思路如下:

  1. 在原本数据集中随机抽取最少可以拟合出模型的数据量进行拟合。最少数据量一般由模型位置的参数来确定(例如直线就是两个),假设拟合得到的模型为M。
  2. 利用M,对数据集中剩余数据计算各个数据与模型M的误差值p,若p<给定阈值n,则认为该数据为内点;若大于阈值,则认为是外点。计算所有内点和外点,得到内点的集合S。
  3. 判断集合S的点数是否大于给定的点数阈值K,若大于K,则认为该次拟合的模型适合离得。若小于K,则认为不合理,直接丢弃。
  4. 若模型合理,则在用得到的内点集合S与之前随机得到的点再拟合依次模型,得到新的模型M’
  5. 重新随机采样,重复1-4过程,得到多个模型M’。
  6. 若采样次数达到给定阈值,则停止采样,就在得到的M’模型中,选择最优的作为最终结果。或者当某一模型M’的误差在给定精度阈值内,则停止采样,以该模型为最终结果。

平面分割

1
2
3
4
5
6
7
8
pcl::PointIndices::Ptr inliers_plane;
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_PLANE); //设置平面模型
seg.setMethodType(pcl::SAC_RANSAC); //使用RANSAC算法
seg.setDistanceThreshold(0.01) //容差范围0.01m
seg.setInputCloud(cloud);
seg.segment(*inliers_planc, *coefficients); //得到模型中点的索引,模型参数

法线估计

1
2
3
4
5
6
7
pcl::PointCloud<pcl::Normal>::Ptr cloud_normals (new pcl::PointCloud<pcl::Normal>);  //保存法线信息的点云
pcl::NormalEstimation<pcl::PointXYZ, pcl::Normal> ne;
ne.setInputCloud(cloud);
pcl::search::KdTree<pcl::PointXYZ>::Ptr tree (new pcl::search::KdTree<pcl::PointXYZ> ());
ne.setSearchMethod(tree) //设置搜索方法
ne.setRadiusSearch(0.03); //搜索半径,利用0.03米范围内的点计算法线
ne.compute(*cloud_normals)

圆柱分割

1
2
3
4
5
6
7
8
9
10
11
12
pcl::PointIndices::Ptr inliers_cylinder;
pcl::SACSegmentation<pcl::PointXYZ> seg;
seg.setOptimizeCoefficients(true);
seg.setModelType(pcl::SACMODEL_CYLINDER); //设置圆柱模型
seg.setMethodType(pcl::SAC_RANSAC); //使用RANSAC算法
seg.setNormalDistanceWeight(0.1) //法线在估计的权重
seg.setMaxIterations(10000); //迭代次数
seg.setDistanceThreshold(0.05); //距离容差
seg.setRadiusLimits(0, 0.1); //半径范围
seg.setInputCloud(cloud); //输入点云
seg.setInputNormals(cloud_normals); //输入法线点云
seg.segment(*inliers_cylinder, *coefficients_cylinder); //得到圆柱的点,模型参数

索引提取

1
2
3
4
5
6
pcl::PointIndices::Ptr inliers;
pcl::ExtractIndices<pcl::PointXYZ> extract;
extract.setInputCloud(cloud);
extract.setIndices(inliers);
extract.setNegative(false);
extract.filter(*cloud_p);

5. 可视化

程序中可视化

第一种方式,会造成程序暂停,需要先在点云窗口中按w,调整到一个比较好的视角,退出时按q

1
2
3
4
pcl::visualization::CloudViewer viewer("Simple Cloud Viewer"); //括号内是窗口名称
viewer.showCloud(cloud);
while(!viewer.wasStopped()){
}

第二种方式,将显示放在了循环内,不会造成程序中断

1
2
pcl::visualization::CloudViewer viewer("Simple Cloud Viewer");
while()

命令行可视化

1
pcl_viewer xxx.pcd

参考链接:

  1. 哈工大竞技机器人队. 【视觉组竞培营】第四讲 PCL点云库. Bilibili. 2022.09.30
  2. 芒果的技术博客. vcpkg安装pcl-visualization模块. 2021.10.28
  3. Point Cloud Library. Tutorials
  4. Cc1924. Ubuntu18安装新版本PCL-1.13,并和ROS自带PCL-1.8共存. CSDN. 2023.03.07