g2o 中定义好的常用边的类型

前言

        G2O的边分为两种类型的边,固定长度的边(BaseFixedSizedEdge)、可变长度的边(BaseVariableSizedEdge)。其中,固定长度的边又分为BaseUnaryEdge(一元边)、BaseBinaryEdge(二元边),可变长度的边又引申出了BaseMultiEdge(多元边)。

         g2o 边的源码 

//***g2o源码 g2o/g2o/core/base_unary_edge.h ***//
/**
 * \brief 模板化 BaseUnaryEdge,单边
 *
 * D  : int 类型,表示测量值的维度(Dimension)
 * E  : 测量值的数据类型
 */
template <int D, typename E, typename VertexXi>
class BaseUnaryEdge : public BaseEdge<D,E>{
    // 类的具体实现...
};
//***g2o源码 g2o/g2o/core/base_binary_edge.h ***
/**
 * \brief 模板化 BaseBinaryEdge,双边
 *
 * D  : int 类型,表示测量值的维度(Dimension)
 * E  : 测量值的数据类型
 * VertexXi  : 边连接的顶点类型
 * VertexXj  : 边连接的顶点类型
 */
template <int D, typename E, typename VertexXi, typename VertexXj>
class BaseBinaryEdge : public BaseEdge<D, E>{
    // 类的具体实现...
};
//***g2o源码 g2o/g2o/core/base_multi_edge.h ***//
/**
 * D  : int 类型,表示测量值的维度(Dimension)
 * E  : 测量值的数据类型
 */
template <int D, typename E>
using BaseMultiEdge = BaseVariableSizedEdge<D, E>;

        一般来说定义 Edge 需要重写这几个函数 ,需要注意的是,一旦指定了linearizeOplus(),就会使用该雅可比矩阵进行优化;如果不对该函数进行重写,就会使用自动求导的方式进行优化

//*** g2o源码 g2o/g2o/core/optimizable_graph.h ***// 

// 读盘、存盘函数,不需要进行读/写操作的话,仅仅声明一下就可以
// 纯虚函数,从流中读取顶点,即顶点的内部状态
virtual bool read(std::istream& is) = 0;
// 纯虚函数,将顶点写入到流
virtual bool write(std::ostream& os) const = 0;

// 边误差计算函数,使用当前边对应顶点计算得到的测量值与真实的测量值之间的误差
// 计算边的误差并将其存储在内部结构中
virtual void computeError() = 0;
// 边雅可比函数,计算在当前顶点的值下,误差对优化变量的偏导数,即 Jacobian
// 将流形空间中边的约束线性化,并将结果存储在给定的工作空间中
virtual void linearizeOplus(JacobianWorkspace& jacobianWorkspace) = 0;

         g2o 边模版

 class myEdge: public g2o::BaseBinaryEdge<errorDim, errorType, Vertex1Type, Vertex2Type>{
   public:
     EIGEN_MAKE_ALIGNED_OPERATOR_NEW      

     // myEdge 类的构造函数,调用了基类 BaseBinaryEdge<D, E, VertexXi, VertexXj> 的构造函数,初始化该边。
     myEdge() : BaseBinaryEdge<D, E, VertexXi, VertexXj>() {}
    

     bool read(std::istream& is) override {
       // 从输入流读取边数据的实现
     }
 
     bool write(std::ostream& os) const override {
       // 将边数据写入输出流的实现
     }

     void computeError() override{
          // 误差 = 估计值 - 测量值
          _error = /* 估计值 */ - _measurement;
          /**
          _error = /* _measurement 矩阵的逆*/  * /* 估计值 */;
          */
      }      

      // 误差对优化变量的偏导数
      void linearizeOplus() override{
          _jacobianOplusXi(pos, pos) = /* Jacobian */;       
          /**
          _jocobianOplusXj(pos, pos) = /* Jacobian */;
          */
      }      

    private:
      // data
  }

         g2o 中定义好的常用边的类型汇总如下,我们挑选几个常用的详细介绍

// 测量值:平面2D位姿,顶点:平面2D位姿
EdgeSE2Prior : public BaseUnaryEdge<3, SE2, VertexSE2>
// 测量值:平面2D点,顶点:平面2D位姿
EdgeSE2XYPrior : public BaseUnaryEdge<2, Vector2, VertexSE2>
// 测量值:平面2D点,顶点:空间3D点
EdgeXYPrior : public BaseUnaryEdge<2, Vector2, VertexPointXY>
// 测量值:欧式变换矩阵(4x4),顶点:空间3D位姿(四元数旋转)
EdgeSE3Prior : public BaseUnaryEdge<6, Isometry3, VertexSE3>
// 测量值:空间3D点,顶点:空间3D位姿(四元数旋转)
EdgeSE3XYZPrior : public BaseUnaryEdge<3, Vector3, VertexSE3>
// 测量值:空间3D点,顶点:空间3D点
EdgeXYZPrior : public BaseUnaryEdge<3, Vector3, VertexPointXYZ>


