I. Introduction to Ceres
Ceres is a library for solving least-squares problems. We only need to define the problem to be optimized and then let it perform the computation.
① Basic Concepts Common least-squares problems have the following form:

- Parameter blocks: optimization variables such as , …
- Cost functions (residual blocks/error terms):
- Kernel function: ρ(·). The objective function is formed by summing many squared terms after applying the kernel function
② Usage
- Define each parameter block. A parameter block is simply a vector, but it can also have a special structure such as a quaternion or Lie algebra.
- Define how each residual block is computed. A residual block performs a custom computation on the parameter blocks and returns a residual value; the sum of squares is then used as the value of the objective function.
- Define how the Jacobian is computed.
- Add all parameter blocks and residual blocks to the Problem object defined by Ceres, and call the Solve function to solve the problem
II. Installing Ceres
First, download the Ceres source code
git clone https://github.com/ceres-solver/ceres-solver.git
Install the dependencies required by Ceres
sudo apt install libsuitesparse-dev libcxsparse3 libgflags-dev libgoogle-glog-dev libgtest-dev
Then enter the folder, compile, and install Ceres. This takes quite a while, about 20min.
cd ceres-solver/
mkdir build
cd build
cmake ..
make
sudo make install
After installation, if you can find the Ceres header files in /usr/local/include/ceres/ and the library file /usr/local/lib/libceres.a is also present, the installation was successful and you can use Ceres for optimization computations.
ll /usr/local/include/ceres/
ll /usr/local/lib/libceres.a
III. Fitting a Curve with Ceres
This example program depends on opencv and Eigen, which must be installed in advance.
The code in main.cpp is as follows:
#include <iostream>
#include <opencv2/core/core.hpp>
#include <ceres/ceres.h>
#include <chrono>
using namespace std;
// 构建代价函数的计算模型
struct CURVE_FITTING_COST{
CURVE_FITTING_COST(double x, double y) : _x(x), _y(y) {}
// 重载(),仿函数
template<typename T>
bool operator()(
const T *const abc, // 模型参数,有3维
T *residual) const {
residual[0] = T(_y) - ceres::exp(abc[0] * T(_x) * T(_x) + abc[1] * T(_x) + abc[2]); // y-exp(ax^2+bx+c)
return true;
}
const double _x, _y;
};
int main(int argc, char **argv) {
//定义数据参数
double ar = 1.0, br = 2.0, cr = 1.0; //真实参数值
double ae = 2.0, be = -1.0, ce = 5.0; //估计参数值
int N = 100; //数据点个数
double w_sigma = 1.0; //噪声Sigma值
double inv_sigma = 1.0 / w_sigma;
cv::RNG rng; //随机数产生器
//生成100个带高斯噪声的数据
vector<double> x_data, y_data;
for (int i = 0; i < N; i++){
double x = i / 100.0;
x_data.push_back(x);
y_data.push_back(exp(ar * x * x + br * x + cr) + rng.gaussian(w_sigma * w_sigma));
}
double abc[3] = {ae, be, ce};
//构建最小二乘问题
ceres::Problem problem;
for (int i = 0; i < N; i++){
//添加误差项。使用自动求导,模板参数:误差类型、输出维度、输入维度、维数要与前面struct中一致
problem.AddResidualBlock(new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(new CURVE_FITTING_COST(x_data[i], y_data[i])),nullptr,abc);
//nullptr为核函数不使用为空,abc为待估计参数
}
//配置并运行求解器
ceres::Solver::Options options; //定义配置项
options.linear_solver_type = ceres::DENSE_NORMAL_CHOLESKY; //配置增量方程的解法
options.minimizer_progress_to_stdout = true; //输出到cout
ceres::Solver::Summary summary; //定义优化信息
chrono::steady_clock::time_point t1 = chrono::steady_clock::now(); //计时:求解开始时间
ceres::Solve(options, &problem, &summary); //开始优化求解!
chrono::steady_clock::time_point t2 = chrono::steady_clock::now(); //计时:求解结束时间
chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1); //计算求解耗时
//输出信息
cout << "solve time cost = " << time_used.count() << "s." << endl; //输出求解耗时
cout << summary.BriefReport() << endl; //输出简要优化信息
cout << "estimated a, b, c = ";
for (auto a:abc) //输出优化变量
cout << a << " ";
cout << endl;
return 0;
}
CMakeLists.txt contains the following:
cmake_minimum_required(VERSION 3.20)
project(ceresCurveFitting)
set(CMAKE_CXX_STANDARD 14)
# OpenCV库
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
# Ceres库
find_package(Ceres REQUIRED)
include_directories(${CERES_INCLUDE_DIRS})
# Eigen库
include_directories("/usr/include/eigen3")
# 定义可执行文件
add_executable(ceresCurveFitting main.cpp)
# 链接库
target_link_libraries(ceresCurveFitting ${OpenCV_LIBS})
target_link_libraries(ceresCurveFitting ${CERES_LIBRARIES})

Comments