SLAM算法与工程实践——CMake使用(1)

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
相关推荐
NuyoahC16 分钟前
算法笔记(十一)——优先级队列(堆)
c++·笔记·算法·优先级队列
楚灵魈23 分钟前
[Linux]从零开始的网站搭建教程
linux·运维·服务器
小小不董25 分钟前
《Linux从小白到高手》理论篇:深入理解Linux的网络管理
linux·运维·服务器·数据库·php·dba
FL16238631291 小时前
[C++]使用纯opencv部署yolov11-pose姿态估计onnx模型
c++·opencv·yolo
sukalot1 小时前
windows C++-使用任务和 XML HTTP 请求进行连接(一)
c++·windows
这可就有点麻烦了1 小时前
强化学习笔记之【TD3算法】
linux·笔记·算法·机器学习
DY009J1 小时前
深度探索Kali Linux的精髓与实践应用
linux·运维·服务器
程序员-珍1 小时前
虚拟机ip突然看不了了
linux·网络·网络协议·tcp/ip·centos
ぃ扶摇ぅ1 小时前
Windows系统编程(三)进程与线程二
c++·windows
码农小白2 小时前
linux驱动:(22)中断节点和中断函数
linux·运维·服务器