// 测量值:平面2D点,顶点:平面2D点、平面2D点
EdgePointXY : public BaseBinaryEdge<2, Vector2, VertexPointXY, VertexPointXY>
// 测量值:平面2D位姿,顶点:平面2D位姿、平面2D位姿
EdgeSE2 : public BaseBinaryEdge<3, SE2, VertexSE2, VertexSE2>
// 测量值:平面2D点,顶点:平面2D位姿、平面2D点
EdgeSE2PointXY : public BaseBinaryEdge<2, Vector2, VertexSE2, VertexPointXY>
// 测量值:空间3D点,顶点:空间3D点、空间3D点
EdgePointXYZ : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexPointXYZ>
// 测量值:欧式变换矩阵(4x4),顶点:空间3D位姿(四元数旋转)、空间3D位姿(四元数旋转)
EdgeSE3 : public BaseBinaryEdge<6, Isometry3, VertexSE3, VertexSE3>
// 测量值:空间3D点,顶点:空间3D位姿(四元数旋转)、空间3D点
EdgeSE3PointXYZ : public BaseBinaryEdge<3, Vector3, VertexSE3, VertexPointXYZ>
// 测量值:空间3D点,顶点:空间3D点、空间3D位姿(SE3,李代数旋转)
EdgeStereoSE3ProjectXYZ : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>
// 测量值:平面2D点,顶点:空间3D点、空间3D位姿(SE3,李代数旋转)
EdgeSE3ProjectXYZ : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>
// 测量值:平面2D点,顶点:空间3D点、空间3D位姿(SE3,李代数旋转)
EdgeProjectXYZ2UV : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>
// 测量值:空间3D点,顶点:空间3D点、空间3D位姿(SE3,李代数旋转)
EdgeProjectXYZ2UVU : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>
// 测量值:Sim3,顶点:Sim3、Sim3
EdgeSim3 : public BaseBinaryEdge<7, Sim3, VertexSim3Expmap, VertexSim3Expmap>
// 测量值:空间2D点,顶点:空间3D点、Sim3
EdgeSim3ProjectXYZ : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSim3Expmap>
// 测量值:空间2D点,顶点:空间3D点、Sim3
EdgeInverseSim3ProjectXYZ : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSim3Expmap>

一、EdgeSE3Prior : public BaseUnaryEdge<6, Isometry3, VertexSE3> 

//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_prior.h ***//
#ifndef G2O_EDGE_SE3_PRIOR_H_
#define G2O_EDGE_SE3_PRIOR_H_

#include "vertex_se3.h"
#include "g2o/core/base_unary_edge.h"
#include "parameter_se3_offset.h"
#include "g2o_types_slam3d_api.h"
namespace g2o {
  /**
   * \brief prior for an SE3 element
   *
   * Provides a prior for a 3d pose vertex. Again the measurement is represented by an
   * Isometry3 matrix.
   */
  class G2O_TYPES_SLAM3D_API EdgeSE3Prior : public BaseUnaryEdge<6, Isometry3, VertexSE3> {
  public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    EdgeSE3Prior();
    virtual bool read(std::istream& is);
    virtual bool write(std::ostream& os) const;

    // return the error estimate as a 3-vector
    void computeError();
    
    // jacobian
    virtual void linearizeOplus();

