视觉SLAM14讲 Ceres和G2O

1. 安装问题

1.1 Ceres安装

  • 安装依赖项
    sudo apt-get install liblapack-dev libsuitesparse-dev libcxsparse3.1.2 libgflags-dev libgoogle-glog-dev libgtest-dev
  • 进入Ceres目录,然后一顿常规操作
mkdir build
cd build
cmake ..
make -j8
sudo make install  #important

最后一句sudo make install很重要,一定不能漏掉,否则无法在/usr/local/include/ceres下找到Ceres的头文件。

1.2 G2O安装

  • 安装依赖项
    sudo apt-get install libqt4-dev qt4-qmake libqglviewer-dev libsuitesparse-dev libcxsparse3 libcholmod3
    有些博客上面的依赖项,安装的是libcholmod,会出现报错。那是因为找不到这样包,高博在第二版的书中写的是libcholmods。

  • 剩下的部分和ceres安装一样

2. Ceres拟合曲线

参考博客https://www.jianshu.com/p/e5b03cf22c80

2.1 三个求解步骤

  • 第一步:构建cost function(代价函数),也就是定义残差块(误差项)的结构体。这一部分需要仿函数(Functor)来实现,在结构体内重载()运算符。这种方法可以使得Ceres像调用函数一样,对该类的某个对象(比如a)调用a<double>()方法。
  • 第二步:通过代价函数构建待求解的优化问题,比如最小二乘问题。
  • 第三步:配置求解器参数并求解问题,这个步骤就是设置方程怎么求解,求解过程是否输出等,然后调用一下solve方法。

2.2 构建代价函数结构体

这里的使用了仿函数的技巧,即在CostFunction结构体内,对()进行重载,这样的话,该结构体的一个实例就能具有类似一个函数的性质,在代码编写过程中就能当做一个函数一样来使用。

struct CURVE_FITTING_COST
{
    CURVE_FITTING_COST ( double x, double y ) : _x ( x ), _y ( y ) {}
    // 残差的计算
    template <typename T>
    //重载()运算符,输入待寻优的参数abc,输出残差
    bool operator() (
            const T* const abc,     // 模型参数,有3维,模板类型为T的常量指针
            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;    // x,y数据
};

关于仿函数,对结构体和类的一个实例,比如Myclass类中的一个实例Obj1,如果Myclass里对()进行了重载,那Obj1被创建以后,就可以将Obj1这个实例当做函数来使用,比如Obj(x)这样。下面的代码可以有助于理解。

//仿函数的示例代码
#include<iostream>
using namespace std;
class Myclass
{
public:
    Myclass(int x):_x(x){};
    int operator()(const int n)const{
    return n*_x;
  } //重载()运算符,在括号内输入n,得到n*x
private:
    int _x;
};

int main()
{
    Myclass Obj1(5);
    cout<<Obj1(3)<<endl;
    system("pause");
return 0;
} //输出15,因为第一个Obj1(5)中的5是构造函数的变量x,而第二个Obj1(3)中的3是重载运算符()里面的3,是一种运算!

了解了仿函数技巧的使用方法后,在回头看看ceres使用中构造CostFuction 的具体方法:CostFunction结构体中,对括号符号重载的函数中,参数有两个,一个是待优化的变量abc,另一个是残差residual,也就是代价函数的输出。
重载了()符号之后,CostFunction就可以传入AutoDiffCostFunction方法来构建寻优问题了。

2.3 通过代价函数构建最小二乘问题

// 构建最小二乘问题
    ceres::Problem problem;
    for ( int i=0; i<N; i++ )
    {
        problem.AddResidualBlock (     // 向问题中添加误差项
                // 使用自动求导,模板参数:误差类型,输出维度(abc),输入维度(残差),维数要与前面struct中一致
                new ceres::AutoDiffCostFunction<CURVE_FITTING_COST, 1, 3> (
                        new CURVE_FITTING_COST ( x_data[i], y_data[i] )
                ),
                nullptr,            // 核函数,这里不使用,为空
                abc                 // 待估计参数
        );
    }

构建最小二乘问题时,需要将所有误差项依次添加进去。而每个误差项又是由前面定义的结构体构成的。需要注意的是,每个误差项需要指定代价函数求导的方法,有三种方法可供选择:自动求导、数值求导和自行推导。一般采用自动求导,既方便,又节约运行时时间。
以自动求导为例,ceres::AutoDiffCostFunction是个模板类,后两个模板参数为输出维度和输入维度,必须与前面定义的结构体中的residual和abc的维度一样。

2.4 配置并求解问题

    // 配置求解器
    ceres::Solver::Options options;     // 这里有很多配置项可以填
    options.linear_solver_type = ceres::DENSE_QR;  // 增量方程如何求解
    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()<<" seconds. "<<endl;

    // 输出结果
    cout<<summary.BriefReport() <<endl;
    cout<<"estimated a,b,c = ";
    for ( auto a:abc ) cout<<a<<" ";
    cout<<endl;

使用ceres::Solver::Options配置求解器。这个类有许多字段,每个字段都提供了一些枚举值供用户选择。所以需要时只要查一查文档就知道怎么设置了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268