0
点赞
收藏
分享

微信扫一扫

PLS-VIO 仿真用开源代码阅读理解

代码阅读

0. line_geometry

定义了一系列线特征几何操作。

  1. plk_to_pose
    返回相机坐标系下的plk坐标。
template <typename T>
Eigen::Matrix<T, 6, 1> plk_to_pose(Eigen::Matrix<T, 6, 1> plk_w,
                                   Eigen::Matrix<T, 3, 3> Rcw,
                                   Eigen::Matrix<T, 3, 1> tcw) {

  Eigen::Matrix<T, 3, 1> nw = plk_w.head(3);
  Eigen::Matrix<T, 3, 1> vw = plk_w.tail(3);

  Eigen::Matrix<T, 3, 1> nc = Rcw * nw + skew_symmetric(tcw) * Rcw * vw;
  Eigen::Matrix<T, 3, 1> vc = Rcw * vw;

  Eigen::Matrix<T, 6, 1> plk_c;
  plk_c.head(3) = nc;
  plk_c.tail(3) = vc;
  return plk_c;
}

  1. plk_from_pose
    返回世界坐标系下的plk坐标。
template <typename T>
Eigen::Matrix<T, 6, 1> plk_from_pose(Eigen::Matrix<T, 6, 1> plk_c,
                                     Eigen::Matrix<T, 3, 3> Rcw,
                                     Eigen::Matrix<T, 3, 1> tcw) {

  Eigen::Matrix<T, 3, 3> Rwc = Rcw.transpose();
  Eigen::Matrix<T, 3, 1> twc = -Rwc * tcw;
  return plk_to_pose(plk_c, Rwc, twc);
}

参考如下( 注意图中右上角少了一个负号 ):
在这里插入图片描述


  1. pluckerOrigin
    返回相机光心到直线的距离。
inline Eigen::Vector3d pluckerOrigin(const Vector6d &plk) {

  const Eigen::Vector3d &n = plk.head<3>();
  const Eigen::Vector3d &v = plk.tail<3>();
  Eigen::Vector3d f = n.cross(v);
  double g = v.squaredNorm();

  Eigen::Vector3d p0 = 1. / g * f;

  return p0;
}

其中 p 0 = n × v ∣ ∣ v ∣ ∣ 2 = ∣ ∣ n ∣ ∣ ∣ ∣ v ∣ ∣ \textbf{p}_0=\frac{\textbf{n}\times\textbf{v}}{||\textbf{v}||^2}=\frac{||\textbf{n}||}{||\textbf{v}||} p0=v2n×v=vn,包含了原点到直线的距离信息。


  1. orth_to_plk
    正交参数化表示法转化成plk坐标。
template <typename T>
Eigen::Matrix<T, 6, 1> orth_to_plk( Eigen::Matrix<T, 4, 1> orth) {

    Eigen::Matrix<T, 6, 1> plk;

    Eigen::Matrix<T, 3, 1> theta = orth.head(3);
    T phi = orth[3];

    T s1 = ceres::sin(theta[0]);
    T c1 = ceres::cos(theta[0]);
    T s2 = ceres::sin(theta[1]);
    T c2 = ceres::cos(theta[1]);
    T s3 = ceres::sin(theta[2]);
    T c3 = ceres::cos(theta[2]);

    Matrix<T, 3, 3> R;
    R <<
            c2 * c3, s1 * s2 * c3 - c1 * s3, c1 * s2 * c3 + s1 * s3,
            c2 * s3, s1 * s2 * s3 + c1 * c3, c1 * s2 * s3 - s1 * c3,
            -s2, s1 * c2, c1 * c2;

    T w1 = ceres::cos(phi);
    T w2 = ceres::sin(phi);
    T d = w1 / w2;      // 原点到直线的距离

    Matrix<T, 3, 1> u1 = R.col(0);
    Matrix<T, 3, 1> u2 = R.col(1);

    Matrix<T, 3, 1> n = w1 * u1;
    Matrix<T, 3, 1> v = w2 * u2;

    plk.head(3) = n;
    plk.tail(3) = v;

    return plk;
}

参考如下:
在这里插入图片描述


  1. plk_to_orth
    plk坐标转换到正交参数表示。
Vector4d plk_to_orth(Vector6d plk) {
    Vector4d orth;
    Vector3d n = plk.head(3);
    Vector3d v = plk.tail(3);

    Vector3d u1 = n / n.norm();
    Vector3d u2 = v / v.norm();
    Vector3d u3 = u1.cross(u2);

    // todo:: use SO3
    orth[0] = atan2(u2(2), u3(2));
    orth[1] = asin(-u1(2));
    orth[2] = atan2(u1(1), u1(0));

    Vector2d w(n.norm(), v.norm());
    w = w / w.norm();
    orth[3] = asin(w(1));

    return orth;
}

在这里插入图片描述


  1. TrimLine
    输入参数为两个线段端点坐标和直线的plk坐标,获取3d空间直线修整后的两端点坐标。
    在这里插入图片描述
inline Vector6d TrimLine(const Eigen::Vector3d &spt, const Eigen::Vector3d &ept,
                         const Vector6d &plk) {

  Eigen::Vector3d p11 = spt / spt.z(); //归一化
  Eigen::Vector3d p21 = ept / ept.z(); //归一化
  Eigen::Vector2d ln = (p11.cross(p21)).head(2); //法向量
  ln = ln / ln.norm();

  Eigen::Vector3d p12 = Eigen::Vector3d(p11(0) + ln(0), p11(1) + ln(1),
                                        1.0); // 直线垂直方向上移动一个单位
  Eigen::Vector3d p22 = Eigen::Vector3d(p21(0) + ln(0), p21(1) + ln(1), 1.0);
  Eigen::Vector3d cam = Eigen::Vector3d(0, 0, 0);

  Eigen::Vector4d pi1 = pi_from_ppp(cam, p11, p12); //三点确认一个平面 ax + by + cz + d = 0,返回Vector4d的平面方程 (a,b,c,d)
  Eigen::Vector4d pi2 = pi_from_ppp(cam, p21, p22);

  Eigen::Matrix4d Lc;
  Eigen::Vector3d nc, vc;

  nc = plk.head(3);
  vc = plk.tail(3);

  Lc << skew_symmetric(nc), vc, -vc.transpose(), 0; //获取直线法向量的对偶plk矩阵
  Eigen::Vector4d e1 = Lc * pi1;
  Eigen::Vector4d e2 = Lc * pi2;
  e1 = e1 / e1(3);
  e2 = e2 / e2(3);

  Eigen::Vector3d pts_1(e1(0), e1(1), e1(2));
  Eigen::Vector3d pts_2(e2(0), e2(1), e2(2));

  Vector6d res;
  res.head(3) = pts_1;
  res.tail(3) = pts_2;
  return res;
}

