Ceres solver是一个开源C++库,用于建模和解决大型、复杂的优化问题。它可用于解决具有边界约束的非线性最小二乘问题和一般无约束优化问题。它是一个成熟,功能丰富且性能强大的库,自2010年以来一直在Google的生产中使用。
# 从github上git clone最近源代码
git clone https://ceres-solver.googlesource.com/ceres-solver
# 安装相关依赖
# CMake
sudo apt-get install cmake
# google-glog + gflags
sudo apt-get install libgoogle-glog-dev libgflags-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
# 源码编译安装
mkdir build
cd build
cmake ..
make -j3
make test
# Optionally install Ceres, it can also be exported using CMake which
# allows Ceres to be used without requiring installation, see the documentation
# for the EXPORT_BUILD_DIR option for more information.
make install
首先要编写CMakeLists.txt文件保证头文件和动态库可以被找到。
/*CMakeLists.txt*/
cmake_minimum_required(VERSION 3.8.0)
project(ceres_example)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(Ceres REQUIRED)
include_directories(
${CERES_INCLUDE_DIRS}
)
add_executable(ceres_example
ceres_example.cpp)
target_link_libraries(ceres_example
${CERES_LIBRARIES}
)
然后是Ceres官网的一个简单的例程。
#include <ceres/ceres.h>
class CostFunctor {
public:
template <typename T>
bool operator()(const T* const x, T* residual) const
{
residual[0] = 10.0 - x[0];
return true;
}
};
int main(int argc, char const* argv[])
{
double initial_x = 5.0;
double x = initial_x;
// Build the problem.
ceres::Problem problem;
// Set up the only cost function (also known as residual). This uses
// auto-differentiation to obtain the derivative (jacobian).
ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
problem.AddResidualBlock(cost_function, nullptr, &x);
// Run the solver!
ceres::Solver::Options options;
options.linear_solver_type = ceres::DENSE_QR;
options.minimizer_progress_to_stdout = true;
ceres::Solver::Summary summary;
Solve(options, &problem, &summary);
std::cout << summary.BriefReport() << "\n";
std::cout << "x : " << initial_x
<< " -> " << x << "\n";
return 0;
}
出现下列输出,说明Ceres安装正常。
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 2.69e-05 8.58e-05
1 1.249750e-07 1.25e+01 5.00e-04 5.00e+00 1.00e+00 3.00e+04 1 8.01e-05 2.31e-04
2 1.388518e-16 1.25e-07 1.67e-08 5.00e-04 1.00e+00 9.00e+04 1 1.00e-05 2.56e-04
Ceres Solver Report: Iterations: 3, Initial cost: 1.250000e+01, Final cost: 1.388518e-16, Termination: CONVERGENCE
x : 5 -> 10
对于通过Ceres库去构建一个最小二乘的模型是非常简单的。接下来会介绍我的一些使用经验与例子。
一般我们需要通过一个残差类去描述一个代价。根据模型情况有时候也需要使用数值求导ceres::DynamicAutoDiffCostFunction。
//代价类
class CostFunctor{
public:
//构造函数
CostFunctor()
{
}
//通过仿函数去构造代价函数,只有模板函数支持自动求导,普通函数可以使用数值求导
template <typename T>
bool operator()(T const* const* parameters, T* residuals) const
{
return true;
}
//自动求导
static ceres::DynamicAutoDiffCostFunction<CostFunctor>*
create()
{
auto cost_function = new ceres::DynamicAutoDiffCostFunction<CostFunctor>(new CostFunctor);
//添加一个优化量
cost_function->AddParameterBlock(1);
//设置一个代价量
cost_function->SetNumResiduals(1);
return cost_function;
}
};
添加残差。
//这里有多少优化量直接列在后面,也可以使用std::vector<double*> parameter_blocks
problem.AddResidualBlock(CostFunctor::create(),nullptr,&x,&y);
设置常数上下限或者设置不变量。
problem.SetParameterLowerBound(&x,0,lower_bound);
problem.SetParameterUpperBound(&x,0,upper_bound);
problem.SetParameterBlockConstant(&x);
配置求解器参数
//配置求解器
ceres::Solver::Options options;
//options.linear_solver_type = ceres::DENSE_QR; //小规模最小二乘问题
options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY; //大规模稀疏最小二乘问题
options.max_solver_time_in_seconds = solve_time_limit_; //时间限制
options.max_num_iterations = 2000; //迭代次数限制
options.num_threads = 1; //线程数
options.minimizer_progress_to_stdout = false;
//求解
ceres::Solver::Summary summary;
ceres::Solve(options, &problem, &summary);