目录

Point Cloud Library的基础入门及资料查找方法

Point Cloud Library项目的编译使用

官方教程源地址: https://pointclouds.org/documentation/tutorials/using_pcl_pcl_config.html

官方教程中提供了各平台比较通用的项目编译方法,这里作简单的翻译补充。

创建cpp文件和CMakeLists.txt

在项目文件夹中,我们创建一个调用了pcl库保存pcd文件的模块的示例c++源码文件pcd_write.cpp,文件内容我们先不管。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>

int
  main ()
{
  pcl::PointCloud<pcl::PointXYZ> cloud;

  // Fill in the cloud data
  cloud.width    = 5;
  cloud.height   = 1;
  cloud.is_dense = false;
  cloud.resize (cloud.width * cloud.height);

  for (auto& point: cloud)
  {
    point.x = 1024 * rand () / (RAND_MAX + 1.0f);
    point.y = 1024 * rand () / (RAND_MAX + 1.0f);
    point.z = 1024 * rand () / (RAND_MAX + 1.0f);
  }

  pcl::io::savePCDFileASCII ("test_pcd.pcd", cloud);
  std::cerr << "Saved " << cloud.size () << " data points to test_pcd.pcd." << std::endl;

  for (const auto& point: cloud)
    std::cerr << "    " << point.x << " " << point.y << " " << point.z << std::endl;

  return (0);
}

这个文件要先被编译成可执行文件,才能够运行使用。这里,我们使用CMake这个跨平台编译工具来对cpp文件进行编译。为此需要创建一个CMakeLists.txt来告诉CMake怎么编译。

不理解上面这段话没问题,在pcd_write.cpp同一个目录下创建一个CMakeLists.txt即可。

CMakeLists.txt内容如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
## 这一行声明了需要的cmake最低版本号,不用改
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)

## 这一行是项目名称,一般没什么影响
project(your_project_name)

## 这一行表示需要PCL最低1.2版本的所有组件,不清楚不要修改
find_package(PCL 1.2 REQUIRED)

## 下面这三行不用改
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

## 这一句告诉CMake从单个源文件pcd_write.cpp建立一个名叫main的可执行文件
## 源文件文件名需要根据实际修改
## 可执行文件名称可根据个人喜好修改
add_executable (main pcd_write.cpp)
##               ^^^^ ^^^^^^^^^^^^^

## 这一句表示将PCL库文件与可执行文件main链接
## 这一句中可执行文件的名称要与前面一致
target_link_libraries (main ${PCL_LIBRARIES})
##                      ^^^^

正常使用中,我们只需要注意源文件名可执行文件名保持正确,位置已在文件中标出

编译文件

正确创建完成上面这两个文件后,项目目录结构如下所示,可以开始对cpp源文件进行编译了!

1
2
3
project_folder
      |-- pcd_write.cpp
      |-- CMakeLists.txt

我们先在项目文件夹中创建一个名为build的空文件夹,我们将在里面完成编译,文件夹叫build是约定俗成的,一般不修改

现在项目目录结构是这样的

1
2
3
4
project_folder
      |-- pcd_write.cpp
      |-- CMakeLists.txt
      |-- build

我们从文件进入build文件夹,右键打开终端。或者在终端里手动输入命令切换到build目录下。

执行下面的指令,即可完成编译

1
2
cmake ..
make

编译成功后,终端中应该可以看到类似这样的信息

1
2
3
4
Scanning dependencies of target main
[ 50%] Building CXX object CMakeFiles/main.dir/pcd_write.cpp.o
[100%] Linking CXX executable main
[100%] Built target main

运行文件

现在我们可以输入以下指令运行可执行文件main,观察源cpp文件的效果

1
./main

使用示例文件得到的终端运行显示结果应该类似于这样,运行成功

1
2
3
4
5
6
Saved 5 data points to test_pcd.pcd.
  0.352222 -0.151883 -0.106395
  -0.397406 -0.473106 0.292602
  -0.731898 0.667105 0.441304
  -0.734766 0.854581 -0.0361733
  -0.4607 -0.277468 -0.916762

我们可以看到在CMakeLists.txt中设置的名为main的可执行文件放在build目录下。现在项目目录结构如下

