1. 为什么PCL 1.8在Ubuntu 16.04上是个“硬骨头”
搞点云处理的人,绕不开PCL(Point Cloud Library)。不管是做SLAM、三维重建,还是激光雷达标定,PCL基本都是标配。但如果你手上的开发环境恰好是Ubuntu 16.04,又想装PCL 1.8,那这件事就有点意思了——它不是那种“一条命令搞定”的活儿,而是一个需要你理解依赖关系、编译原理和版本兼容性的系统工程。
我先说结论:Ubuntu 16.04自带的PCL版本是1.7.x,而PCL 1.8引入了一些API变化和新模块(比如对VTK 7的支持、更完善的KDTree实现、以及一些点云滤波器的优化),很多人因为项目需要必须上1.8。但Ubuntu 16.04的默认软件源里没有PCL 1.8的预编译包,你只能自己编译,或者找第三方PPA。自己编译这件事,坑多到你怀疑人生。
这篇文章适合谁看?如果你是一个在Ubuntu 16.04上做点云相关开发的工程师或研究生,正在被PCL 1.8的安装折磨,或者你还没开始装但想提前知道会遇到什么,那这篇内容就是给你写的。我会把整个安装过程中最常遇到的问题、背后的原因、以及我实际踩过的坑,一个一个拆开讲清楚。不是那种“复制粘贴命令就完事”的教程,而是让你理解为什么要这么做,以及出问题了怎么自己排查。
2. 安装前的整体思路与方案选型
2.1 为什么我不推荐直接apt install
很多人的第一反应是sudo apt-get install libpcl-dev。在Ubuntu 16.04上,这条命令装出来的是PCL 1.7.1。如果你项目里用的API恰好是1.8才有的,比如某些pcl::PointCloud的新的成员函数,或者你依赖的某个库明确要求PCL 1.8,那1.7.1就不够用。
有人会想去找PPA。确实有一些第三方维护的PPA提供PCL 1.8的deb包,但这里有几个问题:第一,PPA的维护者不一定及时更新,依赖关系可能和你的系统其他库冲突;第二,PPA装的PCL可能缺少某些可选模块(比如可视化模块对VTK的依赖版本不对);第三,也是最关键的,你无法控制编译选项,比如是否开启CUDA、是否用特定的SIMD指令集优化。对于做性能敏感的点云处理来说,这些编译选项直接影响运行效率。
所以我的建议是:源码编译。虽然麻烦,但可控。
2.2 源码编译的整体流程
源码编译PCL 1.8的流程大致是这样的:先装一堆依赖库,然后从GitHub下载PCL 1.8的源码,用CMake配置,make编译,最后make install。听起来简单,但每一步都有坑。
依赖库这块,PCL 1.8需要的核心依赖包括:Boost(1.58以上)、Eigen(3.0以上)、FLANN(1.7以上)、VTK(6.2以上,但推荐7.x)、QHull(2012.1以上)。可选依赖包括:OpenNI2(点云采集)、Qt5(可视化界面)、CUDA(GPU加速)。Ubuntu 16.04的默认源里,Boost是1.58,Eigen是3.2,FLANN是1.7.1,VTK是6.2。看起来版本都够,但问题出在VTK上——PCL 1.8的可视化模块对VTK 7的支持更好,而Ubuntu 16.04默认是VTK 6.2,这就导致如果你要用可视化功能,可能需要自己编译VTK 7。
还有一个隐藏的坑:Ubuntu 16.04默认的GCC版本是5.4,这个版本编译PCL 1.8是没问题的,但如果你之前装过其他版本的GCC并且切换过默认编译器,可能会遇到C++标准不兼容的问题。PCL 1.8要求C++11,GCC 5.4默认支持C++11,但如果你用的是GCC 4.8,那就得手动加-std=c++11。
2.3 依赖库的版本匹配逻辑
这里我要展开讲一下为什么版本匹配这么重要。PCL的各个模块之间是有依赖关系的,比如pcl_visualization依赖VTK,pcl_kdtree依赖FLANN,pcl_search依赖FLANN和KdTree。如果你装的VTK版本和PCL编译时期望的不一致,链接阶段就会报错,典型的是“undefined reference tovtkXXX”这种。
我实测下来,PCL 1.8和VTK 7.1的搭配是最稳的。VTK 6.2虽然也能编译,但在使用pcl::visualization::PCLVisualizer的时候,某些渲染窗口的回调函数会有问题,比如鼠标事件不响应。这个坑我在一个三维重建项目里踩过,当时调了整整两天才发现是VTK版本的问题。
FLANN的版本也要注意。Ubuntu 16.04默认的FLANN 1.7.1是够用的,但如果你自己编译FLANN,记得要装libflann-dev,而且编译PCL的时候要确保CMake能找到FLANN的config文件。有时候FLANN装在了/usr/local/lib,但CMake默认只搜/usr/lib,这就需要手动指定FLANN_ROOT。
3. 核心依赖库的安装与避坑指南
3.1 Boost和Eigen:看似简单,也有暗坑
Boost和Eigen是PCL的基础依赖,Ubuntu 16.04的默认源里都有。安装命令很简单:
sudo apt-get install libboost-all-dev libeigen3-dev但这里有个细节:libboost-all-dev会装一大堆Boost的组件,包括一些你可能用不到的。如果你磁盘空间紧张,可以只装必要的:libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-date-time-dev libboost-iostreams-dev。不过我个人建议全装,因为PCL的某些模块可能会间接依赖其他Boost组件,少装了后面报错更麻烦。
Eigen的坑在于头文件路径。Ubuntu 16.04的libeigen3-dev装完后,头文件在/usr/include/eigen3,但有些代码里写的是#include <Eigen/Core>,这就需要你在编译时加-I/usr/include/eigen3。CMake里可以用include_directories(/usr/include/eigen3),或者更规范的做法是用find_package(Eigen3 REQUIRED)然后include_directories(${EIGEN3_INCLUDE_DIR})。
注意:如果你之前手动编译过Eigen并且装在了
/usr/local/include/eigen3,那系统里可能有两个版本的Eigen。这时候CMake可能会找到错误的版本,导致编译报错。解决办法是用sudo updatedb && locate Eigen/Core确认实际路径,然后在CMake里显式指定。
3.2 FLANN:版本和路径的双重考验
FLANN的安装命令:
sudo apt-get install libflann-devUbuntu 16.04装的是FLANN 1.7.1,这个版本对PCL 1.8来说够用。但如果你要自己编译FLANN(比如你需要最新的FLANN 1.8+),那就要注意了:自己编译的FLANN默认装在/usr/local,而PCL的CMake脚本默认去/usr找。这时候你需要在CMake配置PCL时加上:
cmake -DFLANN_INCLUDE_DIR=/usr/local/include \ -DFLANN_LIBRARY=/usr/local/lib/libflann.so ..或者更简单的方法:在/usr/local/lib下确认有libflann.so和libflann_cpp.so,然后设置环境变量LD_LIBRARY_PATH包含/usr/local/lib。
我遇到过一个很隐蔽的问题:系统里同时存在libflann1.7和libflann1.8,编译PCL时CMake找到了1.8的头文件但链接到了1.7的库,结果运行时报“symbol lookup error”。排查方法是ldd你的可执行文件,看它实际链接的是哪个FLANN库。
3.3 VTK:最折腾的依赖,没有之一
VTK是PCL可视化模块的核心依赖,也是整个安装过程中最容易出问题的部分。Ubuntu 16.04默认的VTK是6.2,但PCL 1.8的官方文档推荐VTK 7.x。如果你不需要可视化功能,可以在编译PCL时加-DBUILD_visualization=OFF,这样就绕过了VTK。但如果你要做点云可视化,那VTK这一关必须过。
自己编译VTK 7.1的步骤大致是:下载源码,用CMake配置,make,make install。配置的时候要注意几个选项:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local \ -DBUILD_SHARED_LIBS=ON \ -DVTK_Group_Qt=ON \ -DVTK_QT_VERSION=5 \ -DCMAKE_BUILD_TYPE=Release ..VTK_Group_Qt和VTK_QT_VERSION这两个选项是为了支持Qt5的可视化界面。如果你不需要Qt,可以关掉。BUILD_SHARED_LIBS=ON是生成动态库,这样PCL链接的时候更方便。
编译VTK 7.1的时间比较长,取决于你的机器配置,大概20到40分钟。编译完之后,make install会把库装到/usr/local/lib,头文件在/usr/local/include/vtk-7.1。
提示:VTK 7.1编译完后,你可能需要运行
sudo ldconfig来更新动态库缓存,否则PCL编译时可能找不到VTK的库。
还有一个坑:如果你之前装过VTK 6.2(比如通过apt),那系统里会有两个VTK。PCL的CMake脚本可能会优先找到6.2的config文件。解决办法是在CMake配置PCL时显式指定-DVTK_DIR=/usr/local/lib/cmake/vtk-7.1。
3.4 QHull和其他可选依赖
QHull是PCL做凸包计算和曲面重建时需要的库:
sudo apt-get install libqhull-devUbuntu 16.04的QHull版本是2012.1,满足PCL 1.8的要求。但如果你要用pcl::ConcaveHull,建议装QHull 2015+,因为2012.1在某些边界情况下会有数值精度问题。自己编译QHull的话,注意它的头文件路径是/usr/local/include/libqhull,CMake里要设置QHULL_INCLUDE_DIR。
OpenNI2是可选的,只有你需要从深度相机(比如Kinect)采集点云时才需要。Ubuntu 16.04可以sudo apt-get install libopenni2-dev,但版本比较老。如果你用的是较新的深度相机,可能需要自己编译OpenNI2。
4. 源码编译PCL 1.8的完整实操过程
4.1 下载源码与目录结构
PCL 1.8的源码可以从GitHub的release页面下载。我建议下载tar.gz包而不是直接git clone,因为git clone会拉取整个历史记录,体积大且没必要。下载后解压:
tar -xzvf pcl-1.8.0.tar.gz cd pcl-1.8.0目录结构大概是:common、io、kdtree、search、visualization等模块各占一个子目录,根目录有CMakeLists.txt。编译前建议先看一下CMakeLists.txt里的PCL_VERSION,确认是1.8.0。
4.2 CMake配置的关键参数
在源码目录下建一个build目录,然后进去运行CMake:
mkdir build && cd build cmake -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DBUILD_visualization=ON \ -DBUILD_apps=OFF \ -DBUILD_examples=OFF \ -DBUILD_tools=OFF \ -DBUILD_global_tests=OFF \ -DVTK_DIR=/usr/local/lib/cmake/vtk-7.1 \ ..这里解释一下几个关键参数:
CMAKE_BUILD_TYPE=Release:生成优化后的代码,运行速度快。如果你要调试PCL本身,可以用Debug,但编译出来的库会大很多。CMAKE_INSTALL_PREFIX=/usr/local:安装路径。不建议装到/usr,因为会和apt装的PCL 1.7冲突。BUILD_visualization=ON:开启可视化模块。如果你不需要,设为OFF可以省去VTK的麻烦。BUILD_apps、BUILD_examples、BUILD_tools:这些是PCL自带的示例程序和工具,编译它们会额外花时间,而且容易因为依赖不全而报错。建议全部设为OFF。BUILD_global_tests=OFF:不编译测试代码,省时间。VTK_DIR:显式指定VTK的cmake配置目录,避免找到错误的VTK版本。
CMake配置完成后,它会输出一个摘要,列出哪些模块会被编译,哪些依赖找到了,哪些没找到。一定要仔细看这个摘要。如果某个你需要的模块显示“NO”,那就说明对应的依赖没找到,需要回去检查。
4.3 编译过程中的资源管理与时间预估
配置成功后,开始编译:
make -j$(nproc)-j$(nproc)表示用所有CPU核心并行编译。PCL 1.8的代码量不小,在4核机器上大概需要30到50分钟,8核机器上20到30分钟。编译过程中内存占用会比较高,特别是visualization模块,因为VTK的模板代码很吃内存。如果你的机器内存小于8GB,建议用make -j2或者make -j4,避免内存耗尽导致编译进程被系统杀掉。
编译过程中最常见的错误是“fatal error: XXX.h: No such file or directory”。这通常是某个依赖的头文件路径没被正确包含。比如pcl_visualization编译时报“vtkRenderer.h not found”,那就是VTK的头文件路径没加进去。解决办法是检查CMake配置时VTK_DIR是否设置正确,或者手动在CMakeLists.txt里加include_directories(${VTK_INCLUDE_DIRS})。
另一个常见错误是链接错误:“undefined reference toXXX”。这通常是库的顺序问题或者库没找到。PCL的CMake脚本一般会处理好链接顺序,但如果你自己改了CMakeLists.txt,可能会打乱顺序。解决办法是确保依赖库在被依赖库的后面,比如-lpcl_visualization -lvtkRendering。
4.4 安装与环境变量配置
编译完成后,安装:
sudo make install安装完成后,PCL的头文件在/usr/local/include/pcl-1.8,库文件在/usr/local/lib,CMake配置文件在/usr/local/share/pcl-1.8。
接下来要配置环境变量,让系统能找到PCL。编辑~/.bashrc,添加:
export PCL_ROOT=/usr/local export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib然后source ~/.bashrc。LD_LIBRARY_PATH是为了让运行时能找到PCL的动态库。如果你不设置这个,运行程序时可能会报“error while loading shared libraries: libpcl_common.so.1.8”。
注意:有些系统里
/usr/local/lib已经在默认的库搜索路径里了,那就不需要额外设置LD_LIBRARY_PATH。你可以用ldconfig -p | grep pcl来确认。
4.5 验证安装是否成功
写一个最简单的测试程序:
#include <pcl/point_types.h> #include <pcl/point_cloud.h> #include <iostream> int main() { pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>); cloud->width = 5; cloud->height = 1; cloud->points.resize(cloud->width * cloud->height); for (size_t i = 0; i < cloud->points.size(); ++i) { cloud->points[i].x = 1024 * rand() / (RAND_MAX + 1.0f); cloud->points[i].y = 1024 * rand() / (RAND_MAX + 1.0f); cloud->points[i].z = 1024 * rand() / (RAND_MAX + 1.0f); } std::cout << "Cloud size: " << cloud->points.size() << std::endl; return 0; }编译命令:
g++ test_pcl.cpp -o test_pcl -I/usr/local/include/pcl-1.8 -I/usr/include/eigen3 -L/usr/local/lib -lpcl_common -lpcl_io如果编译成功并且运行输出“Cloud size: 5”,那说明PCL 1.8的基本功能已经可用了。如果报错,根据错误信息回去检查对应的依赖。
5. 常见问题与排查技巧实录
5.1 编译时报“Could NOT find VTK”
这是最常见的问题。原因通常是CMake没找到VTK的配置文件。VTK的CMake配置文件叫VTKConfig.cmake,一般在/usr/local/lib/cmake/vtk-7.1或者/usr/lib/cmake/vtk-6.2。解决办法是在CMake配置PCL时加-DVTK_DIR=/path/to/vtk。如果你不确定VTK装在哪,用find / -name "VTKConfig.cmake" 2>/dev/null找一下。
还有一种情况是VTK找到了但版本不对。PCL 1.8要求VTK 6.2以上,如果你系统里只有VTK 5.x,那就会报版本不满足。这时候只能自己编译VTK 7.x。
5.2 运行时报“symbol lookup error”
这个错误通常发生在运行阶段,提示某个符号找不到。原因一般是链接时用的库版本和运行时加载的库版本不一致。比如编译时链接的是/usr/local/lib/libpcl_common.so.1.8,但运行时系统先找到了/usr/lib/libpcl_common.so.1.7。
排查方法:用ldd your_program | grep pcl看实际加载的是哪个库。如果是1.7的,那就需要调整LD_LIBRARY_PATH,把/usr/local/lib放在前面。或者更彻底的方法:卸载apt装的PCL 1.7(sudo apt-get remove libpcl-dev),避免冲突。
5.3 可视化模块编译失败
pcl_visualization模块编译失败的原因很多,最常见的是VTK版本不匹配和Qt版本问题。如果你用的是VTK 7.1和Qt5,一般没问题。但如果VTK编译时没开VTK_Group_Qt,那PCL的可视化模块就找不到Qt的接口,编译会报错。
解决办法:重新编译VTK,确保VTK_Group_Qt=ON和VTK_QT_VERSION=5。如果你不需要Qt界面,可以在PCL的CMake配置里加-DBUILD_visualization=OFF,直接跳过这个模块。
5.4 内存不足导致编译中断
编译PCL的visualization模块时,如果机器内存不够,make进程会被系统杀掉,报“Killed”或者“virtual memory exhausted”。解决办法是减少并行编译的job数,用make -j2或者make -j1。另外可以临时增加swap空间:
sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo swapon /swapfile编译完后再关掉swap:sudo swapoff /swapfile && sudo rm /swapfile。
5.5 常见问题速查表
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| CMake报“Could NOT find VTK” | VTK未安装或路径不对 | 指定-DVTK_DIR或安装VTK 7.x |
| 编译报“undefined reference” | 库链接顺序或版本不对 | 检查ldd输出,调整LD_LIBRARY_PATH |
| 运行报“symbol lookup error” | 运行时加载了旧版本库 | 卸载旧版PCL或调整库搜索路径 |
| 可视化模块编译失败 | VTK未开Qt支持 | 重编译VTK加VTK_Group_Qt=ON |
| 编译过程被Killed | 内存不足 | 减少-j数或增加swap |
| 找不到Eigen头文件 | 头文件路径未包含 | 加-I/usr/include/eigen3 |
| FLANN链接错误 | FLANN库路径不对 | 指定FLANN_LIBRARY或ldconfig |
5.6 几个我踩过的坑
第一个坑:我在一台机器上同时装了apt的PCL 1.7和源码编译的PCL 1.8,结果CMake配置新项目时总是找到1.7的config文件。后来发现是/usr/lib/cmake/pcl-1.7在CMake的搜索路径里优先级更高。解决办法是在项目的CMakeLists.txt里显式设置set(PCL_DIR "/usr/local/share/pcl-1.8")。
第二个坑:编译PCL 1.8时开了BUILD_examples=ON,结果examples里的某些代码依赖OpenNI2,而我没装OpenNI2,导致编译中断。后来把BUILD_examples关掉就顺利通过了。所以如果你不是非要看示例代码,建议关掉。
第三个坑:VTK 7.1编译完后没有运行sudo ldconfig,导致PCL编译时链接不到VTK的库。报错信息是“cannot find -lvtkCommonCore-7.1”,看起来像是库不存在,实际上是动态库缓存没更新。运行sudo ldconfig后问题解决。
6. 编译后的项目集成与CMake配置
6.1 在自己的项目中使用PCL 1.8
装好PCL 1.8后,你自己的项目怎么用它?最规范的方式是通过CMake的find_package:
cmake_minimum_required(VERSION 3.5) project(MyPCLProject) find_package(PCL 1.8 REQUIRED) include_directories(${PCL_INCLUDE_DIRS}) link_directories(${PCL_LIBRARY_DIRS}) add_definitions(${PCL_DEFINITIONS}) add_executable(my_app main.cpp) target_link_libraries(my_app ${PCL_LIBRARIES})这里的关键是find_package(PCL 1.8 REQUIRED),它会去找PCL 1.8的config文件。如果系统里同时有1.7和1.8,CMake可能会找到1.7。为了确保找到1.8,可以在find_package之前加:
set(PCL_DIR "/usr/local/share/pcl-1.8")这样CMake就会直接去指定目录找。
6.2 处理多版本共存的问题
如果你的系统里既有apt装的PCL 1.7,又有源码编译的PCL 1.8,那在编译项目时一定要小心。除了设置PCL_DIR,还要确保链接时用的是1.8的库。可以在CMakeLists.txt里打印${PCL_LIBRARIES}看看实际链接的是哪些库:
message(STATUS "PCL libraries: ${PCL_LIBRARIES}") message(STATUS "PCL include dirs: ${PCL_INCLUDE_DIRS}")如果输出的路径是/usr/lib而不是/usr/local/lib,那就说明找到的是1.7。这时候需要调整CMAKE_PREFIX_PATH:
set(CMAKE_PREFIX_PATH "/usr/local")6.3 静态链接与动态链接的选择
默认情况下,PCL编译的是动态库。如果你想把PCL静态链接到你的程序里(比如为了发布独立的可执行文件),需要在编译PCL时加-DBUILD_SHARED_LIBS=OFF。但静态链接PCL会带来一些问题:第一,可执行文件体积会很大(可能几百MB);第二,某些模块(比如可视化)静态链接时会有符号冲突。所以除非有特殊需求,一般还是用动态链接。
7. 一些实用的经验补充
如果你只是想做点云的基本处理(滤波、分割、配准),不需要可视化,那完全可以跳过VTK,编译PCL时加-DBUILD_visualization=OFF。这样能省去大量麻烦,编译时间也能缩短一半。
另外,如果你用的是Docker做开发环境,可以考虑基于ubuntu:16.04镜像自己构建一个包含PCL 1.8的镜像。Dockerfile里把依赖安装和PCL编译的步骤写进去,一次构建,到处使用。这样避免了在每台机器上重复踩坑。构建的时候注意把apt-get的缓存清理掉,减小镜像体积。
还有一点:PCL 1.8的某些模块对C++标准有要求,比如pcl::PointCloud的某些新特性需要C++11。如果你在项目里用的是C++98,可能会编译失败。解决办法是在CMakeLists.txt里加set(CMAKE_CXX_STANDARD 11)和set(CMAKE_CXX_STANDARD_REQUIRED ON)。
最后分享一个我个人的习惯:每次装完这种复杂的库,我都会写一个简单的测试程序,把常用的模块(common、io、kdtree、search、visualization)都调用一遍,确认没有链接错误和运行时错误。这个测试程序我会保留下来,以后换机器或者重装系统时直接拿来验证环境是否配好。这个习惯帮我省了很多“以为装好了结果项目编译不过”的时间。