2、点云基础与PCL库:点云数据结构、PCL库安装与使用、点云可视化

各位同学,欢迎来到第二章。今天我们来聊聊点云和PCL库。说实话,点云这东西,刚接触自动驾驶时我也觉得挺玄乎的——一堆三维点,能看出啥名堂?后来做项目做多了才发现,点云就是激光雷达的“眼睛”,看不懂点云,感知模块基本就废了一半。

这一章我会带着大家,从点云的数据结构讲起,再到PCL库的安装和使用,最后教你怎么把点云“画”出来。嗯,都是基本功,但也是后面所有感知算法的基础。

2.1 点云数据结构:别被“点”字骗了

点云,说白了就是一堆三维坐标点的集合。每个点通常包含(x, y, z)坐标,有时候还带着强度值、时间戳、甚至颜色信息。你想想看,激光雷达每秒扫出几十万个点,这些点堆在一起,就构成了周围环境的“三维快照”。

在Autoware里,点云数据最常用的格式是PCL的pcl::PointCloud<T>。这个模板类可以装不同类型的点,比如:

点类型 包含字段 典型用途
pcl::PointXYZ x, y, z 基础三维坐标
pcl::PointXYZI x, y, z, intensity 带反射强度的点
pcl::PointXYZRGB x, y, z, r, g, b 带颜色信息的点
pcl::PointXYZIR x, y, z, intensity, ring 多线激光雷达常用

我个人习惯用pcl::PointXYZI,因为强度值在目标检测时特别有用。比如车道线反射强度高,树干反射强度低,这个差异能帮我们做很多事。

核心要点:点云不是无序的点集。PCL内部用std::vector存储点,所以你可以像操作数组一样访问每个点。但要注意,点云数据量很大,动辄几十万点,遍历时别用太重的操作。

2.2 PCL库安装:踩过的坑我都替你踩了

PCL(Point Cloud Library)是点云处理的瑞士军刀。安装它,说简单也简单,说坑也多。我在项目中遇到过好几次版本不兼容的问题,尤其是和ROS一起用的时候。

推荐安装方式:

# Ubuntu 20.04 + ROS Noetic
sudo apt install libpcl-dev pcl-tools

# 或者从源码编译(我建议新手别折腾)
git clone https://github.com/PointCloudLibrary/pcl.git
cd pcl && mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make -j4
sudo make install

注意:如果你用ROS,千万别手动装PCL再装ROS的PCL桥接包,容易冲突。直接用sudo apt install ros-noetic-pcl-conversions,ROS会自动帮你搞定依赖。

我曾经有一次,为了用PCL的最新特性,从源码编译了1.12版本,结果ROS的pcl_ros死活找不到新版本的库。折腾了两天,最后还是乖乖用apt装了1.10版本。嗯,有时候“够用”比“最新”更重要。

2.3 PCL库使用:从读取到处理

装好PCL后,咱们写个最简单的程序——读取一个点云文件并打印点数量。这就像学编程时的“Hello World”,但更实用。

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <iostream>

int main() {
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZI>);
    
    if (pcl::io::loadPCDFile<pcl::PointXYZI>("map.pcd", *cloud) == -1) {
        PCL_ERROR("Couldn't read file map.pcd\n");
        return -1;
    }
    
    std::cout << "Loaded " << cloud->points.size() << " points." << std::endl;
    
    // 遍历每个点
    for (size_t i = 0; i < cloud->points.size(); ++i) {
        // 访问点的坐标和强度
        float x = cloud->points[i].x;
        float y = cloud->points[i].y;
        float z = cloud->points[i].z;
        float intensity = cloud->points[i].intensity;
    }
    
    return 0;
}

这段代码干了三件事:加载PCD文件、检查是否成功、遍历点云。你可能会问,为什么要用::Ptr?这是PCL的智能指针,能自动管理内存。在Autoware里,点云数据几乎都是用智能指针传递的,避免拷贝大块数据。

小技巧:处理点云时,尽量用cloud->points[i]而不是cloud->at(i)。前者是直接数组访问,后者有边界检查,慢不少。点云数据量大,性能差距很明显。

2.4 点云可视化:把数据变成“画”

光看数字没意思,咱们得把点云画出来。PCL自带的可视化工具pcl::visualization::PCLVisualizer就挺好用。不过在实际项目中,我更喜欢用RViz——毕竟Autoware的感知结果最终都要在RViz里看。

用PCLVisualizer快速查看:

#include <pcl/visualization/cloud_viewer.h>

pcl::visualization::CloudViewer viewer("Simple Cloud Viewer");
viewer.showCloud(cloud);
while (!viewer.wasStopped()) {
    // 保持窗口打开
}

这段代码会弹出一个窗口,显示你的点云。你可以用鼠标旋转、缩放。嗯,就这么简单。

但如果你想把点云发布到ROS里,让Autoware的其他模块也能看到,就得用pcl_ros了:

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

ros::Publisher pub = nh.advertise<pcl::PointCloud<pcl::PointXYZI>>("/lidar_points", 1);
pub.publish(cloud);

在RViz里添加一个PointCloud2显示,选择话题/lidar_points,就能看到你的点云了。为什么用PointCloud2?因为ROS的消息格式就是sensor_msgs::PointCloud2,PCL的pcl::PointCloud可以通过pcl::toROSMsg转换。

避坑指南:我曾经在可视化时发现点云颜色不对,查了半天才发现是强度值没归一化。PCL的强度值范围是0-1的浮点数,但有些激光雷达输出的是0-255的整数。记得做归一化,否则显示出来一片黑。

2.5 知识体系总览

为了让大家更直观地理解本章的知识结构,我画了一张图。你看完应该能明白点云、PCL库和可视化之间的关系。

点云基础与PCL库知识体系 点云数据结构 PointCloud<T> / 点类型 PCL库安装与使用 apt安装 / 源码编译 / API 点云可视化 PCLVisualizer / RViz PointXYZ / PointXYZI / PointXYZRGB 强度值 / 时间戳 / Ring PCD文件读写 / 点云遍历 智能指针 / 内存管理 CloudViewer / PCLVisualizer ROS发布 / RViz显示 核心目标:掌握点云数据的读写、处理与可视化 为后续的滤波、分割、聚类等感知算法打下基础

这张图把本章的三个核心模块串起来了。你从左边开始,先搞懂点云里存的是什么数据;然后中间学PCL库怎么装、怎么用;最后右边学怎么把点云画出来。三个模块缺一不可。

2.6 实战小练习

光看不练假把式。我建议你动手做两件事:

  1. 下载一个PCD文件(网上有很多公开数据集,比如KITTI),用PCL的pcl_viewer命令打开看看:pcl_viewer map.pcd
  2. 写一个ROS节点,订阅/velodyne_points话题,把点云数据打印出来。看看一帧点云有多少个点,强度值的范围是多少。

我的经验:第一次看到点云在RViz里显示出来时,那种感觉挺奇妙的。你会看到周围的环境变成了密密麻麻的点——树是点、车是点、路也是点。嗯,这就是激光雷达眼中的世界。

好了,这一章就到这里。点云基础打牢了,后面学滤波、分割、聚类才会顺手。记住,PCL库是你最得力的工具,多用、多试、多踩坑,慢慢就熟了。

可可精品资料库