文章目录
- 简介和函数说明
- [PCL 点云可视化核心原理](#PCL 点云可视化核心原理)
- PCL可视化核心知识点
- [PCL 可视化完整代码示例](#PCL 可视化完整代码示例)
-
- [基础点云可视化(CloudViewer 快速预览)](#基础点云可视化(CloudViewer 快速预览))
- [高级可视化(PCLVisualizer 多窗口、标注、法向量、交互)](#高级可视化(PCLVisualizer 多窗口、标注、法向量、交互))
简介和函数说明
PCL(Point Cloud Library)的可视化模块主要提供两个核心类:CloudViewer (简单快速)和 PCLVisualizer (功能强大)。此外,还提供了直方图可视化、深度图像可视化以及一个命令行工具 pcl_viewer。
下表整理了所有主要可视化函数和方法的详细说明:
| 类/模块 | 函数/方法 | 描述 | 关键参数/特性 |
|---|---|---|---|
| CloudViewer | CloudViewer (string window_name) |
构造函数,创建一个简单的可视化窗口。 | window_name:窗口名称 |
showCloud (cloud) |
在窗口中显示点云。该函数是同步的,执行后会等待渲染完成。 | cloud:要显示的点云指针 |
|
runOnVisualizationThread (callback) |
注册一个回调函数,该函数在每帧渲染时都会被调用,用于执行自定义绘制或更新。 | callback:回调函数,接受PCLVisualizer引用 |
|
runOnVisualizationThreadOnce (callback) |
注册一个回调函数,该函数在可视化线程中仅被执行一次,常用于初始化设置。 | callback:一次性回调函数 |
|
wasStopped () |
检查窗口是否已经被用户关闭。 | 返回 bool 值 |
|
| PCLVisualizer | PCLVisualizer::Ptr (string window_name) |
构造函数,创建一个功能完备的可视化对象(通常使用智能指针)。 | window_name:窗口名称 |
addPointCloud (cloud, ...) |
将点云添加到视窗中。可以配合颜色处理器(PointCloudColorHandler)实现不同着色。 |
cloud, color_handler, id |
|
setBackgroundColor (r, g, b) |
设置视窗的背景颜色。 | r, g, b:0-1或0-255的颜色值 |
|
addCoordinateSystem (scale) |
在视窗中添加一个表示坐标轴的3D坐标系。 | scale:坐标轴的大小 |
|
initCameraParameters () |
初始化相机参数,以便更好地适应场景。 | ||
addSphere (center, radius, ...) |
在指定位置添加一个球体。 | center, radius, id |
|
addLine (point1, point2, ...) |
在两点之间添加一条线段。 | point1, point2, id |
|
addPlane (coefficients, id) |
根据模型系数添加一个平面。 | coefficients, id |
|
addCone (coefficients, id) |
根据模型系数添加一个圆锥体。 | coefficients, id |
|
addPointCloudNormals (cloud, normals, ...) |
同时显示点云及其法线。 | cloud, normals, level, scale |
|
setPointCloudRenderingProperties (...) |
设置点云的渲染属性,如点的大小、透明度等。 | property, value, id |
|
createViewPort (xmin, ymin, xmax, ymax, viewport_id) |
在窗口中创建多个独立的视口,用于显示不同数据。 | 归一化坐标 [0,1], viewport_id |
|
spin () / spinOnce () |
spin()进入事件循环直到窗口关闭;spinOnce()处理一次事件后返回。 |
||
| PCLHistogramVisualizer | addHistogramData (data, bins, ...) |
添加并显示一维数据的直方图。 | data, bins |
view () |
显示直方图窗口。 | ||
| RangeImageVisualizer | showRangeImage (range_image) |
显示深度图像(Range Image)。 | range_image:输入的深度图像 |
| 命令行工具 | pcl_viewer <file_name.pcd> [options] |
一个独立的命令行程序,用于快速查看PCD文件。 | -bc r,g,b背景色, -ps X点大小等 |
说明 :
CloudViewer是PCLVisualizer的一个轻量级封装,使用更简单但不能用于多线程。如果你的需求超过简单的显示(如绘制法线、形状或多视口),推荐直接使用功能更强大的PCLVisualizer。所有高级渲染功能都依赖于VTK(Visualization Toolkit)库。
PCL 点云可视化核心原理
PCL 可视化的核心是基于 VTK(Visualization Toolkit)图形库实现的三维渲染与交互,本质是将点云的三维坐标、颜色、法向量等数据,通过 VTK 的渲染管线转化为屏幕上的可视化图像,同时支持鼠标 / 键盘交互操作。
核心渲染流程
数据封装
将 PCL 点云(PointCloud)转换为 VTK 可识别的多边形数据(vtkPolyData),包含点坐标、颜色、法向量等属性。
渲染管道
VTK 通过 "数据源→映射器(Mapper)→演员(Actor)→渲染器(Renderer)→渲染窗口(RenderWindow)→交互器(Interactor)" 的管线,完成数据到图像的转化。
- 映射器(Mapper):负责将点云数据转换为图形基元(点、线、面)。
- 演员(Actor):承载图形基元,控制显示属性(颜色、大小、透明度)。
- 渲染器(Renderer):管理场景中的所有演员,设置背景、视角。
- 渲染窗口(RenderWindow):承载渲染器,作为显示载体。
- 交互器(Interactor):处理鼠标、键盘事件,实现旋转、平移、缩放等交互。
交互渲染
启动交互循环,实时响应用户操作,动态更新渲染画面
PCL可视化核心知识点
核心可视化类
PCL 对 VTK 进行了封装,提供了更简洁的可视化接口,核心类如下:
- pcl::visualization::PCLVisualizer
最通用的可视化类,支持单 / 多窗口、点云 / 形状 / 文本渲染、自定义交互 - pcl::visualization::CloudViewer
简化版可视化类,仅支持单窗口点云显示,接口极简 - pcl::visualization::RangeImageVisualizer
专门用于深度图 /range image 可视化
点云显示配置知识点
点云显示属性
- 点大小通过setPointCloudRenderingProperties设置,控制点的渲染尺寸(值越大点越粗)。
- 点颜色:支持单色渲染、按坐标 / 强度 / 标签着色、RGB 彩色点云直接显示。
- 设置点云的透明程度,用于多层点云叠加显示。
- 渲染模式:支持点渲染(默认)、线框渲染、面渲染(适用于有序点云 / 网格)。
窗体与视图配置
- 单窗口 / 多窗口:createViewPort创建子视图,实现多窗口并排显示(对比不同点云)。
- 背景色:setBackgroundColor设置窗口背景(默认黑色,可改为白色 / 灰色)
- 窗口标题 / 大小:初始化时设置窗口名称,setSize控制窗口像素尺寸。
- 相机视角:setCameraPosition设置初始观察视角,resetCamera重置视角。
文字标注
- 文本标注:addText添加静态文本,addText3D添加 3D 空间文本(随视角移动)。
- 几何形状:addSphere/addCube/addLine/addArrow添加球体、立方体、线段、箭头,用于标注关键点、法向量、边界框。
- 坐标系统:addCoordinateSystem添加世界坐标系(红 X、绿 Y、蓝 Z),辅助空间定位。
交互操作知识点
默认交互快捷键
- 旋转视图 鼠标左键拖动
- 平移视图 鼠标右键拖动
- 缩放视图 鼠标滚轮滚动
- 重置视角 按R键
- 显示 / 隐藏坐标轴 按C键
- 截图保存 按P键
- 退出可视化 按Q键
自定义交互事件
通过registerKeyboardCallback/registerPointPickingCallback注册回调函数,实现自定义键盘事件、点拾取事件(如点击点云输出坐标、选中点高亮)
特殊点云可视化
- 彩色点云:直接加载PointXYZRGB类型点云,无需额外配置即可显示 RGB 颜色。
- 法向量可视化:addPointCloudNormals渲染点云法向量(箭头表示方向,可设置箭头长度 / 密度)。
- 关键点可视化:通过setPointCloudRenderingProperties将关键点设为不同颜色 / 大小,突出显示。
- 网格 / 曲面可视化:支持PolygonMesh类型数据,渲染点云重构后的曲面。
可视化优化知识点
- 多线程渲染:CloudViewer默认多线程,PCLVisualizer需手动处理(避免主线程阻塞)。
- 点云更新:updatePointCloud动态更新点云数据(无需重新创建可视化对象,适用于实时点云)。
- 性能优化:大规模点云可先降采样(VoxelGrid)再可视化,减少渲染压力;关闭不必要的标注 / 形状,提升帧率。
PCL 可视化完整代码示例
基础点云可视化(CloudViewer 快速预览)
cpp
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/cloud_viewer.h>
#include <iostream>
// 点云类型定义(XYZ+RGB彩色点云)
using PointT = pcl::PointXYZRGB;
int main() {
// 1. 加载点云(也可模拟生成随机点云)
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
// 方式1:加载本地PCD文件
if (pcl::io::loadPCDFile<PointT>("cloud.pcd", *cloud) == -1) {
// 若加载失败,模拟生成1000个彩色随机点
cloud->width = 1000;
cloud->height = 1;
cloud->points.resize(cloud->width * cloud->height);
for (auto& point : cloud->points) {
point.x = rand() % 100;
point.y = rand() % 100;
point.z = rand() % 100;
point.r = rand() % 256; // 随机红色通道
point.g = rand() % 256; // 随机绿色通道
point.b = rand() % 256; // 随机蓝色通道
}
std::cout << "未找到cloud.pcd,已生成随机彩色点云" << std::endl;
}
// 2. 创建CloudViewer可视化对象
pcl::visualization::CloudViewer viewer("基础点云可视化");
// 3. 显示点云
viewer.showCloud(cloud);
// 4. 等待窗口关闭(阻塞主线程)
while (!viewer.wasStopped()) {
// 可在此处添加实时更新逻辑
}
return 0;
}
高级可视化(PCLVisualizer 多窗口、标注、法向量、交互)
cpp
#include <pcl/point_types.h>
#include <pcl/io/pcd_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/features/normal_3d.h>
#include <pcl/filters/voxel_grid.h>
#include <iostream>
#include <thread>
using PointT = pcl::PointXYZRGB;
using NormalT = pcl::Normal;
// 自定义键盘交互回调函数
void keyboardEventOccurred(const pcl::visualization::KeyboardEvent& event, void* nothing) {
if (event.getKeySym() == "h" && event.keyDown()) {
std::cout << "===== 帮助信息 =====" << std::endl;
std::cout << "鼠标左键:旋转视图" << std::endl;
std::cout << "鼠标右键:平移视图" << std::endl;
std::cout << "滚轮:缩放视图" << std::endl;
std::cout << "R键:重置视角" << std::endl;
std::cout << "P键:截图保存" << std::endl;
std::cout << "Q键:退出可视化" << std::endl;
std::cout << "H键:显示帮助" << std::endl;
}
}
// 自定义点拾取回调函数
void pointPickingEventOccurred(const pcl::visualization::PointPickingEvent& event, void* viewer_void) {
if (event.getPointIndex() == -1) return;
float x, y, z;
event.getPoint(x, y, z);
std::cout << "选中点坐标:(" << x << ", " << y << ", " << z << ")" << std::endl;
}
int main() {
// 1. 加载并预处理点云(降采样+法向量估计)
pcl::PointCloud<PointT>::Ptr cloud(new pcl::PointCloud<PointT>);
pcl::PointCloud<PointT>::Ptr cloud_filtered(new pcl::PointCloud<PointT>);
pcl::PointCloud<NormalT>::Ptr cloud_normals(new pcl::PointCloud<NormalT>);
// 加载点云(失败则生成随机点)
if (pcl::io::loadPCDFile<PointT>("cloud.pcd", *cloud) == -1) {
cloud->width = 2000;
cloud->height = 1;
cloud->points.resize(cloud->width * cloud->height);
for (auto& point : cloud->points) {
point.x = rand() % 100;
point.y = rand() % 100;
point.z = rand() % 100;
point.r = rand() % 256;
point.g = rand() % 256;
point.b = rand() % 256;
}
}
// 降采样(优化可视化性能)
pcl::VoxelGrid<PointT> vg;
vg.setInputCloud(cloud);
vg.setLeafSize(1.0f, 1.0f, 1.0f); // 体素大小1m
vg.filter(*cloud_filtered);
std::cout << "原始点云数量:" << cloud->size() << ",降采样后:" << cloud_filtered->size() << std::endl;
// 估计法向量(用于法向量可视化)
pcl::NormalEstimation<PointT, NormalT> ne;
ne.setInputCloud(cloud_filtered);
pcl::search::KdTree<PointT>::Ptr tree(new pcl::search::KdTree<PointT>);
ne.setSearchMethod(tree);
ne.setRadiusSearch(3.0f); // 邻域半径3m
ne.compute(*cloud_normals);
// 2. 创建PCLVisualizer对象
pcl::visualization::PCLVisualizer::Ptr viewer(new pcl::visualization::PCLVisualizer("高级点云可视化"));
viewer->setBackgroundColor(0.0, 0.0, 0.0); // 黑色背景
viewer->setWindowName("PCL高级可视化示例");
viewer->setSize(1200, 600); // 窗口尺寸1200x600
// 3. 创建双窗口(左:原始点云+标注,右:降采样点云+法向量)
int v1(0), v2(0);
viewer->createViewPort(0.0, 0.0, 0.5, 1.0, v1); // 左窗口(x0,y0,x1,y1)
viewer->createViewPort(0.5, 0.0, 1.0, 1.0, v2); // 右窗口
viewer->setViewPortName("原始点云+标注", v1);
viewer->setViewPortName("降采样点云+法向量", v2);
// 4. 左窗口:显示原始点云+文本+坐标系+线段
viewer->addPointCloud(cloud, "original_cloud", v1);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "original_cloud"); // 点大小2
viewer->addText("原始点云(1000个点)", 10, 10, 16, 1.0, 1.0, 1.0, "original_text", v1); // 白色文本
viewer->addCoordinateSystem(50.0, "original_coord", v1); // 坐标系(尺寸50)
viewer->addLine(cloud->points[0], cloud->points[1], 1.0, 0.0, 0.0, "line1", v1); // 红色线段(连接前两个点)
// 5. 右窗口:显示降采样点云+法向量+球体标注
viewer->addPointCloud(cloud_filtered, "filtered_cloud", v2);
viewer->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 3, "filtered_cloud"); // 点大小3
viewer->addPointCloudNormals<PointT, NormalT>(cloud_filtered, cloud_normals, 10, 2.0f, "normals", v2); // 法向量(每10个点显示1个,箭头长2)
viewer->addSphere(cloud_filtered->points[0], 5.0f, 0.0, 1.0, 0.0, "sphere1", v2); // 绿色球体(标注第一个点)
viewer->addText("降采样点云+法向量", 10, 10, 16, 0.0, 1.0, 0.0, "filtered_text", v2); // 绿色文本
// 6. 注册自定义交互事件
viewer->registerKeyboardCallback(keyboardEventOccurred, (void*)nullptr); // 键盘事件
viewer->registerPointPickingCallback(pointPickingEventOccurred, (void*)viewer.get()); // 点拾取事件
// 7. 启动可视化循环(非阻塞,用spinOnce+sleep)
while (!viewer->wasStopped()) {
viewer->spinOnce(100); // 每100ms刷新一次
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
return 0;
}