ubuntu16.04 Ceres 安装及 find_package() 命令使用原理


摘要:

本文讲述了 Ceres 的基本安装,以及涉及到 cmake 容易出错的知识点 find_package() 使用,以及多版本 Ceres、OpenCV 使用的基本原理。

正文:


1、安装相应的依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
# CMake
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev
# BLAS & LAPACK
sudo apt-get install libatlas-base-dev
# Eigen3
sudo apt-get install libeigen3-dev
# SuiteSparse and CXSparse (optional)
# - If you want to build Ceres as a *static* library (the default)
# you can use the SuiteSparse package in the main Ubuntu package
# repository:
sudo apt-get install libsuitesparse-dev

2、编译安装(Ceres 有一些编译选项可参考官网)

(1)、默认路径/选项安装(标准路径)
1
2
3
4
5
mkdir build
cd build
cmake ..
make -j4
sudo make install

此时直接 find_package(Ceres)即可找到该库。

(2)、指定路径/选项安装(非标准路径)

这里的选项,可以参考给定的 ceres 官网,下面仅以指定安装目录为例,也称为局部安装,适用于多个安装版本。

1
2
3
4
5
mkdir build 
cd build
cmake -D CMAKE_INSTALL_PREFIX="/some/where/local" ..
make -j4
make install

此时在 CMakeLists.txt 中使用 find_package(Ceres REQUIRED PATHS "/some/where/local/ ")才会找到 Ceres 库的头文件、库文件。

注意: 这种安装方式可以适用于多版本 Ceres 的使用,如果安装了两个 Ceres 在不同的目录中,那么在使用的过程中,我们只要在 find_package( ) 中 PATHS 后面指定其中一个版本的安装路径即可。

(3)、非标准安装的另一种替代品(ceres 特有的选项)
1
2
3
4
mkdir build
cd build
cmake -D EXPORT_BUILD_DIR=ON ..
make -j4

因为默认情况下,Ceres 安装到系统默认路径/标准路径,然后才可以通过 find_package 查找到。对于 export( PACKAGE Ceres ),这种方式会让 HOME 目录下的 .cmake 中出现 packages/Ceres 文件夹,在 Ceres 文件夹里有一个文件。该文件记录了包含 CeresConfig.cmake 的地址,一般是编译 Ceres 的原始目录地址,比如这里的 build 目录所在的绝对路径,然后 cmake 会根据该文件提供的路径找到 CeresConfig.cmake 文件,进而利用 CeresConfig.cmake 中的语句,找到 Ceres 库的头文件和库文件。这个是 cmake 特有的搜索方式,参考 user’s local CMake package registry。接下来就可以直接在 CMakeLists.txt 中 find_package(Ceres REQUIRED) 了。这部分会在『 推广』部分再次提到。Ceres 库本身会通过 EXPORT_BUILD_DIR=ON 开启这种方式。

(4)、通用的方式(仅编译不安装,适合多版本)
1
2
3
4
mkdir build 
cd build
cmake ..
make -j4

通过上面前三种方式可以了解到,find_package() 本质上是在查找 CeresConfig.cmake 这个文件,前三种方式使用了不同的 cmake 搜索路径,我们也可以直接在 CMakeLists.txt 中加上 set(Ceres_DIR "包含 CeresConfig.cmake文件的绝对路径,一般编译后的文件夹中"),然后在 find_package(Ceres),CMake 就会自动的在这个路径下查找执行 CeresConfig.cmake 文件,之后这个 Ceres 库我们就可以使用了。即使多个版本,我们只要把上面 set 语句的路径部分替换一下就能使用不同版本 Ceres了。

(5)、总结

为了更好的移植 CMakeLists.txt 代码,也就是假设别人使用的是第一种安装方式。即安装到了系统的默认路径,那么我们在 CMakeLists.txt 中只能写 find_package(Ceres Required),这样一来,我们最好做的就是按照 (3) 方式,也就是在 home 目录下 .cmake/packages 文件中自动出现 Ceres 文件夹,然后里面的文件中写上了含有 CeresConfig.cmake 文件的绝对地址。如果我们有多个 Ceres 编译好的源码版本,我们可以在 .cmake/packages/Ceres /xxx_filename 文件中修改为不同 Ceres 源码版本目录地址,这样就可以使用不同的 Ceres 版本了。当发布代码的时候,其他人使用不必做任何修改即可运行。