    /* 类中其他函数 */


} // end namespace
//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_prior.cpp ***//
#include "edge_se3_prior.h"

#include <iostream>

#include "isometry3d_gradients.h"

namespace g2o {
using namespace std;

// point to camera projection, monocular
EdgeSE3Prior::EdgeSE3Prior() : BaseUnaryEdge<6, Isometry3, VertexSE3>() {
  setMeasurement(Isometry3::Identity());
  information().setIdentity();
  _cache = 0;
  _offsetParam = 0;
  resizeParameters(1);
  installParameter(_offsetParam, 0);
}

bool EdgeSE3Prior::read(std::istream& is) {
  bool state = readParamIds(is);
  Vector7 meas;
  state &= internal::readVector(is, meas);
  setMeasurement(internal::fromVectorQT(meas));
  state &= readInformationMatrix(is);
  return state;
}

bool EdgeSE3Prior::write(std::ostream& os) const {
  writeParamIds(os);
  internal::writeVector(os, internal::toVectorQT(measurement()));
  writeInformationMatrix(os);
  return os.good();
}

void EdgeSE3Prior::computeError() {
  Isometry3 delta = _inverseMeasurement * _cache->n2w();
  _error = internal::toVectorMQT(delta);
}

void EdgeSE3Prior::linearizeOplus() {
  VertexSE3* from = static_cast<VertexSE3*>(_vertices[0]);
  Isometry3 E;
  Isometry3 Z, X, P;
  X = from->estimate();
  P = _cache->offsetParam()->offset();
  Z = _measurement;
  internal::computeEdgeSE3PriorGradient(E, _jacobianOplusXi, Z, X, P);
}
//*** g2o源码 g2o/g2o/types/slam3d/isometry3d_gradients.h ***//
template <typename Derived>
void computeEdgeSE3PriorGradient(Isometry3& E,
                                 const Eigen::MatrixBase<Derived>& JConstRef,
                                 const Isometry3& Z, const Isometry3& X,
                                 const Isometry3& P = Isometry3::Identity()) {
  Eigen::MatrixBase<Derived>& J =
      const_cast<Eigen::MatrixBase<Derived>&>(JConstRef);
  J.derived().resize(6, 6);
  // compute the error at the linearization point
  const Isometry3 A = Z.inverse() * X;
  const Isometry3& B = P;
  Isometry3::ConstLinearPart Ra = extractRotation(A);
  Isometry3::ConstLinearPart Rb = extractRotation(B);
  Isometry3::ConstTranslationPart tb = B.translation();
  E = A * B;
  Isometry3::ConstLinearPart Re = extractRotation(E);

  Eigen::Matrix<number_t, 3, 9, Eigen::ColMajor> dq_dR;
  compute_dq_dR(dq_dR, Re(0, 0), Re(1, 0), Re(2, 0), Re(0, 1), Re(1, 1),
                Re(2, 1), Re(0, 2), Re(1, 2), Re(2, 2));

  J.setZero();

  // dte/dt
  J.template block<3, 3>(0, 0) = Ra;

  // dte/dq =0
  // dte/dqj
  {
    Matrix3 S;
    skew(S, tb);
    J.template block<3, 3>(0, 3) = Ra * S;
  }

  // dre/dt =0

  // dre/dq
  {
    number_t buf[27];
    Eigen::Map<Eigen::Matrix<number_t, 9, 3, Eigen::ColMajor> > M(buf);
    Matrix3 Sx, Sy, Sz;
    internal::skew(Sx, Sy, Sz, Rb);
// 检测当前编译器是否为 Clang
#ifdef __clang__
    Matrix3 temp = Ra * Sx;
    Eigen::Map<Matrix3> M2(temp.data());
    Eigen::Map<Matrix3> Mx(buf);
    Mx = M2;
    temp = Ra * Sy;
    Eigen::Map<Matrix3> My(buf + 9);
    My = M2;
    temp = Ra * Sz;
    Eigen::Map<Matrix3> Mz(buf + 18);
    Mz = M2;
#else
    Eigen::Map<Matrix3> Mx(buf);
    Mx = Ra * Sx;
    Eigen::Map<Matrix3> My(buf + 9);
    My = Ra * Sy;
    Eigen::Map<Matrix3> Mz(buf + 18);
    Mz = Ra * Sz;
#endif
    J.template block<3, 3>(3, 3) = dq_dR * M;
  }
}

二、EdgeSE3XYZPrior : public BaseUnaryEdge<3, Vector3, VertexSE3> 

//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_xyzprior.h ***//
#ifndef G2O_EDGE_SE3_PRIOR_XYZ_H
#define G2O_EDGE_SE3_PRIOR_XYZ_H

#include "g2o/core/base_unary_edge.h"
#include "g2o_types_slam3d_api.h"
#include "parameter_se3_offset.h"
#include "vertex_se3.h"

namespace g2o {
/**
 * \brief Prior for a 3D pose with constraints only in xyz direction
 * \brief 仅在 xyz 方向上具有约束的 3D位姿 先验
 */
class G2O_TYPES_SLAM3D_API EdgeSE3XYZPrior
    : public BaseUnaryEdge<3, Vector3, VertexSE3> {
 public:
  virtual bool read(std::istream& is);
  virtual bool write(std::ostream& os) const;
  virtual void computeError();
  virtual void linearizeOplus();
  
  /* 类中其他函数 */

 protected:
  virtual bool resolveCaches();
  ParameterSE3Offset* _offsetParam;
  CacheSE3Offset* _cache;
};

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_xyzprior.cpp ***//
#include "edge_se3_xyzprior.h"

namespace g2o {

bool EdgeSE3XYZPrior::read(std::istream& is) {
  readParamIds(is);
  internal::readVector(is, _measurement);
  return readInformationMatrix(is);
}

bool EdgeSE3XYZPrior::write(std::ostream& os) const {
  writeParamIds(os);
  internal::writeVector(os, measurement());
  return writeInformationMatrix(os);
}

void EdgeSE3XYZPrior::computeError() {
  const VertexSE3* v = static_cast<const VertexSE3*>(_vertices[0]);
  _error = v->estimate().translation() - _measurement;
}

void EdgeSE3XYZPrior::linearizeOplus() {
  const VertexSE3* v = static_cast<const VertexSE3*>(_vertices[0]);
  _jacobianOplusXi.block<3, 3>(0, 0) = v->estimate().rotation();
  _jacobianOplusXi.block<3, 3>(0, 3) = Eigen::Matrix3d::Zero();
}

}  // namespace g2o

三、 EdgePointXYZ : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexPointXYZ>

//*** g2o源码 g2o/g2o/types/slam3d/edge_pointxyz.h ***//
#ifndef G2O_EDGE_POINTXYZ_H
#define G2O_EDGE_POINTXYZ_H

#include "g2o/config.h"
#include "g2o/core/base_binary_edge.h"
#include "g2o_types_slam3d_api.h"
#include "vertex_pointxyz.h"

namespace g2o {

class G2O_TYPES_SLAM3D_API EdgePointXYZ
    : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexPointXYZ> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  EdgePointXYZ();

  void computeError() {
    const VertexPointXYZ* v1 = static_cast<const VertexPointXYZ*>(_vertices[0]);
    const VertexPointXYZ* v2 = static_cast<const VertexPointXYZ*>(_vertices[1]);
    _error = (v2->estimate() - v1->estimate()) - _measurement;
  }
  virtual bool read(std::istream& is);
  virtual bool write(std::ostream& os) const;

  /* 类中其他函数 */

#ifndef NUMERIC_JACOBIAN_THREE_D_TYPES
  virtual void linearizeOplus();
#endif

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/slam3d/edge_pointxyz.cpp ***//
#include "edge_pointxyz.h"

namespace g2o {

EdgePointXYZ::EdgePointXYZ()
    : BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexPointXYZ>() {
  _information.setIdentity();
  _error.setZero();
}

bool EdgePointXYZ::read(std::istream& is) {
  Vector3 p;
  is >> p[0] >> p[1] >> p[2];
  setMeasurement(p);
  for (int i = 0; i < 3; ++i)
    for (int j = i; j < 3; ++j) {
      is >> information()(i, j);
      if (i != j) information()(j, i) = information()(i, j);
    }
  return true;
}

bool EdgePointXYZ::write(std::ostream& os) const {
  Vector3 p = measurement();
  os << p.x() << " " << p.y() << " " << p.z();
  for (int i = 0; i < 3; ++i)
    for (int j = i; j < 3; ++j) os << " " << information()(i, j);
  return os.good();
}

#ifndef NUMERIC_JACOBIAN_THREE_D_TYPES
void EdgePointXYZ::linearizeOplus() {
  _jacobianOplusXi = -Matrix3::Identity();
  _jacobianOplusXj = Matrix3::Identity();
}
#endif

}  // namespace g2o

