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

资讯详情

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

DSP-SLAM在Ubuntu 20.04上的编译配置与踩坑实战指南

DSP-SLAM在Ubuntu 20.04上的编译配置与踩坑实战指南

1. 项目概述:DSP-SLAM是什么,为什么值得折腾一遍

先说结论:DSP-SLAM是一套把语义级物体先验融进稠密SLAM建图框架里的开源系统,说白了就是让机器人在建图的同时,不仅知道墙在哪、桌子在哪,还能通过预先训练好的3D物体模型,把这些物体以“可识别、可操作”的方式直接重建出来。我是在一个室内移动机器人项目里需要语义级障碍物感知时才盯上它的,当时在Ubuntu 20.04上折腾了差不多三个整天,踩了一堆编译和运行期的坑,最后把整个流程跑通之后,回头再看这套代码,其实它的架构思路非常清晰,只要你把依赖环境准备好,编译和运行并没有想象中那么劝退。

这篇文章就是把我当时在Ubuntu 20.04下配置DSP-SLAM的完整过程、踩坑记录、配置文件修改心得全部整理出来,给正在跟这套代码搏斗的人一个可以直接“抄作业”的参考。如果你是做视觉SLAM、语义建图、机器人导航方向的研究生或者工程师,又或者你只是对“语义先验如何和传统几何SLAM结合”感兴趣,这篇文章都值得往下看。

DSP-SLAM的核心创新点在于它没有把物体检测当作一个后处理步骤,而是把物体的3D模型(通过DeepSDF学出来的隐式曲面)直接放进SLAM的优化框架里,让这些物体模型反过来约束相机位姿和地图点。这意味着它跑出来的地图不只是“一堆点云”,而是“一幅带有物体级语义标签的结构化地图”——比如你在客厅走一圈,地图里除了点云墙,还有一个单独建模的沙发物体。这种表示对后续的机器人抓取、物体操作、人机交互都非常有价值。

我实测下来的总体感受是:这套代码对环境的挑剔程度属于“中等偏上”,不算是那种开箱即用的项目,但只要你的依赖版本卡准,按顺序编译,基本不会遇到无解的问题。下面我把整个配置过程拆开讲,每步都给出我当时为什么这么做、做了什么、遇到了什么问题,以及最终怎么解决的。

2. 环境准备与依赖选型思路

2.1 为什么选Ubuntu 20.04,底层依赖怎么对应

DSP-SLAM的代码仓库明确支持Ubuntu 20.04,官方README里给出的是基于ROS Noetic以及ORB-SLAM3的依赖体系。但注意,它其实不强制要求装ROS,你完全可以在纯Ubuntu 20.04系统上编译运行,只是它内部有一部分代码把ROS的可选依赖包出来了——如果你不打算接机器人平台,完全可以关掉ROS相关的宏定义。

系统层面的第一件事是确认编译器版本。Ubuntu 20.04默认的GCC是9.x,DSP-SLAM要求C++17标准,用GCC 9完全没问题。不建议你用Ubuntu 22.04去跑,因为22.04的GCC是11.x,OpenCV版本也更激进,编译时会遇到更多ABI兼容问题,没必要给自己加戏。

然后是关键依赖库的选型,我这里直接列一个我当时实测通过的版本组合:

依赖库推荐版本说明
Eigen33.3.7或更高线性代数基础库,建议用apt安装
OpenCV3.4.x(或4.2.0)图像处理和相机标定相关,4.x也可以
Pangolin0.6(commit 7b9b3c)可视化窗口库,建议从源码编译指定版本
CUDA11.0/11.1/11.2需要GPU支持,用于神经网络推理
cuDNN8.0/8.1与CUDA配套即可
g2o、DBoW2ORB-SLAM3内置版本不需要单独安装,跟着源码编译

提示:CUDA和cuDNN版本一定要配对,不要盲目装最新的。DSP-SLAM里DeepSDF的代码是基于PyTorch的C++部署方式编译的,PyTorch对CUDA版本非常敏感,我用CUDA 11.0 + cuDNN 8.0 + PyTorch 1.7.1这组组合顺利通过编译,目前来看是兼容性比较稳的搭配。

2.2 CUDA和cuDNN安装的实操记录

如果你机器上已经有NVIDIA驱动,可以先在终端里输入nvidia-smi确认驱动支持的CUDA版本。这一步非常关键,因为驱动决定的最高CUDA版本是硬上限。比如你的驱动是470.x,那它最高支持CUDA 11.4,你装CUDA 11.0就完全没问题。