3、推广

对于 slam 常用的 Pangolin 、Sophus 库都是只编译即可。因为在他们的 CMakeLists.txt 中都有 export(PACKAGE Sopus/Pangolin ),然后 cmake 会自动将包含 SopusConfig.cmake 和 PangolinConfig.cmake 文件的路径分别放到 ./cmake/packages/Sophus/xxx_filename 和 .cmake/packages/Pangolin/xxx_filename,然后系统会自动找到。这种方式其实我们可以推广到其他的库,比如 OpenCV 库,我们可以仅编译 OpenCV 的源码不安装。然后在 .cmake/packages/ 中建立 OpenCV 文件夹和一个任意名字的文件,里面写上 OpenCV 源码中包含 OpenCVConfig.cmake 文件的目录地址, 一般是 build 目录,下面列出 .cmake/packages 文件夹下的一些库。

需要注意:这种方式只适用于源码库在编译后提供了 xxxConfig.cmake 文件。这种方式属于 Config 模式。对于经常用到的 g2o 库,因为库的作者没有提供 G2OConfig.cmake 文件。所以没法用这种方式。只能用 Module 模式,这在『 第 4 部分』会讲到。

4、技巧

现在简单说一下 find_package()命令常用的查找技巧,首先我们要知道 find_package() 就是为了找到库的头文件和库文件。然而包含库的头文件和库文件的变量都在 Findxxx.cmake 或者 xxxConfig.cmake 中定义着,所以我们只要找到当中的一种 .cmake 文件即可。然后执行这个 .cmake 文件,我们就能得到库来使用。对于 cmake 而言,有两种查找方式。下面分别详细介绍。

(1)、Module 模式(查找Findxxx.cmake 文件)

CMake 会优先搜索 CMAKE_MODULE_PATH 指定的路径,然后搜索自己自带的 Modules 路径。所以一般来说,最常用就是在自己工程的 CMakeLists.txt 文件中更改 CMAKE_MODULE_PATH 这个变量值,或者把包含 Findxxx.cmake 文件的路径附加到这个变量中。之后就可以使用 find_package() 查找到对应的库。两种方式对应如下代码:

1
2
3
4
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake_modules/") # 后面这个路径就是在自己的工程目录中添加一个 cmake_modules 文件

# 一般推荐使用这个方式添加
LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

对于 CMake 本身自带的模块路径,我们没必要添加 Findxxx.cmake 文件到其中。只要利用好 CMake 为我们提供的 CMAKE_MODULE_PATH 这个变量即可。

(2)、Config 模式(查找 xxxConfig.cmake 文件)

这个文件是库的作者提供的,或者你看明白库作者写的 CMakeLists.txt 文件后,修改生成 xxxConfig.cmake 文件),上面讲解的 Ceres 安装方式就是利用这种模式。CMake 会优先搜索 xxx_DIR 指定的路径,然后第二优先级才是搜索 /usr/lib/cmake/xxx/ 中对应的xxxConfig.cmake 文件。因此最常使用的做法,指定 xxx_DIR 到包含 xxxConfig.cmake 文件的路径。下面是最常用的命令。

1
2
set(xxx_DIR "dir") # dir 是包含 xxxConfig.cmake 文件的路径 
find_package()
(3)、总结

可以看出,CMake 提供了灵活的查找库的方式。不论是 Config 模式还是 Module 模式,我们都可以自己定义包含 .cmake 文件的路径,然后用 find_package() 来查找。当然在第 2 点总结部分已经说明,对于 Config 模式,我们就用第 2 点总结推荐的方法。对于一些库,作者没有提供 xxxConfig.cmake 文件,只有 Findxxx.cmake 文件,那么我们只能用 Module 模式,然后推荐用 LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules),把当前工程用到的 .cmake 文件放在 cmake_modules 文件夹中。这样我们就可以顺利使用 find_package() 查找使用库了。上面介绍的仅仅是 find_package() 经常使用的方法,更进一步介绍可参考 cmake 官网

