Ubuntu 16.04源码编译PCL 1.8:依赖陷阱与报错排查实战
1. 为什么PCL 1.8在Ubuntu 16.04上是个老大难如果你在Ubuntu 16.04上折腾过PCLPoint Cloud Library点云库1.8大概率经历过这样的场景照着官方教程敲完sudo apt-get install libpcl-dev结果装上的版本是1.7.1跟项目要求的1.8对不上或者从源码编译cmake跑到一半突然报出一堆关于VTK、Boost、Eigen的依赖错误屏幕刷得比翻书还快最后只能对着满屏红字发呆。PCL 1.8这个版本有点特殊。它发布于2017年前后正好卡在Ubuntu 16.04Xenial Xerus的生命周期中段。Ubuntu 16.04官方源里默认带的是PCL 1.7.1而1.8引入了不少新特性比如对VTK 7的支持、改进的KDTree搜索、新的点云配准算法等。很多做三维重建、SLAM、激光雷达处理的团队当时都指定要用1.8版本。问题在于Ubuntu 16.04自带的依赖库版本尤其是VTK和Boost跟PCL 1.8的要求存在微妙的错位这就导致安装过程远没有想象中那么一键完成。这篇文章面向的是需要在Ubuntu 16.04上从源码编译安装PCL 1.8的开发者不管你是刚接触点云处理的新手还是被依赖问题折磨过的老手我都会把整个流程拆开讲清楚。重点不是给你一串命令让你复制粘贴而是让你明白每一步为什么要这么做遇到报错时该往哪个方向排查。我会按照依赖准备→源码编译→常见报错排查→验证与使用的顺序展开中间穿插我自己踩过的坑和总结出来的经验。提示本文假设你已经有一台能正常联网的Ubuntu 16.04机器并且具备基本的Linux命令行操作能力。如果你用的是虚拟机建议内存至少分配4GB编译PCL 1.8对内存的消耗不小。2. 依赖库的版本陷阱VTK、Boost、Eigen到底该装哪个2.1 Ubuntu 16.04源里的依赖版本现状在动手编译PCL 1.8之前必须先搞清楚Ubuntu 16.04官方源里各个依赖库的版本。很多人编译失败根本原因就是依赖版本不匹配。我整理了一张表把PCL 1.8需要的核心依赖和Ubuntu 16.04源里的实际版本做个对照依赖库PCL 1.8要求Ubuntu 16.04源版本是否满足Boost≥1.581.58刚好满足Eigen≥3.03.2.92满足VTK≥6.2推荐7.x6.2勉强满足FLANN≥1.71.7.1满足Qhull≥2012.12012.1满足OpenNI2可选未默认安装需手动处理从表里能看出来Boost和Eigen基本没问题FLANN和Qhull也够用。真正容易出问题的是VTK。PCL 1.8的CMake配置里对VTK的版本检查比较严格Ubuntu 16.04源里的VTK 6.2虽然理论上满足最低要求但在实际编译过程中VTK 6.2的某些头文件路径和PCL 1.8的find_package(VTK)逻辑存在冲突尤其是涉及到vtkPolyData和vtkVertexGlyphFilter这些模块时容易出现找不到符号的错误。2.2 依赖安装的正确姿势我的建议是先把基础依赖通过apt装好VTK如果源里的版本能用就先用实在不行再考虑从源码编译VTK 7。以下是基础依赖的安装命令sudo apt-get update sudo apt-get install -y git build-essential cmake cmake-gui \ libboost-all-dev libeigen3-dev libflann-dev libvtk6-dev \ libqhull-dev libusb-1.0-0-dev libopenni2-dev这里有几个细节值得说明。第一libvtk6-dev装的是VTK 6.2的开发文件如果你后面编译PCL时遇到VTK相关的链接错误可以尝试换成libvtk7-dev但Ubuntu 16.04的官方源里没有VTK 7的包需要自己添加PPA或者源码编译。第二libopenni2-dev是可选依赖如果你不需要处理Kinect等深度相机的数据可以不装但装了也没坏处PCL会自动检测。第三cmake-gui建议装上后面排查CMake配置问题时图形界面比命令行直观得多。注意不要试图用apt-get install libpcl-dev来安装PCL 1.8Ubuntu 16.04源里只有1.7.1。如果你已经装了1.7.1建议先卸载干净避免头文件和库文件混在一起导致编译时出现莫名其妙的错误。卸载命令是sudo apt-get remove libpcl-dev然后再手动删除/usr/include/pcl-1.7和/usr/lib/x86_64-linux-gnu/libpcl_*残留文件。2.3 为什么VTK版本这么关键VTK在PCL里主要负责点云的可视化比如pcl_visualizer这个工具就依赖VTK。PCL 1.8的源码里visualization模块的CMakeLists.txt对VTK版本有明确的判断逻辑。如果检测到的VTK版本低于7.0它会禁用某些可视化功能但不会直接报错。问题在于Ubuntu 16.04的VTK 6.2在编译时默认使用的C标准是C98而PCL 1.8的部分代码用了C11的特性这就可能导致链接阶段出现undefined reference错误。我当时的解决办法是在CMake配置时显式指定C11标准cmake -DCMAKE_CXX_FLAGS-stdc11 ..这一行看起来简单但能解决不少因为C标准不一致导致的编译错误。如果你用的是VTK 7这个问题就不存在因为VTK 7默认就是C11。3. 从源码编译PCL 1.8的完整流程3.1 获取源码与目录规划PCL 1.8的源码可以从官方GitHub仓库获取。我建议把源码放在~/pcl目录下编译目录单独建一个build文件夹这样源码和编译产物分开后面清理或者重新编译都方便。cd ~ git clone https://github.com/PointCloudLibrary/pcl.git pcl cd pcl git checkout pcl-1.8.0这里注意git checkout pcl-1.8.0是切换到1.8.0的标签。如果你直接克隆master分支那是更新的版本可能引入新的依赖问题。PCL 1.8.0是1.8系列里最稳定的发布版建议就用这个。3.2 CMake配置阶段的关键参数进入源码目录后创建build目录并运行cmakecd ~/pcl mkdir build cd build cmake -DCMAKE_BUILD_TYPERelease \ -DCMAKE_CXX_FLAGS-stdc11 \ -DBUILD_GPUOFF \ -DBUILD_appsOFF \ -DBUILD_examplesOFF \ -DBUILD_toolsON \ -DBUILD_visualizationON \ ..这几个参数我逐个解释一下。CMAKE_BUILD_TYPERelease是开启优化编译出来的库性能更好但编译时间会更长。BUILD_GPUOFF是因为GPU模块依赖CUDA如果你没有NVIDIA显卡或者没装CUDA开了只会报错。BUILD_apps和BUILD_examples关掉可以节省大量编译时间除非你需要那些示例程序。BUILD_toolsON会编译pcl_viewer等实用工具建议保留。BUILD_visualizationON是开启可视化模块如果你不需要可视化可以关掉能省不少事。CMake配置完成后终端会输出一份总结列出哪些模块会被编译、哪些依赖被找到。一定要仔细看这份总结特别是VTK、Boost、Eigen这几项确认它们的状态是found而不是not found。如果某个依赖显示未找到后面编译大概率会失败。3.3 编译与安装的实操细节配置没问题后就可以开始编译了。PCL 1.8的代码量很大即使用make -j4并行编译在普通机器上也要跑20到40分钟。如果你的机器内存小于4GB建议用make -j2甚至make单线程避免内存不足导致编译进程被系统杀掉。make -j4编译过程中如果出现错误终端会停在出错的地方。这时候不要慌先看错误信息里提到的文件名和行号然后去源码里找到对应的文件看看是哪一行代码出了问题。常见的错误类型包括头文件找不到、符号未定义、类型不匹配。头文件找不到通常是依赖库的include路径没配置对符号未定义往往是链接阶段缺少某个库类型不匹配则可能是C标准或者依赖库版本的问题。编译完成后安装到系统目录sudo make install sudo ldconfigldconfig这一步很重要它刷新系统的动态链接库缓存让新安装的PCL库能被正确找到。很多人编译安装完PCL后运行程序时提示找不到libpcl_common.so就是因为忘了执行ldconfig。4. 编译过程中最常见的五类报错与排查思路4.1 VTK相关报错从找不到头文件到符号未定义VTK相关的报错是PCL 1.8编译过程中出现频率最高的。典型错误信息长这样fatal error: vtkPolyData.h: No such file or directory或者undefined reference to vtkPolyData::New()第一种情况说明CMake没有正确找到VTK的include路径。解决办法是在CMake配置时手动指定VTK_DIRcmake -DVTK_DIR/usr/lib/cmake/vtk-6.2 ..注意路径里的vtk-6.2要跟你实际安装的VTK版本对应。你可以用dpkg -L libvtk6-dev | grep vtkConfig.cmake来找到确切的路径。第二种情况是链接阶段找不到VTK的库文件。这通常是因为CMake找到了VTK的头文件但没有正确链接对应的库。你可以在CMakeLists.txt里手动添加find_package(VTK REQUIRED) include(${VTK_USE_FILE}) target_link_libraries(your_target ${VTK_LIBRARIES})如果你是在编译PCL本身时遇到这个问题那可能是PCL的CMake脚本对VTK 6.2的兼容性有问题。我当时的做法是升级到VTK 7虽然麻烦一点但一劳永逸。4.2 Boost相关报错版本够但组件缺失Boost的报错通常长这样fatal error: boost/make_shared.hpp: No such file or directory或者undefined reference to boost::system::system_category()第一种情况说明Boost的头文件路径没被正确包含。Ubuntu 16.04的Boost头文件在/usr/include/boost正常情况下CMake能自动找到。如果找不到检查一下是不是装了多个版本的Boost导致路径混乱。第二种情况是链接时缺少Boost的某个组件库。PCL 1.8依赖Boost的system、filesystem、thread等组件。你可以用dpkg -l | grep libboost看看装了哪些Boost库。如果缺少某个组件用sudo apt-get install libboost-system-dev libboost-filesystem-dev libboost-thread-dev补上。4.3 Eigen相关报错路径与对齐问题Eigen的报错相对少见但一旦出现就比较棘手。典型错误是fatal error: Eigen/Core: No such file or directory这说明Eigen的include路径没配置对。Ubuntu 16.04的Eigen头文件在/usr/include/eigen3但有些CMake脚本会去/usr/include/Eigen找路径对不上就报错。解决办法是在CMake配置时加上cmake -DEIGEN_INCLUDE_DIR/usr/include/eigen3 ..另一个Eigen相关的坑是内存对齐问题。PCL的某些类使用了Eigen的固定大小向量化类型如果编译时没有开启-marchnative或者对齐选项不对运行时会直接崩溃报invalid pointer或者segmentation fault。解决办法是在CMake配置时加上cmake -DCMAKE_CXX_FLAGS-stdc11 -marchnative ..4.4 FLANN与Qhull的隐形问题FLANN和Qhull的报错通常不会直接出现在编译阶段而是隐藏在链接或者运行阶段。比如编译时一切正常但运行程序时提示error while loading shared libraries: libflann.so.1.7: cannot open shared object file这说明系统找不到FLANN的动态库。解决办法是确认/usr/lib/x86_64-linux-gnu下有libflann.so然后执行sudo ldconfig刷新缓存。如果还是没有检查一下是不是FLANN装到了/usr/local/lib下而/etc/ld.so.conf里没有包含这个路径。Qhull的问题更隐蔽它主要用于凸包计算PCL的surface模块会用到。如果Qhull版本不匹配编译时可能不报错但运行相关功能时会得到错误的结果或者直接崩溃。建议用dpkg -l | grep qhull确认版本确保是2012.1或更高。4.5 内存不足导致的编译中断这个问题在虚拟机上特别常见。编译PCL 1.8的某些模块比如segmentation和registration时单个编译单元的内存占用可能超过2GB。如果你的机器只有2GB内存make -j4会同时跑4个编译进程内存瞬间被吃满系统会触发OOM Killer把编译进程杀掉终端上看到的就是Killed或者c: internal compiler error: Killed (program cc1plus)。解决办法有两个一是减少并行编译的进程数用make -j1或者make -j2二是增加交换空间swap。临时增加swap的方法sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile这样就有了4GB的交换空间编译时内存不够会用到swap虽然速度慢一点但至少不会中断。5. 安装后的验证与项目集成5.1 用pcl_viewer快速验证安装结果编译安装完成后第一件事是验证PCL是否真的可用。最简单的方法是运行pcl_viewer这是PCL自带的一个点云可视化工具。如果你编译时开了BUILD_toolsON它应该已经被安装到/usr/local/bin下。pcl_viewer如果终端输出用法说明说明PCL的基本功能正常。如果提示command not found检查一下/usr/local/bin是否在PATH里。如果提示找不到某个动态库回到上一节检查ldconfig是否执行了。更进一步的验证是写一个最小的C程序读取一个点云文件并输出点数。创建一个test_pcl.cpp#include pcl/io/pcd_io.h #include pcl/point_types.h #include iostream int main() { pcl::PointCloudpcl::PointXYZ::Ptr cloud(new pcl::PointCloudpcl::PointXYZ); if (pcl::io::loadPCDFilepcl::PointXYZ(test.pcd, *cloud) -1) { std::cerr Couldnt read file test.pcd std::endl; return -1; } std::cout Loaded cloud-width * cloud-height data points from test.pcd std::endl; return 0; }对应的CMakeLists.txtcmake_minimum_required(VERSION 3.5) project(test_pcl) find_package(PCL 1.8 REQUIRED) include_directories(${PCL_INCLUDE_DIRS}) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS}) add_executable(test_pcl test_pcl.cpp) target_link_libraries(test_pcl ${PCL_LIBRARIES})编译运行mkdir build cd build cmake .. make ./test_pcl如果一切正常你会看到Loaded XXX data points的输出。这一步能跑通说明PCL 1.8已经正确安装并且可以被项目引用了。5.2 在ROS项目中集成PCL 1.8的注意事项如果你是在ROSRobot Operating System环境下使用PCL情况会稍微复杂一点。ROS Kinetic默认依赖的是PCL 1.7如果你手动安装了PCL 1.8可能会出现版本冲突。ROS的pcl_ros包在编译时会去/usr/include/pcl-1.7找头文件而你装的1.8在/usr/local/include/pcl-1.8路径对不上就会报错。解决办法是在ROS包的CMakeLists.txt里显式指定PCL的路径set(PCL_DIR /usr/local/share/pcl-1.8) find_package(PCL 1.8 REQUIRED)同时确保/usr/local/lib在链接器的搜索路径里。如果ROS的pcl_ros包本身需要重新编译你可能需要修改它的CMakeLists.txt把PCL版本要求改成1.8。这个过程比较繁琐但一旦配置好后续使用就没问题了。提示如果你不需要ROS的pcl_ros包只是在自己的节点里用PCL那就不需要重新编译pcl_ros直接在节点的CMakeLists.txt里find_package(PCL 1.8 REQUIRED)即可。5.3 版本共存与切换的实用技巧有时候你既需要PCL 1.7比如某些老项目依赖又需要PCL 1.8。这种情况下建议把PCL 1.8安装到/usr/local默认路径而PCL 1.7保持在/usr下。然后在项目的CMakeLists.txt里通过PCL_DIR变量来指定用哪个版本# 用PCL 1.8 set(PCL_DIR /usr/local/share/pcl-1.8) find_package(PCL 1.8 REQUIRED) # 用PCL 1.7 set(PCL_DIR /usr/share/pcl-1.7) find_package(PCL 1.7 REQUIRED)这样切换起来比较灵活不需要反复卸载安装。唯一需要注意的是两个版本的头文件路径不同编译时不会互相干扰但运行时如果同时加载了两个版本的库可能会出问题。所以一个程序里只用其中一个版本。6. 几个让我印象深刻的踩坑经历6.1 那个让我折腾了一下午的undefined reference to pcl::search::Search这个错误出现在我编译一个自定义的PCL程序时。代码里用了pcl::search::KdTree头文件也包含了#include pcl/search/kdtree.h但链接时就是报undefined reference to pcl::search::Searchpcl::PointXYZ::Search()。我检查了CMakeLists.txtfind_package(PCL 1.8 REQUIRED)和target_link_libraries都写了${PCL_LIBRARIES}也加上了但就是不行。后来我用make VERBOSE1看了实际的链接命令发现链接器只链接了libpcl_common.so和libpcl_io.so没有链接libpcl_search.so。原因是${PCL_LIBRARIES}变量里虽然包含了所有PCL库但CMake在解析时可能因为某些原因漏掉了pcl_search。解决办法是手动把pcl_search加到链接列表里target_link_libraries(your_target ${PCL_LIBRARIES} pcl_search)这个问题的根源是PCL的CMake配置文件在某些情况下不能正确导出所有组件库。如果你也遇到类似的undefined reference先确认对应的库有没有被链接上用make VERBOSE1看链接命令是最直接的排查方法。6.2 VTK 6.2与PCL 1.8的爱恨情仇前面提到过VTK版本的问题我这里再展开说一下。我一开始用的是Ubuntu 16.04源里的VTK 6.2编译PCL 1.8时visualization模块一直报错错误信息是vtkVertexGlyphFilter相关的符号找不到。我查了VTK 6.2的文档发现vtkVertexGlyphFilter这个类在VTK 6.2里是存在的但PCL 1.8的代码里用了一些VTK 7才引入的API比如vtkPolyData::SetPoints的重载版本。最后的解决办法是从源码编译VTK 7.1。这个过程本身也不轻松VTK 7.1的编译需要CMake 3.1以上Ubuntu 16.04自带的CMake是3.5刚好够用。编译VTK 7.1大概花了30分钟安装到/usr/local下。然后重新配置PCL的CMake指定VTK_DIR/usr/local/lib/cmake/vtk-7.1这次visualization模块顺利编译通过。如果你不想编译VTK 7另一个办法是关掉PCL的visualization模块-DBUILD_visualizationOFF这样就不需要VTK了。但代价是你不能用pcl_viewer等可视化工具对于调试点云算法来说不太方便。6.3 编译到99%时突然报错是什么体验有一次我编译PCL 1.8前面99%的模块都顺利通过了最后一个模块好像是tools报错错误信息是cannot find -lvtkRenderingOpenGL。这个问题是因为VTK 6.2的库文件名跟PCL 1.8的CMake脚本里写的不一致。VTK 6.2的OpenGL渲染库叫libvtkRenderingOpenGL.so但PCL的CMake脚本可能去找libvtkRenderingOpenGL2.soVTK 7的名字。解决办法是在/usr/lib/x86_64-linux-gnu下创建一个软链接sudo ln -s libvtkRenderingOpenGL.so libvtkRenderingOpenGL2.so这个操作有点 hack但确实能解决问题。不过更推荐的做法还是升级到VTK 7避免这类兼容性问题。7. 关于PCL 1.8安装的最后几点经验如果你问我在Ubuntu 16.04上装PCL 1.8最省事的方案是什么我会说先把VTK 7编译安装好然后再编译PCL 1.8。虽然多花30分钟编译VTK但能避免后面80%的报错。VTK 6.2虽然理论上能用但实际用起来坑太多不值得在这上面浪费时间。另外编译PCL 1.8时一定要留足内存和磁盘空间。源码加上编译产物大概需要5到8GB的磁盘空间。如果你的/home分区比较小建议把源码放到空间大的分区下编译。内存方面4GB是底线8GB以上会比较从容。还有一点如果你只是需要用PCL的基本功能比如读写PCD文件、做简单的滤波和配准其实可以考虑用PCL 1.7.1Ubuntu 16.04源里直接apt-get install就能装省去编译的麻烦。只有当你确实需要1.8的新特性比如改进的NormalEstimation或者新的Registration算法时才值得折腾源码编译。最后分享一个我常用的检查命令安装完成后跑一下能快速确认PCL的版本和关键依赖pkg-config --modversion pcl_common pkg-config --cflags --libs pcl_common如果输出里显示版本是1.8.0并且include路径指向/usr/local/include/pcl-1.8那就说明安装成功了。如果版本还是1.7.1检查一下PKG_CONFIG_PATH环境变量确保/usr/local/lib/pkgconfig在搜索路径里。