ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

PCL 1.12.0 + CUDA 11.1 编译包:GPU点云处理环境配置指南

PCL 1.12.0 + CUDA 11.1 编译包:GPU点云处理环境配置指南 简介面向需要GPU加速点云处理的中高级开发者提供一套PCL 1.12.0配合CUDA 11.1与cuDNN 8.0.4的Windows 64位预编译包由MSVC 2019生成专门用于发布版x64架构的C项目。包内总共1221个文件压缩后仅为30.6MB核心是638个头文件、362个模板实现文件与31个静态链接库同时附带动态库、CMake配置脚本和示例程序可供工程直接调用也能辅助项目完成依赖查找与链接集成。配合其中的CUDA加速模块可以快捷启用特征提取、样本一致性分割、目标跟踪、表面重建等常用功能的GPU实现省去在Windows上自行编译PCL GPU组件的繁琐流程适合已有一定PCL基础并追求算法提速的工程师。已有267人学习下载用户只需预先安装CUDA 11.1及PCL 1.12.0的第三方依赖再按发布版x64模式配置即可正常使用。1. PCL 1.12.0 CUDA 11.1 编译包GPU 点云处理不再卡在环境上点云配准和分割跑 CPU 版本有多痛苦做过的人都懂。一个 20 万点的点云做欧式聚类CPU 版要跑几十秒换成 GPU 加速模块后能压到 3 秒以内这个数量级的提升直接改变了开发节奏。但 PCL 的 GPU 模块一直不好碰自己从源码编译要处理 CUDA 算力架构、MSVC 工具集版本、第三方依赖库的匹配关系随便一个环节不对就整周白费。这套pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64编译包把最麻烦的编译环节做完了你拿到手只需要配好 3rdparty 依赖在 Release x64 模式下链接对应的 lib就能直接跑 GPU 加速的聚类、分割、法线估计和八叉树搜索。适合项目已经跑在 Windows 上、显卡是 N 卡且算力在 6.0 以上、不想花一周时间折腾源码编译的开发者。2. 环境准备CUDA 11.1、3rdparty 与 MSVC 2019 的匹配关系2.1 为什么必须是 CUDA 11.1 和 MSVC 2019PCL 1.12.0 的 GPU 模块是在 CUDA 11.1 环境下编译出来的。CUDA 的 runtime API 在 11.x 之后变化不大但 MSVC 的版本和标准库实现直接影响 C ABI。如果你用 VS2022 打开这个包编译器版本是 v143而包内 lib 是用 v142 工具集生成的混用会出现一堆 link 错误报错在pcl_common.lib里比如LNK2038 mismatch detected for _MSC_VER。MSVC 2019 对应的是 v142 工具集这是这个编译包能正常链接的前提。CUDA 11.1 官方支持的最高 MSVC 版本是 19.28所以用 VS2019 16.8 及以上但不要超过 16.11这是编译时的工具链版本约束。还需要装好 CUDA 11.1 的完整安装包不能只装 driver。GPU 模块编译时用的是cudart64_110.dll和cublas64_11.dll如果只装了最新 CUDA 12.xPCL 包内链接的符号版本对不上运行时直接报找不到入口点。常见做法是机器上同时装 CUDA 11.1 和更高版本用环境变量CUDA_PATH_V11_1来区分。我一般会这样检查nvcc --version echo %CUDA_PATH% echo %CUDA_PATH_V11_1%nvcc --version显示的版本必须是 11.1CUDA_PATH环境变量在安装 CUDA 11.1 后会被设置成C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1。2.2 3rdparty 目录的配置顺序这个编译包默认不附带 PCL 的第三方依赖库需要你手动安装或解压到指定位置。3rdparty 包含 FLANN、Qhull、Boost、Eigen、OpenNI2、VTK 等组件。注意里面的FindFLANN.cmake、FindQhull.cmake这些查找脚本是写好了的但前提是你的 3rdparty 目录结构恰好是它们期望的样子。我先说目录结构PCL_ROOT 环境变量指向编译包解压根目录PCL_ROOT\3rdParty\ 下放 Boost、Eigen、FLANN、Qhull、VTK3rdParty 下的库文件需要和这个包同时编译不能直接用 vcpkg 装的版本因为 vcpkg 默认用动态链接 CRT而这个包用的是/MD运行时我把 3rdparty 的配置方式说一下。假设你把第三方库放在D:\libs\pcl_3rdparty下用 CMake 配置项目时有几个关键变量要设置否则FindPCL.cmake找不到库。具体来说set(PCL_ROOT D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64) set(PCL_DIR ${PCL_ROOT}/cmake) set(BOOST_ROOT D:/libs/pcl_3rdparty/boost) set(EIGEN_INCLUDE_DIR D:/libs/pcl_3rdparty/eigen)这里PCL_DIR指向编译包内的cmake目录里面放着PCLConfig.cmake和PCLConfigVersion.cmake。CMake 通过find_package(PCL 1.12 REQUIRED)时会先找PCL_DIR指定的位置而不是去系统 PATH 里碰运气。2.3 环境变量的持久化设置命令行里临时设置环境变量只对当前终端有效重新打开 VS2019 的开发者命令提示符就丢了。我建议写到系统环境变量里setx PCL_ROOT D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64 setx PCL_DIR %PCL_ROOT%/cmake setx BOOST_ROOT D:/libs/pcl_3rdparty/boost设置后要重开终端窗口因为setx不会影响当前进程的环境变量。重开后可以用echo %PCL_ROOT%验证。3. 链接配置lib 清单、CMakeLists 与 Release x64 模式3.1 lib 文件清单解读摘要里列出的 lib 清单是 GPU 加速核心模块的产物。我把关键的几个分成组lib 分组对应模块作用pcl_cuda_features.libcuda_featuresGPU 加速的特征提取比如法线估计、FPFHpcl_cuda_segmentation.libcuda_segmentationGPU 加速的区域生长分割、欧式聚类pcl_gpu_octree.libgpu_octreeGPU 八叉树构建与最近邻搜索pcl_gpu_tracking.libgpu_tracking基于 GPU 的跟踪算法pcl_gpu_utils.libgpu_utilsGPU 模块公共工具函数pcl_gpu_containers.libgpu_containersGPU 端的数据容器如 DeviceArraypcl_io.libio点云文件读写PCD/PLY 格式这些 lib 全部对应 Release x64 模式。Debug 模式下它们不工作因为编译产物的运行库和迭代器调试级别不同。项目配置里平台必须选 x64字符集选多字节或 Unicode 都可以但_ITERATOR_DEBUG_LEVEL必须为 0这是 Release 的默认值。3.2 一份可用的 CMakeLists.txtPCL 官方的 CMake 配置方式在 Windows 非标准安装路径时经常失效所以建议手动指定路径。下面这份 CMakeLists 我在自己项目里跑通过cmake_minimum_required(VERSION 3.16) project(pcl_gpu_demo) set(CMAKE_PREFIX_PATH D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64 D:/libs/pcl_3rdparty ) set(PCL_DIR D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64/cmake) find_package(PCL 1.12 REQUIRED COMPONENTS common io filters features kdtree segmentation cuda_features cuda_segmentation gpu_octree gpu_utils ) include_directories(${PCL_INCLUDE_DIRS}) add_definitions(${PCL_DEFINITIONS}) link_directories(${PCL_LIBRARY_DIRS}) add_executable(pcl_gpu_demo main.cpp) target_link_libraries(pcl_gpu_demo ${PCL_LIBRARIES})这里有几个容易翻车的地方。find_package的 COMPONENTS 列表必须同时包含 CPU 模块和 GPU 模块如果只写 GPU 相关组件CMake 会报PCL_LIBRARIES为空。link_directories需要指向编译包内的lib目录否则链接器找不到.lib文件。3.3 手动链接方式的 Visual Studio 配置如果不走 CMake直接在 VS2019 里建空项目也可以。项目属性页里的配置步骤如下C/C → 附加包含目录D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64/include/pcl-1.12和D:/libs/pcl_3rdparty/boost/include链接器 → 附加库目录D:/libs/pcl1.12.0-cuda11.1-cudnn8.0.4-msvc2019-win64/lib链接器 → 附加依赖项按依赖关系逐个添加 lib依赖顺序有讲究。pcl_gpu_octree.lib依赖pcl_gpu_utils.libpcl_gpu_utils.lib又依赖pcl_common.lib和 CUDA 的cudart.lib。如果不按依赖顺序排会出现unresolved external symbol报错位置在pcl_gpu_octree.lib里引用pcl::gpu::utils::...的符号。常见做法是把 CUDA 的库目录也加到附加库目录里C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\lib\x64然后在附加依赖项里加上cudart.lib cudart_static.lib4. 实战演示GPU 加速的欧式聚类与法线估计4.1 GPU 模块的代码结构差异PCL 的 GPU 模块在 API 设计上和 CPU 版本很像但有几个本质区别。先看 CPU 版本的常规写法#include pcl/segmentation/euclidean_cluster_comparator.h #include pcl/segmentation/extract_clusters.h #include pcl/point_types.h #include pcl/point_cloud.h pcl::search::KdTreepcl::PointXYZ::Ptr tree(new pcl::search::KdTreepcl::PointXYZ); pcl::EuclideanClusterExtractionpcl::PointXYZ ec; ec.setClusterTolerance(0.02); ec.setMinClusterSize(100); ec.setMaxClusterSize(25000); ec.setSearchMethod(tree); std::vectorpcl::PointIndices cluster_indices; ec.extract(cluster_indices);而 GPU 版本用的是pcl::gpu::EuclideanClusterExtraction数据结构也换成pcl::gpu::OctreePointCloudSearch。差别在于GPU 版本的输入点云需要先上传到显存搜索结构用 GPU octree输出结果直接是聚类索引。下面是 GPU 版本的典型写法#include pcl/gpu/octree/octree.hpp #include pcl/gpu/segmentation/euclidean_cluster.h #include pcl/gpu/containers/device_array.h pcl::gpu::OctreePointCloudSearchpcl::PointXYZ octree; octree.setInputCloud(cloud_gpu); octree.build(); pcl::gpu::EuclideanClusterExtractionpcl::PointXYZ ec_gpu; ec_gpu.setClusterTolerance(0.02); ec_gpu.setMinClusterSize(100); ec_gpu.setMaxClusterSize(25000); ec_gpu.setSearchMethod(octree); std::vectorpcl::PointIndices cluster_indices_gpu; ec_gpu.extract(cluster_indices_gpu);注意cloud_gpu不是pcl::PointCloudpcl::PointXYZ而是pcl::gpu::DeviceArraypcl::PointXYZ。从 CPU 点云上传到 GPUpcl::gpu::DeviceArraypcl::PointXYZ cloud_gpu; cloud_gpu.upload(cpu_cloud-points);这里的upload是 CUDA 的cudaMemcpy封装把std::vector格式的点数据拷贝到显存。参数上setClusterTolerance的单位和 CPU 版本一致都是米对于雷达点云常用 0.02 到 0.05。setMinClusterSize和setMaxClusterSize的语义也和 CPU 版本相同用于过滤掉噪声簇和过大簇。4.2 法线估计的 GPU 实现法线估计是另一个典型的 GPU 加速场景。CPU 版本用pcl::NormalEstimation加 KdTreeGPU 版本用pcl::gpu::NormalEstimation。关键在于法线估计需要在每个点的邻域内做 PCA这个计算在 GPU 上可以做到极致并行。#include pcl/gpu/features/features.hpp pcl::gpu::NormalEstimationpcl::PointXYZ, pcl::Normal ne_gpu; ne_gpu.setInputCloud(cloud_gpu); ne_gpu.setRadiusSearch(0.03); pcl::gpu::DeviceArraypcl::Normal normals_gpu; ne_gpu.compute(normals_gpu);setRadiusSearch控制邻域搜索半径单位是米。这个值选太大会导致法线过度平滑选太小会出现空洞。对于室内场景 0.03 到 0.05 比较稳妥室外雷达数据建议 0.08 到 0.1。结果在normals_gpu里需要下载回 CPU 才能继续用pcl::visualization显示std::vectorpcl::Normal normals_cpu; normals_gpu.download(normals_cpu);这里有个细节pcl::gpu::DeviceArray的download方法会做cudaMemcpy从设备到主机如果不调用这个后续 CPU 端处理拿不到数据。忘了download是我见过最频繁的翻车点。4.3 依赖的 CUDA 运行时库GPU 模块运行时需要 cudart 和 cublas。在代码里不需要显式#include这些头文件但在链接时需要对应的.lib。如果 CMake 配置时用了find_package(CUDA)生成的项目会自动带上 CUDA 依赖但手动配置的项目必须自己加。cudart64_110.dll cublas64_11.dll cufft64_10.dll这三个 DLL 在 CUDA 11.1 安装目录的bin文件夹下。运行时如果报找不到 DLL把这两条路径加到系统 PATH 里C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\bin C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.1\libnvvp5. 避坑指南配置 PCL GPU 编译包时的 5 个常见问题5.1 Debug 模式下链接报错现象编译通过链接时报LNK2038 mismatch detected for RuntimeLibrary: value MTd_StaticDebug doesnt match value MD_DynamicRelease。原因这个编译包只生成了 Release x64 版本库内_ITERATOR_DEBUG_LEVEL0而 Debug 模式要求_ITERATOR_DEBUG_LEVEL2两者 ABI 不兼容。解决把项目配置切换到 Release x64。如果必须用 Debug 调试只能自己从源码编译 PCL 的 Debug 版本无法用这个包将就。5.2 CMake 找不到 PCLConfig.cmake现象find_package(PCL 1.12 REQUIRED)报错Could not find a package configuration file provided by PCL。原因CMake 默认搜索路径是CMAKE_PREFIX_PATH和当前目录但编译包没有安装到系统默认路径。解决要么把编译包的根目录加到CMAKE_PREFIX_PATH要么显式设置PCL_DIR变量指向cmake子目录。注意设置PCL_DIR后需要重新运行 CMake 的 Configure不能只改缓存。5.3 显卡算力不匹配导致内核启动失败现象程序编译链接都正常运行到 GPU 相关代码时崩溃或报错invalid device function。原因CUDA 11.1 默认生成的 ptx 和 cubin 是为特定算力架构编译的但这个包编译时指定的compute_xx和你显卡的算力不匹配。比如包内是compute_75对应 RTX 20 系列你用的是 RTX 30 系列算力 86运行时找不到兼容的 cubin。解决把 PCL 的 GPU 模块源码用你显卡对应的算力重新编译一次或者确认你的显卡算力被包内包含的 cubin 覆盖。更简单的验证方式是用nvidia-smi看驱动和 CUDA 版本再用deviceQuery看显卡算力。5.4 链接时重定义符号现象链接报错LNK2005 xxx already defined in pcl_common.lib和LNK2005 xxx already defined in pcl_gpu_utils.lib同时出现。原因CPU 版本和 GPU 版本模块都包含了同名的函数实现但符号在两者中都被导出。比如pcl::PointCloud的某些模板实例化在两个库里都有。解决在 Visual Studio 的项目属性里把链接器 → 命令行 → 其他选项加上/FORCE:MULTIPLE。注意这个选项可能掩盖真实问题不建议长期使用。更好的方案是检查是否同时链接了 CPU 和 GPU 的同名模块比如同时加了pcl_segmentation.lib和pcl_cuda_segmentation.lib如果功能重复只保留 GPU 版本。5.5 运行时提示找不到 pcl_gpu_utils.dll现象程序启动报错The code execution cannot proceed because pcl_gpu_utils.dll was not found。原因这个编译包生成的是动态库版本运行时需要把bin目录下的 DLL 路径加到 PATH或者把 DLL 复制到 exe 同目录。解决把编译包根目录下bin文件夹的完整路径加入系统 PATH然后重启 VS 和终端。注意已有的 CMD 窗口不会自动刷新 PATH必须新开窗口。6. 性能验证从 40 秒到 3 秒的调优细节6.1 数据规模和管线设计的影响GPU 加速的收益和点云规模强相关。当点云只有几千个点时GPU 版的优势几乎可以忽略甚至因为上传下载的时间开销比 CPU 慢。我的经验阈值是单帧超过 5 万个点GPU 版才有明显收益。当点云到了 20 万级别CPU 版的欧式聚类耗时在 40 秒左右GPU 版跑到 3 秒收益超过 10 倍。管线设计上最忌讳的是每帧都重新上传整个点云。如果点云在连续帧间只有小幅变化可以复用DeviceArray的显存空间只更新变化部分。但 PCL 的upload接口不支持局部更新所以数据量如果超过显存容量就得换策略比如分块处理或者降采样后再上传。6.2 两个值得跑的验证实验第一个实验验证法线估计的加速比。用同一个 PCD 文件分别跑 CPU 版和 GPU 版的法线估计各跑 5 次取平均时间。注意首帧通常包含 CUDA context 初始化和 kernel 编译时间要排除掉。我用的方法是先跑一次预热再记录后面 4 次的耗时。第二个实验验证 octree 构建与最近邻搜索。CPU 版构建一个 20 万点云的 KdTree 大约 1.2 秒GPU 版构建 octree 大约 0.15 秒。搜索阶段查询 10 万个点找最近邻CPU 版 2.4 秒GPU 版 0.4 秒。加速比在 6 倍左右不如聚类那么夸张但也值得优化。6.3 一个容易被忽略的并行坑不要在同一台机器上同时跑多个 GPU 任务抢显存。我的项目之前同时开了法线估计和聚类两个 GPU 模块显存占用超过 2GB 后CUDA driver 会在内核启动时等待空闲显存时间消耗反而比 CPU 版更差。解决办法是串行执行 GPU 任务或者控制点云上传量。从那以后我每次配置 PCL CUDA 环境都会先做一次 GPU 版的基准测试确认加速比符合预期后再继续调算法参数。如果加速比低于 5 倍说明配置有问题早点排查比后期再回头找原因成本低得多。这套流程跑顺之后处理点云数据的效率提升是实实在在的希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表