1.自己定义顶点类、边类或者用已经有的。
1.1定义顶点
例子
class CurveFittingVertex: public g2o::BaseVertex<3, Eigen::Vector3d>
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
virtual void setToOriginImpl() // 重置
{
_estimate << 0,0,0;
}

virtual void oplusImpl( const double* update ) // 更新
{
_estimate += Eigen::Vector3d(update);
}
// 存盘和读盘:留空
virtual bool read( istream& in ) {}
virtual bool write( ostream& out ) const {}
};
自己能够操作的,修改类名为自己的类名。修改顶点的维度和类型,就是g2o:BaseVertex<>里的两个参数。
函数setToOriginImpl{}里设置_estimate的初值。你可以如例子中_estimate<<0,0,0,也可以_estimate=SE3Quat(),也可以留空。
oplusImpl是设定估计的更新值的函数,一般里面的变量都是更新值const double* update,至于这个update你可以设成update_什么的。然后函数里估计值会变成估计值和更新值的和或积,而更新值的类型必须修改成和顶点类型一致。也可以重写这个函数,在括号后输入override,而在{}定义一个新参数v,这个v可以和顶点类型不一致,v是由update得到或者由update和其他参数一起得到,然后是_estimate+=v,最后在设顶点估计值的时候,估计值要和v的类型一致。最重要的就是这个oplusImpl函数。
而存盘和读盘函数,可以选择留空,或者在{}里输入你想输入的数,比如return false;等
也可以设置参数。
1.2定义边
class CurveFittingEdge: public g2o::BaseUnaryEdge<1,double,CurveFittingVertex>
{
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
CurveFittingEdge( double x ): BaseUnaryEdge(), _x(x) {}
// 计算曲线模型误差
void computeError()
{
const CurveFittingVertex* v = static_cast<const CurveFittingVertex*> (_vertices[0]);
const Eigen::Vector3d abc = v->estimate();
_error(0,0) = _measurement - std::exp( abc(0,0)*_x*_x + abc(1,0)*_x + abc(2,0) ) ;
}
virtual bool read( istream& in ) {}
virtual bool write( ostream& out ) const {}
public:
double _x; // x 值, y 值为 _measurement
};
可操作的:边类名,边的顶点数,边数据类型,和边顶点类型。也就是g2o::BaseUnaryEdge<>中的内容。
CurveFittingEdge( double x ): BaseUnaryEdge(), _x(x) {}这个是类赋值,可以有,也可以没有,如果有,那么要定义参数_x,因为这里是把x赋值给_x.
最重要的就是计算误差函数computeError(),具体操作在{},主要是要给出_error的具体形式。
一般赋值_vertices[i]给顶点,比如v或者v1,v2等。这里v的类型就是之前我们定义的顶点类的指针。一般是这样定义的
const 顶点类* v=static_cast<const 顶点类*>(_vertices[0])
要么把v->estimate估计值传给新的值,要么直接用来计算误差值。误差值的是由测量值_measuremen减去预测值得到的,预测值是根据v->estimate得到的,具体怎么得按实际情况来。
还有linearizeOplus函数,这个函数是求雅克比矩阵,也可以没有这个函数。雅克比矩阵也就是误差函数对顶点的求导值。不是范数啊。
过程:先把_vertices[0],_vertices[1]等赋值给顶点,跟computeError函数里一模一样,也是
const 顶点类* 顶点名=static_cast<const 顶点类*>(_vertices[0]);
是跟computeError函数里重名的。
也会用到顶点的估计值来计算出雅克比矩阵的每一项的值。一般都是
_jacobianOplusXi(i,j)=;
2.定义图模型
typedef g2o::BlockSolver< g2o::BlockSolverTraits<3,1> > Block; // 每个误差项优化变量维度为3,误差值维度为1
Block::LinearSolverType* linearSolver = new g2o::LinearSolverDense<Block::PoseMatrixType>(); // 线性方程求解器
Block* solver_ptr = new Block( linearSolver ); // 矩阵块求解器
// 梯度下降方法,从GN, LM, DogLeg 中选
g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg( solver_ptr );
// g2o::OptimizationAlgorithmGaussNewton* solver = new g2o::OptimizationAlgorithmGaussNewton( solver_ptr );
// g2o::OptimizationAlgorithmDogleg* solver = new g2o::OptimizationAlgorithmDogleg( solver_ptr );
g2o::SparseOptimizer optimizer; // 图模型
optimizer.setAlgorithm( solver ); // 设置求解器
optimizer.setVerbose( true ); // 打开调试输出
上面的基本就是套路了。
自己操作的是定义自己的误差项的优化变量的维度,还有误差值的维度,就是g2o::BlockSolverTraits<> 里的两个值。
线性方程求解器如果是提取特征点之类的用Dense,稠密的,如果是要进行消元什么的,用Cholmod.
优化算法一般用的都是列文伯格,也可以用高斯牛顿等。
过程就是由线性方程求解器linearSolver得到矩阵块求解器solver_ptr,由矩阵块求解器得到梯度下降方法solver,图模型再设置算法为solver.