 四、EdgeSE3 : public BaseBinaryEdge<6, Isometry3, VertexSE3, VertexSE3>

        测量值:欧式变换矩阵(4x4),顶点:两个空间3D位姿(四元数旋转)

        其中,测量值是两个顶点之间的相对 3D 位姿测量

//*** g2o源码 g2o/g2o/types/slam3d/edge_se3.h ***//
#ifndef G2O_EDGE_SE3_H_
#define G2O_EDGE_SE3_H_

#include "g2o/core/base_binary_edge.h"
#include "g2o_types_slam3d_api.h"
#include "vertex_se3.h"

namespace g2o {

/**
 * \brief Edge between two 3D pose vertices
 *
 * The transformation between the two vertices is given as an Isometry3.
 * If z denotes the measurement, then the error function is given as follows:
 * z^-1 * (x_i^-1 * x_j)
 */
class G2O_TYPES_SLAM3D_API EdgeSE3
    : public BaseBinaryEdge<6, Isometry3, VertexSE3, VertexSE3> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW;
  EdgeSE3();
  virtual bool read(std::istream& is);
  virtual bool write(std::ostream& os) const;

  void computeError();

  /* 类中其他函数 */

  void linearizeOplus();

};

}  // namespace g2o
//*** g2o源码 g2o/g2o/types/slam3d/edge_se3.cpp ***//
#include "edge_se3.h"

#include <iostream>

#include "isometry3d_gradients.h"

#ifdef G2O_HAVE_OPENGL
#include "g2o/stuff/opengl_primitives.h"
#include "g2o/stuff/opengl_wrapper.h"
#endif

namespace g2o {
using namespace std;

EdgeSE3::EdgeSE3() : BaseBinaryEdge<6, Isometry3, VertexSE3, VertexSE3>() {
  information().setIdentity();
}

bool EdgeSE3::read(std::istream& is) {
  Vector7 meas;
  internal::readVector(is, meas);
  // normalize the quaternion to recover numerical precision lost by storing as
  // human readable text
  Vector4::MapType(meas.data() + 3).normalize();
  setMeasurement(internal::fromVectorQT(meas));
  if (is.bad()) return false;
  readInformationMatrix(is);
  return is.good() || is.eof();
}

bool EdgeSE3::write(std::ostream& os) const {
  internal::writeVector(os, internal::toVectorQT(measurement()));
  return writeInformationMatrix(os);
}

void EdgeSE3::computeError() {
  VertexSE3* from = static_cast<VertexSE3*>(_vertices[0]);
  VertexSE3* to = static_cast<VertexSE3*>(_vertices[1]);
  Isometry3 delta =
      _inverseMeasurement * from->estimate().inverse() * to->estimate();
  _error = internal::toVectorMQT(delta);
}

void EdgeSE3::linearizeOplus() {
  // BaseBinaryEdge<6, Isometry3, VertexSE3, VertexSE3>::linearizeOplus();
  // return;

  VertexSE3* from = static_cast<VertexSE3*>(_vertices[0]);
  VertexSE3* to = static_cast<VertexSE3*>(_vertices[1]);
  Isometry3 E;
  const Isometry3& Xi = from->estimate();
  const Isometry3& Xj = to->estimate();
  const Isometry3& Z = _measurement;
  internal::computeEdgeSE3Gradient(E, _jacobianOplusXi, _jacobianOplusXj, Z, Xi,
                                   Xj);
}

}  // namespace g2o
//*** g2o源码 g2o/g2o/types/slam3d/isometry3d_gradients.h ***//
template <typename Derived>
void computeEdgeSE3Gradient(Isometry3& E,
                            Eigen::MatrixBase<Derived> const& JiConstRef,
                            Eigen::MatrixBase<Derived> const& JjConstRef,
                            const Isometry3& Z, const Isometry3& Xi,
                            const Isometry3& Xj) {
  Eigen::MatrixBase<Derived>& Ji =
      const_cast<Eigen::MatrixBase<Derived>&>(JiConstRef);
  Eigen::MatrixBase<Derived>& Jj =
      const_cast<Eigen::MatrixBase<Derived>&>(JjConstRef);
  Ji.derived().resize(6, 6);
  Jj.derived().resize(6, 6);
  // compute the error at the linearization point
  const Isometry3 A = Z.inverse();
  const Isometry3 B = Xi.inverse() * Xj;

  E = A * B;

  Isometry3::ConstLinearPart Re = extractRotation(E);
  Isometry3::ConstLinearPart Ra = extractRotation(A);
  Isometry3::ConstLinearPart Rb = extractRotation(B);
  Isometry3::ConstTranslationPart tb = B.translation();

  Eigen::Matrix<number_t, 3, 9, Eigen::ColMajor> dq_dR;
  compute_dq_dR(dq_dR, Re(0, 0), Re(1, 0), Re(2, 0), Re(0, 1), Re(1, 1),
                Re(2, 1), Re(0, 2), Re(1, 2), Re(2, 2));

  Ji.setZero();
  Jj.setZero();

  // dte/dti
  Ji.template block<3, 3>(0, 0) = -Ra;

  // dte/dtj
  Jj.template block<3, 3>(0, 0) = Re;

  // dte/dqi
  {
    Matrix3 S;
    skewT(S, tb);
    Ji.template block<3, 3>(0, 3) = Ra * S;
  }

  // dte/dqj: this is zero

  number_t buf[27];
  Eigen::Map<Eigen::Matrix<number_t, 9, 3, Eigen::ColMajor> > M(buf);
  Matrix3 Sxt, Syt, Szt;
  // dre/dqi
  {
    skewT(Sxt, Syt, Szt, Rb);
    Eigen::Map<Matrix3> Mx(buf);
    Mx.noalias() = Ra * Sxt;
    Eigen::Map<Matrix3> My(buf + 9);
    My.noalias() = Ra * Syt;
    Eigen::Map<Matrix3> Mz(buf + 18);
    Mz.noalias() = Ra * Szt;
    Ji.template block<3, 3>(3, 3) = dq_dR * M;
  }

  // dre/dqj
  {
    Matrix3& Sx = Sxt;
    Matrix3& Sy = Syt;
    Matrix3& Sz = Szt;
    skew(Sx, Sy, Sz, Matrix3::Identity());
    Eigen::Map<Matrix3> Mx(buf);
    Mx.noalias() = Re * Sx;
    Eigen::Map<Matrix3> My(buf + 9);
    My.noalias() = Re * Sy;
    Eigen::Map<Matrix3> Mz(buf + 18);
    Mz.noalias() = Re * Sz;
    Jj.template block<3, 3>(3, 3) = dq_dR * M;
  }
}

