【SLAM】在 ubuntu 18.04 arm 中以 ROS 环境编译与运行 ORB_SLAM3
在 ubuntu18.04arm 中于 ROS 环境编译与运行 ORB_SLAM3,并以 TUM 和 EuRoC 数据集测试了 ROS 下单目、双目和 RGB-D 运行。
1. 引言
在之前的博客中,已经介绍了基于虚拟机 docker 环境以及云端的 AutoDL 环境运行 ORB_SLAM3 的步骤。
在实际场景中,SLAM 通常是需要在机器人平台上运行的,ROS 就是一个比较常见的机器人开发平台,在 ROS 环境中运行,可以方便我们的 SLAM 系统与其他机器人功能模块(比如导航和路径规划)进行通信,而且在 ros node 模式下运行的 SLAM 更适合多机通讯环境,比如实现多机器人协同建图、机器人和 PC 交互等等功能。
ORB_SLAM3 在本地运行的基础上,提供了在 ROS 中运行的机制,本文简述了如何在 ubuntu 18.04 arm 环境中安装 ROS 环境、编译 ORB_SLAM3 ROS 版本,以及用现有的数据集模拟摄像头信号输入来使用 ORB_SLAM3 的全流程。
本文示例环境:ubuntu 22.04 arm 虚拟机下启动的 ubuntu18.04 docker 容器。
2. 安装 ROS 环境
参考【Linux】在 ubuntu18.04arm 中安装 ROS 环境一文进行安装,主要基于 ROS 官网的教程。
3. 编译 ROS 环境的 ORB_SLAM3
本文撰写时 UZ-SLAMLab/ORB_SLAM3
的最新提交为 2022 年 2 月 10 日的 4452a3c4ab75b1cde34e5505a36ec3f9edcdc4c4
,后文的教程基于此提交点。
3.1. 编译命令
3.1.1. 先编译普通版本
本文不赘述安装 ORB_SLAM3 依赖项的步骤,您可以参考【SLAM】于 ubuntu18.04 上纯 CPU 运行 GCNv2_SLAM 的记录(ARM64/AMD64) | 慕雪的寒舍 一文中的依赖项安装步骤。参考博客里面的步骤安装 opencv、eigen3、Pangolin6.0 就可以了,不需要安装 libtorch。
其中需要注意的是,如果你需要编译 ROS 的 ORB_SLAM3,opencv 不能安装 3.4.5 版本,必须安装 3.2.0 版本!好消息是,如果你是跟随者本站或者 ROS 官网的教程安装的 ROS,那么 opencv 3.2.0 版本已经和 ROS 一起安装在你的系统里面了。
经过博主的尝试,原文中给出的 OpenCV 3.4.5 的编译安装方法同样可以用于 3.2.0 版本,只需要将下载链接中的版本号更换为 3.2.0 即可。
安装完毕依赖后,先以普通模式编译 ORB_SLAM3,因为 ROS 版本依赖于普通版本才能进行编译,这一点必须要注意。
1 | git clone https://github.com/UZ-SLAMLab/ORB_SLAM3.git ORB_SLAM3 |
3.1.2. 修改 build_ros.sh 脚本
编译完毕普通版本后,才可以执行 build_ros.sh
脚本。但是先别急,cat 这个脚本,看看里面写了啥
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3# cat build_ros.sh |
其中第一个命令就有问题了,最新版本的 ORB_SLAM3 源码里面,Examples 路径下已经没有 ROS 目录了,这个目录现在是 Examples_old/ROS/ORB_SLAM3
。
如果你没有注意这个问题就开始执行 build_ros.sh
脚本,问题就会出现,因为 Examples/ROS/ORB_SLAM3
目录当前不存在,该脚本实际上是直接在项目根目录下创建了 build 目录然后 cmake 开始编译的,编译的完全不是 ROS 版本。
这个脚本里面还有另外一个坑,那就是 make -j
后面没有写任何数字。这会导致 make 无休止地使用系统资源,直到把你的整个系统内存和 swap 都吃光光。根据我找到的博客,ORB_SLAM3 的编译最多能吃掉 16GB 内存,完全是个洪水猛兽。
最开始我没有注意这个脚本,以为它没有任何问题,就直接执行了,最终在编译时遇到了如下错误。
1 | c++: internal compiler error: Killed (program cc1plus) |
这个错误就是因为系统没有资源了。开另外一个终端,再启动编译,你可以轻易地观察到编译是怎么把整个系统内存给吃光光的,最终物理内存和 swap 都没了,操作系统自然会 kill 掉编译进程,从而导致了上述报错。
如果你直接搜索 “ORB_SLAM3 编译失败” 等字样,可能会搜到相关教程让你加大 swap 文件,这是一个可选项,但还不够可选,因为在我的测试中,即便再给出 2GB 的 swap 文件,编译依旧会因为内存不足而失败。下面给出新增 swap 文件的命令。
1 | # 安装 |
实际上,在物理内存大于 8GB 的环境中不需要这么麻烦,前文提到了脚本里面 make 没有写线程数,解决办法就是修正这个脚本,首先是修正目录,其次是将 make 后面加上 -j4
来限制 make 使用的资源,这样就能绕过内存不足导致的编译错误了。修改后的脚本如下:
1 | echo "Building ROS nodes" |
如果你的环境物理内存低于 8GB,可以考虑在加大 swap 文件的同时进一步减少 make 使用的线程数,比如 make -j2
。
3.1.3. 编译 ROS 版本
修正了 build_ros.sh 脚本之后,就可以开始编译 ROS 版本了。
1 | chmod +x build_ros.sh |
再次提醒,ROS 版本的编译依赖于普通版本,需要先编译普通版本!
3.2. 编译期间遇到的各种问题
部分问题参考:记录配置 ubuntu18.04 下运行 ORBSLAM3 的 ros 接口的过程及执行单目 imu 模式遇到的问题
3.2.1. cmake 错误 ROS_PACKAGE_PATH
遇到的第一个错误应该是环境变量有关,编译 ros 版本的时候,需要将源码路径加入到环境变量中,才可以正常编译。
1 | CMake Error at /opt/ros/melodic/share/ros/core/rosbuild/private.cmake:99 (message): |
在 ORB_SLAM3 项目根目录下执行如下命令即可
1 | export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$PWD/Examples_old/ROS/ORB_SLAM3 |
修正之后即可正常开始编译。注意后续执行 rosrun 的 bash 也需要执行此命令,建议直接将其写入 ~/.bashrc
中!
3.2.2. 头文件 se3.hpp 找不到
编译到 30% 的时候,会提示 <sophus/se3.hpp>
头文件找不到
1 | [ 30%] Building CXX object CMakeFiles/Mono_Inertial.dir/src/ros_mono_inertial.cc.o |
这个第三方库 sophus 已经在 Thirdparty 文件夹里面自带了,刚刚我们编译普通版本的时候,其实也已经编译好了这个第三方库了。要做的就是进它的目录 make install 安装一下。
1 | cd Thirdparty/Sophus/build/ |
安装以后就解决这个问题了。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3# cd Thirdparty/Sophus/build/ |
3.2.3. OpenCV 版本不匹配
前文提到过,OpenCV 必须安装 3.2.0 版本,是因为 cv_bridge 默认会指向 ROS 自带的 3.2.0 版本,而不是我们自己安装的其他版本,最终编译或者运行的时候就会导致链接错误或者 coredump 错误。
1 | /usr/bin/ld: warning: libopencv_core.so.3.2, needed by /opt/ros/melodic/lib/libcv_bridge.so, may conflict with libopencv_core.so.3.4 |
如果你已经安装了其他版本的 opencv,需要删掉它们。从源码安装的话,直接进入 opencv 源码的 build 目录执行 make uninstall
就可以卸载掉自己安装的 opencv。
删掉了之后就会发现系统里面只剩下和 ros 一起安装的 3.2.0 版本 opencv 了,应该是下载 ros 环境的时候自动安装的。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3# pkg-config --modversion opencv |
随后我们需要修改 CMakeLists 文件,让编译的时候使用 opencv 3.2.0 版本。首先是根目录下的 CMakeLists,修改如下部分的版本号为 3.2 即可
1 | # ORB_SLAM3/CMakeLists.txt |
然后是 Examples_old/ROS/ORB_SLAM3
里面的 CMakeLists 文件,修改如下部分为 3.2.0 版本
1 | # ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/CMakeLists.txt |
接下来就要删除所有编译缓存,重新编译一遍。
1 | rm -rf Thirdparty/g2o/build/ |
注意 ORB_SLAM3 的普通版本也需要重新编译!因为最开始编译的时候我的普通版本是基于 opecv 3.4.5 编译的,如果不重新基于 opencv 3.2.0 编译普通版本,编译 ROS 的时候就会提示 libORB_SLAM3.so
需要 3.4.5 版本的 opencv 才能正常链接,终究还是版本对不上。
另外,我们从源码使用 cmake 构建安装的 opencv 3.2.0 一般设置的安装目录都会和 ROS 自带的目录不同,比如 cmake 构建的安装目录是 /usr/local/lib
,ROS 安装的目录是 /usr/lib/aarch64-linux-gnu
。所以 cmake 构建的时候会出现多个如下关于 opencv 的 warning,可以不用管,不会影响程序编译和运行,因为它们都是 opencv 3.2.0 版本。
1 | runtime library [libopencv_core.so.3.2] in /usr/lib/aarch64-linux-gnu may be hidden by files in: |
当然,你也可以选择直接使用 ROS 携带的 opencv 3.2.0,不自己重新编译安装一个版本了(其实也不需要)。
3.2.4. AR 代码错误(四处)
修复了 opencv 版本问题后,接下来就会遇到一堆由于 Examples_old/ROS/ORB_SLAM3/src/AR
路径下的代码问题导致的错误。如果你不需要使用 MonoAR 也就是单目 AR 功能,可以直接 注释掉 CMakeLists 文件里面 71 行的如下部分,跳过 AR 代码的编译。
1 | # ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/CMakeLists.txt |
注释掉之后,应该就可以成功编译其他部分了
1 | [rosbuild] Including /opt/ros/melodic/share/roslisp/rosbuild/roslisp.cmake |
如果你需要 MonoAR,就需要上手改代码了 。参考 github.com/UZ-SLAMLab/ORB_SLAM3/issues/442,依次修复问题。
这部分修改很杂,你可以直接根据我的提交记录来修改:Fix compile error of ROS AR, used opencv 3.2.0 for ROS compile. · musnows/ORB_SLAM3@4e1cbdb · GitHub
1)类型 Sophus::SE3<float>
和 cv::Mat
不匹配
1 | /work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ros_mono_ar.cc: In member function 'void ImageGrabber::GrabImage(const ImageConstPtr&)': |
修改 ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ros_mono_ar.cc
的 151 行,被注释掉的是源代码
1 | // cv::Mat Tcw = mpSLAM->TrackMonocular(cv_ptr->image,cv_ptr->header.stamp.toSec()); |
2)'eigen2cv' is not a member of 'cv'
错误
1 | /work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ros_mono_ar.cc:155:9: error: 'eigen2cv' is not a member of 'cv' |
在 ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ros_mono_ar.cc
和 ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ViewerAR.h
顶部添加三个头文件
1 |
3)类型 Eigen::Matrix<float, 3, 1>
和 cv::Mat
不匹配
1 | /work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ViewerAR.cc: In member function 'void ORB_SLAM3::Plane::Recompute()': |
修改 ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ViewerAR.cc:530
处代码
1 | // cv::Mat Xw = pMP->GetWorldPos(); |
4)尾插错误 no matching function for call to 'std::vector<cv::Mat>::push_back(Eigen::Vector3f)'
这个问题也是类型不匹配
1 | /work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ViewerAR.cc: In member function 'ORB_SLAM3::Plane* ORB_SLAM3::ViewerAR::DetectPlane(cv::Mat, const std::vector<ORB_SLAM3::MapPoint*>&, int)': |
修改 ORB_SLAM3/Examples_old/ROS/ORB_SLAM3/src/AR/ViewerAR.cc:405
的代码
1 | // vPoints.push_back(pMP->GetWorldPos()); |
3.3. 成功编译
修改完毕上述四个问题后,就应该可以编译成功了。
编译完成后会多出很多可执行文件。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3# ls |
注意这些可执行文件是不能直接运行的,因为它们是针对 ROS 设计的文件。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3# ./Mono |
4. 在 ROS 下运行项目
参考博客:ORB-SLAM3 的 CMake 与 ROS 编译以及测试;ORB SLAM 2 demo 复现(普通模式 + ROS 模式) - 简书;
4.1. 下载 TUM 和 EuRoC 数据集
因为是在 ROS 环境下运行,所以数据集不能用之前下载的 tgz 格式的了,必须使用 ROS 专门的 bag 格式的数据集。
- TUM RGB-D 数据集:cvg.cit.tum.de/data/datasets/rgbd-dataset/download;
- EuRoC 双目数据集:robotics.ethz.ch/~asl-datasets/ijrr_euroc_mav_dataset/machine_ha…
在 TUM 数据集的下载页面中,往下滑可以看到每个数据集的简单介绍,这里就能下载到 bag 格式的数据集。
比如 fr1/desk
对应的 rosbag 文件的下载链接:
1 | wget https://cvg.cit.tum.de/rgbd/dataset/freiburg1/rgbd_dataset_freiburg1_desk.bag |
下载完毕后,可以使用 rosbag info 命令查看数据集中有的 topic 信息:
- Topic 是一个命名的通信管道,用于在不同的 ROS 节点之间传递信息;
- 每个 Topic 都有一个唯一的名称,节点可以通过这个名称来订阅这个 topic 的信息;
- Topic 中的数据以 message 的格式传输,message 是 ROS 中定义好的数据结构,如
sensor_msgs/Image
、geometry_msgs/Pose
等;
举个例子,fr1/desk
数据集的 Topic 信息如下,其中 depth 就是深度数据,rgb 就是普通的彩色录像数据。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3# rosbag info datasets/TUM/rgbd_dataset_freiburg1_desk.bag |
而给出的 EuRoC 双目数据集的 Topic 如下,有两个 cam 就对应了左侧和右侧的两个相机。
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3/datasets# rosbag info MH_01_easy.bag |
假设我们使用自己的摄像头的话,也是利用 ROS 的工具将我们摄像头的数据输入到一个 Topic 中,这样就可以供系统的其他组件,比如 SLAM 系统来读取,以此实现在 ROS 系统上硬件输入和软件的读取。这便是使用 ROS 模式和普通模式的最大区别,普通模式下我们必须要直接提供程序的数据输入源,才能让程序运行起来;而 ROS 模式下我们可以先把整个 SLAM 系统启动起来,再通过我们想要的方式往 SLAM 系统订阅的 Topic 里面喂数据即可。
4.2. 单目运行
刚刚我们下载的 TUM fr1/desk 的数据集,即可以用作 RGB-D 模式的输入,又可以做单目摄像头的输入,因为深度数据是独立于 RGB 单目数据的。
刚刚我们直接运行 Mono 的时候,就打印出了一个 Usage
1 | root@ubuntu-linux-22-04-02-desktop:/work/ORB_SLAM3/Examples_old/ROS/ORB_SLAM3# ./Mono |
我们要使用的就是这个命令,rosrun 代表启动一个节点,ORB_SLAM3
是我们当前使用的包名称,也就是 CMakeLists 里面注册的项目名称,Mono 是我们要执行的可执行文件名称。后面的两个参数分别是词袋文件和相机的配置文件。
最终执行的命令如下,需要在两个终端中执行(在项目根目录执行)
1 | # 打开终端A |
如果执行 rosrun 的时候提示 ORB_SLAM3 找不到,则需要执行如下命令(前文提到过)
1 | export ROS_PACKAGE_PATH=$ROS_PACKAGE_PATH:$PWD/Examples_old/ROS/ORB_SLAM3 |
执行了之后,就会启动 ORB_SLAM3 的 Map Viewer,此时是黑屏的,因为么有任何数据被送到 ORB_SLAM3 订阅的 Topic 中。
再新建一个终端,执行如下命令,将 TUM 数据集 bag 文件里面的 Topic 绑定到 ORB_SLAM3 订阅的 Topic 上,这样就能获取到数据了。
1 | rosbag play \ |
其中,最后一个参数 /camera/rgb/image_color:=/camera/image_raw
指代将 bag 文件中的 /camera/rgb/image_color
绑定到 /camera/image_raw
上,后者就是 ORB_SLAM3 订阅的相机原始数据的 Topic,相当于将 bag 中已有的图像数据重新喂给了我们的 SLAM 系统。
因为 ROS 的 Topic 机制,这种喂进去的数据集和接一个摄像头得到的实时数据,对于订阅这个 Topic 的 SLAM 系统而言是完全一样的!
执行这个命令后,就能在 GUI 里面看到相机的数据流和 SLAM 的建图了
4.3. RGB-D 运行
RGB-D 相机也是使用相同的命令来执行,先在另外一个终端执行 roscore,然后执行如下命令
1 | rosrun ORB_SLAM3 RGBD Vocabulary/ORBvoc.txt Examples_old/RGB-D/TUM1.yaml |
刚启动的时候也是黑屏
再开另外一个终端,开始喂我们的数据集,这里用了两个:=
号,分别绑定了原始的 RGB 相机数据到 /camera/rgb/image_raw
,绑定了深度数据到 /camera/depth_registered/image_raw
上
1 | rosbag play \ |
随后 GUI 里面也开始显示图像了
等运行结束后,会发现此时 SLAM 的建图结果是不对的,所有的点都在很小的一块区域中
作为对比,下图为本地虚拟机在普通模式下运行时的 RGB-D 建图结果,很明显和上图完全不一样。
这是因为 Examples_old/RGB-D/TUM1.yaml
数据配置有问题。在 TUM 官网上提到了这两个数据集在 ROS 和非 ROS 中是不一样的,实际上这个文件里面也有注释
1 | # Deptmap values factor |
官网说明:cvg.cit.tum.de/data/datasets/rgbd-dataset/file_formats#intrinsic…
Color images and depth maps
We provide the time-stamped color and depth images as a gzipped tar file (TGZ).
- The color images are stored as 640x480 8-bit RGB images in PNG format.
- The depth maps are stored as 640x480 16-bit monochrome images in PNG format.
- The color and depth images are already pre-registered using the OpenNI driver from PrimeSense, i.e., the pixels in the color and depth images correspond already 1:1.
- The depth images are scaled by a factor of 5000, i.e., a pixel value of 5000 in the depth image corresponds to a distance of 1 meter from the camera, 10000 to 2 meter distance, etc. A pixel value of 0 means missing value/no data.
这里是深度值的校正系数 (factor),使用时的计算公式为 Z = depth_image[v,u] / factor
,在 ROS 中要把它改成 1 才可以。
1 | factor = 5000 # for the 16-bit PNG files |
将 DepthMapFactor 修改为 1.0
之后的建图就正常一些了
4.4. 双目运行
下载 EuRoC 对应的 rosbag:MH_01_easy.bag,上文已经给出过该数据集对应的 Topic 了,其中要用到的 Topic 是左右两个摄像头的数据 /cam0/image_raw
和 /cam1/image_raw
。ORB_SLAM3 中双目 Stereo 接收的 Topic 分别为 /camera/left/image_raw
和 /camera/right/image_raw
,因此在运行时也需要绑定一下 Topic。
1 | topics: /cam0/image_raw 3682 msgs : sensor_msgs/Image |
双目的 rosrun 命令最后多了一个 bool 类型参数 do_rectify
,含义为是否进行矫正,根据需要选择 true 或 false。
1 | Usage: rosrun ORB_SLAM3 Stereo path_to_vocabulary path_to_settings do_rectify |
执行如下命令,先启动 Stereo 双目模式下的 SLAM,然后开始播放数据集,同样是使用:=
分别绑定左侧和右侧两个摄像头的数据。
1 | # 终端a |
也是成功运行起来了
5. The end
本文介绍了如何在 ROS 环境下编译 ORB_SLAM3,并使用 TUM 和 EuRoC 数据集测试单目、双目、RGB-D 三种模式在 ROS 下运行的效果。希望对你有帮助!
更新:ORB_SLAM2 的 ROS 运行命令和本文记录的完全一致,只需要把 rosrun 里面的包名改成 ORB_SLAM2 就可以了。下图是在 ROS 模式下运行 ORB_SLAM2 的 RGB-D 的截图。