从编译运行orbslam2说起
从编译运行orbslam2说起
0.基础
"0.基础"而不是"0基础",把它放在1的前面,用0来强调做某件事之前必须已经完成某事的概念.正所谓磨刀不误砍柴工,如果你不想浪费3个小时坎坷的去砍柴,那最好老老实实花上1个小时磨好刀.
本文面向具有cmake,linux shell,ROS基础的SLAM研究者.请读者自行学习以下知识:
- robot SLAM基础与orbslam2简介
- cmake基础
- linux与linux shell基础
- ROS基础
1.编译
第三方库的安装或现场编译
首先orbslam2作为一个大型程序,并不是完全自己造的轮子,而是在一些非核心的地方使用了第三方库的.学计算机的都知道如果自己程序使用了第三方库,编译时需要找到头文件,链接时需要找到库文件.大型程序的构建过程也是一样的,orblsam2需要找到第三方库的头文件和库文件,这个交给cmake来处理,我们重点来谈谈它的第三方库.这又分为两个种:
- 直接使用第三方库.在orbslam2中就是OpenCV,Pangolin等.这一部分必须事先已经正确安装到系统中了
- 对第三方库做了修改的.在orbslam2中就是g2o,dbow等.这一部分必须由作者提供修改后的源码,现场编译
为了说明这点,我们可以查看工程的CMakeLists.txt文件
target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBS}
${EIGEN3_LIBS}
${Pangolin_LIBRARIES}
${PROJECT_SOURCE_DIR}/Thirdparty/DBoW2/lib/libDBoW2.so
${PROJECT_SOURCE_DIR}/Thirdparty/g2o/lib/libg2o.so
)
从上面代码可以看出,程序链接了5个库,其中OpenCV,EIGEN3,Pangolin来自系统中,DBoW2和g2o来自现场编译后生成的库文件.故我们在构建orbslam等大型工程时,不是一上来就编译,而是要先解决依赖.这里略去第三方库的安装和编译方法.
编译核心API库
当所有依赖的库文件和头文件都准备就绪,就可以开始编译orbslam了.接着看CMakeLists.txt:
add_library(${PROJECT_NAME} SHARED
src/System.cc
src/Tracking.cc
src/LocalMapping.cc
src/LoopClosing.cc
src/ORBextractor.cc
src/ORBmatcher.cc
src/FrameDrawer.cc
src/Converter.cc
src/MapPoint.cc
src/KeyFrame.cc
src/Map.cc
src/MapDrawer.cc
src/Optimizer.cc
src/PnPsolver.cc
src/Frame.cc
src/KeyFrameDatabase.cc
src/Sim3Solver.cc
src/Initializer.cc
src/Viewer.cc
)
注意到作者的源码都编译成了一个动态库,根据cmake的命名规则,应该是一个叫libORB_SLAM2.so的文件.但是为什么不直接编译可执行程序?非要多此一举地搞一个动态库?其实不然,之所以这样做出于两点原因:
- 大型程序的编写都是OOP,众所周知OOP的优点有很多:便于分析,设计,编码,维护.这样说很抽象,简单举几个例子:
- 例子1:小明编写了一个几万行的main.cpp,然后他崩溃了.后来他用OO思想设计了类,在main.cpp中调用,程序缩减到百行
- 例子2:小明要编写3个单独的VO,回环,优化程序,他建立了三个大型工程,然后他崩溃了.后来他用OO思想设计了类,写了3个程序调用这些类,再后来他创造性的把三个模块整合,写出了完整的slam程序
- 例子3:小明编写了一个单目slam程序,老板要求他再写一个双目的,他又得重新造轮子了,所以他依然崩溃了,后来他用OO思想设计了类,在已有程序中增加了一个双目类,成功完成了双目slam程序
- 封装成核心API库,便于发布给其他开发者使用
封装成核心API库是一种有益之法,尤其是当你自己需要构建大型程序时,读者可自行体会.
编译作者提供的demo,并将其链接到上面生成的核心API库
再往下看,cmake就开始编译作者提供的demo了.作者提供了4个200来行左右的demo源程序,分别是rgbd_tum.cc,stereo_kitti.cc,mono_tum.cc,mono_kitti.cc
# Build examples
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/RGB-D)
add_executable(rgbd_tum
Examples/RGB-D/rgbd_tum.cc)
target_link_libraries(rgbd_tum ${PROJECT_NAME})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Stereo)
add_executable(stereo_kitti
Examples/Stereo/stereo_kitti.cc)
target_link_libraries(stereo_kitti ${PROJECT_NAME})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Monocular)
add_executable(mono_tum
Examples/Monocular/mono_tum.cc)
target_link_libraries(mono_tum ${PROJECT_NAME})
add_executable(mono_kitti
Examples/Monocular/mono_kitti.cc)
target_link_libraries(mono_kitti ${PROJECT_NAME})
总共生成了4个demo程序,先编译,后link到前面生成的核心API库上.
2.运行
程序的运行并不像我们在使用win时的那么简单,因为程序具有专业性,不具备相关专业基础知识的人往往会在各种错误提示中浪费自己的时间.但总的来说也是有规律可循的,其实也简单,仅仅是提供程序运行所必要的数据和配置文件即可.推荐大家保持良好的习惯,阅读官方提供的运行说明.了解程序运行所需要的数据和配置,事先将其准备好,然后按照作者的教程运行.orbslam2的运行略.
3.练习:编译运行作者提供的基于ros的demo
请注意,以上的程序是普通的桌面程序,是直接利用驱动来读取相机数据的,下面编译基于ros的demo,当然这部分也是略
4.报错
软件工程是个系统工程,一个小问题也会造成大错误.所以报错是件很平常的事.即使是对于老手.面对报错,我们应该
- 首先心态上要放平稳
- 其次要仔细查看错误提示,冷静地做逻辑分析
- 最后要善用google,你要相信自己不是特殊的,你踩过的坑别人肯定也踩过
5.启发
如果看完一篇文章,没有收获,纯属浪费时间;如果看完一篇文章,能解决另一个问题,那么起码不亏;如果看完一篇文章,能解决一类问题,你才叫正真的赚到了.读到这里,如果你觉得没有收获,请关闭页面,不要再浪费你自己的时间.如果你现在觉得学习了一些经验技巧,那么就可以继续了.
6.提高:重新启程,触类旁通
你可以尝试编译运行较新的DSO,这里我们来试试高翔博士基于orbslam2改写的orbslam2带点云程序
1
首先下载源码.看过说明之后大概有了了解,但仍然不甚明了,不知orbslam2_modified.zip与ORB_SLAM2_modifiedy文件夹有什么异同.这是我遇到的第一个问题,详细查看了ORB_SLAM2_modifiedy下的文件,确实有修改,且第三方库完整,决定编译这个文件夹.
2
接着照例查看CMakeLists.txt,其内容基本与orbslam相同,但是从逻辑上讲应该增加点云库的,这里我们先不管它.然后查看build.sh的安装脚本,过程也是与orbslam2大同小异,只是最后多了一个词典转换程序(提供),用于讲词典转换成二进制的.经过查看之后,得出的结论是,编译过程基本相似,改动主要在代码层面.
3
通过刚才的文件查看,发现一些潜在问题,就是build文件夹已经存在,甚至第三方库已经编译成功.若干demo的可执行程序已经编译出来了.表面上看是捡了便宜,不用编译了,其实这样是会带来许多麻烦的.为什么这么说?
- cmake过的build文件夹内含有编译信息,记录了文件绝对路径,你下载下来,接着make,会报错说找不到文件.这时应该删除build,重新cmake
- 已经在别人的机器上编译好的程序或库,在自己的机器上很有可能是不能运行的,尤其是这些程序需要动态库的情况下,会出现一堆找不到各种动态库
4
- 开始编译第三方库,用make clean清理已经生成的,删除build,重新编译dbow和g2o两个库,成功.
- 开始编译核心API库和demo,编译错误,找不到pcl的一个头文件,印证了前面的逻辑推理,在CMakeLists.txt中加pcl,继续编译,成功.
- 开始编译基于ros的demo,出现错误,提示出现相同文件.因为我机器上编译过orbslam2的基于ros的demo,所以可能是ros包重复.把原来的包移个位置,继续编译,成功.
5
- 最终选择运行基于ros的rgbddemo程序,先确认数据与配置,词典位置,相机参数文件位置,以及最重要的订阅的话题,核对发布的话题,与rgbddemo中订阅的话题是否一致,发现不一致,修改源程序的话题订阅,重新编译,通过.
- ok,万事俱备,先开一个发布kinect数据的launch,然后运行rgbddemo程序,最终效果如下所示:
7.沉思
走到这里,你已经会编译运行各种开源slam项目了,也许你是付出很多时间精力,学习各种cmake,shell教程,被各种报错折磨,但是我还是要打击你一下,构建开源项目是最基本的工程素养,外面随便一个工程师都会.到目前为止我们只是编译别人的工程,跑跑别人的代码.
- 但是你的ideal在哪儿?这是研究者之于工程师最本质的区别,你不仅要有工程师的能力,还要有创新的思维.
- 在程序员,大学生,工程师泛滥的年代,作为研究生的你如何脱引而出,创造价值?
- ......