2.1图模型添加顶点
CurveFittingVertex* v = new CurveFittingVertex();
v->setEstimate( Eigen::Vector3d(0,0,0) );
v->setId(0);
optimizer.addVertex( v );
一般的模式就是先设置顶点,如果是多于一个顶点的话,以顶点数量为最大值做一个for循环,在for循环里,设置顶点,顶点估计值,顶点id,是否可以边缘化,也就是之后要不要进行消元,图模型添加边。形式如下
顶点类* v=new 顶点类();要有一个括号,如果有输入值的话,可以放在括号里。
v->setId(i);
v->setEstimate();//估计值如果有初值,就添加上,如果没有,就设成和顶点类型相同的,并且设为0.如果顶点中update被重写,那么估计值类型和顶点类型不一致
v->setMarginlized(true);//默认false,需要设置的时候都是true
optimizer.addVertex(v)
注意顶点和边都是指针,所以用->访问。
2.2图模型添加边
for ( int i=0; i<N; i++ )
{
CurveFittingEdge* edge = new CurveFittingEdge( x_data[i] );
edge->setId(i);
edge->setVertex( 0, v ); // 设置连接的顶点
edge->setMeasurement( y_data[i] ); // 观测数值
edge->setInformation( Eigen::Matrix<double,1,1>::Identity()*1/(w_sigma*w_sigma) ); // 信息矩阵:协方差矩阵之逆
optimizer.addEdge( edge );
}
根据边的数量做一个for循环,先设置边。然后边要设置id,顶点,测量值,信息矩阵,图模型添加边。
形式如下,设边名称为edge的话
边类* edge=new 边类();
edge->setId(j);
edge->setVertex(0,v);
edge->setMeasurement();测量值是必须得有的,视具体情况而定。
edge->setInformation();设置信息矩阵,信息矩阵是协方差矩阵的逆,所以也可以称之为设置协方差矩阵。
edge->setParameterId();//一般没有这一项
optimizer->addEdge(edge);

2.3图模型求解
optimizer.initializeOptimization();
optimizer.optimize(100);
直接就这两句就可以了。
optimize()括号内的数值可以修改。