在这里插入图片描述

  1. line_to_orth
    线段参数转正交参数表示,这里线段参数 前三维为线段上一点 P P P(光心到直线垂线交点),后三维为方向向量,所以和plk_to_orth基本一致。
Vector4d line_to_orth(Vector6d line) {
    Vector4d orth;
    Vector3d p = line.head(3);
    Vector3d v = line.tail(3);
    Vector3d n = p.cross(v);

    Vector3d u1 = n / n.norm();
    Vector3d u2 = v / v.norm();
    Vector3d u3 = u1.cross(u2);

    orth[0] = atan2(u2(2), u3(2));
    orth[1] = asin(-u1(2));
    orth[2] = atan2(u1(1), u1(0));

    Vector2d w(n.norm(), v.norm());
    w = w / w.norm();
    orth[3] = asin(w(1));

    return orth;

}
  1. orth_to_line
    从正交表示转化为线段参数,注意这里的线段参数前三维是从相机光心到直线的垂线落点。
Vector6d orth_to_line(Vector4d orth) {
    Vector6d line;

    Vector3d theta = orth.head(3);
    double phi = orth[3];

    // todo:: SO3
    double s1 = sin(theta[0]);
    double c1 = cos(theta[0]);
    double s2 = sin(theta[1]);
    double c2 = cos(theta[1]);
    double s3 = sin(theta[2]);
    double c3 = cos(theta[2]);

    Matrix3d R;
    R <<
      c2 * c3, s1 * s2 * c3 - c1 * s3, c1 * s2 * c3 + s1 * s3,
            c2 * s3, s1 * s2 * s3 + c1 * c3, c1 * s2 * s3 - s1 * c3,
            -s2, s1 * c2, c1 * c2;

    double w1 = cos(phi);
    double w2 = sin(phi);
    double d = w1 / w2;      // 原点到直线的距离

    line.head(3) = -R.col(2) * d;
    line.tail(3) = R.col(1);

    return line;
}

  1. pi_from_ppp
    三点确立一个平面,返回的是平面方程 a x + b y + c z + d = 0 ax+by+cz+d=0 ax+by+cz+d=0 [ a , b , c , d ] [a, b, c, d] [a,b,c,d]
Vector4d pi_from_ppp(Vector3d x1, Vector3d x2, Vector3d x3) {
    Vector4d pi;
    pi << (x1 - x3).cross(x2 - x3), -x3.dot(
            x1.cross(x2)); // d = - x3.dot( (x1-x3).cross( x2-x3 ) ) = - x3.dot( x1.cross( x2 ) )

    return pi;
}

  1. pipi_plk
    两个平面相交确立一条直线,返回plk坐标。
Vector6d pipi_plk(Vector4d pi1, Vector4d pi2) {
    Vector6d plk;
    Matrix4d dp = pi1 * pi2.transpose() - pi2 * pi1.transpose();

    plk << dp(0, 3), dp(1, 3), dp(2, 3), -dp(1, 2), dp(0, 2), -dp(0, 1);
    return plk;
}

参考如下:在这里插入图片描述


  1. plucker_origin
    获取光心到直线的垂直点。
Vector3d plucker_origin(Vector3d n, Vector3d v) {
    return v.cross(n) / v.dot(v);
}

  1. point_to_pose & point_from_pose
    将空间点的坐标进行两个坐标系下的转换。

  1. line_to_pose & line_from_pose
    将空间直线的参数进行两个坐标系下的转换,其中线参数前3维是光心到直线的垂直点。

  1. plk_to_pose & plk_from_pose
    将空间直线的plk坐标进行两个坐标系下的转换

1. general_line_factor.h

以优化两帧位姿的GeneralLineErrorTwoCameraTerm为例。

namespace general_line {
    class GeneralLineErrorTwoCameraTerm {
    public:
        EIGEN_MAKE_ALIGNED_OPERATOR_NEW

        GeneralLineErrorTwoCameraTerm(const Eigen::Vector4d &obs_t_,
                                      const Eigen::Vector3d &x_dir,
                                      const Eigen::Matrix3d &R_c_l_)
                : obs_t(obs_t_), x_dir_(x_dir), R_c_l(R_c_l_) {}
  • obs_t :目标帧(target frame)的线段特征量测,包括两个端点的归一化坐标。
  • x_dir :线段局部坐标系原点在主相机坐标系(host frame)下的坐标。即
    在这里插入图片描述
  • R_c_l :从局部参数空间到相机坐标系的变换矩阵,即 R P C \textbf{R}^C_P RPC 或者 R L C \textbf{R}^C_L RLC

