第4章 点云可视化:让数据“说话”

点云可视化,说白了就是给你的激光雷达数据装上一双眼睛。我刚开始做感知时,总觉得算法跑通了就行,直到有一次调试地面分割,死活找不到参数问题——后来可视化一看,原来是坐标系搞反了。嗯,从那以后我再也不敢跳过可视化这一步了。

4.1 用PCL可视化点云——基础操作

PCL里最常用的可视化工具就是 pcl::visualization::PCLVisualizer。我个人习惯把它封装成一个函数,方便反复调用。

#include <pcl/visualization/pcl_visualizer.h>

void visualizePointCloud(pcl::PointCloud<pcl::PointXYZ>::Ptr cloud) {
    pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("点云显示"));
    viewer->setBackgroundColor(0, 0, 0); // 黑色背景
    viewer->addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
    viewer->setPointCloudRenderingProperties(
        pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "sample cloud");
    
    while (!viewer->wasStopped()) {
        viewer->spinOnce(100);
    }
}

这里有个小坑:spinOncespin 的区别。我曾在项目里用 spin 导致界面卡死,后来改成 spinOnce 配合循环就解决了。

小技巧: 点云显示卡顿?试试降低点云密度。用 pcl::VoxelGrid 下采样后再可视化,速度能快好几倍。

4.2 调整视角与颜色——让数据更直观

默认的灰色点云看久了容易疲劳。我建议根据点云属性上色,比如高度、强度或者距离。

4.2.1 按高度上色

pcl::visualization::PointCloudColorHandlerGenericField<pcl::PointXYZ> color_handler(cloud, "z");
viewer->addPointCloud<pcl::PointXYZ>(cloud, color_handler, "colored cloud");

这样地面是蓝色,高处是红色,一眼就能看出结构。我在做路沿检测时,就靠这个快速定位了误分割的区域。

4.2.2 调整视角

PCL 默认视角是从 Z 轴正方向看。你可以用鼠标拖拽旋转,也可以用代码设置固定视角:

viewer->setCameraPosition(0, 0, 10,  // 相机位置
                          0, 0, 0,   // 焦点位置
                          0, 1, 0);  // 上方向
避坑指南: 我曾经在调试时发现点云显示不全,折腾了半天——原来是相机位置设在了点云内部。记得把相机放到点云外部,距离至少是点云直径的1.5倍。

4.3 多窗口显示——对比分析利器

做地面分割时,我经常需要同时看原始点云和分割后的结果。多窗口显示就派上用场了。

pcl::visualization::PCLVisualizer::Ptr viewer1(new pcl::visualization::PCLVisualizer("原始点云"));
pcl::visualization::PCLVisualizer::Ptr viewer2(new pcl::visualization::PCLVisualizer("地面分割结果"));

// 设置窗口位置
viewer1->setPosition(0, 0);
viewer1->setSize(640, 480);
viewer2->setPosition(640, 0);
viewer2->setSize(640, 480);

// 分别添加点云
viewer1->addPointCloud(original_cloud, "original");
viewer2->addPointCloud(ground_cloud, "ground");

while (!viewer1->wasStopped() && !viewer2->wasStopped()) {
    viewer1->spinOnce(100);
    viewer2->spinOnce(100);
}

你想想看,左边是原始数据,右边是处理结果,参数调没调对一目了然。我有个同事甚至用四个窗口同时对比不同算法的效果。

窗口数量 适用场景 建议布局
2个 原始 vs 处理结果 左右并排
3个 原始 + 地面 + 障碍物 上1下2
4个 多算法对比 2x2网格

4.4 交互式选取点云——调试的“手术刀”

有时候你需要精确知道某个区域的点云信息。PCL 提供了交互式选取功能,说白了就是让你用鼠标框选点云。

4.4.1 点选模式

class PointPickingCallback : public pcl::visualization::PCLVisualizer::PointPickingCallback {
    void pointPickingEvent(const pcl::visualization::PointPickingEvent& event) override {
        float x, y, z;
        if (event.getPointIndex() != -1) {
            event.getPoint(x, y, z);
            std::cout << "选中点坐标: " << x << ", " << y << ", " << z << std::endl;
        }
    }
};

viewer->registerPointPickingCallback(new PointPickingCallback());

按下 Shift + 鼠标左键就能选中一个点。我在标定激光雷达和相机时,就靠这个功能手动选取对应点。

4.4.2 区域选取

框选区域需要用到 AreaPickingEvent。嗯,这个稍微复杂一点,但很实用:

viewer->registerAreaPickingCallback([](const pcl::visualization::AreaPickingEvent& event) {
    std::vector<int> indices;
    event.getPointsIndices(indices);
    std::cout << "选中了 " << indices.size() << " 个点" << std::endl;
});
注意: 区域选取默认用鼠标拖拽矩形框。如果你发现框选没反应,检查一下是不是开启了其他交互模式。我曾经被这个坑了半小时——原来是不小心按了 'r' 键切换到了旋转模式。

4.5 实战经验总结

做点云可视化这么多年,我总结了几条铁律:

  • 颜色就是信息——别用默认灰色,按高度或强度上色能发现很多隐藏问题
  • 多窗口是调试利器——同时看原始数据和处理结果,对比效率翻倍
  • 交互式选取是最后的手段——自动算法搞不定时,手动选点分析往往能救命
  • 性能优化不能忘——点云太大时先下采样,不然可视化会卡成PPT

我记得有一次,客户反馈我们的地面分割在某个场景下效果差。我打开可视化一看,发现是地面坡度太大导致算法误判。用交互式选取工具量了几个点的高度差,很快就定位了问题——阈值设得太死了。

所以说,可视化不只是“看看而已”。它是你调试算法的眼睛,是发现问题的手电筒。把这章的内容练熟了,后面的地面分割实战你会轻松很多。