打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
非线性优化库G20实战套路

01

G2O框架预览

非线性优化库g2o实战中,基本按照如上图5步进行;

02

G2O实战套路

2.0 准备工作

BlockSolver

1.BlockSolver用于计算稀疏的雅可比矩阵和Hessian矩阵;

2.BlockSolverTraits<P, L>:P表示姿态纬度(流形manifold下最小表示),L表示三维点维度;为构建雅可比矩阵和Hessian矩阵提供尺度;

3.为便于使用,此处采用typedef缩写,或系统自带缩写:

typedef g2o::BlockSolver<BlockSolverTraits<3,1>> Block

typedef BlockSolver<BlockSolverTraits<6,3>> BlockSolver_6_3

//用于BA/3D slam;

typedef BlockSolver<BlockSolverTraits<7,3>> BlockSolver_7_3;

//用于带尺度的BA/3D slam;

typedef BlockSolver<BlockSolverTraits<3,2>> BlockSolver_3_2

//用于BA/2D 3自由度 slam;

此外还具有变尺寸:

using BlockSolverX = BlockSolverPL<Eigen::Dynamic, 

                                                                         Eigen::Dynamic>;

2.1 建立线性求解器

建立线性求解器LinearSolver,用于H△x=b,△x=H.inv()*b; 

(结合实际情况替换Block)

第1步

Block::LinearSolverType* linearSolver =new    

                   g2o::LinearSolverDense(Block::PoseMatrixType);

其他求解器

LinearSolverDense 

使用dense Cholesky分解法

LinearSolverEigen

依赖项只有eigen,与Csparse差不多

LinearSolverCholmod 

使用sparse cholesky分解法

LinearSolverCSparse

使用CSparse法

LinearSolverPCG

使用preconditioned conjugate gradient 

2.2 建立块求解器

建立块求解器BlockSolver,用LinearSolver对其初始化;

第2步

Block* solver_ptr = new 

             Block(std::unique_ptr<Block::LinearSolverType>

                                                                         (linearSolver));

2.3 建立总求解器

从GN/LM/DogLeg中选择优化算法,并用块求解器初始化

第3步

g2o::OptimizationAlgorithmLevenberg* solver =new 

g2o:OptimizationAlgorithmLevenberg(std::unique_ptr<Block>                                                                            (solver_ptr));

其他优化算法求解器

OptimizationAlgorithmGaussNewton 

高斯牛顿

OptimizationAlgorithmDogleg

DL

2.4 建立稀疏优化器

建立稀疏优化器optimizer,并为其设定求解器

第4步

g2o::SparseOptimizer optimizer;

optimizer.setAlgorithm(solver);

optimizer.setVerbose(true);   //打开调试输出

2.5 添加顶点

第5步

5.1 初始化顶点

g2o::VertexSE3Expmap* vSE3 =

                                new g2o::VertexSE3Expmap();

5.2 顶点赋初值

vSE3->setEstimate(

              Converter::soSE3Quat(pFrame->mTcw));                //setEstimate是继承而来

5.3 顶点设定编号

vSE3->setId(idx);                      

//如果单个节点就是0;

//多个节点看情况确定,setId是继承而来;

5.4 顶点是否参与优化

vSE3->setFixed(false);        

//参与优化所以无需固定;

//有些定点只是提供约束不优化所以需要固定true

5.5 顶点是否对齐shur,针对三维点类型

vSE3->setMarginalized(false);

5.6 顶点加入优化器optimizer

optimizer.addVertex(vSE3);

//addVertex是继承而来

自定义顶点

class VertexSE3Expmap::public      

                              g2o::BaseVertex<6, SE3Quat>  //6:流形空间中定点的最小维度表示

//SE3Quat:顶点数据类型

{

public:

EIGEN_MAKE_ALIGNED_OPERATOR_NEW     //Eigen库在类当中使用时必须添加,以便位对齐

VertexSE3Expmap (){}       //顶点构造函数

virtual void read(std::istream& is) {}         

virtual void write(std::ostream& os) const {}

 //read和write函数一般情况下可忽略

virtual void setOriginImpl()  //设定顶点初值或重置

{

    _estimate = SE3Quat ();                   

   //SE3Quat是顶点中成员的初始化方式

}                                          

 //例如构造函数

SE3Quat(){

_r.setIdentity();  

_t.setZero(); }

virtual void oplusImpl(const double* update) override   //定点的更新方式

{                                          

//Eigen::Map<const Vector6d>update(_update);

_estimate += /*update*/;

//setEstimate(SE3Quat::exp(update)*estimate());

}

}

常见顶点

VertexSE2 : public BaseVertex<3, SE2> 

//2D pose Vertex, (x,y,theta)

VertexSE3 : public BaseVertex<6, Isometry3> 

//6d vector (x,y,z,qx,qy,qz) (note that we

leave out the w part of the quaternion)

VertexPointXY : public BaseVertex<2, Vector2>

VertexPointXYZ : public BaseVertex<3, Vector3>

VertexSBAPointXYZ : public BaseVertex<3, Vector3>

// SE3 Vertex parameterized internally with a transformation matrix and externally with

its exponential map

VertexSE3Expmap : public BaseVertex<6, SE3Quat>

// SBACam Vertex, (x,y,z,qw,qx,qy,qz),(x,y,z,qx,qy,qz) (note that we leave out the w part