安装CUDA 11.0的话,我用的是runfile方式,建议不要用deb方式,因为deb会把驱动也一起装,如果不小心覆盖了现有驱动,NVIDIA驱动和系统的内核模块一旦对不上,重启后会直接黑屏。我当时为了避免麻烦,选择把runfile下载到/tmp目录执行,并且在安装选项里取消勾选Driver,只安装CUDA Toolkit本身。

sudo sh cuda_11.0.3_450.51.06_linux.run --toolkit --silent --override

装完之后,一定要在~/.bashrc里加上环境变量,否则编译时找不到nvcc:

export PATH=/usr/local/cuda-11.0/bin:$PATH export LD_LIBRARY_PATH=/usr/local/cuda-11.0/lib64:$LD_LIBRARY_PATH export CUDA_HOME=/usr/local/cuda-11.0

cuDNN就更简单,去NVIDIA官网下载对应CUDA 11.0的cuDNN 8.0压缩包,然后解压把头文件和库文件拷贝到CUDA目录下即可。我当时下载的是cudnn-11.0-linux-x64-v8.0.5.39.tgz,解压后执行:

sudo cp cuda/include/cudnn.h /usr/local/cuda-11.0/include/ sudo cp cuda/lib64/libcudnn* /usr/local/cuda-11.0/lib64/ sudo chmod a+r /usr/local/cuda-11.0/include/cudnn.h /usr/local/cuda-11.0/lib64/libcudnn*

验签的方式是直接编译一遍,或者很简单地运行一下python3 -c "import torch; print(torch.cuda.is_available())",如果返回True,说明PyTorch已经能调起CUDA了。这个检查动作虽然基础,但是非常值得做,因为DSP-SLAM的cmake配置会在找PyTorch时顺便检查CUDA能力,如果这里失败,后面编译必挂。

2.3 Python环境和PyTorch版本的重要性

DSP-SLAM在运行阶段需要调用一个预训练的DeepSDF物体模型,这部分代码是用C++接PyTorch的libtorch来实现的,但构建和模型加载时会依赖Python侧的PyTorch来生成一些预处理数据。所以你需要保证Python环境里也有一个能和libtorch版本对得上的PyTorch。

我当时在系统里装的是Python 3.8(Ubuntu 20.04自带),用venv创建了一个干净的虚拟环境,然后执行:

pip install torch==1.7.1 torchvision==0.8.2

为什么强调版本一致?因为libtorch和torch的版本如果差太多,最典型的错误是undefined symbol或者module not found,这种问题排查起来很折磨人。你宁可多花二十分钟把版本对齐,也别在编译报错时去猜原因。

3. 源码获取与整体架构拆解

3.1 从GitHub拉取代码后的目录结构分析

DSP-SLAM的官方仓库地址在GitHub上,clone时记得带--recursive,因为它的第三方依赖(比如ORB-SLAM3的子模块)是通过submodule方式引入的。

git clone --recursive https://github.com/JingwenWang95/DSP-SLAM.git cd DSP-SLAM

拉完后你会发现代码结构非常典型,核心目录大致有这几个:

  • src/:SLAM主程序源码,包含系统初始化、跟踪、局部建图、回环检测等模块,这部分代码大量沿用了ORB-SLAM3的结构,如果你之前读过ORB-SLAM3的源码,看起来会很亲切。
  • src/DeepSDF:预训练DeepSDF模型的加载与推理接口,包括如何从shape code解码物体曲面、如何把隐式表示转换为点云等。
  • src/object_slam:DSP-SLAM特有的模块,专门负责物体级地图的表示和更新,包括物体检测结果如何关联、物体模型如何与点云对齐等。
  • Vocabulary/:ORB词袋模型文件,用于回环检测和重定位,这个文件比较大,如果下载失败或路径不对,回环检测一启动就会崩溃。
  • Examples/:测试用的配置文件、运行脚本和数据集读取接口。
  • cmake/:所有自定义CMake模块,包括寻找依赖库的配置文件。

当时我第一次读这套代码时最不习惯的是:它把物体检测器做成了一个独立模块,但又不依赖ROS的感知管线,而是把检测结果缓存成obj文件,再在SLAM主线程里异步加载。也就是说,你可以拿一张RGBD图像作为输入,先检测出沙发、椅子、显示器这类物体,然后SLAM系统会尝试把这些检测框对应的点云和DeepSDF生成的物体模型匹配起来。

