十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Windows点云可视化软件搭建:Qt+PCL+VS2015组合实战详解

Windows点云可视化软件搭建:Qt+PCL+VS2015组合实战详解 简介QT5.9.6与PCL1.8.1在VS2015环境下的整合示例面向需要在图形界面中处理与显示3D点云数据的开发者。该Demo展示了如何在Qt窗口中集成PCL点云库完成点云读入、预处理、特征提取与可视化等典型流程并涉及多线程与UI交互设计适合正在搭建点云处理工具链或学习QtPCL项目配置的初中级C工程师。压缩包共27个文件包含cpp/h源文件、obj编译中间文件、qrc资源脚本、ui界面定义、pro工程文件及可执行exe等整体约233KB可直接运行并对照工程结构学习模块划分。已有2356人学习下载。通过分析工程配置与核心代码可掌握Pro文件链接PCL依赖、信号槽驱动点云处理、PCLVisualizer嵌入Qt界面等实操经验有助于独立完成同类三维可视化工具的原型搭建。 人在项目现场拿着一个还没跑通的 demo 去给客户演示结果双击 exe 直接闪退点云窗口弹不出来。这种尴尬经历过一次就够了。后来我固定用 QT5.9.6 PCL1.8.1 vs2015 这一套组合来做 Windows 下的点云可视化软件基本告别了环境装三小时、编译错一大篇的折腾。这篇就把完整的软件 demo 搭法掰开揉碎写出来包括为什么锁死这三个版本、每一步配置的底层逻辑、代码结构怎么组织、以及我踩过之后不得不写下来的坑。适合刚入坑 PCL 开发、或者被 Qt 和 PCL 版本兼容性折磨过的朋友参考。先说清楚这套方案能做什么一个基于 Qt 窗口的点云显示软件支持读取 PCD 文件、在窗口里用鼠标旋转缩放、切换点云颜色和大小并且为后续接入滤波、配准、分割等 PCL 算法预留了清晰的结构。整个 demo 从空工程到跑通按文章顺序操作一两个小时就能搞定。1. 为什么是这三个版本兼容性搭配的底层逻辑很多新手第一次装 PCL习惯性去官网下最新版然后再装一个最新的 Qt结果 CMake 配置阶段就一片红。这里面的核心问题不是哪个版本新而是编译器的 ABI 兼容性。1.1 PCL 1.8.1 和 VS2015 的绑定关系PCL 官方发布的 Windows 预编译包是绑定特定 MSVC 工具集编译出来的。PCL 1.8.1 的 All-in-One 安装包明确对应 msvc2015也就是 Visual Studio 2015 的 MSVC 14.0 工具集。这套预编译包里的所有 .lib 和 .dll都是用 VS2015 编译的其 C 标准库、运行时、类和函数的符号命名规则都基于 MSVC 14.0。如果你用 VS2017 或更高版本去链接这套库会发生什么VS2017 默认使用 v141 工具集虽然 MSVC 声称部分版本向后兼容但 PCL 1.8.1 依赖的 Boost、VTK、Eigen 等一堆库全部是旧工具集编译的牵一发动全身。实际操作中最常见的错误就是LNK2038 运行时库不匹配以及一堆莫名其妙的重定义符号错误。与其和编译器的兼容性作斗争不如直接锁死 VS2015用官方编译好的库省下的时间足够写好整个 demo 的功能代码。1.2 QT 5.9.6 为什么是 LTS 版本里的安全牌Qt 版本和 MSVC 工具集的对应关系也很严格。Qt 官方安装包会区分 msvc2013、msvc2015、msvc2017、msvc2019 等版本。QT 5.9.6 是 Qt 5.9 LTS 系列里的一个重要补丁版本官方提供了msvc2015 64 位预编译包这正好和 PCL 1.8.1 的编译器匹配。有人会问Qt 6 都出来了为什么不选新的Qt 6 彻底移除了很多老接口PCL 1.8.1 里捆绑的 VTK 7.0.0 也无法适配 Qt 6 的渲染管线。而 Qt 5.15 虽然也是 LTS但它没有提供面向 VS2015 的预编译包只有 msvc2017 和 msvc2019 版本。所以用 QT5.9.6 PCL1.8.1 这套组合本质上是让Qt 的编译工具集 PCL 的编译工具集 VS2015一条链路完全对齐后续 CMake 配置才会顺利。提示如果你用 Qt 5.15 VS2019 去编译 PCL 1.13也可以跑但你必须自己从源码编译整套 PCL过程很痛苦尤其是 VTK 和 Boost 的编译时长足以劝退大多数人。1.3 64 位的选择一条不能走错的路PCL 官方 All-in-One 包只有64 位版本这就决定了你的整个工程必须是 x64 平台。Qt 也要选择msvc2015_64目录下的版本而不是msvc201532 位。这个细节非常重要一旦 Qt 装了 32 位、PCL 是 64 位CMake 阶段不会立刻报错但链接阶段会疯狂报无法解析的外部符号而且这类错误非常难排查看起来像是代码写错了实际是位数不匹配。这里还藏着一个大坑VS2015 对应的 CMake 生成器名称是 Visual Studio 14 2015 Win64。VS 的内部版本号和对外名称经常不一致VS2015 对外叫 Visual Studio 2015内部版本是 14.0CMake 里反而用内部版本号。无数人在这里填错成 VS2017 或 VS2019导致整个工程平台工具集错乱。下面整理一下这套环境的核心版本对应关系方便对照组件版本说明Visual Studio2015内部版本 14.0项目平台工具集CMake 生成器填 Visual Studio 14 2015 Win64PCL1.8.1 All-in-One预编译 64 位捆绑 VTK 7.0.0Qt5.9.664 位 msvc2015_64LTS 版本CMake3.10 以上需要完整支持 Qt5 和 PCL 的 find_package 逻辑三方库Boost 1.61 / Eigen 3.2.9 / FLANN / Qhull随 PCL All-in-One 自动安装到 3rdParty 目录2. 环境搭建全流程从下载到 CMake 顺利生成2.1 下载前的准备清单别急着安装先把所有安装包下齐避免装到一半发现缺东西。需要准备PCL 1.8.1 All-in-One从 PCL 官方 GitHub Release 页面找PCL-1.8.1-AllInOne-msvc2015-win64.exe。这个安装包会同时装好 PCL 本体和所有第三方依赖库包括 OpenNI2、VTK、Boost、Eigen 等。QT 5.9.6从 Qt 官方 Archive 下载qt-opensource-windows-x86-msvc2015_64-5.9.6.exe。注意下载时选对 msvc2015_64 版本不要下成 MinGW 版本MinGW 和 MSVC 完全不兼容。Visual Studio 2015如果没有正版授权用 Community 社区版即可。安装时务必勾选C 桌面开发工作负载这个组件里包含了 Windows SDK 和 MSVC 编译器。CMake下载 Windows 64 位安装包建议 3.20 以上的版本对 Qt5 的 find_package 支持更完善。2.2 安装顺序与路径规范安装顺序有讲究我的推荐顺序是先装 VS2015再装 QT再装 PCL最后装 CMake。先装 VS 是因为 Qt 和 PCL 都会在安装阶段探测系统中已有的编译环境后装 Qt 和 PCL 则是因为这两者都带有自己的 Qt 插件或批处理脚本需要读取 VS 的安装路径来注册环境。安装路径千万不要有中文和空格C:\Qt\Qt5.9.6和C:\Program Files\PCL 1.8.1这种带空格的路径在 CMake 阶段容易出问题我建议都装到纯英文无空格的路径比如D:\Qt\Qt5.9.6、D:\PCL\PCL1.8.1。安装完 PCL 后需要手动检查环境变量。自动安装脚本通常会在系统环境变量里添加PCL_ROOT和OPENNI2_INCLUDE但我在多台机器上装过有几台并没有自动设置。需要添加或确认以下变量PCL_ROOT D:\PCL\PCL1.8.1 OPENNI2_INCLUDE D:\PCL\PCL1.8.1\3rdParty\OpenNI2\Include OPENNI2_REDIST D:\PCL\PCL1.8.1\3rdParty\OpenNI2\Redist同时在 PATH 变量里添加这几个目录后续运行时需要找到 DLLD:\PCL\PCL1.8.1\bin D:\PCL\PCL1.8.1\3rdParty\VTK\bin D:\PCL\PCL1.8.1\3rdParty\Boost\lib D:\PCL\PCL1.8.1\3rdParty\OpenNI2\Redist D:\Qt\Qt5.9.6\5.9.6\msvc2015_64\bin配置完环境变量后建议重启系统或至少在命令行里执行echo %PCL_ROOT%确认路径生效。2.3 CMakeLists.txt 的核心配置逻辑这套环境的骨头在 CMakeLists.txt 里配置正确了后续生成 VS 工程几乎不会出问题。给出一个我用过多次、稳定可用的最小配置cmake_minimum_required(VERSION 3.10) project(PCLQtDemo) set(CMAKE_CXX_STANDARD 14) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(CMAKE_PREFIX_PATH D:/Qt/Qt5.9.6/5.9.6/msvc2015_64 D:/PCL/PCL1.8.1 D:/PCL/PCL1.8.1/3rdParty/VTK ) find_package(Qt5 COMPONENTS Widgets REQUIRED) find_package(PCL 1.8 REQUIRED COMPONENTS common io filters visualization) add_executable(PCLQtDemo main.cpp MainWindow.cpp MainWindow.h ) target_include_directories(PCLQtDemo PRIVATE ${PCL_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ) target_link_libraries(PCLQtDemo PRIVATE ${PCL_LIBRARIES} Qt5::Widgets ) add_definitions(${PCL_DEFINITIONS})几个关键点单独说一下第一行CMAKE_AUTOMOC必须打开因为 Qt 的信号槽机制需要 moc 预处理头文件中的Q_OBJECT宏。这个开关是 Qt 工程和普通 C 工程最本质的区别之一忘记打开会导致链接阶段报很多槽函数的 undefined reference。CMAKE_PREFIX_PATH的作用是告诉 CMake 去哪里找 Qt 和 PCL 的配置文件。指定到 Qt 的msvc2015_64目录和 PCL 的根目录后find_package内部会去这些路径下寻找Qt5WidgetsConfig.cmake和PCLConfig.cmake。如果你在这一步反复报 Could not find a package configuration file九成是 CMAKE_PREFIX_PATH 填错或路径本身不存在。PCL 1.8 REQUIRED之后用COMPONENTS声明需要哪些模块这里只声明了common io filters visualization四个对应点数处理、文件读写、滤波算法和可视化。不要不加选择地把所有组件全列上工程编译时间和链接体积会成倍增加。2.4 用 CMake GUI 生成 VS 工程打开 CMake GUI第一行源码目录指向代码所在文件夹第二行 build 目录单独建一个build文件夹不要和源码混在一起。点 Configure此时弹出生成器选择窗口这里一定要选Visual Studio 14 2015 Win64。如果你机器上装的是 VS2017这个选项列表里可能会有 Visual Studio 15 2017 Win64一定要往下滚找到 2015 那一项或者手动确认 VS2015 安装无误。平台下拉框选 x64然后点击 Finish。配置完成后CMake 会生成一大堆变量。此时检查几个关键变量是否已经正确填入PCL_DIR指向D:/PCL/PCL1.8.1/cmakeQt5_DIR指向D:/Qt/Qt5.9.6/5.9.6/msvc2015_64/lib/cmake/Qt5CMAKE_PREFIX_PATH包含了 Qt 和 PCL 的路径确认无误后点 Generate生成完成后打开.sln解决方案文件把解决方案配置改成Release或Debug建议先 Release 跑通因为 PCL 预编译包在 Release 模式下稳定性更好。在解决方案资源管理器里右键项目选择生成。第一次编译需要一点时间但顺利的话不会超过两分钟因为这批库都是预编译的编译的只是你的 demo 源码。3. Demo 设计与代码实现做一个能转能点的点云查看器3.1 功能边界小而完整比大而全重要一个合格的软件 demo不在于功能多而在于它演示了完整的技术链路界面框架、点云数据接入、渲染显示、用户交互。这套链路打通后任何 PCL 算法都能往里面塞。所以 demo 我设计了三个功能读取本地 PCD 点云文件并显示在窗口中用鼠标拖拽旋转点云、滚轮缩放切换点云的显示颜色和点大小不需要做文件保存、不需要做算法处理那都属于后续扩展。先把链路跑通。3.2 工程目录结构与main.cpp的入口写法工程文件结构很简单PCLQtDemo/ ├── CMakeLists.txt ├── main.cpp ├── MainWindow.h ├── MainWindow.cppmain.cpp是整个程序的入口负责创建 QApplication 并显示主窗口#include QApplication #include MainWindow.h int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow w; w.show(); return app.exec(); }QApplication 是所有 Qt GUI 程序的地基它负责管理事件循环、窗口系统和全局资源。点云可视化里的鼠标交互、定时刷新全部依赖事件循环驱动app.exec()一进入程序就开始监听鼠标键盘事件。3.3 MainWindow 的布局把 QVTKWidget 嵌入 Qt 窗口PCL 1.8.1 捆绑的 VTK 7.0.0 提供了QVTKWidget这个 Qt 控件它本质上是一个封装了 VTK 渲染窗口的 QWidget可以像普通控件一样放入 Qt 的布局管理器。在MainWindow.h里声明界面相关的成员#pragma once #include QMainWindow #include QVTKWidget.h #include pcl/visualization/pcl_visualizer.h class QPushButton; class QSlider; class QComboBox; class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent nullptr); ~MainWindow() override; private slots: void onOpenPCD(); void onChangeColor(int index); void onChangePointSize(int value); private: void setupUI(); void loadPointCloud(const QString filePath); QVTKWidget *qvtkWidget; QPushButton *openButton; QSlider *sizeSlider; QComboBox *colorComboBox; pcl::visualization::PCLVisualizer::Ptr viewer; pcl::PointCloudpcl::PointXYZ::Ptr cloud; };Q_OBJECT宏是 Qt 信号槽机制的关键它声明了这个类拥有元对象信息。CMake 里的AUTOMOC会扫描头文件并自动生成 moc 代码这个过程不需要你手动写任何东西。MainWindow.cpp里最重要的函数是构造函数和setupUI()。构造函数中创建 PCLVisualizer 对象时有一个必须注意的细节——第二个参数要传falseMainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , cloud(new pcl::PointCloudpcl::PointXYZ) { setupUI(); viewer.reset(new pcl::visualization::PCLVisualizer(viewer, false)); qvtkWidget-SetRenderWindow(viewer-getRenderWindow()); viewer-setupInteractor(qvtkWidget-GetInteractor(), qvtkWidget-GetRenderWindow()); viewer-setBackgroundColor(0.1, 0.1, 0.1); }PCLVisualizer 的构造函数如果传true它会自己创建一个独立窗口完全不经过 Qt 的界面。我们必须传false然后通过SetRenderWindow把 VTK 的渲染窗口绑定到 QVTKWidget 内部这一步是Qt 窗口里显示点云的核心机制。setupInteractor则把鼠标交互事件绑定到 QVTKWidget 上这样窗口就能响应鼠标拖拽。3.4 加载点云的完整代码loadPointCloud函数负责从文件读取点云并在渲染器里显示void MainWindow::loadPointCloud(const QString filePath) { pcl::io::loadPCDFilepcl::PointXYZ(filePath.toStdString(), *cloud); viewer-removeAllPointClouds(); pcl::visualization::PointCloudColorHandlerCustompcl::PointXYZ color(cloud, 255, 128, 0); viewer-addPointCloudpcl::PointXYZ(cloud, color, cloud); viewer-setPointCloudRenderingProperties( pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, cloud); viewer-resetCamera(); qvtkWidget-update(); }这段代码做了四件事用 PCL 的loadPCDFile读文件用PointCloudColorHandlerCustom把点云颜色设成橙色RGB 值对应 255, 128, 0把点云添加到渲染器并取名 cloud这个名称是后续操作这个点云对象的唯一标识最后调用resetCamera让视角自动对准点云。setPointCloudRenderingProperties里的PCL_VISUALIZER_POINT_SIZE是点大小属性这个参数直接影响视觉体验。PCL 原生的默认值可能是 1在普通显示器上会显得非常细碎调到 2 或 3 会明显舒服。槽函数onOpenPCD负责弹出文件选择对话框并调用加载逻辑void MainWindow::onOpenPCD() { QString filePath QFileDialog::getOpenFileName( this, 打开点云, ., PCD 文件 (*.pcd)); if (!filePath.isEmpty()) { loadPointCloud(filePath); } }颜色切换和点大小滑块的逻辑本质上就是调用setPointCloudRenderingProperties修改参数不需要重新加载点云渲染器会实时更新。这里不把完整代码贴出来了核心就是上面这些。4. 我在这套环境里踩过的坑真实排查链与解决方案4.1 链接阶段 LNK2038第一个拦路虎第一次在这套环境里编译我遇到了LNK2038: mismatch detected for RuntimeLibrary: value MD_DynamicRelease doesnt match value MTd_StaticDebug。这一步说的是运行时库不一致原因是一个目标用了 Release 的动态库配置另一个目标却在用 Debug 的静态库配置。排查链路是这样的先确认解决方案配置里每个项目的编译模式是否统一。PCL 预编译库自带了 Release 版pcl_io.lib和 Debug 版pcl_io_debug.lib。如果解决方案配置选的是 Release但 Qt 的库路径或多线程 DLL 配置在 Debug 模式就会出现上述错误。解决办法很简单编译时把解决方案配置、CMake 的CMAKE_BUILD_TYPE、以及 Windows SDK 的运行时库设置全部统一成 Release 或全部统一成 Debug不要混用。CMAKE_BUILD_TYPE默认在 VS 工程里是不生效的因为它生成的是多配置工程真正的开关是 VS 工具栏上的配置选择框。这导致很多人忽略了编译模式和库配置之间的关系。我的经验是先用 Release 把整套流程跑通Debug 模式在需要断点调试时再切过去。4.2 无法解析的外部符号QT 库顺序与 VTK 模块另一个高频错误是LNK2019 unresolved external symbol看起来像是函数声明了却没定义。这类错误的原因往往不是你代码写错而是链接库没有加全。PCL 的 visualization 模块依赖 VTK 的许多库但 CMake 的${PCL_LIBRARIES}在某些版本配置中并不会自动附加 VTK 的库文件。如果你在生成阶段发现PCL_LIBRARIES里只有pcl_common.lib、pcl_io.lib、pcl_visualization.lib这些 PCL 自家的库没有 VTK 相关的vtkRenderingCore、vtkRenderingQt、vtkInteractionWidgets那么在 CMakeLists.txt 里手动添加 VTK 库引用target_link_libraries(PCLQtDemo PRIVATE ${PCL_LIBRARIES} ${VTK_LIBRARIES} Qt5::Widgets )注意顺序Qt5::Widgets一定要放在最后这是 MSVC 链接器的一个奇葩特性——它对库的依赖顺序极其敏感被依赖的库必须放在依赖它的库之后。PCL 依赖 Qt所以 PCL 的库在前、Qt 的库在后。4.3 运行闪退DLL 缺失与 OpenGL 驱动问题编译通过后运行界面都还没弹出来程序就退出了。这种闪退在发布 demo 的可执行文件时尤常见通常原因有三类第一类是DLL 缺失。在开发环境跑得动是因为安装了 PCL All-in-One 和 Qt它们把 DLL 路径写进了系统 PATH。但换一台机器跑 exe 时components.dll、pcl_common.dll、boost_filesystem-vc140-mt-1_61.dll这些动态库全部找不到程序直接退出。解决办法是把 2.2 节列出的所有 bin 目录里的 DLL 复制到 exe 同目录下或者用 Dependencies 这类工具扫描 exe 的依赖项后逐个复制。第二类是VTK 的 Qt 插件问题。VTK 7.0.0 在运行时需要一个名为QVTKWidgetPlugin的插件来支持 QVTKWidget 嵌入 Qt Designer但发布时通常不需要这个插件反而容易因为 Qt 插件目录内容不全导致崩溃。我的建议是发布时不要复制 Qt 的 plugins 目录下的 imageformats 之外的任何东西避免覆盖混淆。第三类是OpenGL 版本问题。VTK 7 默认使用 OpenGL 2.1 兼容模式新版 NVIDIA 驱动在部分旧显卡上会出现渲染上下文创建失败。遇到这种情况可以尝试把点云渲染属性中的PCL_VISUALIZER_POINT_SIZE调成 1 测试但根本解法是升级显卡驱动。这个坑比较挑硬件不是每个人都会遇到。4.4 Debug 模式跑不了 Release 模式正常的怪现象还有一个很典型的现象Release 模式下点云显示正常切到 Debug 模式就报flann::Index相关错误或者直接崩溃。这在 PCL 1.8.1 里不是 bug而是你编译 Debug 版时链接了 Release 版的 Boost 或 FLANN 库。因为 PCL 安装包内的三方库通常只有 Release 版Debug 版用的是另一套libboost_*.dll。要完全解决需要重新编译整个 PCL成本很高。我的建议是demo 阶段只使用 Release 配置不要把精力浪费在 Debug 和 Release 的库混搭上。PCL 的预编译包在实际工程里就是 Release 优先的调试 PCL 算法逻辑可以输出日志而不是依赖断点。5. demo 跑通之后从示例到工具的扩展思路5.1 点云渲染卡顿的优化方向如果加载一个几百 MB 的 PCD 文件QVTKWidget 里的渲染会明显卡顿。PCL 自带的渲染管线其实并不高效它默认把每个点当作独立的绘制单元。在 demo 阶段可以采用两个思路优化一是改变点云的渲染类型。addPointCloud之外PCL 提供了addPointCloudpcl::PointXYZ(cloud, color, cloud)的变体本质上用的还是 VTK 的 vtkPolyData 来组织点数据。可以在加载后调用viewer-setPointCloudRenderingProperties(PCL_VISUALIZER_POINT_SIZE, 1, cloud)把点大小降到 1降低填充率。二是对点云做体素滤波后再显示。pcl::VoxelGrid可以把点云降采样到固定分辨率的体素栅格用pcl::filters模块几行代码就能实现pcl::VoxelGridpcl::PointXYZ voxel; voxel.setInputCloud(cloud); voxel.setLeafSize(0.01f, 0.01f, 0.01f); pcl::PointCloudpcl::PointXYZ::Ptr filtered(new pcl::PointCloudpcl::PointXYZ); voxel.filter(*filtered);对于体素尺寸 0.01 米大概可以把百万级点云压缩到几十万级渲染流畅度有质的提升。实际项目中我在做算法预研时一定会先做这一步既不影响观察整体形状又能让交互保持流畅。5.2 把算法接口留出来后续迭代的方向demo 的架构价值在于方便扩展。我建议在 MainWindow 中预留一个processCloud()的纯虚函数或者空实现接口后续接滤波算法时只需要在槽函数里调用新算法再调用viewer-updatePointCloud即可。这样整个工程就从一个查看器一步步变成了一个可处理的点云工作站。实际工作中我在这个 demo 框架上做过后续扩展加入直通滤波、加入 RANSAC 平面分割、加入两个点云配准的模块。每次扩展的核心工作量都在算法本身框架几乎不用动。最后再分享一个实战技巧发布 demo 时别只复制 exe 和 DLL记得把 PCL 的3rdParty\VTK\bin里所有vtk*.dll也复制上。我在一台没有安装任何开发环境的干净机器上测试过缺了这些文件程序会非常安静地在启动阶段消失掉。把这几个目录处理好这套 QT5.9.6 PCL1.8.1 vs2015 的点云 demo 就能稳稳交到任何人手里。本文还有配套的精品资源点击获取
返回列表