of the quaternion.

// qw is assumed to be positive, otherwise there is an ambiguity in qx,qy,qz as a

rotation

VertexCam : public BaseVertex<6, SBACam>

// Sim3 Vertex, (x,y,z,qw,qx,qy,qz),7d vector,(x,y,z,qx,qy,qz) (note that we leave out

the w part of the quaternion.

VertexSim3Expmap : public BaseVertex<7, Sim3>

2.6 添加边

第6步

6.1 初始化边

g2o::EdgeSE3ProjectXYZOnlyPose*  e = new g2o:: EdgeSE3ProjectXYZOnlyPose ();

6.2 设置边的顶点

e->setVertex(0,

dynamics_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(0))); 

//单元边

e->setVertex(1, pose);          

//如果是双元边,则0对应三维点顶点,

//1对应位姿顶点

6.3 设置id

e->setId(i);    

6.4 设置测量值

e->setMeasurement(obs);       

//obs是测量值

6.5 设置协方差矩阵

e->setInformation(

              Eigen::Matrix2d::Identity()*inSigma2); 

6.6 设置鲁邦函数

g2o::RobustKernelHuber* rk = 

                         new g2o:: RobustKernelHuber;

e->setRobustKernel(rk);

6.7 设置边中其他相关参数,若有

e->fx = pFrame->fx;

e->fy = pFrame->fy;

e->cx = pFrame->cx;

e->cy = pFrame->cy;

6.8  添加边

optimizer.addEdge(e);

自定义-单元边

class EdgeSE3ProjectXYZOnlyPose: public  g2o::

BaseUnaryEdge<2,vector2d, VertexSE3Expmap>

/单元边<测量值维度,测量值类型,边对应的定点>

public:

EIGEN_MAKE_ALIGNED_OPERATOR_NEW

EdgeSE3ProjectXYZOnlyPose (){}

virtual bool read(istream& in) {}

virtual bool write(ostream& out) const {}

virtual void computeError() override  

#计算测量值和顶点计算值的误差

{

const VertexSE3Expmap* v1 = static_cast

            <const VertexSE3Expmap*>(_vertices[0]);

vector2d obs(_measurement);

_error=obs–cam_project(v1->estimate().map(Xw));

//利用定点v1的变换矩阵使用map将世界坐标转换到

//相机坐标,然后cam_project投影到图像坐标

}

virtual void linearizeOplus() override   

//当前顶点值下该误差对优化变量的偏导数,

//雅可比矩阵

{

}

private:

  Vector3d Xw;  double fx,fy,cx,cy;

}

Vector2 CameraParameters::cam_project(const Vector3 & trans_xyz) const {

Vector2 proj = project2d(trans_xyz);

Vector2 res;

res[0] = proj[0]*fx+ cx;

res[1] = proj[1]*fy + cy;

return res;

Vector3 map (const Vector3& xyz) const {

return s*(r*xyz) + t;

}

自定义-双元边

class EdgeProjectXYZ2UV : public BaseBinaryEdge

<2, Vector2D,

VertexSBAPointXYZ, VertexSE3Expmap>

 /双元边<测量值维度,测量值类型,边对应的定点>

{

public:                                      

EIGEN_MAKE_ALIGNED_OPERATOR_NEW;

//1. 默认初始化

EdgeProjectXYZ2UV();

//2. 计算误差

void computeError()

{

//李群相机位姿v1

const VertexSE3Expmap* v1 = static_cast<const VertexSE3Expmap*>(_vertices[1]);

// 顶点v2

const VertexSBAPointXYZ* v2 = static_cast<const VertexSBAPointXYZ*>(_vertices[0]);

//相机参数

const CameraParameters * cam

= static_cast<const CameraParameters *>(parameter(0));

//误差计算,测量值减去估计值,也就是重投影误差obs-cam

//估计值计算方法是T*p,得到相机坐标系下坐标,然后在利用camera2pixel()函数得到像素坐标。

Vector2D obs(_measurement);

_error = obs-cam->cam_map

                    (v1->estimate().map(v2->estimate()));

}

//3. 线性增量函数,也就是雅克比矩阵J的计算方法

virtual void linearizeOplus();

//4. 相机参数

CameraParameters * _cam;

bool read(std::istream& is);

bool write(std::ostream& os) const;

};

G2O重要成员变量和函数

_measurement:存储观测值

_error:存储computeError() 函数计算的误差

_vertices[]:存储顶点信息,比如二元边的话,_vertices[] 的大小为2,存储顺序和调用setVertex(int, vertex) 是设定的int 有关(0 或1)

setId(int):来定义边的编号(决定了在H矩阵中的位置)

setMeasurement(type) 函数来定义观测值

setVertex(int, vertex) 来定义顶点

setInformation() 来定义协方差矩阵的逆

2.7 设置优化参数

设置优化参数,开始优化;

第7步

optimizer.initializeOptimization();

optimizer.optimize(100);  //迭代100次

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
g2o优化顶点和边1 2 3 (长文)
ORB-SLAM3 单目地图初始化(终结篇)
ceres实现的pnp解算后的位姿优化代码详解
VINS
法向量、旋转矩阵计算(五十一)
自动驾驶:基于PCL的激光雷达感知
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服