4. PCL点云库入门

说实话,很多做激光雷达的朋友一开始都绕不开PCL这个库。PCL全称Point Cloud Library,是点云处理领域的事实标准。我当年刚接触点云时,第一件事就是跟PCL死磕——安装、编译、踩坑、再编译……嗯,今天我就把这些经验掰开揉碎讲给你听。

4.1 PCL库安装与配置

安装PCL,说白了就两条路:要么用包管理器一键安装,要么源码编译。我个人建议初学者走第一条路,省心。

4.1.1 Ubuntu系统下的安装

如果你用的是Ubuntu,那最简单了。打开终端,敲几行命令就行:

sudo apt-get update
sudo apt-get install libpcl-dev pcl-tools

装完之后,验证一下:

pcl_viewer --help

如果能看到帮助信息,说明装好了。我在项目中遇到过一个问题——装完PCL后找不到头文件。后来发现是版本冲突。建议你装之前先看看系统里有没有旧版本:

dpkg -l | grep pcl
我曾经踩过的坑:Ubuntu 18.04默认的PCL版本是1.8,而20.04是1.10。如果你用源码编译了更高版本,记得把系统默认的PCL卸干净,否则链接时会报一堆莫名其妙的错误。

4.1.2 Windows下的安装

Windows下我推荐用vcpkg。你想想看,手动配置环境变量多麻烦?vcpkg一键搞定:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
.\vcpkg install pcl:x64-windows

装完之后,在CMakeLists.txt里加上:

find_package(PCL REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
target_link_libraries(your_project ${PCL_LIBRARIES})

4.1.3 macOS下的安装

macOS用户用Homebrew:

brew install pcl

我个人习惯在macOS上做原型开发,因为调试方便。但要注意,M1芯片的Mac有些PCL模块编译会报错,建议用Rosetta2模式。

4.2 PCL核心模块介绍

PCL的模块划分很清晰。我画了一张图,帮你快速建立整体认知:

PCL核心模块架构图 原始点云数据 滤波 (Filters) 去噪、下采样 特征 (Features) 法线、PFH、FPFH 分割 (Segmentation) 地面、聚类 配准 (Registration) ICP、NDT KdTree / Octree 空间索引 表面 (Surface) 重建、网格化 识别 (Recognition) 物体识别 I/O模块 PCD、PLY读写 可视化 (Visualization)

这张图里,我重点标出了几个你一定会用到的模块:

  • Filters模块:说白了就是给点云"洗澡"。去掉离群点、降采样、裁剪ROI区域,都靠它。我在做路沿检测时,先用直通滤波把范围卡到路面区域,再用体素滤波降采样,速度能快3倍。
  • Features模块:计算点云的特征描述子。比如法线、PFH、FPFH。这些是后续配准和识别的基础。
  • Segmentation模块:把点云分成不同的区域。地面分割、欧式聚类分割,自动驾驶里天天用。
  • Registration模块:点云配准。ICP算法是经典中的经典,多帧点云拼接全靠它。
  • Visualization模块:可视化。调试时离不开它。

4.3 PCL点云类型转换

PCL里点云类型很多,最常用的是pcl::PointXYZ和pcl::PointXYZRGB。你想想看,雷达只给xyz坐标,但相机能提供颜色信息,怎么融合?类型转换就派上用场了。

4.3.1 常见点云类型

类型 字段 适用场景
PointXYZ x, y, z 纯几何点云
PointXYZRGB x, y, z, r, g, b 带颜色的点云
PointXYZI x, y, z, intensity 带反射强度的点云
PointNormal x, y, z, normal_x, normal_y, normal_z 带法线的点云

4.3.2 类型转换实战

我举个例子,把PointXYZ转成PointXYZRGB:

#include <pcl/point_types.h>
#include <pcl/point_cloud.h>

// 原始点云
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud_xyz(new pcl::PointCloud<pcl::PointXYZ>());
// 目标点云
pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_rgb(new pcl::PointCloud<pcl::PointXYZRGB>());

// 转换
pcl::copyPointCloud(*cloud_xyz, *cloud_rgb);

// 给所有点赋红色
for (auto& point : cloud_rgb->points) {
    point.r = 255;
    point.g = 0;
    point.b = 0;
}
小技巧:我在项目中遇到过一个问题——用copyPointCloud转换时,如果源点云有NaN值,目标点云也会保留NaN。建议转换前先做一步滤波,把无效点去掉。

4.4 PCL可视化基础

可视化是调试的"眼睛"。PCL提供了pcl::visualization::PCLVisualizer,功能很强大。

4.4.1 最简单的可视化

#include <pcl/visualization/cloud_viewer.h>

pcl::visualization::CloudViewer viewer("点云查看器");
viewer.showCloud(cloud_xyz);

while (!viewer.wasStopped()) {
    // 保持窗口打开
}

这段代码能跑,但功能有限。我建议你用PCLVisualizer,更灵活:

#include <pcl/visualization/pcl_visualizer.h>

pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer->setBackgroundColor(0, 0, 0);  // 黑色背景
viewer->addPointCloud<pcl::PointXYZ>(cloud_xyz, "sample cloud");
viewer->setPointCloudRenderingProperties(
    pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");

while (!viewer->wasStopped()) {
    viewer->spinOnce(100);
}

4.4.2 添加辅助元素

调试时,光看点云不够。我习惯加上坐标系和文字标注:

// 添加坐标系(原点,长度1.0)
viewer->addCoordinateSystem(1.0);

// 添加文字
viewer->addText("原始点云", 10, 10, 20, 1.0, 1.0, 1.0, "text1");

// 添加球体标记
pcl::PointXYZ center(0.0, 0.0, 0.0);
viewer->addSphere(center, 0.1, 1.0, 0.0, 0.0, "sphere1");
避坑指南:我曾经在可视化时发现点云显示不全,折腾了半天才发现是坐标系范围没设置好。建议加上viewer->setCameraPosition(0, 0, 5, 0, 0, 0);来调整视角。

4.4.3 多视角可视化

有时候需要对比原始点云和处理后的点云。PCL支持多视口:

int v1(0), v2(1);
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1);  // 左半屏
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2);  // 右半屏

viewer->addPointCloud(cloud_original, "original", v1);
viewer->addPointCloud(cloud_filtered, "filtered", v2);

嗯,这个功能我在做滤波效果对比时天天用。左边看原始点云,右边看滤波后的,效果一目了然。

好了,PCL入门就讲到这里。记住一句话:PCL是个工具,别怕踩坑,多写代码多调试,慢慢就熟了。