 五、EdgeSE3PointXYZ : public BaseBinaryEdge<3, Vector3, VertexSE3, VertexPointXYZ>

//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_pointxyz.h ***//
#ifndef G2O_EDGE_SE3_POINT_XYZ_H_
#define G2O_EDGE_SE3_POINT_XYZ_H_

#include "g2o/core/base_binary_edge.h"
#include "g2o_types_slam3d_api.h"
#include "parameter_se3_offset.h"
#include "vertex_pointxyz.h"
#include "vertex_se3.h"

namespace g2o {

/*! \class EdgeSE3PointXYZ
 * \brief g2o edge from a track to a point node
 */
// first two args are the measurement type, second two the connection classes
class G2O_TYPES_SLAM3D_API EdgeSE3PointXYZ
    : public BaseBinaryEdge<3, Vector3, VertexSE3, VertexPointXYZ> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW
  EdgeSE3PointXYZ();
  virtual bool read(std::istream& is);
  virtual bool write(std::ostream& os) const;

  // return the error estimate as a 3-vector
  void computeError();
  // jacobian
  virtual void linearizeOplus();

  /* 类中其他函数 */

}  // namespace g2o
//*** g2o源码 g2o/g2o/types/slam3d/edge_se3_pointxyz.cpp ***//
#include "edge_se3_pointxyz.h"

#include "parameter_se3_offset.h"

#ifdef G2O_HAVE_OPENGL
#include "g2o/stuff/opengl_wrapper.h"
#endif

#include <iostream>

#ifdef G2O_HAVE_OPENGL
#include "g2o/stuff/opengl_primitives.h"
#include "g2o/stuff/opengl_wrapper.h"
#endif

namespace g2o {
using namespace std;

// point to camera projection, monocular
EdgeSE3PointXYZ::EdgeSE3PointXYZ()
    : BaseBinaryEdge<3, Vector3, VertexSE3, VertexPointXYZ>() {
  information().setIdentity();
  J.fill(0);
  J.block<3, 3>(0, 0) = -Matrix3::Identity();
  cache = 0;
  offsetParam = 0;
  resizeParameters(1);
  installParameter(offsetParam, 0);
}

bool EdgeSE3PointXYZ::read(std::istream& is) {
  readParamIds(is);
  Vector3 meas;
  internal::readVector(is, meas);
  setMeasurement(meas);
  readInformationMatrix(is);
  return is.good() || is.eof();
}

bool EdgeSE3PointXYZ::write(std::ostream& os) const {
  bool state = writeParamIds(os);
  state &= internal::writeVector(os, measurement());
  state &= writeInformationMatrix(os);
  return state;
}

void EdgeSE3PointXYZ::computeError() {
  // from cam to point (track)
  // VertexSE3 *cam = static_cast<VertexSE3*>(_vertices[0]);
  VertexPointXYZ* point = static_cast<VertexPointXYZ*>(_vertices[1]);

  Vector3 perr = cache->w2n() * point->estimate();

  // error, which is backwards from the normal observed - calculated
  // _measurement is the measured projection
  _error = perr - _measurement;
  //    std::cout << _error << std::endl << std::endl;
}

void EdgeSE3PointXYZ::linearizeOplus() {
  // VertexSE3 *cam = static_cast<VertexSE3 *>(_vertices[0]);
  VertexPointXYZ* vp = static_cast<VertexPointXYZ*>(_vertices[1]);

  Vector3 Zcam = cache->w2l() * vp->estimate();

  //  J(0,3) = -0.0;
  J(0, 4) = -2 * Zcam(2);
  J(0, 5) = 2 * Zcam(1);

  J(1, 3) = 2 * Zcam(2);
  //  J(1,4) = -0.0;
  J(1, 5) = -2 * Zcam(0);

  J(2, 3) = -2 * Zcam(1);
  J(2, 4) = 2 * Zcam(0);
  //  J(2,5) = -0.0;

  J.block<3, 3>(0, 6) = cache->w2l().rotation();

  Eigen::Matrix<number_t, 3, 9, Eigen::ColMajor> Jhom =
      offsetParam->inverseOffset().rotation() * J;

  _jacobianOplusXi = Jhom.block<3, 6>(0, 0);
  _jacobianOplusXj = Jhom.block<3, 3>(0, 6);

  // std::cerr << "just linearized." << std::endl;
  // std::cerr << "_jacobianOplusXi:" << std::endl << _jacobianOplusXi <<
  // std::endl; std::cerr << "_jacobianOplusXj:" << std::endl <<
  // _jacobianOplusXj << std::endl;
}

}  // namespace g2o