5、拓展

对于刚刚『 第 3 部分』提到的 export 导出库,可以有如下的例子,我们以 Sophus 库为例。Sophus 库在 CMakeLists.txt 里面开始设定了 Sophus_INCLUDE_DIR、Sophus_LIBRARIES 变量,然后通过 configure_file() 语句根据 SophusConfig.cmake.in 文件生成了 SophusConfig.cmake 文件。命令如下:

1
2
3
CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/SophusConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/SophusConfig.cmake @ONLY IMMEDIATE ) # @only 表示在 SophusConfig.cmake.in 中只有 @var@ 会被替代,而 ${var} 这种不会被替代。
export( PACKAGE Sophus ) # 将当前目录地址导出到 .cmake/packages/Sophus/xxxfilename 里面

configure_file() 的详细用法参考这里。上面这句简单解释就是将前面的 SophusConfig.cmake.in 最后通过 CMakeLists.txt 文件的一些变量替换,最后生成了 SophusConfig.cmake 文件。下面分别列出原始文件以及替换生成的文件。

  • 原始文件 SophusConfig.cmake.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
################################################################################
# Sophus source dir
set( Sophus_SOURCE_DIR "@CMAKE_CURRENT_SOURCE_DIR@")

################################################################################
# Sophus build dir
set( Sophus_DIR "@CMAKE_CURRENT_BINARY_DIR@")

################################################################################
set( Sophus_INCLUDE_DIR "@Sophus_INCLUDE_DIR@" )
set( Sophus_INCLUDE_DIRS "@Sophus_INCLUDE_DIR@" )

set( Sophus_LIBRARIES "@Sophus_LIBRARIES@" )
set( Sophus_LIBRARY "@Sophus_LIBRARIES@" )

set( Sophus_LIBRARY_DIR "@Sophus_LIBRARY_DIR@" )
set( Sophus_LIBRARY_DIRS "@Sophus_LIBRARY_DIR@" )
  • 替换生成的文件 SophusConfig.cmake
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
################################################################################
# Sophus source dir
set( Sophus_SOURCE_DIR "/home/gcj/LocalUser/Repo/slam/Sophus")

################################################################################
# Sophus build dir
set( Sophus_DIR "/home/gcj/LocalUser/Repo/slam/Sophus/build")

################################################################################
set( Sophus_INCLUDE_DIR "/home/gcj/LocalUser/Repo/slam/Sophus;/usr/local/include/eigen3" )
set( Sophus_INCLUDE_DIRS "/home/gcj/LocalUser/Repo/slam/Sophus;/usr/local/include/eigen3" )

set( Sophus_LIBRARIES "/home/gcj/LocalUser/Repo/slam/Sophus/build/libSophus.so" )
set( Sophus_LIBRARY "/home/gcj/LocalUser/Repo/slam/Sophus/build/libSophus.so" )

set( Sophus_LIBRARY_DIR "/home/gcj/LocalUser/Repo/slam/Sophus/build" )
set( Sophus_LIBRARY_DIRS "/home/gcj/LocalUser/Repo/slam/Sophus/build" )

有了 SophusConfig.cmake 文件,在 CMakeLists.txt 中,通过 export(PACKAGE Sophus),就可以在 $HOME 目录下的 ./cmake/packages/ 自动生成了 Sophus 文件夹和一个文件,文件内容为当前包含 SophusConfig.cmake 文件的地址。一般就是 Sophus 源码包中自己建立的 build 文件夹的绝对地址。

6、参考资料

1、https://www.cnblogs.com/newneul/p/8364924.html

2、http://ceres-solver.org/installation.html#

3、http://ceres-solver.org/installation.html#customizing-the-build

4、https://blog.csdn.net/bytxl/article/details/50637277

5、https://cmake.org/cmake/help/v3.2/manual/cmake-packages.7.html#user-package-registry

6、https://cmake.org/cmake/help/v3.0/command/find_package.html

-------------Thank you for reading this article!-------------
坚持原创技术分享,您的支持将鼓励我继续创作!
0%