这套流程跑通之后,地图里不仅有稀疏的路标点和稠密点云,还会把匹配好的物体模型直接“附着”在正确的空间位置上,所以后期做物体级导航的时候,直接查询这个地图就能拿到“物体在哪里”的信息。

3.2 编译顺序为什么要严格按依赖来

DSP-SLAM的编译顺序非常讲究,我强烈建议按以下顺序执行:

  1. 先单独编译Thirdparty/DBoW2和Thirdparty/g2o,因为这两个库是ORB-SLAM3的地基,如果你跳过直接编译主项目,CMake在查找库文件时会报找不到链接目标。
  2. 编译src/DeepSDF,这个模块依赖libtorch和CUDA,是整套代码里编译最慢的部分,单独编译的好处是出错时定位方便。
  3. 最后再编译项目主体src/object_slam。

在dsp-slam根目录下,我最终用的编译命令组是:

cd Thirdparty/DBoW2 mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j4 cd ../../g2o mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j4 cd ../../../src/DeepSDF mkdir build && cd build cmake .. -DCMAKE_BUILD_TYPE=Release make -j8

这样一步步来,每一步如果失败,你都能很快定位是哪一个第三方库的问题。我见过很多人在根目录直接一个cmake ..就开编,结果报错了不知道该查哪,只能全删了重来。

注意:make -j8这种并行编译参数要看你CPU核数来定,如果内存不够大,比如8GB以下,建议用make -j4,否则编译过程可能直接把内存挤爆导致卡死。

4. 编译过程中的核心坑点与解决方案

4.1 libtorch的CMake查找路径问题

这一节是整篇文章含金量最高的部分,因为几乎所有人编译DSP-SLAM都会挂在同一个地方:CMake找不到libtorch。

DSP-SLAM的CMakeLists里会通过find_package(Torch REQUIRED)来找libtorch,但Torch的CMake配置文件默认不会出现在系统路径里。你必须显式指定Torch_DIR,例如下载的libtorch解压在~/libtorch目录下,那么:

cd build cmake .. -DCMAKE_BUILD_TYPE=Release \ -DTorch_DIR=$HOME/libtorch/share/cmake/Torch

这里有个细节:如果你是用pip install torch装的PyTorch,它的C++库并不会被完整安装到系统目录,只有libtorch这个独立压缩包才会带有完整的share/cmake/Torch目录。所以我的建议是,不要只依赖pip安装的torch来编译,一定要单独去PyTorch官网下载对应CUDA版本的libtorch包,解压到固定目录,再让CMake去找它。

我当时下载的是libtorch-cxx11-abi-shared-with-deps-1.7.1+cu110.zip,解压到~/libtorch之后,在~/.bashrc里加了一行:

export Torch_DIR=$HOME/libtorch/share/cmake/Torch

这样就省得每次cmake都手动传参了。

4.2 OpenCV的版本冲突和未定义引用

编译到主程序时,另一个高频报错是OpenCV相关的“未定义引用”,比如cv::Mat的操作、cv::imread这类接口报链接错误。这种情况下,八成是因为你系统里同时装了多个OpenCV版本,CMake查到了4.x的头文件,但链接时却用了3.x的库,两者ABI不兼容。

解决方法非常直接:把所有OpenCV统一到一个版本。我当时是直接把OpenCV 3.4.15从源码编译安装到/usr/local,同时把系统里apt装的OpenCV 4.x标记为不需要,避免备选路径干扰:

sudo apt remove libopencv-dev

然后编译OpenCV时,CMake参数里建议显式关闭不需要的模块,减少编译时间:

cmake .. -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DBUILD_EXAMPLES=OFF \ -DBUILD_TESTS=OFF \ -DBUILD_opencv_python3=OFF

编译安装完之后,再回到DSP-SLAM的build目录,把CMake缓存清掉重新配置一遍:

rm -rf CMakeCache.txt CMakeFiles cmake .. make -j4

如果你像我一样之前先用apt装过OpenCV,那这一步删缓存是必须做的,否则CMake缓存里还残留着旧版本的路径信息,恶心得很。

4.3 关于-march=native和指令集兼容的提醒

