SLAM算法与工程实践系列文章
下面是SLAM算法与工程实践系列文章的总链接,本人发表这个系列的文章链接均收录于此
SLAM算法与工程实践系列文章链接
下面是专栏地址:
SLAM算法与工程实践系列专栏
文章目录
前言
这个系列的文章是分享SLAM相关技术算法的学习和工程实践
SLAM算法与工程实践------CMake使用(1)
Make 概述
CMake 是一个项目构建工具,并且是跨平台的。关于项目构建我们所熟知的还有 Makefile(通过 make 命令进行项目的构建),大多是 IDE 软件都集成了 make,比如:VS 的 nmake、linux 下的 GNU make、Qt 的 qmake 等,如果自己动手写 makefile,会发现,makefile 通常依赖于当前的编译平台,而且编写 makefile 的工作量比较大,解决依赖关系时也容易出错。
而 CMake 恰好能解决上述问题, 其允许开发者指定整个工程的编译流程,在根据编译平台,自动生成本地化的Makefile和工程文件,最后用户只需 make 编译即可,所以可以把 CMake 看成一款自动生成 Makefile 的工具,其编译流程如下图:
-
蓝色虚线表示使用 makefile 构建项目的过程
-
红色实线表示使用 cmake 构建项目的过程
介绍完 CMake 的作用之后,再来总结一下它的优点:
-
跨平台
-
能够管理大型项目
-
简化编译构建过程和编译过程
-
可扩展:可以为 cmake 编写特定功能的模块,扩充
cmake 功能
CMake 构建项目2种方式:中央集权、地方分权
一般来说,我们的工程是存在多个目录的。使用CMakeLists.txt构建工程有两种方法。
第一种:中央集权式
工程存在多个目录,只用一个CMakeLists.txt文件来管理
c++
//include文件夹
include
inc1.h
inc2.h
//source文件夹
source
srcl.cpp
src2.cpp
//app为主函数文件夹
app
main.cpp
//CMakeLists.txt和include、source及app位于同级目录下
CMakeLists.txt
一个典型的案例就是ORB-SLAM2代码,它只在最外层使用了一个CMakeLists.txt来构建整个工程。
第二种:地方分权
工程存在多个目录,每个源文件目录都使用一个CMakeLists.txt文件来管理。
c++
//include文件夹
include
incl.h
inc2.h
//source文件夹下除了源文件,还有CMakeLists.txt文件
source
srcl.cpp
src2.cpp
CMakeLists.txt
//app为主函数文件夹,其下除了源文件,还有CMakeLists.txt文件
app
main.cpp
CMakeLists.txt
//CMakeLists.txt和include、source及app位于同级目录下
CMakeLists.txt
一个典型的案例就是《视觉SLAM十四讲:从理论到实践》里的源代码,我们以该书第13章中的代码为例进行说明。
它在最外层使用了一个CMakeLists.txt来构建整个工程,如下所示。
cmake
cmake_minimum_required(VERSION 2.8)
project(myslam)
set(CMAKE_BUILD_TYPE Release)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O3 -fopenmp -pthread")
list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
############### dependencies ######################
# Eigen
include_directories("/usr/include/eigen3")
# OpenCV
find_package(OpenCV 3.1 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# pangolin
find_package(Pangolin REQUIRED)
include_directories(${Pangolin_INCLUDE_DIRS})
# Sophus
find_package(Sophus REQUIRED)
include_directories(${Sophus_INCLUDE_DIRS})
# G2O
find_package(G2O REQUIRED)
include_directories(${G2O_INCLUDE_DIRS})
# glog
find_package(Glog REQUIRED)
include_directories(${GLOG_INCLUDE_DIRS})
# gtest
find_package(GTest REQUIRED)
include_directories(${GTEST_INCLUDE_DIRS})
# gflags
find_package(GFlags REQUIRED)
include_directories(${GFLAGS_INCLUDE_DIRS})
# csparse
find_package(CSparse REQUIRED)
include_directories(${CSPARSE_INCLUDE_DIR})
set(THIRD_PARTY_LIBS
${OpenCV_LIBS}
${Sophus_LIBRARIES}
${Pangolin_LIBRARIES} GL GLU GLEW glut
g2o_core g2o_stuff g2o_types_sba g2o_solver_csparse g2o_csparse_extension
${GTEST_BOTH_LIBRARIES}
${GLOG_LIBRARIES}
${GFLAGS_LIBRARIES}
pthread
${CSPARSE_LIBRARY}
)
enable_testing()
############### source and test ######################
include_directories(${PROJECT_SOURCE_DIR}/include)
add_subdirectory(src)
add_subdirectory(test)
add_subdirectory(app)
CMake 的使用
CMake 支持大写、小写、混合大小写的命令。如果在编写 CMakeLists.txt 文件时使用的工具有对应的命令提示,那么大小写随缘即可,不要太过在意。
注释
注释行
CMake 使用 #
进行行注释,可以放在任何位置。
cmake
# CMakeLists.txt 注释
cmake_minimum_required(VERSION 3.0.0)
注释块
CMake 使用 #[[ ]] 形式进行块注释。
cmake
#[[ 这是一个 CMakeLists.txt 注释
这是一个 CMakeLists.txt 注释
这是一个 CMakeLists.txt 注释]]
cmake_minimum_required(VERSION 3.0.0)
简单使用示例
(1)准备工作,为了方便测试,在本地准备一下几个 .c
的测试文件
- add.c
c
#include <stdio.h>
#include "head.h"
int add(int a, int b)
{
return a+b;
}
- sub.c
c
#include <stdio.h>
#include "head.h"
// 你好
int subtract(int a, int b)
{
return a-b;
}
- mult.c
c
#include <stdio.h>
#include "head.h"
int multiply(int a, int b)
{
return a*b;
}
- div.c
c
#include <stdio.h>
#include "head.h"
double divide(int a, int b)
{
return (double)a/b;
}
- head.h
c
#ifndef _HEAD_H
#define _HEAD_H
// 加法
int add(int a, int b);
// 减法
int subtract(int a, int b);
// 乘法
int multiply(int a, int b);
// 除法
double divide(int a, int b);
#endif
- main.c
c
#include <stdio.h>
#include "head.h"
int main()
{
int a = 20;
int b = 12;
printf("a = %d, b = %d\n", a, b);
printf("a + b = %d\n", add(a, b));
printf("a - b = %d\n", subtract(a, b));
printf("a * b = %d\n", multiply(a, b));
printf("a / b = %f\n", divide(a, b));
return 0;
}
(2)上述文件的目录结构如下:
bash
$ tree
.
├── add.c
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
(3)加 CMakeLists.txt
文件
在上述源文件所在目录下添加一个新文件 CMakeLists.txt,文件内容如下:
cmake
cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)
接下来依次介绍一下在 CMakeLists.txt 文件中添加的三个命令:
cmake_minimum_required
:指定使用的 cmake 的最低版本- 可选,非必须,如果不加可能会有警告
project
:定义工程名称,并可指定工程的版本、工程描述、web 主页地址、支持的语言(默认情况支持所有语言),如果不需要这些都是可以忽略的,只需要指定出工程名字即可。
cmake
# PROJECT 指令的语法是:
project(<PROJECT-NAME> [<language-name>...])
project(<PROJECT-NAME>
[VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
[DESCRIPTION <project-description-string>]
[HOMEPAGE_URL <url-string>]
[LANGUAGES <language-name>...])
add_executable
:定义工程会生成一个可执行程序
cmake
add_executable(可执行程序名 源文件名称)
这里的可执行程序名和 project
中的项目名没有任何关系
源文件名可以是一个也可以是多个,如有多个可用空格或 ;
间隔
cmake
# 样式1
add_executable(app add.c div.c main.c mult.c sub.c)
# 样式2
add_executable(app add.c;div.c;main.c;mult.c;sub.c)
(4)执行 CMake 命令
将 CMakeLists.txt
文件编辑好之后,就可以执行 cmake 命令了。
bash
# cmake 命令原型
cmake CMakeLists.txt文件所在路径
# 在此处即为
cmake .
bash
$ tree
.
├── add.c
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── mult.c
└── sub.c
0 directories, 7 files
robin@OS:~/Linux/3Day/calc$ cmake .
当执行 cmake
命令之后,CMakeLists.txt 中的命令就会被执行,所以一定要注意给 cmake 命令指定路径的时候一定不能出错。
执行命令之后,看一下源文件所在目录中是否多了一些文件:
bash
$ tree -L 1
.
├── add.c
├── CMakeCache.txt # new add file
├── CMakeFiles # new add dir
├── cmake_install.cmake # new add file
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile # new add file
├── mult.c
└── sub.c
我们可以看到在对应的目录下生成了一个 makefile
文件,此时再执行 make 命令,就可以对项目进行构建得到所需的可执行程序了。
bash
$ make
Scanning dependencies of target app
[ 16%] Building C object CMakeFiles/app.dir/add.c.o
[ 33%] Building C object CMakeFiles/app.dir/div.c.o
[ 50%] Building C object CMakeFiles/app.dir/main.c.o
[ 66%] Building C object CMakeFiles/app.dir/mult.c.o
[ 83%] Building C object CMakeFiles/app.dir/sub.c.o
[100%] Linking C executable app
[100%] Built target app
# 查看可执行程序是否已经生成
$ tree -L 1
.
├── add.c
├── app # 生成的可执行程序
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
├── CMakeLists.txt
├── div.c
├── head.h
├── main.c
├── Makefile
├── mult.c
└── sub.c
最终可执行程序 app 就被编译出来了(可执行程序的名字是在 CMakeLists.txt 中指定的)。
综上,所用的命令如下所示
bash
# CMakeLists.txt中内容如下
cmake_minimum_required(VERSION 3.0)
project(CALC)
add_executable(app add.c div.c main.c mult.c sub.c)
# CMakeLists.txt就在当前目录下,所以用.表示即可
cmake .
make
实践了上面的简单例子就可以知道,如果在 CMakeLists.txt 文件所在目录执行了 cmake 命令之后就会生成一些目录和文件(包括 makefile 文件),如果再基于 makefile文件执行 make 命令,程序在编译过程中还会生成一些中间文件和一个可执行文件,这样会导致整个项目目录看起来很混乱,不太容易管理和维护,此时我们就可以把生成的这些与项目源码无关的文件统一放到一个对应的目录里边,一般将这个目录命名为 build
:
bash
# 先创建 build 文件夹
$ mkdir build
# 进入 build 文件夹
$ cd build
# 开始编译
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/Linux/build
现在 cmake 命令是在 build 目录中执行的,但是 CMakeLists.txt 文件是 build 目录的上一级目录中,所以 cmake 命令后指定的路径为 ..
,即当前目录的上一级目录。
当命令执行完毕之后,在 build 目录中会生成一个 makefile 文件
bash
$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile
1 directory, 3 files
这样就可以在 build 目录中执行 make 命令编译项目,生成的相关文件自然也就被存储到 build 目录中了。
这样通过 cmake 和 make 生成的所有文件就全部和项目源文件隔离开了。
综上,用cmake执行编译的命令为
bash
# 注意要删除除了CMakeLists.txt以外的,之前产生的cmake相关的文件
mkdir build
cd build
cmake ..
make