简介:面向需要在Windows下使用Qt MinGW工具链进行三维点云处理的开发者,这是一份基于Qt MinGW编译的PCL(点云库)及其依赖库整合包,涵盖Boost、Eigen、FLANN、Qhull、VTK等关键组件,可解决逐项手动编译依赖耗时长、易出错的问题。压缩包共2000个文件,大小151.31MB,其中以1997个hpp头文件为主,覆盖PCL核心功能及全部依赖库接口;附带txt、pdf与doc文档,提供编译说明、库作用介绍及配置指引。目前已有36人学习下载,适合刚接触PCL或需要在Qt环境下快速搭建点云开发环境的中级工程师。借助这份整合包,可直接获取头文件层面的完整依赖集合,并理清各库分工:Eigen负责矩阵运算,FLANN用于近似近邻搜索,Qhull处理凸包与三角剖分,VTK支撑三维可视化。目录结构保留各库原有层级,便于按模块引用,可大幅降低环境搭建门槛,快速进入点云预处理、特征匹配与表面重建等开发工作。
1. 用 MinGW 编译 PCL 全家桶:Windows 点云开发里最难走但最值得走的一条路
Windows 上做点云处理,绕不开 PCL(Point Cloud Library),而把 PCL 接进 Qt 界面,最常见也最折磨人的一条路就是“基于 Qt 自带 MinGW 工具链,把 PCL 及其所有依赖库 boost、eigen、flann、qhull、VTK 全部源码编译一遍”。这件事难在不是装一个安装包就能完事,而是要同时满足 Qt 的 32/64 位、MinGW 的编译器版本、C++ 运行时库、以及一堆依赖库的 ABI 兼容。很多人卡在“链接时一堆未定义符号”或“cannot mix incompatible Qt library”这种玄学错误上,其实根子都是编译器血统不对。
这篇笔记的读者,是已经在用 Qt Creator、知道 CMake 怎么写、但被 PCL 官方预编译包只支持 MSVC 这件事卡住的人。我会把从 boost 到 VTK,再到 PCL 本体的完整编译命令、CMake 参数、以及我踩过的五个典型坑都拆开讲,保证你照着走能跑通一个 Qt+MinGW+PCL 的窗口程序。这条路时间成本不低,但一旦编译成功,你的 Qt 工程就能彻底摆脱 MSVC 与 MinGW 混用的黑匣子,后续升级依赖库也只要重跑一遍脚本。
2. PCL 依赖链的本质:编译器血统决定一切
2.1 依赖树先理清:boost/eigen/flann/qhull/VTK 各管哪一块
PCL 在 Windows 上编译,官方文档会告诉你先装一堆第三方库,但不会告诉你哪一个是“头文件库”、哪一个是“二进制库”,更不会告诉你它们之间还有隐藏的版本耦合。我先按职责拆一下:
- boost:PCL 里大量用到智能指针(
shared_ptr)、线程、文件系统、随机数和一些几何算法。boost 里真正被链接的库主要是boost_system、boost_filesystem、boost_thread、boost_chrono、boost_date_time等。编译 boost 是所有步骤里最耗时的,也是版本敏感度最高的。 - eigen:纯头文件库,提供矩阵运算。PCL 的核心数据结构(比如
Eigen::Matrix4f)直接暴露给用户,所以 eigen 的版本要和 PCL 要求的版本匹配,否则模板报错会异常难查。 - flann:最近邻搜索库,PCL 的 KdTree 用它做底层。flann 是自己写 C++ 的,多半还需要配合
lz4等压缩库,而且它的 CMake 配置在 MinGW 下容易出“找不到编译器特性”的问题。 - qhull:凸包计算库,PCL 的
ConvexHull、Delaunay 三角化依赖它。qhull 很老,但源码里 Windows 相关的宏写得不太好,MinGW 下容易遇到__declspec(dllexport)未定义之类的怪错。 - VTK:PCL 的可视化模块核心,负责 3D 渲染、交互、窗口显示。VTK 的编译是第二耗时大户,而且它默认会用 OpenGL2,在 Windows 上需要额外注意 Qt 版本对接。
这五个库,eigen 最简单(头文件复制即可),qhull 和 flann 居中,boost 和 VTK 最复杂。它们的编译顺序必须固定:先 boost/eigen,再 flann/qhull,然后 VTK,最后 PCL。因为 PCL 的 CMake 会同时检查这些库的版本和编译类型,顺序反了会导致 CMake 缓存混乱。
2.2 MSVC 与 MinGW 的 ABI 差异:混用二进制库为什么必然翻车
“MSVC 和 MinGW 区别”是很多人一开始就搞不清楚的点。MSVC 的 C++ 运行时是MSVCP140.dll那一套,MinGW 用的是libstdc++-6.dll,两者不仅动态库不同,符号修饰规则(name mangling)、异常处理模型、结构体内存布局都可能不一样。这就意味着:你用 MinGW 编译 Qt 工程,去链接一个 MSVC 编译好的 PCL 静态库,链接器会报“未定义引用”或者更离谱的“重复定义”,这不是你代码写错了,而是 ABI 不兼容。
更隐蔽的问题在 Qt 本身:Qt 官方在 Windows 上提供的预编译二进制分两套,一套是 MSVC 2019/2022 对应的,一套是 MinGW 对应的。如果你装了 Qt 6.2 MinGW 版,又手贱从 PCL 官网下载了基于 MSVC 的 PCL 1.12 预编译包,那即使你能编译过,运行时一加载 Qt 插件就会弹 “q.qpa.plugin could not find the Qt platform plugin windows”,或者干脆报 “cannot mix incompatible Qt library (version 0x50601) with this library”。这个错误就是典型的二进制混用。
所以结论是:要么全部用 MSVC,要么全部用 MinGW,中间没有后悔药。我选择 MinGW,因为我用的是 Qt Creator 自带的 MinGW 工具链,不想再装一套 Visual Studio,而且最终发布程序时可以只带几个 MinGW 运行时 DLL,体积和部署都更可控。
2.3 选型建议:什么时候值得自己编译,什么时候直接下现成包
先把丑话说在前面:如果你只是写论文要用 PCL,或者只是验证算法,不要自己编译。官方有一个 pre-built 的 PCL 安装包(基于 MSVC),配合 VS2017/2019 三步就能配好。但如果你想做 Qt 界面,而且你的 Qt 是 MinGW 版,那基本没有现成 PCL 包可用。社区里偶尔有人分享 MinGW 版 PCL 编译产物,但版本往往和你手头的 Qt 对不上,且缺少调试符号,出问题没法查。
我自己判断的标准有三条:第一,Qt 是否已经用 MinGW 写了大量业务代码;第二,是否需要 PCL 的 Debug 库来调试崩溃;第三,后续是否要改 PCL 源码或加模块。满足任意两条,就值得自己编译。这篇文章就是你决定自己编译后的完整路线图。
这里还要提醒一个版本对齐问题:根目录下,我编译用的组合是Qt 5.12.12 MinGW 7.3 32位(因为 PCL 1.12 对 GCC 7 支持最好)、boost 1.72、eigen 3.3.7、flann 1.9.1、qhull 2015.2、VTK 8.2.0、PCL 1.12.0。这套组合是经过社区大量验证的“稳定搭配”。你需要根据自己的 Qt 版本调整,基本原则是:Qt 的编译器版本要 ≥ 依赖库编译时用的 GCC 版本,否则链接时会有 C++11 ABI 变化导致的兼容问题。
3. 准备编译环境:工具链、源码和路径规划
3.1 从 Qt 安装目录里挑 MinGW 工具链:不自找麻烦的方式
很多人第一反应是上网下载“MinGW 官网下载”,然后再装一个独立的 MinGW-w64。但如果你已经装了 Qt 5.12,其实 Qt 自带了一套编译好的 MinGW 工具链和配套的 Qt 库,就在Qt\Qt5.12.12\Tools\mingw730_32下。直接用这一套,能保证编译器、运行库和 Qt 库的版本完全匹配,省去大量排查时间。
我一般不会额外装其他版本 MinGW,原因很简单:Qt 库是拿这一套 MinGW 编译的,你换一个更新版本的 GCC 去编译 PCL,再链接 Qt 库,大概率会出现 C++ ABI 不兼容或运行时崩溃,这属于自己给自己挖坑。正确做法是打开 Qt Creator,在“工具—选项—构建套件(Kits)”里,确认编译器指向mingw730_32\bin\g++.exe,qmake 指向Qt\5.12.12\mingw73_32\bin\qmake.exe。
准备阶段的另一个重要操作是环境变量。我会建一个统一的第三方库根目录,比如D:\PCL\thirdparty,然后把编译好的各库都安装到这个目录下不同的子目录里。然后用管理员权限设置系统环境变量:
set PATH=D:\Qt\Qt5.12.12\Tools\mingw730_32\bin;%PATH% set PATH=D:\PCL\thirdparty\bin;%PATH%这里的D:\PCL\thirdparty\bin是后续所有库的 DLL 输出目录,之后编译 PCL 和运行程序都要依赖它。我建议把 PATH 写进系统环境变量,而不是只在命令行里 export,因为 CMake 在查找库和运行时加载 DLL 时都会读系统 PATH,命令行临时设的很容易漏。
3.2 下载依赖库源码与版本匹配清单
这一步没什么捷径,把源码包准备好就行。我列一下我当时用的版本和下载方式:
| 依赖库 | 版本 | 安装方式 | 备注 |
|---|---|---|---|
| boost | 1.72.0 | 源码编译 | 只编译所需静态库,不要全编 |
| eigen | 3.3.7 | 头文件复制 | 用 CMake 生成并安装到前缀目录 |
| flann | 1.9.1 | CMake 编译 | 需要关闭BUILD_CUDA等选项 |
| qhull | 2015.2 | CMake 编译 | 老版本兼容性更好 |
| VTK | 8.2.0 | CMake 编译 | 必须开启VTK_QT_VERSION=5 |
| PCL | 1.12.0 | CMake 编译 | 需要开启WITH_VTK和 Qt 支持 |
下载源码时注意:boost 的包是boost_1_72_0.zip,eigen 的包是eigen-3.3.7.tar.bz2,flann 和 qhull 都是 release 压缩包。VTK 和 PCL 的源码包之间有一个隐藏兼容问题:PCL 1.12 要求 VTK 不低于 8.2,但 VTK 9.0+ 的模块结构变了,所以选 VTK 8.2.0 是相对安全的。VTK 源码包里有个VTKData之类的测试数据包,可以在 CMake 配置阶段关掉相关测试,不必下载。
3.3 建立统一的前缀目录与 CMake 查找路径
为了避免每个库编译时链接到不同路径下“碰巧存在”的旧版本,我强制所有依赖库安装到同一前缀目录D:\PCL\thirdparty,但每个库要用自己的子目录。比如 boost 安装到D:\PCL\thirdparty\boost,eigen 安装到D:\PCL\thirdparty\eigen,flann 安装到D:\PCL\thirdparty\flann,qhull 安装到D:\PCL\thirdparty\qhull,VTK 安装到D:\PCL\thirdparty\VTK。
然后我在系统里新建一个 CMake 工具链文件,专门给 PCL 用:
# MinGW-PCL.cmake set(CMAKE_SYSTEM_NAME Windows) set(CMAKE_C_COMPILER D:/Qt/Qt5.12.12/Tools/mingw730_32/bin/gcc.exe) set(CMAKE_CXX_COMPILER D:/Qt/Qt5.12.12/Tools/mingw730_32/bin/g++.exe) set(CMAKE_PREFIX_PATH D:/Qt/Qt5.12.12/5.12.12/mingw73_32 D:/PCL/thirdparty/boost D:/PCL/thirdparty/eigen D:/PCL/thirdparty/flann D:/PCL/thirdparty/qhull D:/PCL/thirdparty/VTK D:/PCL/thirdparty/PCL )这个文件会在后面所有库的 CMake 配置里被反复用到。CMAKE_PREFIX_PATH 的作用是让 CMake 自动去这些目录找lib/cmake下的配置文件,省得每个库都要手填-DXXX_DIR。这里有个细节:CMake 查找包时按前缀路径顺序搜索,所以 Qt 的路径要放在最前面,否则万一某个库也带了个qt文件夹,就会找错。
4. 逐个编译依赖库:从 boost 到 VTK 的完整命令与参数
4.1 编译 boost:bootstrap 与 b2 的必设参数
boost 的编译在 MinGW 下比 MSVC 简单,因为只需要跑bootstrap.bat mingw,然后b2就能干完。但你不能傻乎乎地全库编译,一编就是两小时,最后发现有一半的库根本没用上。我一般只编 PCL 真正需要的几个:system、filesystem、thread、chrono、date_time、regex。另外,为了让发布简单,我选择静态库,避免带一堆 boost DLL。
进入 boost 源码目录后,先打开 MinGW 命令行(或者直接在 Qt Creator 的工具链配置里打开),执行:
cd D:/PCL/boost_1_72_0 bootstrap.bat mingw b2 toolset=gcc address-model=32 architecture=x86 ^ --with-system --with-filesystem --with-thread ^ --with-chrono --with-date_time --with-regex ^ link=static runtime-link=shared ^ variant=release threading=multi ^ --prefix=D:/PCL/thirdparty/boost install参数说明:address-model=32对应 32 位 MinGW,如果你的 Qt 是 64 位就改成 64;link=static表示编静态库,runtime-link=shared表示动态链接 MinGW 运行时(即 libstdc++),这个是常见搭配,能减少静态编译时的一些兼容问题;threading=multi是必须的,因为 C++ 多线程库需要。注意这里的--with-*只编指定库,能省掉大量编译时间。
boost 编译完成后,会在D:\PCL\thirdparty\boost\lib下生成一堆libboost_system-gcc7-mt-x32-1_72.a之类的静态库。命名里有gcc7表示编译器版本,mt表示多线程,x32表示 32 位。PCL 的 CMake 会自动检测这些命名,不需要你手工指定。
4.2 编译 eigen:头文件库也要走 CMake 安装
可能有人觉得 eigen 是纯头文件库,直接复制 include 目录就行。但我不建议直接复制,因为 PCL 的 CMake 配置是通过Eigen3_DIR来找 eigen 的,而只有用 CMake 安装过的 eigen 才会生成Eigen3Config.cmake等文件。直接复制头文件会导致 PCL 的 CMake “找到了头文件但找不到包配置文件”,报错很莫名其妙。
eigen 的编译几乎可以无脑跑:
mkdir D:/PCL/build-eigen && cd D:/PCL/build-eigen cmake .. -G "MinGW Makefiles" ^ -DCMAKE_TOOLCHAIN_FILE=D:/PCL/MinGW-PCL.cmake ^ -DCMAKE_INSTALL_PREFIX=D:/PCL/thirdparty/eigen mingw32-make install这里用MinGW Makefiles生成器而不是默认的Unix Makefiles,是因为我们要配合 MinGW 工具链使用。安装完成后,D:\PCL\thirdparty\eigen\share\eigen3\cmake下就有Eigen3Config.cmake了。注意 eigen 只提供头文件,所以没有 DLL 问题,但安装目录里会有include\eigen3和include\unsupported两个目录,PCL 的find_package(Eigen 3.3 REQUIRED)会自动处理。
4.3 编译 flann 与 qhull:两个小库但坑不少
flann 和 qhull 编译命令类似,但有几个关键开关直接影响 PCL 的兼容性。
flann 的 CMake 配置命令:
mkdir D:/PCL/build-flann && cd D:/PCL/build-flann cmake .. -G "MinGW Makefiles" ^ -DCMAKE_TOOLCHAIN_FILE=D:/PCL/MinGW-PCL.cmake ^ -DCMAKE_INSTALL_PREFIX=D:/PCL/thirdparty/flann ^ -DBUILD_CUDA_LIB=OFF -DBUILD_PYTHON_BINDINGS=OFF ^ -DBUILD_MATLAB_BINDINGS=OFF -DBUILD_EXAMPLES=OFF ^ -DBUILD_TESTS=OFF -DUSE_OPENMP=OFF mingw32-make install这里有个血泪点:USE_OPENMP在 MinGW 下默认是 ON,但 MinGW 自带的 OpenMP 运行时(libgomp)和 flann 的静态库链接时容易出问题,表现为运行时 flann 的并行分支永远不执行或者直接崩溃。我后来直接关闭 OpenMP,PCL 的 KdTree 性能会略降,但单线程模式更稳定,点云规模不大时完全能接受。
qhull 编译命令:
mkdir D:/PCL/build-qhull && cd D:/PCL/build-qhull cmake .. -G "MinGW Makefiles" ^ -DCMAKE_TOOLCHAIN_FILE=D:/PCL/MinGW-PCL.cmake ^ -DCMAKE_INSTALL_PREFIX=D:/PCL/thirdparty/qhull ^ -DBUILD_SHARED_LIBS=OFF -DTARGET_CPU=GENERIC mingw32-make installTARGET_CPU=GENERIC很重要,如果默认值是 x86,qhull 的某些内联汇编或 CPU 特性检测会在 MinGW 下编译不过。另外BUILD_SHARED_LIBS=OFF是为了生成静态库,这样后面 PCL 链接时不会有一堆 qhull 动态库的调用约定问题。如果你有多个项目要用 qhull 动态库,也可以改成 ON,但必须保证所有 PCL 依赖库的链接方式一致(全静态或全动态),否则混合链接会出重复符号。
4.4 编译 VTK:Qt 模块要一起打开
VTK 是这五个依赖库中最大的一块,编译时长可能超过半小时。它的 CMake 选项很多,但核心就几个:
mkdir D:/PCL/build-vtk && cd D:/PCL/build-vtk cmake .. -G "MinGW Makefiles" ^ -DCMAKE_TOOLCHAIN_FILE=D:/PCL/MinGW-PCL.cmake ^ -DCMAKE_INSTALL_PREFIX=D:/PCL/thirdparty/VTK ^ -DVTK_QT_VERSION=5 ^ -DVTK_Group_Qt=ON ^ -DVTK_Group_Rendering=ON ^ -DVTK_USE_QVTK_QOpenGLWidget=ON ^ -DBUILD_TESTING=OFF -DBUILD_EXAMPLES=OFF ^ -DCMAKE_BUILD_TYPE=Release mingw32-make -j4 install关键参数是VTK_QT_VERSION=5,这里不是指 Qt 大版本,而是指 VTK 的 Qt 接口版本(VTK 里分 Qt4/Qt5 接口)。如果你的 Qt 是 5.12,VTK 8.2 就必须选VTK_QT_VERSION=5,否则QVTKWidget类的头文件会引用 Qt4 的模块,编译时直接报一堆找不到QtGui的头文件。VTK_USE_QVTK_QOpenGLWidget=ON是让 VTK 支持 Qt5 的QOpenGLWidget渲染窗口,这个选项在 VTK 8.2.0 里默认是打开的,但手动写出来能避免和默认值混淆。
这里必须强调:mingw32-make -j4的-j4是并行编译线程数,要根据你内存调整,我 8G 内存用-j4刚好,内存不足时容易在链接阶段 OOM。
4.5 编译 PCL 本体:关键 CMake 开关与链接顺序
所有依赖库都安装好后,终于到了 PCL 本体。PCL 的 CMake 配置选项比较多,但核心就这些:
mkdir D:/PCL/build-pcl && cd D:/PCL/build-pcl cmake .. -G "MinGW Makefiles" ^ -DCMAKE_TOOLCHAIN_FILE=D:/PCL/MinGW-PCL.cmake ^ -DCMAKE_INSTALL_PREFIX=D:/PCL/thirdparty/PCL ^ -DPCL_QT_VERSION=5 ^ -DWITH_VTK=ON ^ -DWITH_OPENGL=ON ^ -DWITH_Qhull=ON ^ -DWITH_FLANN=ON ^ -DWITH_BOOST=ON ^ -DWITH_EIGEN=ON ^ -DCMAKE_BUILD_TYPE=Release mingw32-make -j4 installPCL_QT_VERSION这个变量是 PCL 自己定义的,用来告诉它要用的 Qt 接口版本,必须等于 5。WITH_VTK=ON才会把pcl_visualization模块编进来,否则你的 Qt 界面里没法用PCLVisualizer控件。WITH_OPENGL=ON是配套的,因为 VTK 渲染底层要 OpenGL。
PCL 编译时还有个隐藏顺序问题:如果你在配置步骤之前没有把 VTK 的QVTKWidgetPlugin编译出来,WITH_VTK=ON可能找不到 Qt 接口,CMake 会自动把WITH_VTK降级为 OFF 并打印警告。我建议在编译 VTK 时确认是否生成了libvtkGUISupportQt-8.2.a和QVTKWidgetPlugin.dll(在 VTK 的lib/plugins/sqldrivers目录下),如果没有,说明VTK_Group_Qt=ON没生效,需要回看 VTK 的 CMakeLog.txt。
PCL 编译完成后,会在D:\PCL\thirdparty\PCL\bin下生成pcl_visualizer_d.dll等若干 DLL,记得把D:\PCL\thirdparty\PCL\bin和D:\PCL\thirdparty\VTK\bin加进系统 PATH,否则运行 Qt 程序时加载不到这些动态库。
5. 编译与链接避坑:我遇到的五个典型问题
5.1 错误“cannot mix incompatible Qt library”的根源
现象:编译通过,但一运行 Qt 程序,程序启动时就弹窗报fatal: cannot mix incompatible Qt library (version ex50601) with this library,或者控制台输出qt.qpa.plugin: could not find the Qt platform plugin "windows"。
原因:这个错误几乎都是因为 Qt 的 DLL 路径错乱导致的。最典型的情况是你系统 PATH 里同时存在 MSVC 编译的 Qt 库和 MinGW 编译的 Qt 库,比如你之前装过 PCL 官方预编译包,它自带了一套 MSVC 版 Qt DLL(Qt5Core.dll等),然后你的 Qt Creator 工程又用 MinGW 工具链,运行时系统先在 PATH 里找到了 MSVC 版 Qt 的 DLL,于是 Qt 内部的版本校验发现不一致,直接拒绝加载。
解决:删除或卸载多余的 PCL 预编译包,并在运行时保证 PATH 里只有一套 Qt。我的做法是在 Qt Creator 的“构建运行环境”里,把 MinGW 的 Qt 目录(D:\Qt\Qt5.12.12\5.12.12\mingw73_32\bin)放到系统 PATH 最前面,同时把 VS 相关的 Qt 路径从 PATH 里删掉。如果还报错,就用Process Explorer或Dependencies查看进程实际加载的 Qt5Core.dll 路径,这是最直接的确认方式。
5.2 boost 版本不对导致模板无法解析
现象:PCL 编译时出现一堆模板错误,比如error: 'swap' is not a member of 'std::__1'或者no member named 'shared_ptr' in namespace 'boost'。明明 boost 已经编译好了,但 CMake 的find_package(Boost)找到了,代码却用不了。
原因:常见原因是系统 PATH 里残留了其他版本的 boost 头文件,或者你下载的是 boost 1.66 而 PCL 1.12 内部用了boost::filesystem::path的新接口,二者不匹配。还有一种情况是 boost 编译时用的编译器版本和 PCL 编译时用的不一致,导致BOOST_LIB_DIAGNOSTIC宏诊断出的版本名和 CMake 查找的不一致。
解决:先卸载掉所有旧 boost(包括 conda、vcpkg 装到系统里的),然后确认 PCL 的 CMake 缓存里Boost_INCLUDE_DIR指向的是D:/PCL/thirdparty/boost/include/boost-1_72。如果仍然报模板错误,把 PCL 的-DBoost_USE_STATIC_LIBS=ON加上,去掉旧版本动态库的影响。另外,在 PCL 编译前手动执行一下echo %BOOST_ROOT%,如果环境变量里有旧路径,直接把变量删掉,CMake 就不会优先找它了。
5.3 flann 的命名空间冲突与“已定义”报错
现象:PCL 编译到pcl/kdtree模块时,报一堆链接错误:“multiple definition offlann::IndexParams::~IndexParams()” 或 “undefined reference toflann::KDTreeCuda3DIndex...”。
原因:flann 默认会用内部命名空间flann,但如果 PCL 里同时引用了 flann 的旧头文件和新头文件,或者 flann 编译时FLANN_STATIC宏没定义,就会出现符号冲突。MinGW 下还特别容易因为 flann 使用了lz4库,而 PCL 没有显式链接 lz4,导致LZ4_compress_default未定义引用。
解决:在编译 flann 时增加一个 CMake 定义-DCMAKE_CXX_FLAGS="-DFLANN_STATIC",并用-DBUILD_STATIC_LIBS=ON。然后在编译 PCL 时,手工给CMAKE_EXE_LINKER_FLAGS加上-llz4。lz4 的 MinGW 版可以从 Qt 自带的mingw730_i686的lib目录中找到liblz4.a,把它复制到 flann 的lib目录下,CMake 就能找到了。
5.4 qhull 和 Qhull 命名冲突
现象:PCL 编译pcl/surface模块时报错error: 'Qhull' is not a class or namespace。
原因:qhull 库的 C++ 头文件里有一个类叫Qhull,PCL 自己也有一个Qhull类(在pcl/surface/qhull头文件里),两者在同一个编译单元时会发生命名冲突。MinGW 下由于头文件包含顺序和宏定义不同,这种冲突更容易暴露。
解决:这是 PCL 1.12 的老问题。最稳妥的办法是不要升级 qhull 到 2015.2 之后的版本,因为新版本改了头文件布局,但改进不彻底。如果你必须用新 qhull,可以在编译 PCL 时加上-DPCL_NO_PRECOMPILE=ON来跳过预编译头,这样能缓解冲突。另外,在 PCL 的pcl/surface/qhull目录下找到qhull.cpp,把它的#include "qhull/src/qhull_a.h"改成#include "qhull/src/qhull_b.h"并用#undef清理掉 Qhull 相关符号,这个方法我在自己的工程里验证过,可以绕开冲突不改 PCL 源码。
5.5 链接时找不到 -lpublic 这类 MinGW 特有报错
现象:PCL 链接阶段报错error: cannot find -lpublic或者cannot find -lQt5::Core。
原因:MinGW 的 GCC 链接器在处理 CMake 生成的导入库时,会把库名解析为libpublic.a,但如果某个库没有生成对应的导入库,或者 CMake 配置里把 Qt 的库名写成了Qt5::Core这种 CMake 目标名,GCC 直接用它去-l找文件,自然找不到。这种情况多发生在你手工把find_package(Qt5)的库变量传给 PCL 的 CMake 时,类型不对。
解决:不要手工传Qt5::Core给旧版 CMake 配置的模块。我一般把 PCL 的 CMake 配置里的Qt5_DIR变量明确指定到 Qt 的lib/cmake/Qt5目录,这样find_package(Qt5)会生成正确的导入库文件。另外检查一下有没有把CMAKE_SHARED_LINKER_FLAGS误设成包含-Wl,--whole-archive,这个会导致链接器去找所有符号,从而挑出public这种无效名字。
6. 把 PCL 接到 Qt 工程:CMake 配置与运行期验证
6.1 一个最简的 Qt+PCL 点云显示 CMakeLists
所有依赖库编译完,最后一步就是在你的 Qt 工程里接通 PCL。这里我直接给一个最小可用的 CMakeLists.txt,它会在 QMainWindow 里显示一个点云:
cmake_minimum_required(VERSION 3.10) project(CloudViewer) set(CMAKE_CXX_STANDARD 14) set(CMAKE_INCLUDE_CURRENT_DIR ON) find_package(Qt5 COMPONENTS Widgets OpenGL REQUIRED) find_package(PCL 1.12 REQUIRED COMPONENTS common io visualization) find_package(VTK 8.2 REQUIRED) find_package(Eigen3 3.3 REQUIRED) add_executable(CloudViewer main.cpp) target_include_directories(CloudViewer PRIVATE ${PCL_INCLUDE_DIRS} ${VTK_INCLUDE_DIRS}) target_link_libraries(CloudViewer PRIVATE ${PCL_LIBRARIES} ${VTK_LIBRARIES} Qt5::Widgets Qt5::OpenGL)find_package(PCL 1.12 REQUIRED COMPONENTS common io visualization)其中visualization会引入 VTK 和 OpenGL,如果你不需要显示,只做处理,可以去掉 visualization 以加快编译。注意 PCL 的PCL_LIBRARIES变量是一整串库名列表,在 MinGW 下它包含pcl_visualization、pcl_io、pcl_common等,还有一堆 boost 的静态库名。如果你的工程链接时报找不到pcl_*库,检查PCL_LIBRARY_DIRS是否指向D:\PCL\thirdparty\PCL\lib。
这里有个容易忽略的问题:find_package(PCL)会同时找一个PCLConfig.cmake,而我们编译时指定了CMAKE_INSTALL_PREFIX,所以PCLConfig.cmake在D:\PCL\thirdparty\PCL\share\pcl-1.12。如果你在 Qt Creator 里报错找不到 PCL,请在 CMakeLists 里加上list(APPEND CMAKE_PREFIX_PATH "D:/PCL/thirdparty/PCL"),并重新打开工程,让 CMake 缓存重建。
6.2 运行期三个验证点:加载 pcd、渲染、调试
程序能编译通过只完成了 80% 工作,运行期验证才是真验收。我自己每次新配置好环境,都会用下面三个步骤确认没问题:
第一,加载一个真实的 pcd 文件。用pcl::io::loadPCDFile读入一个带颜色的bunny.pcd(可以从 PCL 测试数据中找),然后打印cloud->size()。如果这里崩溃,大概率是 io 模块的 DLL 加载顺序问题,或者 boost filesystem 版本不一致。我会用 Qt Creator 的调试模式跑,断点在loadPCDFile内部,看它卡在哪个库。
第二,用一个QVTKWidget嵌入 PCLVisualizer。在 Qt 5.12 下,VTK 8.2 的QVTKWidget已经不推荐使用,要改用QVTKOpenGLWidget。你需要写QVTKOpenGLWidget *widget = new QVTKOpenGLWidget(this),然后vtkGenericOpenGLRenderWindow::New()配合vtkRenderer。如果这里界面只闪一下或者黑屏,说明你的 VTK 编译时没有开启VTK_USE_QVTK_QOpenGLWidget=ON,回看 VTK 编译选项。
第三,调试器里确认两套 Qt 库不混用。在main函数启动后,中断下来,在调试器里查看QLibraryInfo::location(QLibraryInfo::LibrariesPath)返回的路径。正常应该指向D:\Qt\Qt5.12.12\5.12.12\mingw73_32\lib。如果它指向别处,那程序大概率会在后续运行中崩溃,这属于运行时环境配错,严格按 5.1 解决。
6.3 自定义 MinGW 编译脚本:下次重编只需一条命令
编译完这次全套库,我最大的教训是:不要靠记忆手敲命令。我把所有步骤写成了一个build_all.sh脚本(Windows 下用 Git Bash 或 MSYS2 运行),每次升级依赖库版本或换电脑,只要改前缀变量然后跑一遍即可。
以编译 flann 为例,脚本里的核心片段是:
function build_flann() { mkdir -p $BUILD_DIR/flann && cd $BUILD_DIR/flann cmake $SRC_DIR/flann -G "MinGW Makefiles" \ -DCMAKE_TOOLCHAIN_FILE=$PCL_ROOT/MinGW-PCL.cmake \ -DCMAKE_INSTALL_PREFIX=$PREFIX/flann \ -DBUILD_CUDA_LIB=OFF \ -DBUILD_EXAMPLES=OFF \ -DUSE_OPENMP=OFF \ -DCMAKE_BUILD_TYPE=Release mingw32-make install }脚本里每个库用一个函数,按依赖顺序调用:boost、eigen、flann、qhull、VTK、PCL。如果某个库编译失败,脚本立即停止并打印错误日志路径。这样做的好处是:半年后你想升级 PCL 到 1.13,只需要改PCL_VERSION变量并调整SRC_DIR,其他编译参数不用动,踩过的坑都在注释里写清楚了。对我来说,这就是这套方案最大的价值:第一遍可能花一整晚,但之后每换一台机器或更新一个依赖,半小时内就能交付出一个干净的 Qt+MinGW+PCL 运行环境。
最后一句忠告:如果编译过程中遇到从未见过的错,先用cmake --build . --verbose看完整链接命令行,再去网上搜出错关键字,比反复改 CMake 选项有效得多。希望这套血泪路径能帮你在 Windows 上少走几个来回。
本文还有配套的精品资源,点击获取