 六、EdgeSE3ProjectXYZ : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>

//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz.h ***//
#ifndef G2O_SBA_EDGEPROJECTXYZ_H
#define G2O_SBA_EDGEPROJECTXYZ_H

#include "g2o/core/base_binary_edge.h"
#include "g2o/types/slam3d/vertex_pointxyz.h"
#include "g2o_types_sba_api.h"
#include "vertex_se3_expmap.h"

namespace g2o {

// Projection using focal_length in x and y directions
class G2O_TYPES_SBA_API EdgeSE3ProjectXYZ
    : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  EdgeSE3ProjectXYZ();

  bool read(std::istream &is);
  bool write(std::ostream &os) const;
  void computeError();
  bool isDepthPositive();

  virtual void linearizeOplus();
  Vector2 cam_project(const Vector3 &trans_xyz) const;

  number_t fx, fy, cx, cy;
};

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz.h ***//
include "edge_project_xyz.h"

namespace g2o {

EdgeSE3ProjectXYZ::EdgeSE3ProjectXYZ()
    : BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>() {}

bool EdgeSE3ProjectXYZ::read(std::istream &is) {
  internal::readVector(is, _measurement);
  return readInformationMatrix(is);
}

bool EdgeSE3ProjectXYZ::write(std::ostream &os) const {
  internal::writeVector(os, measurement());
  return writeInformationMatrix(os);
}

void EdgeSE3ProjectXYZ::computeError() {
  const VertexSE3Expmap *v1 =
      static_cast<const VertexSE3Expmap *>(_vertices[1]);
  const VertexPointXYZ *v2 = static_cast<const VertexPointXYZ *>(_vertices[0]);
  Vector2 obs(_measurement);
  _error = obs - cam_project(v1->estimate().map(v2->estimate()));
}

void EdgeSE3ProjectXYZ::linearizeOplus() {
  VertexSE3Expmap *vj = static_cast<VertexSE3Expmap *>(_vertices[1]);
  SE3Quat T(vj->estimate());
  VertexPointXYZ *vi = static_cast<VertexPointXYZ *>(_vertices[0]);
  Vector3 xyz = vi->estimate();
  Vector3 xyz_trans = T.map(xyz);

  number_t x = xyz_trans[0];
  number_t y = xyz_trans[1];
  number_t z = xyz_trans[2];
  number_t z_2 = z * z;

  Eigen::Matrix<number_t, 2, 3> tmp;
  tmp(0, 0) = fx;
  tmp(0, 1) = 0;
  tmp(0, 2) = -x / z * fx;

  tmp(1, 0) = 0;
  tmp(1, 1) = fy;
  tmp(1, 2) = -y / z * fy;

  _jacobianOplusXi = -1. / z * tmp * T.rotation().toRotationMatrix();

  _jacobianOplusXj(0, 0) = x * y / z_2 * fx;
  _jacobianOplusXj(0, 1) = -(1 + (x * x / z_2)) * fx;
  _jacobianOplusXj(0, 2) = y / z * fx;
  _jacobianOplusXj(0, 3) = -1. / z * fx;
  _jacobianOplusXj(0, 4) = 0;
  _jacobianOplusXj(0, 5) = x / z_2 * fx;

  _jacobianOplusXj(1, 0) = (1 + y * y / z_2) * fy;
  _jacobianOplusXj(1, 1) = -x * y / z_2 * fy;
  _jacobianOplusXj(1, 2) = -x / z * fy;
  _jacobianOplusXj(1, 3) = 0;
  _jacobianOplusXj(1, 4) = -1. / z * fy;
  _jacobianOplusXj(1, 5) = y / z_2 * fy;
}

}  // namespace g2o

七、EdgeProjectXYZ2UV : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>

        测量值:平面2D点,顶点:空间3D点、空间3D位姿(SE3,李代数旋转)        

        3D-2D点的PnP问题,其中,测量值是是该空间3D点对应的图像2D坐标

//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz2uv.h ***//
#ifndef G2O_SBA_EDGEPROJECTXYZ2UV_H
#define G2O_SBA_EDGEPROJECTXYZ2UV_H

#include "g2o/core/base_binary_edge.h"
#include "g2o/types/slam3d/vertex_pointxyz.h"
#include "g2o_types_sba_api.h"
#include "parameter_cameraparameters.h"
#include "vertex_se3_expmap.h"

namespace g2o {

class G2O_TYPES_SBA_API EdgeProjectXYZ2UV
    : public BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW;

  EdgeProjectXYZ2UV();
  bool read(std::istream& is);
  bool write(std::ostream& os) const;
  void computeError();
  virtual void linearizeOplus();