1
2
3
4
5
6
7
project_folder
      |-- pcd_write.cpp
      |-- CMakeLists.txt
      |-- build
            |-- main
            |-- 一些其它文件
                ...

总结

以上就是编译运行一个使用了PCL的项目的步骤。在不同项目中基本只要注意CMakeLists.txt填写正确的源文件名可执行文件名

Point Cloud Library基础使用

pcd点云文件的读取

PCL的pcd点云文件读写模块放在pcl/io/pcd_io.h中,记得include

读取pcd文件的关键代码为

1
2
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
pcl::io::loadPCDFile("pcd文件的路径", *cloud);

第一行的代码创建一个点云类型为pcl::PointXYZ的名为cloud的空点云

第二行的代码从pcd文件中读取点云数据,存入cloud


完整的文件如下

main.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>			 //标准C++库中的输入输出类相关头文件。
#include <pcl/io/pcd_io.h>	 //pcd 读写类相关的头文件。
#include <pcl/point_types.h> //PCL中支持的点类型头文件。

int main(int argc, char **argv)
{
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);

	if (pcl::io::loadPCDFile(argv[1], *cloud) == -1)
	{
		cout << "error input!" << endl;
		return -1;
	}

	// 输出点云信息
	std::cout << "原始点云中包含点数量:" << cloud->points.size() << std::endl;
	std::cout << "height:" << cloud->height << "\nwidth:" << cloud->width << std::endl;

	// 输出各点信息
	for (const auto &point : *cloud)
		std::cout << "    " << point.x
				  << " " << point.y
				  << " " << point.z
				  << std::endl;
    
	return 0;
}

CMakeLists.txt

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)

project(pcd_read)

find_package(PCL 1.2 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable (pcd_read main.cpp)
target_link_libraries (pcd_read ${PCL_LIBRARIES})

在源代码中,下面这一行这样写是为了程序运行时从命令行中获取参数

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

argc将包含参数数量,可执行文件本身占一个参数数量。

argv为参数数组,里面包含了所有的参数。

当终端中执行命令行如下时,程序中argc == 3argv[0] == "./main"argv[1] == "value1"

1
./main value1 value2

读取pcd文件的源代码如下,可以直接在终端执行时通过修改命令行读取不同的pcd文件,避免文件路径写死在程序中,为了读不同的文件浪费时间重复编译。

1
pcl::io::loadPCDFile(argv[1], *cloud);

编译完成后,终端执行命令行如下,就能够读取pcd文件了,文件路径根据实际修改

1
./pcd_read ~/pcl/pcdXYZ_files/cloud00000.pcd 

image-20210604015912820

保存点云到pcd文件

PCL的pcd点云文件写入模块同样放在pcl/io/pcd_io.h中,记得include

PCL将点云写入pcd文件的代码如下,点云数据默认以二进制的格式写入pcd文件中,加快读写速度

1
pcl::io::savePCDFile("path_to_save_pcd_file", *cloud);

如果希望pcd文件中的点云数据以人类可读的格式保存,则使用如下代码

1
pcl::io::savePCDFileASCII("path_to_save_pcd_file", *cloud);

完整示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>			 //标准C++库中的输入输出类相关头文件。
#include <pcl/io/pcd_io.h>	 //pcd 读写类相关的头文件。
#include <pcl/point_types.h> //PCL中支持的点类型头文件。

int main(int argc, char **argv)
{
	pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);

    // pcd文件路径根据实际修改或直接写成从参数里读
	if (pcl::io::loadPCDFile("input.pcd", *cloud) == -1)
	{
		std::cout << "error input!" << std::endl;
		return -1;
	}
    
	// pcd文件路径根据实际修改或直接写成从参数里读
	pcl::io::savePCDFileASCII("output.pcd", *cloud);
    
	return 0;
}

显示点云

包含PCL完整可视化类的头文件为pcl/visualization/pcl_visualizer.h

首先,我们通过下面的代码建立一个名为viewer的可视化类对象,该对象弹出的显示的窗口名为Cloud Viewer

1
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("Cloud Viewer"));

