刚刚在项目中使用的到了ceres,从完全不了解、到安装使用解决问题,花费了整整一个周的时间。所以使用的时候过程中踩了不少的坑,所以特别想记录下来。
ceres解决的问题
Ceres solver 是谷歌开发的一款用于非线性优化的库,在谷歌的开源激光雷达slam项目cartographer中被大量使用。ceres的官网比较详细的使用教程,内容还是挺丰富。
ceres主要用于解决如下的非线性最小二乘的问题
ρi(‖‖fi(xi1,…,xik)‖‖2) 这一部分被成为残差块(ResidualBlock),其中的fi(⋅) 叫做代价函数(CostFunction)。代价函数依赖于一系列参数[xi1,…,xik] ,这一系列参数(均为标量)称为参数块(ParameterBlock)。lj 和 uj 分别是变量块 xj 的上下边界。
安装
ceres可以通过命令行直接安装,也可以下载源码编译静态库使用。首先我们需要解决依赖的问题
依赖
ceres依赖挺多开源库的,但并不是必须的,可以在构建时关闭相应的依赖。
- Eigen是必须的。
- glog推荐使用,内部也提供了替代glog的minilog,但是性能赶不上glog,除了在android平台,其他平台建议使用glog。可以不用使用。
- gflags在测试和示例中有使用到。可以不用使用。
- SuiteSparse在解决大型稀疏线性系统所需。推荐使用。SuiteSparse需要BLAS和LAPACK。(我第一次编译的时候没有添加,为了加快求解的速度,不能不重新编译一下)
- CXSparse 与SuiteSparse类似,但更简单,更慢。 CXSparse不依赖于LAPACK和BLAS。 这使得构建过程更简单,二进制文件更小。可以不用使用。
命令行安装
安装依赖
$ 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)
$ sudo apt-get install libsuitesparse-dev
# - However, if you want to build Ceres as a *shared* library, you must
# add the following PPA:
$ sudo add-apt-repository ppa:bzindovic/suitesparse-bugfix-1319687
$ sudo apt-get update
$ sudo apt-get install libsuitesparse-dev
下载源码编译ceres静态库
编译ceres依赖eigen和glog,因此需要先拉取eigen和glog。
# 拉取glog
$ git clone https://github.com/google/glog.git
# 修改option
# option (WITH_GFLAGS "Use gflags" OFF)
$ vi CMakeLists.txt
# 开始编译
$ cmake ..
$ make -j4
$ make DESTDIR=./output install
# 拉取eigen
$ git clone https://gitlab.com/libeigen/eigen.git
# 拉取ceres
$ git clone https://ceres-solver.googlesource.com/ceres-solver
$ cd ceres-solver
# 项目需求,切换到1.13.0版本
$ git checkout 1.13.0
$ git branch
* (HEAD detached at 1.13.0)
# 根据实际需求,修改option
# option(GFLAGS "Enable Google Flags." OFF)
# option(LAPACK "Enable use of LAPACK." OFF)
# option(OPENMP "Enable threaded solving in Ceres (requires OpenMP)" OFF)
# option(BUILD_TESTING "Enable tests" OFF)
# option(BUILD_EXAMPLES "Build examples" OFF)
vi CMakeList.txt
$ mkdir build && cd build
# 开始编译
$ cmake ..
$ make -j4
$ make DESTDIR=./output install
示例
$ ls
build CMakeLists.txt include lib test1.cpp
$ ls include
ceres Eigen glog
$ ls lib
libceres.a libglog.a
test1.cpp是源自ceres官网的一个例子:
#include <iostream>
#include <ceres/ceres.h>
using namespace std;
using namespace ceres;
struct CostFunctor
{
template <typename T>
bool operator()(const T *const x, T *residual) const
{
residual[0] = T(10.0) - x[0];
return true;
}
};
int main(int argc, char **argv)
{
google::InitGoogleLogging(argv[0]);
// 寻优参数x的初始值,为5
double initial_x = 5.0;
double x = initial_x;
// 第二部分:构建寻优问题
Problem problem;
CostFunction *cost_function =
new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
problem.AddResidualBlock(cost_function, NULL, &x);
//第三部分: 配置并运行求解器
Solver::Options options;
options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x << " -> " << x << "\n";
return 0;
}
CMakeList.txt
$ cat CMakeList.txt
cmake_minimum_required(VERSION 3.4)
project(samples CXX)
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
# This project requires C++11.
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package (Threads REQUIRED)
add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
add_compile_options(-Wall)
FILE(GLOB samples_src_list ${PROJECT_SOURCE_DIR}/*.cpp)
message("${samples_src_list}")
link_directories(${PROJECT_SOURCE_DIR}/lib/)
foreach(src ${samples_src_list})
string(REGEX MATCH "[^/]+$" src_file ${src})
string(REPLACE ".cpp" "" exe_file_name ${src_file})
set(exe_target ${PROJECT_NAME}_${exe_file_name})
add_executable(${PROJECT_NAME}_${exe_file_name} ${src})
target_include_directories(${PROJECT_NAME}_${exe_file_name} PUBLIC include)
target_link_libraries(${PROJECT_NAME}_${exe_file_name}
${CMAKE_THREAD_LIBS_INIT}
ceres
glog
)
install(TARGETS ${PROJECT_NAME}_${exe_file_name} RUNTIME DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/bin)
endforeach(src)
测试
$ ./samples_test1
iter cost cost_change |gradient| |step| tr_ratio tr_radius ls_iter iter_time total_time
0 1.250000e+01 0.00e+00 5.00e+00 0.00e+00 0.00e+00 1.00e+04 0 7.20e-05 1.33e-04
1 1.249750e-07 1.25e+01 5.00e-04 5.00e+00 1.00e+00 3.00e+04 1 6.20e-05 2.84e-04
2 1.388518e-16 1.25e-07 1.67e-08 5.00e-04 1.00e+00 9.00e+04 1 2.19e-05 3.28e-04
Ceres Solver Report: Iterations: 3, Initial cost: 1.250000e+01, Final cost: 1.388518e-16, Termination: CONVERGENCE
x : 5 -> 10