3、点云数据读取与可视化:使用PCL读取PCD文件、使用Open3D可视化点云、调整视角与颜色映射

点云数据拿到手,第一件事是什么?

当然是看看它长什么样。

我刚开始做激光雷达感知那会儿,经常对着几十兆的PCD文件发愣——数据对不对?有没有噪点?目标在哪儿?这些光靠看数字是看不出来的。说白了,可视化就是点云处理的「眼睛」。今天咱们就把这双眼睛擦亮。

3.1 使用PCL读取PCD文件

PCL(Point Cloud Library)是点云处理的老牌库。我个人习惯用C++做底层处理时首选它。PCD文件格式是PCL自家的标准格式,结构清晰,支持多种字段类型。

先看一个最基础的读取示例:

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

// 定义点云对象
pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

// 读取PCD文件
if (pcl::io::loadPCDFile<pcl::PointXYZ>("scene.pcd", *cloud) == -1) {
    PCL_ERROR("Couldn't read file scene.pcd \n");
    return -1;
}

std::cout << "Loaded " << cloud->width * cloud->height << " data points." << std::endl;

嗯,这里要注意:loadPCDFile返回-1表示读取失败。我曾经在项目里忘了检查返回值,结果拿着空指针往下跑,调试了半天才发现是文件路径写错了。

避坑指南: 我曾经因为PCD文件版本不兼容(PCD v0.6 vs v0.7)导致读取失败。建议统一使用PCL 1.8以上版本,兼容性更好。

如果点云带有颜色信息,用pcl::PointXYZRGB类型:

pcl::PointCloud<pcl::PointXYZRGB>::Ptr colored_cloud(new pcl::PointCloud<pcl::PointXYZRGB>);
pcl::io::loadPCDFile("colored_scene.pcd", *colored_cloud);

读取后怎么检查数据质量?我一般会看三个指标:

  • 点云数量:太少可能是裁剪过度,太多要考虑性能
  • 坐标范围:用getMinMax3D()检查,防止离群点
  • NaN值:用isFinite()过滤,不然可视化时会出黑点

3.2 使用Open3D可视化点云

Open3D是我做可视化时的首选。为什么?因为它Python接口简洁,交互流畅,而且开箱即用。你想想看,调试算法时频繁改参数,用Python比C++快太多了。

安装很简单:

pip install open3d

读取并显示点云:

import open3d as o3d

# 读取PCD文件
pcd = o3d.io.read_point_cloud("scene.pcd")

# 可视化
o3d.visualization.draw_geometries([pcd])

就这么三行代码,点云就出来了。我第一次用的时候也觉得太方便了。

小技巧: Open3D支持多种格式:PCD、PLY、XYZ、TXT等。我常用PLY格式保存带颜色的点云,因为文件体积比PCD小。

如果想在可视化窗口里交互操作:

  • 左键拖拽:旋转视角
  • 滚轮:缩放
  • Ctrl + 左键:平移
  • Shift + 左键:框选点云

这些操作在调试时特别有用。我记得有一次排查地面分割效果,就是靠框选功能快速定位了误分割的区域。

3.3 调整视角与颜色映射

默认视角往往不是最佳视角。比如看道路场景,俯视图比侧视图更有用。Open3D提供了灵活的视角控制。

3.3.1 设置视角

import open3d as o3d
import numpy as np

pcd = o3d.io.read_point_cloud("scene.pcd")

# 创建可视化窗口
vis = o3d.visualization.Visualizer()
vis.create_window()
vis.add_geometry(pcd)

# 获取视角控制
ctrl = vis.get_view_control()

# 设置相机参数(前视图)
ctrl.set_front([0, 0, -1])   # 相机朝向
ctrl.set_up([0, -1, 0])      # 上方向
ctrl.set_lookat([0, 0, 0])   # 目标点
ctrl.set_zoom(0.8)           # 缩放

vis.run()
vis.destroy_window()

参数怎么调?我一般先手动旋转到合适视角,然后打印当前参数:

# 在可视化窗口中按 'P' 键,会打印当前相机参数到控制台

这个技巧帮我省了不少时间。你想想看,手动调好视角,记下参数,下次直接复用,多省事。

3.3.2 颜色映射

点云默认是灰色的,但实际应用中我们需要用颜色表达信息。常见的映射方式有:

映射方式 适用场景 代码示例
高度映射 区分地面与障碍物 pcd.colors = o3d.utility.Vector3dVector(colors)
强度映射 识别反光物体 根据intensity字段着色
类别映射 语义分割结果展示 不同类别不同颜色
距离映射 判断远近关系 根据到传感器的距离着色

高度映射的完整实现:

import numpy as np
import open3d as o3d

pcd = o3d.io.read_point_cloud("scene.pcd")
points = np.asarray(pcd.points)

# 获取Z轴高度
z = points[:, 2]
z_min, z_max = z.min(), z.max()

# 归一化到[0,1]并映射到颜色
norm_z = (z - z_min) / (z_max - z_min)

# 使用jet颜色映射
colors = np.zeros((points.shape[0], 3))
colors[:, 0] = norm_z          # 红色通道
colors[:, 1] = 1 - norm_z      # 绿色通道
colors[:, 2] = 0.5 * (1 - norm_z)  # 蓝色通道

pcd.colors = o3d.utility.Vector3dVector(colors)
o3d.visualization.draw_geometries([pcd])
核心要点: 颜色映射的关键是找到合适的「特征值」并归一化到[0,1]区间。我常用的归一化方法是减去最小值再除以范围,但要注意处理异常值。

强度映射稍微复杂一点,因为激光雷达的强度值范围不统一。Velodyne一般是0-255,Ouster是0-65535。我曾经被这个坑过——用Velodyne的代码处理Ouster数据,颜色全黑了。

# 假设pcd有intensity字段
intensity = np.asarray(pcd.intensity)
# 自适应归一化(去掉极端值)
percentile_99 = np.percentile(intensity, 99)
intensity_clipped = np.clip(intensity, 0, percentile_99)
norm_intensity = intensity_clipped / percentile_99

# 灰度映射
colors = np.stack([norm_intensity] * 3, axis=1)
pcd.colors = o3d.utility.Vector3dVector(colors)
注意: 强度值受距离、入射角、目标材质等多因素影响。直接用原始值做颜色映射效果往往不好。我建议先做距离补偿,再归一化。

3.4 实战:多视角可视化

在实际项目中,我经常需要同时看多个视角。比如看俯视图判断道路边界,看侧视图检测行人。Open3D支持多窗口:

# 创建两个窗口
vis1 = o3d.visualization.Visualizer()
vis2 = o3d.visualization.Visualizer()

vis1.create_window(window_name="俯视图", width=640, height=480)
vis2.create_window(window_name="侧视图", width=640, height=480)

vis1.add_geometry(pcd)
vis2.add_geometry(pcd)

# 设置俯视图视角
ctrl1 = vis1.get_view_control()
ctrl1.set_front([0, 0, -1])
ctrl1.set_up([0, -1, 0])

# 设置侧视图视角
ctrl2 = vis2.get_view_control()
ctrl2.set_front([1, 0, 0])
ctrl2.set_up([0, -1, 0])

vis1.run()
vis2.run()
vis1.destroy_window()
vis2.destroy_window()

这个技巧在调试多传感器融合时特别有用。我经常左边放激光雷达点云,右边放相机图像,对照着看,问题一目了然。

3.5 总结

今天的内容就这些。说白了,点云读取和可视化是基本功,但千万别小看它。我见过太多人花大量时间在算法上,结果可视化做得一塌糊涂,调试效率极低。

记住三个要点:

  • PCL读取:注意文件路径和版本兼容性
  • Open3D可视化:交互操作要熟练,快捷键记牢
  • 颜色映射:选对特征值,做好归一化

下一章咱们聊聊点云滤波——怎么把噪点去掉,把地面分离出来。到时候见。