3. 点云可视化:让数据“看得见”

点云可视化,说白了就是给你的激光雷达数据“拍张照”。

我刚开始做点云处理时,总觉得算法调好了就行,可视化可有可无。结果有一次调试滤波参数,死活看不出效果差异,折腾了两天才发现是参数写反了。嗯,从那以后,我再也不敢跳过可视化这一步。

这一节,我们聊聊三个最常用的可视化工具:PCL 的 pcl_viewer、Open3D 的 draw_geometries,以及点云渲染的几种实用技巧。

3.1 PCL 可视化工具:pcl_viewer

PCL 自带一个轻量级可视化工具,叫 pcl_viewer。它是个命令行工具,不需要写代码就能看点云。

用法很简单:

pcl_viewer point_cloud.pcd

窗口弹出来,你就能看到点云了。鼠标左键旋转,右键平移,滚轮缩放。这些操作和大多数 3D 软件差不多,上手很快。

我个人习惯用 pcl_viewer 做快速预览。比如刚采集完数据,先看一眼点云长什么样,有没有明显噪点,地面是不是平的。这些信息几秒钟就能判断出来。

小技巧: pcl_viewer 支持同时打开多个文件。比如你想对比原始点云和滤波后的结果,可以这样:
pcl_viewer original.pcd filtered.pcd
两个点云会叠加显示,用不同颜色区分。

如果你想在代码里用 PCL 的可视化功能,那就得用 pcl::visualization::PCLVisualizer 类。下面是个简单例子:

#include <pcl/visualization/pcl_visualizer.h>

pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("点云显示"));
viewer->addPointCloud(cloud, "cloud");
viewer->spin();

这段代码会创建一个窗口,显示你的点云。注意 spin() 会阻塞程序,直到你关闭窗口。如果你不想阻塞,可以用 spinOnce()

3.2 Open3D 可视化:draw_geometries

Open3D 的可视化接口更 Pythonic,用起来很舒服。核心函数就一个:draw_geometries

import open3d as o3d

pcd = o3d.io.read_point_cloud("point_cloud.pcd")
o3d.visualization.draw_geometries([pcd])

就这么三行,点云就出来了。Open3D 的交互方式和 PCL 类似,但界面更现代,操作更流畅。

我记得有一次做项目,需要同时显示点云和对应的轨迹。用 Open3D 很容易实现:

# 显示点云和轨迹
lines = o3d.geometry.LineSet()
lines.points = o3d.utility.Vector3dVector(trajectory_points)
lines.lines = o3d.utility.Vector2iVector(line_indices)

o3d.visualization.draw_geometries([pcd, lines])

你想想看,点云和轨迹叠在一起,数据关系一目了然。这种直观性,是看数字永远比不了的。

核心区别: PCL 的 pcl_viewer 适合 C++ 项目,性能好;Open3D 的 draw_geometries 适合 Python 快速原型,开发效率高。我一般前期探索用 Open3D,最终部署用 PCL。

3.3 点云渲染:颜色映射、强度渲染、高度渲染

点云默认是白色的,但我们可以给它上色,让信息更丰富。常用的渲染方式有三种。

3.3.1 颜色映射

如果点云本身带有 RGB 信息(比如用相机采集的),直接显示颜色就行。PCL 和 Open3D 都支持:

# Open3D 显示彩色点云
pcd = o3d.io.read_point_cloud("colored.pcd")
o3d.visualization.draw_geometries([pcd])

但很多时候,点云没有颜色。这时候我们可以根据某个属性(比如强度、高度)来映射颜色。

3.3.2 强度渲染

激光雷达每个点都有强度值,反映的是目标反射的激光能量。强度高的点(比如路牌、白线)会更亮,强度低的点(比如黑色路面)会更暗。

在 PCL 中,强度渲染可以这样设置:

pcl::visualization::PCLVisualizer viewer;
viewer.addPointCloud(cloud, "cloud");
viewer.setPointCloudRenderingProperties(
    pcl::visualization::PCL_VISUALIZER_COLOR, 1.0, 0.0, 0.0, "cloud");

不过更常见的做法是用强度值直接控制颜色。Open3D 里可以这样:

# 强度渲染
intensities = np.asarray(pcd.points)[:, 0]  # 假设强度存在第一个通道
colors = plt.cm.jet(intensities / intensities.max())[:, :3]
pcd.colors = o3d.utility.Vector3dVector(colors)
o3d.visualization.draw_geometries([pcd])
注意: 不同激光雷达的强度值范围不一样。Velodyne 一般是 0-255,Ouster 可能是 0-65535。渲染前最好归一化到 0-1 之间,否则颜色会失真。

3.3.3 高度渲染

高度渲染是最直观的。把点的高度(Z 值)映射成颜色,低处用蓝色,高处用红色。这样一眼就能看出地形起伏。

我曾经用高度渲染做过一个项目,需要快速识别道路上的障碍物。高度渲染一开,路面上凸起的物体(比如路肩、石块)立刻变成红色,非常显眼。

Open3D 实现高度渲染:

# 高度渲染
points = np.asarray(pcd.points)
z = points[:, 2]
z_min, z_max = z.min(), z.max()
colors = plt.cm.viridis((z - z_min) / (z_max - z_min))[:, :3]
pcd.colors = o3d.utility.Vector3dVector(colors)
o3d.visualization.draw_geometries([pcd])

这里用了 viridis 色图,它是感知均匀的,不会误导视觉判断。我个人推荐用 viridis 或 plasma,比 jet 更科学。

3.4 交互式可视化:旋转、缩放、选择

可视化不只是“看”,还要“交互”。

旋转和缩放是基本功。PCL 和 Open3D 都支持鼠标拖拽旋转、滚轮缩放。但有个细节要注意:

  • PCL: 左键旋转,中键缩放,右键平移。按住 Shift 可以切换旋转中心。
  • Open3D: 左键旋转,滚轮缩放,Ctrl+左键平移。按 R 键重置视角。

选择功能更高级。比如你想选中点云中的某个区域,提取出来做分析。Open3D 提供了矩形选择工具:

# 交互式选择
vis = o3d.visualization.VisualizerWithEditing()
vis.create_window()
vis.add_geometry(pcd)
vis.run()  # 用户交互选择
picked_points = vis.get_picked_points()  # 获取选中的点索引

运行这段代码后,按住 Ctrl 键,用鼠标框选点云区域,选中的点会变红。关闭窗口后,picked_points 就是选中点的索引列表。

避坑指南: 我曾经在项目里用选择功能提取路面点,结果发现选中的点数量不对。后来排查发现,Open3D 的选择是基于屏幕坐标的,如果点云太密,很多点会被遮挡。解决办法是先降采样,再选择。

PCL 也有类似的选择功能,用 pcl::visualization::PointPickingEvent 实现。不过接口比 Open3D 复杂一些,适合有 C++ 基础的同学。

3.5 小结

可视化是点云处理的“眼睛”。没有它,你就是在盲调参数。

我个人建议:

  • 快速预览用 pcl_viewer 命令行
  • 算法调试用 Open3D 的 draw_geometries
  • 信息展示用高度渲染或强度渲染
  • 数据提取用交互式选择工具

下一节,我们聊聊点云滤波。你会发现,可视化在滤波调试中同样不可或缺。


公众号:蓝海资料掘金营,微信deep3321