还有一个小坑是,某些文档会建议你在编译时给编译器加上-march=native优化参数,但这个参数在DSP-SLAM上未必适用。它会让编译器根据当前CPU的指令集生成优化代码,但如果你之后把二进制拷到别的机器上运行,或者你用的是虚拟机(例如在VMware里跑Ubuntu 20.04),虚拟化环境下CPU特性检测可能变得不可靠,轻则运行崩溃,重则直接非法指令。

我的建议是:保持Release模式,不要额外加-march=native。如果确实想优化,可以选-O2或-O3,但别为了那一点点性能提升给自己埋一个运行期雷。

5. 运行配置、数据集准备与参数调优

5.1 数据集准备:Replica与ScanNet实测经验

DSP-SLAM官方支持的数据集主要是Replica和ScanNet。我用的是Replica的room_0场景,因为它的RGBD数据规模和深度噪声都更适合测试SLAM效果。

如果你的数据集是ScanNet格式,记得先按官方仓库里提供的脚本把sens文件解压成RGB图、深度图和姿势文件。Replica则简单一些,直接下载room0相关的压缩包,解压后里面是frame000000.jpg、depth000000.png这类文件,以及一个traj.txt姿势文件。

在Examples/目录下,你需要找一个YAML配置文件,比如replica.yaml或者scannet.yaml,打开后重点修改以下几项:

  • Dataset.cfgFile:指向数据集的配置文件路径,告诉程序去哪找RGB、深度图的文件夹。
  • Dataset.depthPrefix:深度图前缀名,不要搞错,否则程序找不到深度图。
  • Mesh.voxelSize:网格化时的体素大小,决定了重建的稠密程度和内存占用。
  • Mesh.margin:用于网格化的额外边界范围。

我当时实际把Mesh.voxelSize从默认的0.01改成了0.02,因为我测试场景比较大,0.01会让内存占用爆炸,跑一会儿就OOM。这里不是说你一定要改成0.02,要根据场景大小和机器配置灵活调整。

5.2 关键运行参数解析

运行DSP-SLAM的命令行和ORB-SLAM3很像,基本格式是:

./Examples/DSP-SLAM-settings Vocabulary/ORBvoc.bin Examples/Replica/room0.yaml

但不同之处在于,DSP-SLAM启动时会先尝试加载物体检测结果或物体先验模型,你需要提前配置好预训练模型路径。这部分配置写在YAML文件的几个关键字段里,比如:

  • DeepSDF.modelFile:指向预训练的DeepSDF权重文件。
  • DeepSDF.codeLength:shape code的维度,一般是64或128,不要瞎改,要和权重匹配。
  • detector.use_cnn:如果置为1,则使用CNN实时检测物体;如果置为0,则跳过检测,只做几何重建。

从实际运行效果看,开着物体检测确实会让跟踪线程变慢,因为每次关键帧都要跑一次神经网络的物体识别。如果你只是随便跑通流程,建议先关掉use_cnn,等整个管线能稳定运行了,再打开物体检测功能验证地图里的语义物体效果。

5.3 可视化与地图输出的正确打开方式

DSP-SLAM的可视化依赖Pangolin,启动后会有两个Pangolin窗口:一个是SLAM主窗口,显示相机轨迹和稀疏地图点;另一个是稠密地图/网格窗口,显示已重建的稠密表面和物体模型。如果你发现第二个窗口一直黑屏,大概率是你修改的配置项里**Mesh相关参数没有生效**,或者是在数据集帧类型匹配上出了问题。

另外,地图输出的结果是.ply格式的网格文件,程序会在运行结束后自动保存到当前目录,文件名类似mesh_output.ply。你可以用MeshLab或CloudCompare打开检查重建效果。如果你想边跑边实时看,建议把Mesh.updateIncrement调大一些,比如从1改成5,这样网格更新的频率低一点,CPU压力小很多。

提示:如果发现地图漂移、相机轨迹明显弯曲,最优先排查的不是DSP-SLAM的代码,而是你的标定文件。RGB-D相机在跑DSP-SLAM之前一定要确保深度图和彩色图的配准良好,两个相机的内参、外参都要填对。我有个同事直接拿未标定的Realsense参数跑,结果轨迹和真值差了半米,换了一套标定好之后立刻正常了。

6. 常见问题与排查技巧实录

6.1 编译期的“未定义符号”该怎么快速定位

编译时如果报错形如undefined reference to xxx,先别急着搜报错信息,第一步是确认依赖库的库文件顺序。CMakeLists里如果链接库的书写顺序不对,链接器找不到符号是很常见的,尤其是在静态库之间互相依赖的场景。DSP-SLAM里,object_slam模块依赖了DeepSDF的静态库,而DeepSDF又依赖libtorch,所以CMakeLists里必须保证库的排列是“被依赖的库放在后面”。