 public:
  CameraParameters* _cam;  // TODO make protected member?
};

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz2uv.cpp ***//
#include "edge_project_xyz2uv.h"

namespace g2o {

EdgeProjectXYZ2UV::EdgeProjectXYZ2UV()
    : BaseBinaryEdge<2, Vector2, VertexPointXYZ, VertexSE3Expmap>() {
  _cam = 0;
  resizeParameters(1);
  installParameter(_cam, 0);
}

bool EdgeProjectXYZ2UV::read(std::istream& is) {
  readParamIds(is);
  internal::readVector(is, _measurement);
  return readInformationMatrix(is);
}

bool EdgeProjectXYZ2UV::write(std::ostream& os) const {
  writeParamIds(os);
  internal::writeVector(os, measurement());
  return writeInformationMatrix(os);
}

void EdgeProjectXYZ2UV::computeError() {
  const VertexSE3Expmap* v1 = static_cast<const VertexSE3Expmap*>(_vertices[1]);
  const VertexPointXYZ* v2 = static_cast<const VertexPointXYZ*>(_vertices[0]);
  const CameraParameters* cam =
      static_cast<const CameraParameters*>(parameter(0));
  // cam_map():将相机坐标系下三维点(输入)用内参转换为像素坐标系下的图像坐标(输出)
  // map():将世界坐标系下三维点转换到相机坐标系下
  // v1->estimate().map(v2->estimate()
  // 用 v1 估计的位姿(T_camera_world)把 v2 代表的三维点(P_world)转换到相机坐标系下
  _error = measurement() - cam->cam_map(v1->estimate().map(v2->estimate()));
}

void EdgeProjectXYZ2UV::linearizeOplus() {
  VertexSE3Expmap* vj = static_cast<VertexSE3Expmap*>(_vertices[1]);
  SE3Quat T(vj->estimate());
  VertexPointXYZ* vi = static_cast<VertexPointXYZ*>(_vertices[0]);
  Vector3 xyz = vi->estimate();
  Vector3 xyz_trans = T.map(xyz); //相机坐标系下的点

  number_t x = xyz_trans[0];
  number_t y = xyz_trans[1];
  number_t z = xyz_trans[2];
  number_t z_2 = z * z;

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

  // 重投影误差关于 三维点 的雅可比矩阵
  // g2o 中 SE3Quat 的定义方式是旋转在前,平移在后
  Eigen::Matrix<number_t, 2, 3, Eigen::ColMajor> tmp;
  tmp(0, 0) = cam->focal_length;
  tmp(0, 1) = 0;
  tmp(0, 2) = -x / z * cam->focal_length;

  tmp(1, 0) = 0;
  tmp(1, 1) = cam->focal_length;
  tmp(1, 2) = -y / z * cam->focal_length;

  _jacobianOplusXi = -1. / z * tmp * T.rotation().toRotationMatrix();

  // 重投影误差关于 相机位姿 的雅可比矩阵
  _jacobianOplusXj(0, 0) = x * y / z_2 * cam->focal_length;
  _jacobianOplusXj(0, 1) = -(1 + (x * x / z_2)) * cam->focal_length;
  _jacobianOplusXj(0, 2) = y / z * cam->focal_length;
  _jacobianOplusXj(0, 3) = -1. / z * cam->focal_length;
  _jacobianOplusXj(0, 4) = 0;
  _jacobianOplusXj(0, 5) = x / z_2 * cam->focal_length;

  _jacobianOplusXj(1, 0) = (1 + y * y / z_2) * cam->focal_length;
  _jacobianOplusXj(1, 1) = -x * y / z_2 * cam->focal_length;
  _jacobianOplusXj(1, 2) = -x / z * cam->focal_length;
  _jacobianOplusXj(1, 3) = 0;
  _jacobianOplusXj(1, 4) = -1. / z * cam->focal_length;
  _jacobianOplusXj(1, 5) = y / z_2 * cam->focal_length;
}

}  // namespace g2o
//*** g2o源码 g2o/g2o/types/sba/parameter_cameraparameters.cpp ***//
Vector2 CameraParameters::cam_map(const Vector3 &trans_xyz) const {
  Vector2 proj = project(trans_xyz);
  Vector2 res;
  res[0] = proj[0] * focal_length + principle_point[0];
  res[1] = proj[1] * focal_length + principle_point[1];
  return res;
}

//*** g2o源码 g2o/g2o/types/slam3d/se3quat.h ***//
Vector3 map(const Vector3 & xyz) const
{
  return _r*xyz + _t;
}

八、EdgeProjectXYZ2UVU : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>

//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz2uvu.h ***//
#ifndef G2O_SBA_EDGEPROJECTXYZ2UVU_H
#define G2O_SBA_EDGEPROJECTXYZ2UVU_H

#include "g2o/core/base_binary_edge.h"
#include "g2o/types/slam3d/vertex_pointxyz.h"
#include "g2o_types_sba_api.h"
#include "parameter_cameraparameters.h"
#include "vertex_se3_expmap.h"

namespace g2o {

// Stereo Observations
// U: left u
// V: left v
// U: right u
class G2O_TYPES_SBA_API EdgeProjectXYZ2UVU
    : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW;