		template<typename T>
        // theta, rho: line parameter
        // phi: mahata angle
        bool operator()(const T *const line_ptr, const T *const T_w_i_h_ptr,
                        const T *const T_w_i_t_ptr, const T *const T_i_c_ptr,
                        T *residual_ptr) const {

            using Vector3T = Eigen::Matrix<T, 3, 1>;
            using Vector2T = Eigen::Matrix<T, 2, 1>;
            using Matrix33T = Eigen::Matrix<T, 3, 3>;
            using Vector6T = Eigen::Matrix<T, 6, 1>;
            using QuaternionT = Eigen::Quaternion<T>;
            using TransformT = Twist<T>;
			// line parameter
            T rho = line_ptr[0];
            T theta = line_ptr[1];

            Eigen::Map<const QuaternionT> q_w_i_h(T_w_i_h_ptr + 3);
            Eigen::Map<const Vector3T> t_w_i_h(T_w_i_h_ptr);

            Eigen::Map<const QuaternionT> q_w_i_t(T_w_i_t_ptr + 3);
            Eigen::Map<const Vector3T> t_w_i_t(T_w_i_t_ptr);

            Eigen::Map<const QuaternionT> q_i_c(T_i_c_ptr + 3);
            Eigen::Map<const Vector3T> t_i_c(T_i_c_ptr);

            TransformT T_i_c(q_i_c, t_i_c);

            TransformT T_w_i_h(q_w_i_h, t_w_i_h);   // what is h?  host
            TransformT T_w_i_t(q_w_i_t, t_w_i_t);   // what is t?  target

            TransformT T_w_c_h = T_w_i_h * T_i_c;
            TransformT T_w_c_t = T_w_i_t * T_i_c;

            TransformT T_c_w_h = T_w_c_h.inverse();
            TransformT T_c_w_t = T_w_c_t.inverse();

            TransformT T_t_c_h_c = T_w_c_t.inverse() * T_w_c_h;
            Matrix33T R_t_c_h_c = T_t_c_h_c.rotationMatrix();

输入变量:

  • line_ptr :线段参数,包括 ρ \rho ρ θ \theta θ
  • T_w_i_h_ptr:host帧的body坐标系在世界坐标系下的位姿。
  • T_w_i_t_ptr:target帧的body坐标系在世界坐标系下的位姿。
  • T_i_c_ptr:body和camera坐标系的变换矩阵。

tips: GeneralLineErrorOneCameraTerm 只优化 host frame 的 body位姿,T_w_i_t的时候也是以T_w_i_h_ptr来初始化的。

中间变量:

  • T_t_c_h_c :历史相机帧到目标相机帧的变换矩阵,即 T C i C j = ( T C j W ) T T C i W \textbf{T}^{C_j}_{C_i}=({\textbf{T}^{W}_{C_j}})^T\textbf{T}^{W}_{C_i} TCiCj=(TCjW)TTCiW
  • R_t_c_h_c:host相机帧到目标相机帧的旋转矩阵,不包括平移,即 R C i C j \textbf{R}^{C_j}_{C_i} RCiCj

			Vector3T line_c;
            line_c << ceres::cos(theta), ceres::sin(theta), T(0); //这里还没乘上rho,因为是方向向量
            line_c = R_c_l.cast<T>() * line_c; //  从局部参数空间转换到相机坐标系

在这里插入图片描述


            Vector3T origin_t =  R_t_c_h_c * x_dir_.cast<T>() + rho*T_t_c_h_c.pos;
            Vector3T line_c_t = R_t_c_h_c * line_c;
            Vector3T n_c_t = line_c_t.cross(origin_t);
  • origin_t :目标相机帧下的局部参数坐标系原点的坐标,即 s ′ \textbf{s}^{\prime} s
  • line_c_t :目标相机帧下的线段方向向量,即 c v ′ ^c\textbf{v}^{\prime} cv
  • n_c_t :目标帧的归一化相机坐标系下的直线方程,即 l m i = [ s ′ ] × c v ′ \textbf{l}^{m_i}=[\textbf{s}^{\prime}]\times {^c\textbf{v}^{\prime}} lmi=[s]×cv
    在这里插入图片描述

            T l_norm = n_c_t(0) * n_c_t(0) + n_c_t(1) * n_c_t(1);

            T l_sqrtnorm = ceres::sqrt(l_norm);

            T e1 = T(obs_t(0)) * n_c_t(0) + T(obs_t(1)) * n_c_t(1) + n_c_t(2);
            T e2 = T(obs_t(2)) * n_c_t(0) + T(obs_t(3)) * n_c_t(1) + n_c_t(2);

            Eigen::Map<Vector2T> residual(residual_ptr);

            residual(0) = e1 / l_sqrtnorm;
            residual(1) = e2 / l_sqrtnorm;

            residual =
                    general_line::GeneralLineErrorOneCameraTerm::sqrtInfo_.cast<T>() *
                    residual;

            return true;
        }

在这里插入图片描述


2. general_line_parameterization.h

namespace general_line {
// 定义的误差函数这里是
// define a local parameterization for upadting the angle to be constrained in [-pi, pi)
    class GeneralLineLocalParameterization {
    public:
        template<typename T>
        bool operator()(const T *theta, const T *delta, T *thetaPlusDelta) const {
            thetaPlusDelta[0] = theta[0] + delta[0];    // 逆深度可以直接加减
            thetaPlusDelta[1] = normalizeAngle(theta[1] + T(1.0) * delta[1]);
            return true;
        }

        static ceres::LocalParameterization *create() {
            return (new ceres::AutoDiffLocalParameterization<GeneralLineLocalParameterization, 2, 2>);
        }
    };
} // namespace struct_vio
  • 其中 normalizeAngle 函数是为了将 θ \theta θ 控制在 [-pi, pi]范围内。

3. plucker_line_parameterization

class LineOrthParameterization : public ceres::LocalParameterization {
    virtual bool Plus(const double *x, const double *delta, double *x_plus_delta) const;

    virtual bool ComputeJacobian(const double *x, double *jacobian) const;

    virtual int GlobalSize() const { return 4; };

    virtual int LocalSize() const { return 4; };
};

定义了一个线特征的局部正交参数化类,包括参数更新和雅各比计算等,正交参数化表达参见
SLAM线特征学习(1)——基本的线特征表示与优化推导,在此不再赘述。

4. plucker_projection_factor.h

声明了三种factor :lineProjectionFactor,lineProjectionFactor_incamera 和 lineProjectionFactor_instartframe。

同样定义了两种误差,PluckerLineErrorOneCameraTerm和PluckerLineErrorTwoCameraTerm,差别就在于是否同时优化历史帧和目标帧,这里以OneCameraTerm为例。

class PluckerLineErrorOneCameraTerm {
public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    PluckerLineErrorOneCameraTerm(const Eigen::Vector4d &_pts_i)
            : obs_i(_pts_i) {}

    template<typename T>
    // theta, rho: line parameter
    // phi: mahata angle
    bool operator()(const T *const line_ptr, const T *const T_w_i_t_ptr,
                    const T *const T_i_c_ptr, T *residual_ptr) const {

        using Vector3T = Eigen::Matrix<T, 3, 1>;
        using Vector4T = Eigen::Matrix<T, 4, 1>;
        using Vector2T = Eigen::Matrix<T, 2, 1>;
        using Matrix33T = Eigen::Matrix<T, 3, 3>;
        using Vector6T = Eigen::Matrix<T, 6, 1>;
        using QuaternionT = Eigen::Quaternion<T>;
        using TransformT = Twist<T>;

        // line parameter

        Eigen::Map<const QuaternionT> q_w_i_t(T_w_i_t_ptr + 3);
        Eigen::Map<const Vector3T> t_w_i_t(T_w_i_t_ptr);

        Eigen::Map<const QuaternionT> q_i_c(T_i_c_ptr + 3);
        Eigen::Map<const Vector3T> t_i_c(T_i_c_ptr);

        TransformT T_i_c(q_i_c, t_i_c);

        TransformT T_w_i_t(q_w_i_t, t_w_i_t);

        TransformT T_w_c_t = T_w_i_t * T_i_c;

与之前的GeneralLineErrorOneCameraTerm不同的是,输入变量为T_w_i_t_ptr,即待优化的是目标帧的body位姿。

		Vector4T line_orth;
        line_orth << line_ptr[0], line_ptr[1], line_ptr[2], line_ptr[3];


        Vector6T line_w = orth_to_plk(line_orth);

        Vector6T line_c = plk_from_pose(line_w, T_w_c_t.rotationMatrix(), T_w_c_t.pos);

        Vector3T n_c = line_c.head(3);

        T l_norm = n_c(0) * n_c(0) + n_c(1) * n_c(1);

        T l_sqrtnorm = ceres::sqrt(l_norm);

        T e1 = T(obs_i(0)) * n_c(0) + T(obs_i(1)) * n_c(1) + n_c(2);
        T e2 = T(obs_i(2)) * n_c(0) + T(obs_i(3)) * n_c(1) + n_c(2);

        Eigen::Map<Vector2T> residual(residual_ptr);

        residual(0) = e1 / l_sqrtnorm;
        residual(1) = e2 / l_sqrtnorm;
  • line_orth :世界坐标系下的空间直线的正交4参数表示。
  • 将输入的正交4参数表示的直线坐标转换成plk坐标后,直接得到直线法向量(直线方程),代入求解点到直线距离的残差。
        residual = PluckerLineErrorOneCameraTerm::sqrtInfo_.cast<T>() *
                   residual;
        return true;
    }