我们现在有一个如下定义的一个名为cloud的点云对象

1
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

然后通过以下代码,我们就可以将点云cloud显示在一个名为Cloud Viewer的窗口中,该点云在viewer中的id为cloud1

1
viewer->addPointCloud(cloud, "cloud1");

如果想显示多个点云,要注意id不能重复,否则后面重复id的点云将不会显示

1
2
viewer->addPointCloud(cloud1, "cloud1");
viewer->addPointCloud(cloud2, "cloud2");

点云显示在窗口中后,程序会继续运行,运行完成后会直接退出,同时关闭点云显示窗口,这显然不是我们想要的

使用PCL时可以通过下面的两种方法暂停程序,保持点云显示,直到在点云显示窗口中按q退出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 两种方法二选一

// 方法1,阻塞式,完全停止程序
viewer->spin();

// 方法2,非阻塞式,可以在里面添加其它操作
while(viewer->wasStopped())
{
    // 可如下添加其他操作
	viewer->spinOnce(100);
    // boost::this_thread::sleep(boost::posix_time::microseconds(100000));
}

有的代码用的是system("pause");来暂停程序,该方法只在Windows中有效,通用性差,不灵活,不建议使用


完整代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>

// 这里是为了方便统一调整点云类型
typedef pcl::PointXYZ PointT;

int main(int argc, char **argv)
{
  // 定义点云对象
  pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
  
  // 从命令行参数中直接读取pcd文件路径,写入cloud
  pcl::io::loadPCDFile(argv[1], *cloud);

  // 定义可视化对象
  pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("Cloud Viewer"));

  // --- 显示点云数据 ----
  // "cloud1" 为显示id,默认cloud,显示多个点云时用默认会报警告。
  viewer->addPointCloud(cloud, "cloud1");

  // 阻塞式暂停程序
  viewer->spin();
  
  /*
  // 非阻塞式,二选一
  while (!viewer.wasStopped())
  {
  	viewer->spinOnce(100);
  }
  */
    
  return 0;
}

Point Cloud Library学习指南

中文学习资料

PCL学习可以先看看双愚这位作者写的学习指南和学习笔记,是中文的,很详细清晰,很适合学习理解

https://zhuanlan.zhihu.com/p/268524083 里面对Point Cloud Library整体框架、入门学习作了比较详细的介绍和总结

https://www.yuque.com/huangzhongqing/pcl 这里面是对PCL的学习总结,基本覆盖了正常操作需要的所有应用,对每个部分都有着较为详细的解释说明,而且包含了示例文件。我在学习时参考了很多

官方教程和文档

官方也提供了PCL的使用指南,覆盖了PCL各个模块的使用,不过因为是全英文,刚学习时可能会比较难理解,但是官方教程保证了准确性和规范性,如果英语能力较好还是建议对照着读一读

https://pointclouds.org/documentation/tutorials/index.html

当你想知道某个模块的完整功能使用说明,或是知道某个模块需要引入哪个头文件,或是某个方法的来源,都可以在官方提供的PCL的API的完整说明文档中找到答案。建议对模块有问题的时候,先到里面看一看

https://pointclouds.org/documentation/index.html

PCL问题解决方法查找

遇到问题我们通常先直接搜索引擎找答案,不过PCL开发相关的中文文章和资料占比其实是比较小的,有时如果想更好地找到问题的解决方法,要用英语关键词去搜一下,看看英语的文章资料

  1. 百度

    百度搜索pcl+中文关键词,通常可以解决常见问题,搜到的大部分都是中文内容,不过有时会发现好几篇文章内容都是一模一样的

    百度搜索pcl+英语关键词,可以查到官网页面,有时可以查到其它英语的文章

  2. Google

    用Google搜索pcl+中文关键词,有时可以查到百度没查到但是写得不错的中文文章

    用Google搜索pcl+英语关键词,可以找到比较多的国外的文章以及论坛讨论

  3. Github

    Github上会有一些PCL的示例代码和笔记,以及PCL的应用项目,有时候一些问题在Github里找找,可能可以发现别人已经搭好的项目源代码

    可以直接在Github里搜关键词,也可以在Google里搜关键词+github