  EdgeProjectXYZ2UVU();
  bool read(std::istream& is);
  bool write(std::ostream& os) const;
  void computeError();
  //  virtual void linearizeOplus();
 protected:
  CameraParameters* _cam;
};

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/sba/edge_project_xyz2uvu.cpp ***//
#include "edge_project_xyz2uvu.h"

namespace g2o {

EdgeProjectXYZ2UVU::EdgeProjectXYZ2UVU()
    : BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>() {
  _cam = nullptr;
  resizeParameters(1);
  installParameter(_cam, 0);
}

void EdgeProjectXYZ2UVU::computeError() {
  const VertexSE3Expmap* v1 = static_cast<const VertexSE3Expmap*>(_vertices[1]);
  const VertexPointXYZ* v2 = static_cast<const VertexPointXYZ*>(_vertices[0]);
  const CameraParameters* cam =
      static_cast<const CameraParameters*>(parameter(0));
  _error = measurement() -
           cam->stereocam_uvu_map(v1->estimate().map(v2->estimate()));
}

bool EdgeProjectXYZ2UVU::read(std::istream& is) {
  readParamIds(is);
  internal::readVector(is, _measurement);
  return readInformationMatrix(is);
}

bool EdgeProjectXYZ2UVU::write(std::ostream& os) const {
  writeParamIds(os);
  internal::writeVector(os, measurement());
  return writeInformationMatrix(os);
}

}  // namespace g2o

九、EdgeStereoSE3ProjectXYZ : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>

//*** g2o源码 g2o/g2o/types/sba/edge_project_stereo_xyz.h ***//
#ifndef G2O_SBA_EDGEPROJECTSTEREOXYZ_H
#define G2O_SBA_EDGEPROJECTSTEREOXYZ_H

#include "g2o/core/base_binary_edge.h"
#include "g2o/types/slam3d/vertex_pointxyz.h"
#include "g2o_types_sba_api.h"
#include "vertex_se3_expmap.h"

namespace g2o {

// Projection using focal_length in x and y directions stereo
class G2O_TYPES_SBA_API EdgeStereoSE3ProjectXYZ
    : public BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap> {
 public:
  EIGEN_MAKE_ALIGNED_OPERATOR_NEW

  EdgeStereoSE3ProjectXYZ();

  bool read(std::istream &is);

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

  void computeError() {
    const VertexSE3Expmap *v1 =
        static_cast<const VertexSE3Expmap *>(_vertices[1]);
    const VertexPointXYZ *v2 =
        static_cast<const VertexPointXYZ *>(_vertices[0]);
    Vector3 obs(_measurement);
    _error = obs - cam_project(v1->estimate().map(v2->estimate()), bf);
  }

  bool isDepthPositive() {
    const VertexSE3Expmap *v1 =
        static_cast<const VertexSE3Expmap *>(_vertices[1]);
    const VertexPointXYZ *v2 =
        static_cast<const VertexPointXYZ *>(_vertices[0]);
    return (v1->estimate().map(v2->estimate()))(2) > 0;
  }

  virtual void linearizeOplus();

  Vector3 cam_project(const Vector3 &trans_xyz, const float &bf) const;

  number_t fx, fy, cx, cy, bf;
};

}  // namespace g2o

#endif
//*** g2o源码 g2o/g2o/types/sba/edge_project_stereo_xyz.cpp ***//
#include "edge_project_stereo_xyz.h"

namespace g2o {

EdgeStereoSE3ProjectXYZ::EdgeStereoSE3ProjectXYZ()
    : BaseBinaryEdge<3, Vector3, VertexPointXYZ, VertexSE3Expmap>() {}

Vector3 EdgeStereoSE3ProjectXYZ::cam_project(const Vector3 &trans_xyz,
                                             const float &bf) const {
  const number_t invz = 1.0f / trans_xyz[2];
  Vector3 res;
  res[0] = trans_xyz[0] * invz * fx + cx;
  res[1] = trans_xyz[1] * invz * fy + cy;
  res[2] = res[0] - bf * invz;
  return res;
}

bool EdgeStereoSE3ProjectXYZ::read(std::istream &is) {
  internal::readVector(is, _measurement);
  return readInformationMatrix(is);
}

bool EdgeStereoSE3ProjectXYZ::write(std::ostream &os) const {
  internal::writeVector(os, measurement());
  return writeInformationMatrix(os);
}

void EdgeStereoSE3ProjectXYZ::linearizeOplus() {
  VertexSE3Expmap *vj = static_cast<VertexSE3Expmap *>(_vertices[1]);
  SE3Quat T(vj->estimate());
  VertexPointXYZ *vi = static_cast<VertexPointXYZ *>(_vertices[0]);
  Vector3 xyz = vi->estimate();
  Vector3 xyz_trans = T.map(xyz);

  const Matrix3 R = T.rotation().toRotationMatrix();

  number_t x = xyz_trans[0];
  number_t y = xyz_trans[1];
  number_t z = xyz_trans[2];
  number_t z_2 = z * z;

  _jacobianOplusXi(0, 0) = -fx * R(0, 0) / z + fx * x * R(2, 0) / z_2;
  _jacobianOplusXi(0, 1) = -fx * R(0, 1) / z + fx * x * R(2, 1) / z_2;
  _jacobianOplusXi(0, 2) = -fx * R(0, 2) / z + fx * x * R(2, 2) / z_2;

  _jacobianOplusXi(1, 0) = -fy * R(1, 0) / z + fy * y * R(2, 0) / z_2;
  _jacobianOplusXi(1, 1) = -fy * R(1, 1) / z + fy * y * R(2, 1) / z_2;
  _jacobianOplusXi(1, 2) = -fy * R(1, 2) / z + fy * y * R(2, 2) / z_2;

  _jacobianOplusXi(2, 0) = _jacobianOplusXi(0, 0) - bf * R(2, 0) / z_2;
  _jacobianOplusXi(2, 1) = _jacobianOplusXi(0, 1) - bf * R(2, 1) / z_2;
  _jacobianOplusXi(2, 2) = _jacobianOplusXi(0, 2) - bf * R(2, 2) / z_2;

  _jacobianOplusXj(0, 0) = x * y / z_2 * fx;
  _jacobianOplusXj(0, 1) = -(1 + (x * x / z_2)) * fx;
  _jacobianOplusXj(0, 2) = y / z * fx;
  _jacobianOplusXj(0, 3) = -1. / z * fx;
  _jacobianOplusXj(0, 4) = 0;
  _jacobianOplusXj(0, 5) = x / z_2 * fx;

  _jacobianOplusXj(1, 0) = (1 + y * y / z_2) * fy;
  _jacobianOplusXj(1, 1) = -x * y / z_2 * fy;
  _jacobianOplusXj(1, 2) = -x / z * fy;
  _jacobianOplusXj(1, 3) = 0;
  _jacobianOplusXj(1, 4) = -1. / z * fy;
  _jacobianOplusXj(1, 5) = y / z_2 * fy;

  _jacobianOplusXj(2, 0) = _jacobianOplusXj(0, 0) - bf * y / z_2;
  _jacobianOplusXj(2, 1) = _jacobianOplusXj(0, 1) + bf * x / z_2;
  _jacobianOplusXj(2, 2) = _jacobianOplusXj(0, 2);
  _jacobianOplusXj(2, 3) = _jacobianOplusXj(0, 3);
  _jacobianOplusXj(2, 4) = 0;
  _jacobianOplusXj(2, 5) = _jacobianOplusXj(0, 5) - bf / z_2;
}

}  // namespace g2o

        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值