    static ceres::CostFunction *create(const Eigen::Vector4d &obs_t_) {
        return (new ceres::AutoDiffCostFunction<PluckerLineErrorOneCameraTerm, 2, 4,
                7, 7>(
                new PluckerLineErrorOneCameraTerm(obs_t_)));
    }

public:
    static Eigen::Matrix2d
            sqrtInfo_; // the inverse square root of the measurement covariance matrix
    Eigen::Vector4d obs_i;
};
  • 2, 4, 7, 7 分别代表残差维度、待优化直线参数维度、待优化目标帧body位姿维度和待优化body和camera变换矩阵的维度

5. plucker_projection_factor.cpp

主要定义了ceres优化过程中的雅各比矩阵。

lineProjectionFactor::lineProjectionFactor

总的雅各比链式求解公式如下:

∂ r L ∂ X = ∂ r L ∂ L I ∂ L I ∂ L n ∂ L n ∂ L c { ∂ L c ∂ T w i X [ 0 ~ 6 ] ∂ L c ∂ T b c X [ 7 ~ 13 ] ∂ L c ∂ L o r t h X [ 14 ~ 17 ] \frac{\partial \textbf{r}_{\textbf{L}}}{\partial \textbf{X}}= \frac{\partial \textbf{r}_{\textbf{L}}}{\partial L_{I}} \frac{\partial L_{I}}{\partial L_{n}} \frac{\partial L_{n}}{\partial L_{c}} \begin{cases} \frac{\partial L_{c}}{\partial T_{wi}} & X[0~6] \\ \frac{\partial L_{c}}{\partial T_{bc}} & X[7~13] \\ \frac{\partial L_{c}}{\partial L_{orth}} & X[14~17] \end{cases} XrL=LIrLLnLILcLnTwiLcTbcLcLorthLcX[06]X[713]X[1417]

bool lineProjectionFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const {
    Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);
    Eigen::Quaterniond Qi(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);

    Eigen::Vector3d tic(parameters[1][0], parameters[1][1], parameters[1][2]);
    Eigen::Quaterniond qic(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);

    Eigen::Vector4d line_orth(parameters[2][0], parameters[2][1], parameters[2][2], parameters[2][3]);
    Vector6d line_w = orth_to_plk(line_orth);

    Eigen::Matrix3d Rwb(Qi);
    Eigen::Vector3d twb(Pi);
    Vector6d line_b = plk_from_pose(line_w, Rwb, twb);
    //std::cout << line_b.norm() <<"\n";
    Eigen::Matrix3d Rbc(qic);
    Eigen::Vector3d tbc(tic);
    Vector6d line_c = plk_from_pose(line_b, Rbc, tbc);

    // 直线的投影矩阵K为单位阵
    Eigen::Vector3d nc = line_c.head(3);
    double l_norm = nc(0) * nc(0) + nc(1) * nc(1);
    double l_sqrtnorm = sqrt(l_norm);
    double l_trinorm = l_norm * l_sqrtnorm;

    double e1 = obs_i(0) * nc(0) + obs_i(1) * nc(1) + nc(2);
    double e2 = obs_i(2) * nc(0) + obs_i(3) * nc(1) + nc(2);
    Eigen::Map<Eigen::Vector2d> residual(residuals);
    residual(0) = e1 / l_sqrtnorm;
    residual(1) = e2 / l_sqrtnorm;

//    std::cout <<"---- sqrt_info: ------"<< sqrt_info << std::endl;
//    sqrt_info.setIdentity();

    residual = sqrt_info * residual;

残差形式一如既往,接下来看雅各比的求解。
按雅各比链式法则,首先求解的是 ∂ r L ∂ L I \frac{\partial \textbf{r}_{\textbf{L}}}{\partial L_{I}} LIrL,即误差对图像坐标系下直线方程的雅各比。

if (jacobians) {

        Eigen::Matrix<double, 2, 3> jaco_e_l(2, 3);
        jaco_e_l << (obs_i(0) / l_sqrtnorm - nc(0) * e1 / l_trinorm), (obs_i(1) / l_sqrtnorm - nc(1) * e1 / l_trinorm),
                1.0 / l_sqrtnorm,
                (obs_i(2) / l_sqrtnorm - nc(0) * e2 / l_trinorm), (obs_i(3) / l_sqrtnorm - nc(1) * e2 / l_trinorm),
                1.0 / l_sqrtnorm;

        jaco_e_l = sqrt_info * jaco_e_l;

***

接下来就是求解 ∂ L I ∂ L n ∂ L n ∂ L c \frac{\partial L_{I}}{\partial L_{n}} \frac{\partial L_{n}}{\partial L_{c}} LnLILcLn,由于代码中为了仿真方便直接设置投影矩阵为单位阵,所以下面将直接求解 ∂ L I ∂ L c \frac{\partial L_{I}}{\partial L_{c}} LcLI

Eigen::Matrix<double, 3, 6> jaco_l_Lc(3, 6);
        jaco_l_Lc.setZero();
        jaco_l_Lc.block(0, 0, 3, 3) = Eigen::Matrix3d::Identity();

        Eigen::Matrix<double, 2, 6> jaco_e_Lc;
        jaco_e_Lc = jaco_e_l * jaco_l_Lc;

显然, ∂ L I ∂ L c = [ I 3 × 3 0 3 × 3 ] 3 × 6 \frac{\partial L_{I}}{\partial L_{c}}=\begin{bmatrix} \textbf{I}_{3\times3} & \textbf{0}_{3\times3} \end{bmatrix}_{3\times6} LcLI=[I3×303×3]3×6

接下来首先求解对相机的body位姿 T w i T_{wi} Twi 的雅各比:

		if (jacobians[0]) {
            //std::cout <<"jacobian_pose_i"<<"\n";
            Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);

            Matrix6d invTbc;
            invTbc << Rbc.transpose(), -Rbc.transpose() * skew_symmetric(tbc),
                    Eigen::Matrix3d::Zero(), Rbc.transpose();

            Vector3d nw = line_w.head(3);
            Vector3d dw = line_w.tail(3);
            Eigen::Matrix<double, 6, 6> jaco_Lc_pose;
            jaco_Lc_pose.setZero();
            jaco_Lc_pose.block(0, 0, 3, 3) = Rwb.transpose() * skew_symmetric(dw);   // Lc_t
            jaco_Lc_pose.block(0, 3, 3, 3) = skew_symmetric(
                    Rwb.transpose() * (nw + skew_symmetric(dw) * twb));  // Lc_theta
            jaco_Lc_pose.block(3, 3, 3, 3) = skew_symmetric(Rwb.transpose() * dw);

            jaco_Lc_pose = invTbc * jaco_Lc_pose;
            //std::cout <<invTbc<<"\n"<<jaco_Lc_pose<<"\n\n";

            jacobian_pose_i.leftCols<6>() = jaco_e_Lc * jaco_Lc_pose;

            //std::cout <<jacobian_pose_i<<"\n\n";

            jacobian_pose_i.rightCols<1>().setZero();            //最后一列设成0
        }
  • 这里虽然参数变量为7个维度,但实际上还是对姿态角(3维)和平移(3维)求雅各比,最后一列设为0了,解析计算参考如下,结合李群扰动模型即可求解 (注意图中右上角少了一个负号):
    在这里插入图片描述
  • invTbc,为plk坐标的从相机坐标系到body坐标系的转移矩阵。

对外参变换矩阵 T b c T_{bc} Tbc 的雅各比求解与对body位姿的求解形式一致,就是换了个参数,接下来看对世界坐标系下直线正交参数的雅各比求解:

		if (jacobians[2]) {
            Eigen::Map<Eigen::Matrix<double, 2, 4, Eigen::RowMajor>> jacobian_lineOrth(jacobians[2]);

            Eigen::Matrix3d Rwc = Rwb * Rbc;
            Eigen::Vector3d twc = Rwb * tbc + twb;
            Matrix6d invTwc;
            invTwc << Rwc.transpose(), -Rwc.transpose() * skew_symmetric(twc),
                    Eigen::Matrix3d::Zero(), Rwc.transpose();
            //std::cout<<invTwc<<"\n";

            Vector3d nw = line_w.head(3);
            Vector3d vw = line_w.tail(3);
            Vector3d u1 = nw / nw.norm();
            Vector3d u2 = vw / vw.norm();
            Vector3d u3 = u1.cross(u2);
            Vector2d w(nw.norm(), vw.norm());
            w = w / w.norm();

            Eigen::Matrix<double, 6, 4> jaco_Lw_orth;
            jaco_Lw_orth.setZero();
            jaco_Lw_orth.block(3, 0, 3, 1) = w[1] * u3;
            jaco_Lw_orth.block(0, 1, 3, 1) = -w[0] * u3;
            jaco_Lw_orth.block(0, 2, 3, 1) = w(0) * u2;
            jaco_Lw_orth.block(3, 2, 3, 1) = -w(1) * u1;
            jaco_Lw_orth.block(0, 3, 3, 1) = -w(1) * u1;
            jaco_Lw_orth.block(3, 3, 3, 1) = w(0) * u2;

            //std::cout<<jaco_Lw_orth<<"\n";

            jacobian_lineOrth = jaco_e_Lc * invTwc * jaco_Lw_orth;
        }

参考如下:
在这里插入图片描述


lineProjectionFactor_incamera::lineProjectionFactor_incamera

其中残差是用第 i 帧 (代码中写的是obs_i, 但理论上应该是 obs_j ?)的观测和从第 i 帧 的直线plk参数递推出的第 j 帧的直线plk参数来计算, 如下:
L c j ← R w b j , R b c L w ← R w b i , R b c L c i L_{c_j} \xleftarrow{R_{w_{bj}}, R_{bc}} L_{w} \xleftarrow{R_{w_{bi}}, R_{bc}} L_{c_{i}} LcjRwbj,Rbc LwRwbi,Rbc Lci

  • 仍然是要先求解 ∂ r L ∂ X = ∂ r L ∂ L I ∂ L I ∂ L n ∂ L n ∂ L c \frac{\partial \textbf{r}_{\textbf{L}}}{\partial \textbf{X}}= \frac{\partial \textbf{r}_{\textbf{L}}}{\partial L_{I}} \frac{\partial L_{I}}{\partial L_{n}} \frac{\partial L_{n}}{\partial L_{c}} XrL=LIrLLnLILcLn
  • 对各参数空间的雅各比求解,注意与lineProjectionFactor::lineProjectionFactor形式上略有不同,但仍然是基于同一个线性转换矩阵求解,以 ∂ n w ∂ t \frac{\partial n_w}{\partial t} tnw 为例:
    ∂ n w ∂ t = R b i w T [ R b i w ( t w b i + ∂ t ) ] × d b i ∂ t = [ t w b i + ∂ t ] × R w b i d b i ∂ t = − [ R w b i d b i ] × \frac{\partial n_w}{\partial t}= \frac { R_{b_iw}^T[R_{b_iw}(t_{w_bi}+\partial t)]\times d_{bi}}{{\partial t}}=\frac{[t_{w_bi}+\partial t]\times R_{wb_i}d_{b_i}}{\partial t}=-[R_{wb_i}d_{b_i}]\times tnw=tRbiwT[Rbiw(twbi+t)]×dbi=t[twbi+t]×Rwbidbi=[Rwbidbi]×

lineProjectionFactor_instartframe::lineProjectionFactor_instartframe

该factor只优化直线参数。

  • 仍然是要先求解 ∂ r L ∂ X = ∂ r L ∂ L I ∂ L I ∂ L n ∂ L n ∂ L c \frac{\partial \textbf{r}_{\textbf{L}}}{\partial \textbf{X}}= \frac{\partial \textbf{r}_{\textbf{L}}}{\partial L_{I}} \frac{\partial L_{I}}{\partial L_{n}} \frac{\partial L_{n}}{\partial L_{c}} XrL=LIrLLnLILcLn
  • 然后只计算对直线参数的雅各比,形式一致。

6. pose_local_parameterization

定义了位姿局部参数化的一系列操作,包括参数更新和雅各比计算等,其中

  • virtual int GlobalSize() const { return 7; }; 即参数自由度为7(存在冗余)。
  • virtual int LocalSize() const { return 6; }; 即delta所在正切空间的参数自由度为6 (姿态3+平移3)。

PoseLocalParameterization::Plus

位置更新通过简单加减,姿态更新通过四元数相乘。

PoseLocalParameterization::ComputeJacobian

在局部参数和全局参数的雅各比之间进行转换,参考如下:
在这里插入图片描述
以及
在这里插入图片描述


7. line_manager(比较重要,论文中创新部分)

首先看定义的两个线特征struct。

  1. LineLandmark
struct LineLandmark {
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    bool is_triangulation{false};
    bool boptimize_success{false};
    int id;

    std::array<double, 2> para_data{};
    Eigen::Vector2d para;
    Eigen::Vector3d x_dir;
    Eigen::Vector3d y_dir;
    Eigen::Vector3d z_dir;

    Eigen::Matrix3d R_c_l;

    Eigen::Vec6d plk_w;
    Eigen::Vec6d plk_h;
    Eigen::Vec3d spt_w;
    Eigen::Vec3d ept_w;

    // groudtruth
    Eigen::Vec6d plk_w_gt;
    Eigen::Vector3d spt_w_gt;
    Eigen::Vector3d ept_w_gt;

    // plucker optimazation
    Eigen::Vec4d line_orth_c;
};

待解释。
2. line_obs (简单的线特征观测)

struct line_obs {
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    Eigen::Vector4d obs;
    int landmark_id;
};

包含两个端点的坐标和线特征ID。

triangulate_line

输入变量:

  • Eigen::aligned_unordered_map<int, LineLandmark> &line_landmarks,所有的空间线
  • Eigen::aligned_map<int, Eigen::aligned_map<int, line_obs>> &line_obs_map,每条空间线(第一个int)在某相机帧中(第二个int)的观测
  • Eigen::aligned_vector &poses,所有的位姿
  • bool plucker_optimize,是否进行普吕克优化 ( 0: LINE_OPTIMIZE 1: PLUCKER_OPTIMIZE)
for (auto &landmark : line_landmarks) {
        int landmark_id = landmark.second.id;

        if (line_obs_map.find(landmark_id) == line_obs_map.end() or
            line_obs_map.at(landmark_id).size() < 2) {
            continue;
        }
        int start_frame_id = line_obs_map.at(landmark_id).begin()->first;
        Transformd T_w_s = poses.at(start_frame_id);

遍历所有空间线,找到第一次观测到该空间线的起始帧 S S S 和相应的位姿 T w s T_{ws} Tws,若只在最新一帧观测到或只被一个历史帧观测到,则跳过。

		Eigen::Vector4d obs_line =
                line_obs_map.at(landmark_id).at(start_frame_id).obs;
        Eigen::Vector3d spt{obs_line(0), obs_line(1), 1};
        Eigen::Vector3d ept{obs_line(2), obs_line(3), 1};
        Eigen::Vector4d plane_para = pi_from_ppp(spt, ept, Eigen::Vector3d(0, 0, 0));
        Eigen::Vector3d plane_norm = plane_para.head(3);
        plane_norm.normalize();

获取该空间线在起始帧的线段观测,通过两个端点和相机光心确定一个平面方程,并提取其法向量 plane_norm。

		Eigen::Vector3d mpt = (spt + ept) / 2;
        // Eigen::Vector3d mpt = spt;

        Eigen::Vector3d y_dir = mpt.normalized();
        Eigen::Vector3d x_dir = y_dir.cross(plane_norm);
        x_dir.norm();

        landmark.second.x_dir = y_dir;
        landmark.second.y_dir = x_dir;
        landmark.second.z_dir = plane_norm;

        landmark.second.R_c_l.col(0) = x_dir;
        landmark.second.R_c_l.col(1) = y_dir;
        landmark.second.R_c_l.col(2) = plane_norm;
  • 以线段观测的中点 m_pt 为原点 (论文里以端点为原点建立局部坐标系),建立局部坐标系 P P P ,以光心到 m_pt 为坐标系 P P P 的y轴方向向量,上一步求得的法向量为z轴方向,叉乘获得x轴方向,进一步获得局部坐标系和相机坐标系的转换矩阵 R c l R_{cl} Rcl
  • 需要额外注意的是,landmark.second中存储的xy轴方向,与Rcl中的xy正好相反。
		Eigen::Vector3d obs_dir = ept - spt;
        obs_dir.normalize();
        Eigen::Vector3d tij;
        Eigen::Matrix3d Rij;
        Eigen::Vector4d obsi, obsj; // obs from two frame are used to do triangulation
        // plane pi from ith obs in ith camera frame
        Eigen::Vector4d pii;
        Eigen::Vector3d ni; // normal vector of plane
        double min_cos_theta = 1;

获取线段观测的方向向量,初始化第 i 帧和 第 j 帧的相对位姿、各帧观测、第 i 帧平面和法向量,准备进行三角化。

 		for (const auto &obs : line_obs_map.at(landmark_id)) // 遍历该空间线所有的观测
        {

            int target_frame_id = obs.first;
            if (target_frame_id == start_frame_id) // 第一个观测是start frame 上
            {

                obsi = obs.second.obs;
                Eigen::Vector3d p1(obs.second.obs[0], obs.second.obs[1], 1);
                Eigen::Vector3d p2(obs.second.obs[2], obs.second.obs[3], 1);
                pii = pi_from_ppp(p1, p2, Vector3d(0, 0, 0));
                ni = pii.head(3);
                ni.normalize();
                continue;
            }
            Transformd T_w_t = poses.at(target_frame_id);
            Transformd T_s_t = T_w_s.inverse() * T_w_t;
            // 非start frame(其他帧)上的观测

            Eigen::Vector3d t = T_s_t.pos;              // tij
            Eigen::Matrix3d R = T_s_t.rotationMatrix(); // Rij

            // plane pi from jth obs in ith camera frame
            Vector3d p3(obs.second.obs[0], obs.second.obs[1], 1);
            Vector3d p4(obs.second.obs[2], obs.second.obs[3], 1);
            p3 = R * p3 + t;
            p4 = R * p4 + t;
            Vector4d pij = pi_from_ppp(p3, p4, t);
            Eigen::Vector3d nj = pij.head(3);
            nj.normalize();

            double cos_theta = ni.dot(nj);
            if (cos_theta < min_cos_theta) {
                min_cos_theta = cos_theta;
                tij = t;
                Rij = R;
                obsj = obs.second.obs;
            }
        }
        if (min_cos_theta > 0.998) {
            continue;
            char tmp;
            std::cout << "parital is not enough" << std::endl;
            std::cin >> tmp;
        }
  • 遍历该空间线的所有观测,若为起始帧上的观测,则获取对应的平面方程和法向量ni(?这里之前不是求过了吗?)
  • 若不是起始帧,则求出起始帧和目标帧之间的相对位姿变换 T_s_t ,即旋转 R 和 平移 t。
  • 通过相对位姿变换矩阵,把起始帧的观测两端点坐标转换到目标帧下,确立新的平面方程和法向量 nj。
  • 计算法向量ni和nj之间的的 cos ⁡ \cos cos 角度偏差,若小于当前最小的 cos ⁡ θ \cos\theta cosθ (即变换角度 θ \theta θ 最大),则保存对应的R、t 和观测 obs_j。
  • 遍历结束,获得法向量角度偏差最大的目标帧观测和相对于起始帧的位姿。若min_cos_theta 仍大于阈值( 角度偏差不够大),则警告视差不够。
  • 可以理解为找出与起始帧视差最大的一个目标帧
		// plane pi from jth obs in ith camera frame
        Vector3d p3(obsj(0), obsj(1), 1);
        Vector3d p4(obsj(2), obsj(3), 1);
        p3 = Rij * p3 + tij;
        p4 = Rij * p4 + tij;
        Vector4d pij = pi_from_ppp(p3, p4, tij);

        Vector6d plk = pipi_plk(pii, pij);
        Vector3d n = plk.head(3);
        Vector3d v = plk.tail(3);

        landmark.second.plk_h = plk; // plk in camera frame
        landmark.second.is_triangulation = true;

求出 π j \pi_j πj 在起始帧的平面方程,结合 π i \pi_i πi 求出该空间线在起始帧的plk坐标,赋值给 属性plk_h 并将三角化标志位置 true。
(仿真代码中的plk坐标真值验证代码掠过)。

 		if (!plucker_optimize) {

            Eigen::Vector3d vc_l = landmark.second.R_c_l.transpose() * vc;//转换到局部参数空间
            double cos_thet = std::atan2(vc_l.y(), vc_l.x());

            Eigen::Vector3d line_origin = pluckerOrigin(line_w);
            double distance = landmark.second.x_dir.dot(line_origin);

            // landmark.para.x() = 1.0 / distance;
            landmark.second.para.x() = 0.2;

            //            landmark.second.para.y() = acos(cos_theta);
            landmark.second.para.y() = cos_thet;
        }

若不进行普吕克坐标优化,则

  • 在局部参数坐标系下获取直线方向相对于图中x轴的偏角 θ \theta θ
  • line_origin 是相机光心到直线的垂直点。
  • 初始化空间线的局部参数 ( ρ , θ ) (\rho,\theta) (ρ,θ),其中 ρ \rho ρ 初始化为 0.2。
    在这里插入图片描述
		else {
            // landmark.second.line_orth = plk_to_orth(line_w);
            // todo In order to equality, we use the same initial value to triangulate the line
            Eigen::Vector3d vc_l = landmark.second.R_c_l.transpose() * vc;
            double cos_thet = std::atan2(vc_l.y(), vc_l.x());
            double d = 1 / 0.2;

			//origin_c 为 线段中点在相机坐标系的坐标,但这个d?
            Eigen::Vector3d origin_c = landmark.second.x_dir * d; 
            Eigen::Vector3d line_c;
            line_c << cos(cos_thet), sin(cos_thet), 0;
            line_c = landmark.second.R_c_l * line_c;


            Eigen::Matrix<double, 6, 1> plk_c_h;
            plk_c_h.head(3) = origin_c.cross(line_c);
            plk_c_h.tail(3) = line_c;

            //landmark.second.line_orth_c = plk_to_orth(landmark.second.plk_h);
            landmark.second.line_orth_c = plk_to_orth(plk_c_h);
        }

若进行吕克坐标优化,则

  • 根据局部参数坐标,获取对应的相机坐标系下的plk坐标和正交参数化坐标。
  • 这里的d的数值大小似乎没用,反正都是用来求 n c n_c nc 的? 但这样求出的plk坐标,就没有蕴含 ∣ ∣ n c ∣ ∣ ∣ ∣ v c ∣ ∣ \frac{||n_c||}{||v_c||} vcnc 的距离信息了,固定成0.2。推测会在后续过程中继续优化。

updatelineplk

输入变量和 triangulate_line一样,最终目的是更新每条空间线的plk坐标和空间端点坐标 spt_w和ept_w。

	for (auto &landmark : line_landmarks) {
        int landmark_id = landmark.second.id;
        int host_frame_id = line_obs_map.at(landmark_id).begin()->first;

同样是遍历所有空间线,得到对应的ID号和host_frame的ID号(起始帧?)。

if (!plucker_optimize) {
            double d = 1 / landmark.second.para[0];
            double theta = landmark.second.para[1];

            Eigen::Vector3d origin = landmark.second.x_dir * d;

            Eigen::Vector3d line_c;
            line_c << cos(theta), sin(theta), 0;

            line_c = landmark.second.R_c_l * line_c;


            Eigen::Matrix<double, 6, 1> plk_c_h;
            plk_c_h.head(3) = origin.cross(line_c);
            plk_c_h.tail(3) = line_c;

            landmark.second.plk_h = plk_c_h;

            Transformd T_w_s = poses.at(host_frame_id);

            Eigen::Vec6d line_w = plk_to_pose(landmark.second.plk_h, T_w_s.rotationMatrix(),
                                              T_w_s.pos); // transfrom to world frame
            landmark.second.plk_w = line_w; // transfrom to camera frame
        }
        else {
            Vector4d line_orth_c = landmark.second.line_orth_c;
            Eigen::Vec6d line_c = orth_to_plk(line_orth_c);
            Transformd T_w_s = poses.at(host_frame_id);
            Vector6d line_w = plk_to_pose(line_c, T_w_s.rotationMatrix(), T_w_s.pos); // transfrom to world frame


//            Vector4d line_orth_w = landmark.second.line_orth;
//            Eigen::Vec6d line_w = orth_to_plk(line_orth_w);
//            Transformd T_w_s = poses.at(host_frame_id);
//            Vector6d line_c = plk_from_pose(line_w, T_w_s.rotationMatrix(), T_w_s.pos);

            landmark.second.plk_h = line_c;
            landmark.second.plk_w = line_w;

        }
  • 若没有进行普吕克优化,则获取该空间线的参数 ( ρ , θ ) (\rho, \theta) (ρ,θ),并得到对应的相机坐标系下的plk坐标,进一步结合全局位姿获得世界坐标系下的plk坐标 plk_w。
  • 若进行普吕克优化,则直接获取该空间线在相机坐标系下的正交参数,通过函数转化获得相应的plk_c和plk_w。
		Eigen::Vector3d line_origin = pluckerOrigin(landmark.second.plk_w);
        landmark.second.spt_w = line_origin;
        landmark.second.ept_w = line_origin;
        
        double d1 = 0;
        double d2 = 0;

        auto &line_obs = line_obs_map.at(landmark_id);

        for (const auto &obs : line_obs) {
            int target_id = obs.first;
            Transformd T_w_t = poses.at(target_id);
            Transformd T_t_w = T_w_t.inverse();

            Vector6d plk_t = plk_to_pose(landmark.second.plk_w, T_t_w.rotationMatrix(), T_t_w.pos);
            Eigen::Vec3d p11{obs.second.obs[0], obs.second.obs[1], 1.0};
            Eigen::Vec3d p21{obs.second.obs[2], obs.second.obs[3], 1.0};

            Vector6d cpt = TrimLine(p11, p21, plk_t);

            Eigen::Vector3d cpt1 = cpt.head<3>();
            Eigen::Vector3d cpt2 = cpt.tail<3>();

            if (cpt1(2) < 0 || cpt2(2) < 0) {
                break;
            }

            if ((cpt1 - cpt2).norm() > 8) {
                break;
            }

            landmark.second.boptimize_success = true;

            Eigen::Vector3d wpt1 = T_w_t * cpt1;
            Eigen::Vector3d wpt2 = T_w_t * cpt2;
  • 对该空间线在每个相机帧下的观测,结合在该帧下的plk坐标(plk_t) ,通过trimline获取该帧相机坐标系下的3D空间直线的两个端点坐标,若满足条件则判定 boptimize_success = true ,并结合该帧位姿获取在世界坐标系下的3D空间端点坐标。
			// line project distance
			Eigen::Vector3d line_dir;
            if (landmark.second.plk_w[5] < 0) {
                line_dir = -landmark.second.plk_w.tail<3>();
            } else {
                line_dir = landmark.second.plk_w.tail<3>();
            } //获取世界坐标系下的空间线方向向量

            double d1_tmp = (wpt1 - line_origin).dot(line_dir);
            double d2_tmp = (wpt2 - line_origin).dot(line_dir);

            if (d2_tmp < d1_tmp) {
                std::swap(d1_tmp, d2_tmp);
                Eigen::Vector3d tmp = wpt1;
                wpt1 = wpt2;
                wpt2 = tmp;
            } //令wpt1为离直线垂直点最近的端点

            if (target_id == host_frame_id) {
                landmark.second.spt_w = wpt1;
                landmark.second.ept_w = wpt2;
                d1 = d1_tmp;
                d2 = d2_tmp;
            } //若目标帧就是起始帧,则赋值该空间线的端点3D坐标

            if (d1 == d2) {
                d1 = d1_tmp;
                d2 = d2_tmp;
                continue;
            }

            if (d1_tmp >= d1) {
                d1 = d1_tmp;
                landmark.second.spt_w = wpt1;
            }

            if (d2_tmp <= d2) {
                d2 = d2_tmp;
                landmark.second.ept_w = wpt2;
            }
        }

本质上就是对某条空间线遍历其在所有帧内的观测,通过trimline获取相应的3D空间端点,投影到直线方向,投影模值最大的赋值给该空间线的 spt_w 和 ept_w。

optimize_line_without_plucker

使用的factor 是 general_line::GeneralLineErrorTwoCameraTerm,只优化线参数 ( ρ , θ ) (\rho, \theta) (ρ,θ) (2维) ,相机位姿和外参不参与优化,使用自动求导。

optimize_line_without_plucker2

形式稍有不同,但和上面的基本一致。

optimize_line_with_plucker

使用的factor是 plucker::LineOrthParameterization, 只优化空间线的正交参数 ( 4维),相机位姿和外参不参与优化,使用自动求导。

optimize_line_with_plucker2

形式稍有不同,但和上面的基本一致。

8. line_optimization.cpp (仿真应用)

int main(int argc, char **argv) {

    general_line::GeneralLineErrorOneCameraTerm::sqrtInfo_ =
            460 / 3.2 * Matrix2d::Identity();
    general_line::GeneralLineErrorTwoCameraTerm::sqrtInfo_ =
            460 / 3.2 * Matrix2d::Identity();

    lineProjectionFactor::sqrt_info = 460 / 3.2 * Matrix2d::Identity();

    PluckerLineErrorOneCameraTerm::sqrtInfo_ = 460 / 3.2 * Matrix2d::Identity();
    PluckerLineErrorTwoCameraTerm::sqrtInfo_ = 460 / 3.2 * Matrix2d::Identity();


    std::string pose_path =
            "../bin/cam_pose_tum.txt";

    //  "/home/ubuntu/Downloads/vio_data_simulation/bin/keyframe/all_lines_599.txt"
    std::string line_obs_path =
            "../bin/keyframe/all_lines_";

    std::string line_landmarks_path =
            "../bin/house_model/house.txt";


    // step1: read camera poses
    Eigen::aligned_vector<Transformd> camera_poses;
    std::vector<std::array<double, 7>> camera_pose_parameters;
    std::array<double, 7> T_i_c_paramter{0, 0, 0, 0, 0, 0, 1};
    read_camera_pose(pose_path, camera_poses, camera_pose_parameters);

    // step2: read line landmak groudtruth
    Eigen::aligned_unordered_map<int, LineLandmark> line_landmarks;
    read_linelandmark(line_landmarks_path, line_landmarks);

    // step3: read line observation
    Eigen::aligned_map<int, Eigen::aligned_map<int, line_obs>> line_obs_map;
    read_line_obs(line_obs_path, line_obs_map);

    add_noise_obs(line_obs_map, 4.5);

    // step4: triangulation line

    cout << "line_landmark.size(): " << line_landmarks.size() << endl;

    triangulate_line(line_landmarks,line_obs_map, camera_poses, plucker_optimize);

    if(plucker_optimize)
    {
        //optimize_line_with_plucker(line_landmarks, line_obs_map, camera_pose_parameters, T_i_c_paramter);
        optimize_line_with_plucker2(line_landmarks, line_obs_map, camera_pose_parameters, T_i_c_paramter);

    } else
    {
        //optimize_line_without_plucker(line_landmarks, line_obs_map, camera_pose_parameters, T_i_c_paramter);
        optimize_line_without_plucker2(line_landmarks, line_obs_map, camera_pose_parameters, T_i_c_paramter);
    }

    updatelineplk(line_landmarks, line_obs_map, camera_poses, plucker_optimize);
    savefeatures(line_landmarks, plucker_optimize);

    return 0;
}
  • 首先读取所有的相机位姿、空间直线真值、直线观测(并加入噪声)。
  • 基于已知的相机位姿和直线观测,进行空间线的三角化。
  • 根据相应的优化方式(直线参数优化或者普吕克参数优化)对空间直线参数进行优化。
  • 更新空间直线端点坐标并保存。
  • 完成仿真验证。
举报

相关推荐

0 条评论