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库怎么装、怎么用;最后右边学怎么把点云画出来。三个模块缺一不可。
2.6 实战小练习
光看不练假把式。我建议你动手做两件事:
- 下载一个PCD文件(网上有很多公开数据集,比如KITTI),用PCL的
pcl_viewer命令打开看看:pcl_viewer map.pcd - 写一个ROS节点,订阅
/velodyne_points话题,把点云数据打印出来。看看一帧点云有多少个点,强度值的范围是多少。
我的经验:第一次看到点云在RViz里显示出来时,那种感觉挺奇妙的。你会看到周围的环境变成了密密麻麻的点——树是点、车是点、路也是点。嗯,这就是激光雷达眼中的世界。
好了,这一章就到这里。点云基础打牢了,后面学滤波、分割、聚类才会顺手。记住,PCL库是你最得力的工具,多用、多试、多踩坑,慢慢就熟了。