如果你是在自己改了CMakeLists之后出现的未定义符号,大概率就是这个原因。建议用cmake --build . --verbose查看实际链接命令,检查-l参数的先后顺序。如果顺序确实错了,把对应库文件调换一下位置重新链接就能解决。

6.2 运行时崩溃:段错误和内存不足

段错误出现最多的场景是在加载预训练权重之前,特别是DeepSDF模型文件路径错误时,程序会尝试读取一个空指针,直接退出。此时先检查DeepSDF.modelFile路径是不是相对路径,如果是相对路径,一定要在DSP-SLAM根目录下运行程序,否则工作目录不一致就会找不到文件。

内存不足的问题在跑大场景Replica时尤其常见。解决办法除了调大Mesh.voxelSize之外,还可以在程序启动前用ulimit -s unlimited放大概率栈限制,避免某些递归函数在初始化时就把栈打爆。另外,如果在虚拟机里运行,给虚拟机分配的内存建议至少8GB,否则开两个Pangolin窗口后系统会非常卡顿,甚至直接被杀进程。

6.3 相机位姿不稳定、建图漂移的排查

如果你跑出来的轨迹在起始阶段就明显漂移,先检查是否用了RGBD相机模式。DSP-SLAM对RGBD模式的依赖度很高,如果深度图和彩色图时间戳对不上,帧间匹配的ICP就全是噪声。ScanNet数据集的RGB和深度图已经对齐,但Replica需要确保你下载的是对齐过的版本。

另外,如果机器人的运动速度快、场景纹理少,跟踪丢失是正常现象。这不算是代码bug,而是特征点法SLAM的通病。你可以适当减少相机帧率,或者把FeatureExtractor.nFeatures从默认值调高,比如从1000调到1500,这样特征点多了,跟踪鲁棒性会好一些。

6.4 高频问题速查表

问题现象可能原因解决方法
CMake找不到Torch未设置Torch_DIR下载libtorch并设置环境变量
编译时OpenCV未定义引用多版本OpenCV冲突卸载多余版本,重编统一版本
启动即崩溃DeepSDF权重路径错误检查modelFile路径和工作目录
Pangolin窗口黑屏Mesh参数未生效或数据对齐问题检查Mesh配置,确认RGBD时间戳对齐
跟踪漂移严重相机标定参数错误重新标定相机内参和外参
内存不足被杀死场景大、voxelSize过小调大voxelSize,减少网格更新频率
模型不显示物体use_cnn为0或未配置检测权重开启CNN检测,配置检测模型路径

7. 性能调优与运行实测体会

在调通基础流程之后,我对这套系统的性能特点有了更直观的感受。首先是CPU占用率,DSP-SLAM的跟踪线程和局部建图线程都比较吃CPU,如果你同时开着Pangolin可视化,8核以下的CPU基本会满载。而GPU的占用率反而不是很高,DeepSDF推理只在关键帧触发,日常跟踪过程的GPU占用大概只有30%左右。

如果你最终的目标是把DSP-SLAM搬上机器人,有两个调优建议可以参考:

  1. 关闭实时可视化,只在调试时打开。Pangolin虽然方便,但开销不小,尤其是网格实时更新时,顶点数和面数一多,渲染线程会拖慢整个SLAM的主循环。
  2. 降低关键帧的插入频率。在YAML配置里找到关键帧插入的条件,适当调高最小间隔帧数,这样物体检测和DeepSDF匹配触发的频率会降低,系统整体会更稳定。

我在跑Replica room0的过程中,把Mesh.voxelSize设为0.02、关键帧间隔设置为2倍默认值之后,整个流程可以在不爆内存的情况下稳定跑到结束,生成的稠密地图质量也能满足后续物体级规划的使用需求。

最后再分享一个小技巧:如果你想测试不同DeepSDF权重对物体重建效果的影响,其实不需要重新编译整个项目,只需要在YAML配置里换DeepSDF.modelFile的路径即可。但要保证换的新权重文件的codeLength和原来一致,否则解码出来的shape code维度不匹配,会在运行时直接报维度错误。我当时试过把官方预训练权重换成自己微调过的版本,因为维度一致,整个过程无缝切换,这个设计确实很贴心。

返回列表