g2o的一般过程的更多相关文章

  1. 一起做RGB-D SLAM(8) (关于调试与补充内容)

    “一起做”系列完结后,我收到不少同学给我的反馈.他们提了一些在程序编译/运行过程中的问题.我把它们汇总起来,组成了这个“补充篇”.你也可以看成是一个Q&A. Q: OpenCV的版本?A: 我 ...

  2. 改进地图的vo类

    现在的地图只是各帧特征点的集合.创建地图:局部,全局.局部:只相机位置附近的特征点,用来和当前帧匹配求解相机位置的.全局:不定位,回环检测和地图表达.重点或麻烦:维护局部地图的规模.为了实时,保证地图 ...

  3. vo优化总结

    问题1:位姿估计用的ransac,只用了几个点,如果3d_2d点存在噪声,不行.优化:把这值当做初值,用非线性优化问题2:深度图有误差,深度过近或过远不行,有误差.而特征点往往在物体边缘处,深度测量值 ...

  4. c++ primer plus 第6版 部分二 5- 8章

    ---恢复内容开始--- c++ primer plus 第6版 部分二    5-  章 第五章 计算机除了存储外 还可以对数据进行分析.合并.重组.抽取.修改.推断.合成.以及其他操作 1.for ...

  5. 从零开始一起学习SLAM | 掌握g2o顶点编程套路

    点"计算机视觉life"关注,置顶更快接收消息! ## 小白:师兄,上一次将的g2o框架<从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码>真的很清晰 ...

  6. Ubuntu配置ORB-SLAM2过程中的问题

    https://www.imooc.com/article/details/id/29136 1. 提示“CMAKE_CXX_COMPILER-NOTFOUND ” 具体形式: Check for w ...

  7. 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码

    首发于公众号:计算机视觉life 旗下知识星球「从零开始学习SLAM」 这可能是最清晰讲解g2o代码框架的文章 理解图优化,一步步带你看懂g2o框架 小白:师兄师兄,最近我在看SLAM的优化算法,有种 ...

  8. g2o的使用

    相关文献 1.论文 Grisetti, Giorgio, et al. “A tutorial on graph-based SLAM.” IEEE Intelligent Transportatio ...

  9. 视觉SLAM漫淡(二):图优化理论与g2o的使用

    视觉SLAM漫谈(二):图优化理论与g2o的使用 1    前言以及回顾 各位朋友,自从上一篇<视觉SLAM漫谈>写成以来已经有一段时间了.我收到几位热心读者的邮件.有的希望我介绍一下当前 ...

随机推荐

  1. 2016.7.12 在navicat中用sql语句建表

    参考资料: http://jingyan.baidu.com/article/f0e83a25a8c4b022e5910116.html 即新建query,然后run. (1)点击要新建表的位置,选择 ...

  2. openssl之BIO系列之9---BIO对的创建和应用

    BIO对的创建和应用 ---依据openssl doc/crypto/bio/bio_new_bio_pair.pod翻译和自己的理解写成 (作者:DragonKing Mail:wzhah@263. ...

  3. IIS配置asp.net网站

    http://wenku.baidu.com/view/f8ce6c14767f5acfa0c7cd36.html

  4. solr入门之pinyin4j源代码改写动态加入扩展词及整合进war项目中

    1.初始化时载入用户定义的字典 package net.sourceforge.pinyin4j; import net.sourceforge.pinyin4j.multipinyin.Trie; ...

  5. 如何修改mindmanager默认字体为微软雅黑

    mindmanager默认的字体是Verdana的,如何改为大家喜欢的其他字体呢?比如微软雅黑. 其实很简单,以我使用的是汉化版Mindmanager2012为例,只需要下面几个步骤   第1步:找到 ...

  6. tf树

    tf变换(1)   TF库的目的是实现系统中任一个点在所有坐标系之间的坐标变换,也就是说,只要给定一个坐标系下的一个点的坐标,就能获得这个点在其他坐标系的坐标. 使用tf功能包,a. 监听tf变换:  ...

  7. C语言 指向结构体数组的指针

    当结构体指针变量指向一个结构体变量数组的时候,此时指针变量的值就是结构体数组的首地址 关于如何定义结构体数组,和将结构体指针指向结构体变量数组,不是重点. 重点是,明白结构体指针的是怎么移动的, 我个 ...

  8. 非spring托管的类使用spring脱管的类。

    import org.springframework.beans.BeansException; import org.springframework.context.ApplicationConte ...

  9. poj 1163 The Triangle 记忆化搜索

    The Triangle Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 44998   Accepted: 27175 De ...

  10. C#高级编程八十一天----捕获异常

    捕获异常 前面主要说了关于异常的一些基础和理论知识,没有进入到正真的异常案例,这一讲通过几个案例来描写叙述一下异常的捕获和处理. 案例代码: using System; using System.Co ...