这篇博客主要介绍如何配置ceres库并通过使用ceres库拟合一条曲线来介绍ceres库的简单使用
参考了以下博客:
https://blog.csdn.net/weixin_39373577/article/details/81285420
一、安装依赖项
sudo apt-get install liblapack-dev
sudo apt-get install libsuitesparse-dev
sudo apt-get install libcxsparse3.1.2
sudo apt-get install libgflags-dev
sudo apt-get install libgoogle-glog-dev libgtest-dev
做以下说明:
- 在Ubuntu16.04上应该没有cxsparse3.1.2这个包,应该安装cxsparse3.1.4
- 安装速度可能会非常的慢,这里来个小插曲,说一下如何修改软件源为中科大的源,速度非常快
- 修改/etc/apt/sources.list中的内容为(修改前最好先把该文件复制一份):
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse
deb http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-security main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-updates main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-proposed main restricted universe multiverse
deb-src http://mirrors.ustc.edu.cn/ubuntu/ xenial-backports main restricted universe multiverse
- 更新sudo apt-get update
二、编译ceres
下载地址:https://github.com/ceres-solver/ceres-solver
- 解压,并cd到ceres根目录下
mkdir build
cd build
cmake ..
sudo make install
- 安装完成后,ceres头文件目录为/usr/local/include/ceres、库文件为/usr/local/lib/libceres.a,可以自己检查一下
三、使用ceres拟合一条二次曲线
这里简单的介绍一下思路,下一篇博客会详细的介绍一下两种梯度下降方法。
假设有一条曲线满足如下方程:
其中,
为曲线的参数,
为高斯噪声。
如果我们有N对点(x, y),我们要根据这些点求出曲线的参数,那么问题可以转换为下面的优化问题:
在上式中,待估计的变量是
。
程序:
#include <iostream>
#include <opencv2\core\core.hpp>
#include <ceres\ceres.h>
#include <chrono>
using namespace std;
//代价函数的计算模型
struct CURVE_FITTING_COST
{
const double _x, _y;
CURVE_FITTING_COST(double x, double y) :_x(x), _y(y) {
}
//残差计算
template<typename T>
bool operator()(const T* const abc, T* residual) const {
residual[0] = T(_y) - ceres::exp(abc[0] * T(_x)*T(_x) + abc[1] * T(_y) + abc[2]);
return 0;
}
};
int main(int argc, char *argv[]) {
double a = 1.0, b = 2.0, c = 1.0;
int N = 100;
double w_sigma = 1.0;
cv::RNG rng;
double abc[3] = { 0,0,0 };
vector<double> x_data, y_data;
cout << "generating data: " << endl;
for (int i = 0; i < N; i++) {
double x = i / 100.0;
x_data.push_back(x);
y_data.push_back(exp(a*x*x + b*x + c) + rng.gaussian(w_sigma));
cout << x_data[i] << " " << y_data[i] << endl;
}
//构建最小二乘问题
ceres::Problem problem;
for (int i = 0; i < N; i++) {
problem.AddResidualBlock(
new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3>(
new CURVE_FITTING_COST(x_data[i], y_data[i])
),
nullptr,
abc
);
}
//配置求解器
ceres::Solver::Options options;
options.linear_solver_type = ceres::DENSE_SCHUR;
options.minimizer_progress_to_stdout = true;
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() << " seconds. " << endl;
//输出结果
cout << summary.BriefReport() << endl;
cout << "estimated a, b, c = ";
for (auto a : abc)
cout << a << " ";
cout << endl;
return 0;
}
CMakeLists.txt的内容如下:
cmake_minimum_required( VERSION 2.8 )
project( ceres_curve_fitting )
set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11 -O3" )
# 寻找Ceres库并添加它的头文件
find_package( Ceres REQUIRED )
include_directories( ${CERES_INCLUDE_DIRS} )
# OpenCV
find_package( OpenCV REQUIRED )
include_directories( ${OpenCV_DIRS} )
add_executable( curve_fitting main.cpp )
# 与Ceres和OpenCV链接
target_link_libraries( curve_fitting ${CERES_LIBRARIES} ${OpenCV_LIBS} )
然后编译链接很简单,就不说了。运行结果如下:
有兴趣的,可以自己把曲线画出来,形象的看一下拟合结果。