SLAM--单目尺度漂移(相似变换群Sim3)

相似变换群与李代数

对于单目视觉SLAM,由于单目的不确定性,在闭环检测中为了解决尺度漂移,一般会用到相似变换群
Sim3,用来描述相似变换:
p ′ = [ s R t 0 1 ] p = s R p + t p'=\begin{bmatrix} s\bm R&\bm t\\ \\0&1 \end{bmatrix}p=s\bm{Rp+t} p=sR0t1p=sRp+t
和SO3、SE3类似,可以将其描述为群:
S i m ( 3 ) = { S = [ s R t 0 1 ] ∈ R 4 × 4 } Sim(3)=\{S= \begin{bmatrix} s\bm R&\bm t\\ \\0&1 \end{bmatrix} \in {\mathbb{R}^{4\times 4}} \} Sim(3)={S=sR0t1R4×4}
对应的李代数sim(3)是一个7维的向量(7个自由度):
s i m ( 3 ) = { ζ = [ ρ ϕ σ ] ∈ R 7 ∣ ρ ∈ R 3 , ϕ ∈ s o ( 3 ) , ζ ∧ = [ σ I + ϕ ∧ ρ   0 T 0 ] ∈ R 4 × 4 } sim(3) = \{ \zeta = \left[ {\begin{matrix} \rho \\ \phi \\ \sigma \end{matrix}} \right] \in {\mathbb{R}^7}|\rho \in {\mathbb{R}^3},\phi \in so(3),{\zeta ^ \wedge } = \left[ {\begin{matrix} {\sigma I+{\phi ^ \wedge }}&\rho \\ ~\\ {{0^T}}&0 \end{matrix}} \right]\in {\mathbb{R}^{4\times 4}} \} sim(3)={ζ=ρϕσR7ρR3,ϕso(3),ζ=σI+ϕ 0Tρ0R4×4}
其指数映射为:
[ s R t 0 1 ] = exp ⁡ ( ζ ∧ ) = [ exp ⁡ ( σ ) + exp ⁡ ( ϕ ∧ ) J s ρ   0 T 1 ] \begin{bmatrix} s\bm R&\bm t\\ \\0&1 \end{bmatrix}=\exp(\zeta ^\wedge)=\left[ {\begin{matrix} {\exp(\sigma)+\exp({\phi ^ \wedge }})& J_s\rho \\ ~\\ {{0^T}}&1 \end{matrix}} \right] sR0t1=exp(ζ)=exp(σ)+exp(ϕ) 0TJsρ1
其中
J s = e σ − 1 σ I + σ e σ sin ⁡ θ + ( 1 − e σ cos ⁡ θ ) θ σ 2 + θ 2 a ∧ + ( e σ − 1 σ − ( e σ cos ⁡ θ − 1 ) σ + ( e σ sin ⁡ θ ) θ σ 2 + θ 2 ) a ∧ a ∧ J_s = \frac {e^{\sigma}-1}{\sigma}\bm I+\frac{\sigma e^\sigma \sin \theta+(1-e^\sigma\cos \theta)\theta} {\sigma^2+\theta^2}\bm a^{\wedge}+(\frac {e^{\sigma}-1} {\sigma}-\frac {(e^\sigma\cos \theta-1)\sigma+(e^\sigma \sin \theta)\theta} {\sigma^2+\theta^2})\bm a^{\wedge}\bm a^{\wedge} Js=σeσ1I+σ2+θ2σeσsinθ+(1eσcosθ)θa+(σeσ1σ2+θ2(eσcosθ1)σ+(eσsinθ)θ)aa
 
 

回环检测-Sim3求解

参考文献[1],求解。
对于Sim3群,有三对匹配点:
[ s R t 0 1 ] , x i = s R x ˉ i + t i ∈ ( 1 , 2 , 3 ) \begin{bmatrix} s\bm R&\bm t\\ \\0&1 \end{bmatrix},x_i = s\bm{R} \bar x_i+t \quad i \in (1,2,3) sR0t1xi=sRxˉi+ti(1,2,3)
计算质心:
c = 1 3 ( x 1 + x 2 + x 3 ) , c ˉ = 1 3 ( x ˉ 1 + x ˉ 2 + x ˉ 3 ) c =\frac 1 3(x_1+x_2+x_3),\bar c = \frac 1 3 (\bar x_1+\bar x_2+\bar x_3) c=31(x1+x2+x3),cˉ=31(xˉ1+xˉ2+xˉ3)
减去质心点:
y i = x i − c , y ˉ i = x ˉ i − c ˉ y_i = x_i-c,\quad \bar y_i =\bar x_i-\bar c yi=xic,yˉi=xˉicˉ
得到映射矩阵H:
H = y 1 y ˉ 1 T + y 2 y ˉ 2 T + y 3 y ˉ 3 T H = y_1 \bar y_1^T + y_2 \bar y_2^T+ y_3\bar y_3^T H=y1yˉ1T+y2yˉ2T+y3yˉ3T
奇异值SVD分解:
H = U ⋅ W ⋅ V T H = U\cdot W\cdot V^T H=UWVT
求解得到R、t和尺度因子s:
R = V ⋅ U T , t = c − s R c ˉ   s = ( ∥ y 1 ∥ 2 2 + ∥ y 2 ∥ 2 2 + ∥ y 3 ∥ 2 2 ) 1 2 ( ∥ y ˉ 1 ∥ 2 2 + ∥ y ˉ 2 ∥ 2 2 + ∥ y ˉ 3 ∥ 2 2 ) 1 2 R = V\cdot U^T,\quad t=c-s\bm R\bar c\\~\\ s=\frac {(\left\|y_1 \right\|_2^2+\left\|y_2 \right\|_2^2+\left\|y_3 \right\|_2^2)^{\frac 1 2}} {(\left\| \bar y_1 \right\|_2^2+\left\|\bar y_2 \right\|_2^2+\left\|\bar y_3 \right\|_2^2)^{\frac 1 2}} R=VUT,t=csRcˉ s=(yˉ122+yˉ222+yˉ322)21(y122+y222+y322)21
 
 

Sim3位姿图优化

我们知道SE3的位姿图优化:
e i j ( ξ i , ξ j ) = ln ⁡ [ exp ⁡ ( − ξ i j ∧ ) exp ⁡ ( − ξ i ∧ ) ⋅ exp ⁡ ( ξ j ∧ ) ] ∨ ,   e i j ( ξ i , ξ j ) = ln ⁡ [ T i j − 1 ⋅ T i − 1 ⋅ T j ] ∨ e_{ij}(\xi_i,\xi_j)=\ln[\exp(-\xi_{ij} ^\land) \exp(-\xi_i ^\land)\cdot \exp(\xi_j ^\land)]^\vee, \\ ~\\e_{ij}(\xi_i,\xi_j)=\ln[T_{ij}^{-1}\cdot T_i^{-1}\cdot T_j]^\vee eij(ξi,ξj)=ln[exp(ξij)exp(ξi)exp(ξj)], eij(ξi,ξj)=ln[Tij1Ti1Tj]
同理,对于Sim3的位姿图优化的误差函数可以表示为:
e i j ( ζ i , ζ j ) = ln ⁡ [ exp ⁡ ( − ζ i j ∧ ) exp ⁡ ( − ζ i ∧ ) ⋅ exp ⁡ ( ζ j ∧ ) ] ∨   即 e i j ( ζ i , ζ j ) = ln ⁡ [ S i j − 1 ⋅ S i − 1 ⋅ S j ] ∨ e_{ij}(\zeta_i,\zeta_j)=\ln[\exp(-\zeta_{ij} ^\land) \exp(-\zeta_i ^\land)\cdot \exp(\zeta_j ^\land)]^\vee\\ ~\\ 即\quad e_{ij}(\zeta_i,\zeta_j)=\ln[S_{ij}^{-1}\cdot S_i^{-1}\cdot S_j]^\vee eij(ζi,ζj)=ln[exp(ζij)exp(ζi)exp(ζj)] eij(ζi,ζj)=ln[Sij1Si1Sj]
为了为优化作准备,我们保持每个位姿旋转和平移不变,并设置s=1,只有当回环检测的时候 s ≠ 1 s\ne 1 s=1

雅克比矩阵推导略;具体参考文献[2].

我们依然可以用g2o进行sim3的优化.

附上ORB-SLAM2中优化Sim3的源码:

int Optimizer::OptimizeSim3(KeyFrame *pKF1, KeyFrame *pKF2, vector<MapPoint *> &vpMatches1, g2o::Sim3 &g2oS12, const float th2, const bool bFixScale)
{
    g2o::SparseOptimizer optimizer;
    g2o::BlockSolverX::LinearSolverType * linearSolver;

    linearSolver = new g2o::LinearSolverDense<g2o::BlockSolverX::PoseMatrixType>();

    g2o::BlockSolverX * solver_ptr = new g2o::BlockSolverX(linearSolver);

    g2o::OptimizationAlgorithmLevenberg* solver = new g2o::OptimizationAlgorithmLevenberg(solver_ptr);
    optimizer.setAlgorithm(solver);

    // Calibration
    const cv::Mat &K1 = pKF1->mK;
    const cv::Mat &K2 = pKF2->mK;

    // Camera poses
    const cv::Mat R1w = pKF1->GetRotation();
    const cv::Mat t1w = pKF1->GetTranslation();
    const cv::Mat R2w = pKF2->GetRotation();
    const cv::Mat t2w = pKF2->GetTranslation();

    // Set Sim3 vertex
    g2o::VertexSim3Expmap * vSim3 = new g2o::VertexSim3Expmap();    
    vSim3->_fix_scale=bFixScale;
    vSim3->setEstimate(g2oS12);
    vSim3->setId(0);
    vSim3->setFixed(false);
    vSim3->_principle_point1[0] = K1.at<float>(0,2);
    vSim3->_principle_point1[1] = K1.at<float>(1,2);
    vSim3->_focal_length1[0] = K1.at<float>(0,0);
    vSim3->_focal_length1[1] = K1.at<float>(1,1);
    vSim3->_principle_point2[0] = K2.at<float>(0,2);
    vSim3->_principle_point2[1] = K2.at<float>(1,2);
    vSim3->_focal_length2[0] = K2.at<float>(0,0);
    vSim3->_focal_length2[1] = K2.at<float>(1,1);
    optimizer.addVertex(vSim3);

    // Set MapPoint vertices
    const int N = vpMatches1.size();
    const vector<MapPoint*> vpMapPoints1 = pKF1->GetMapPointMatches();
    vector<g2o::EdgeSim3ProjectXYZ*> vpEdges12;
    vector<g2o::EdgeInverseSim3ProjectXYZ*> vpEdges21;
    vector<size_t> vnIndexEdge;

    vnIndexEdge.reserve(2*N);
    vpEdges12.reserve(2*N);
    vpEdges21.reserve(2*N);

    const float deltaHuber = sqrt(th2);

    int nCorrespondences = 0;

    for(int i=0; i<N; i++)
    {
        if(!vpMatches1[i])
            continue;

        MapPoint* pMP1 = vpMapPoints1[i];
        MapPoint* pMP2 = vpMatches1[i];

        const int id1 = 2*i+1;
        const int id2 = 2*(i+1);

        const int i2 = pMP2->GetIndexInKeyFrame(pKF2);

        if(pMP1 && pMP2)
        {
            if(!pMP1->isBad() && !pMP2->isBad() && i2>=0)
            {
                g2o::VertexSBAPointXYZ* vPoint1 = new g2o::VertexSBAPointXYZ();
                cv::Mat P3D1w = pMP1->GetWorldPos();
                cv::Mat P3D1c = R1w*P3D1w + t1w;
                vPoint1->setEstimate(Converter::toVector3d(P3D1c));
                vPoint1->setId(id1);
                vPoint1->setFixed(true);
                optimizer.addVertex(vPoint1);

                g2o::VertexSBAPointXYZ* vPoint2 = new g2o::VertexSBAPointXYZ();
                cv::Mat P3D2w = pMP2->GetWorldPos();
                cv::Mat P3D2c = R2w*P3D2w + t2w;
                vPoint2->setEstimate(Converter::toVector3d(P3D2c));
                vPoint2->setId(id2);
                vPoint2->setFixed(true);
                optimizer.addVertex(vPoint2);
            }
            else
                continue;
        }
        else
            continue;

        nCorrespondences++;

        // Set edge x1 = S12*X2
        Eigen::Matrix<double,2,1> obs1;
        const cv::KeyPoint &kpUn1 = pKF1->mvKeysUn[i];
        obs1 << kpUn1.pt.x, kpUn1.pt.y;

        g2o::EdgeSim3ProjectXYZ* e12 = new g2o::EdgeSim3ProjectXYZ();
        e12->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(id2)));
        e12->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(0)));
        e12->setMeasurement(obs1);
        const float &invSigmaSquare1 = pKF1->mvInvLevelSigma2[kpUn1.octave];
        e12->setInformation(Eigen::Matrix2d::Identity()*invSigmaSquare1);

        g2o::RobustKernelHuber* rk1 = new g2o::RobustKernelHuber;
        e12->setRobustKernel(rk1);
        rk1->setDelta(deltaHuber);
        optimizer.addEdge(e12);

        // Set edge x2 = S21*X1
        Eigen::Matrix<double,2,1> obs2;
        const cv::KeyPoint &kpUn2 = pKF2->mvKeysUn[i2];
        obs2 << kpUn2.pt.x, kpUn2.pt.y;

        g2o::EdgeInverseSim3ProjectXYZ* e21 = new g2o::EdgeInverseSim3ProjectXYZ();

        e21->setVertex(0, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(id1)));
        e21->setVertex(1, dynamic_cast<g2o::OptimizableGraph::Vertex*>(optimizer.vertex(0)));
        e21->setMeasurement(obs2);
        float invSigmaSquare2 = pKF2->mvInvLevelSigma2[kpUn2.octave];
        e21->setInformation(Eigen::Matrix2d::Identity()*invSigmaSquare2);

        g2o::RobustKernelHuber* rk2 = new g2o::RobustKernelHuber;
        e21->setRobustKernel(rk2);
        rk2->setDelta(deltaHuber);
        optimizer.addEdge(e21);

        vpEdges12.push_back(e12);
        vpEdges21.push_back(e21);
        vnIndexEdge.push_back(i);
    }

    // Optimize!
    optimizer.initializeOptimization();
    optimizer.optimize(5);

    // Check inliers
    int nBad=0;
    for(size_t i=0; i<vpEdges12.size();i++)
    {
        g2o::EdgeSim3ProjectXYZ* e12 = vpEdges12[i];
        g2o::EdgeInverseSim3ProjectXYZ* e21 = vpEdges21[i];
        if(!e12 || !e21)
            continue;

        if(e12->chi2()>th2 || e21->chi2()>th2)
        {
            size_t idx = vnIndexEdge[i];
            vpMatches1[idx]=static_cast<MapPoint*>(NULL);
            optimizer.removeEdge(e12);
            optimizer.removeEdge(e21);
            vpEdges12[i]=static_cast<g2o::EdgeSim3ProjectXYZ*>(NULL);
            vpEdges21[i]=static_cast<g2o::EdgeInverseSim3ProjectXYZ*>(NULL);
            nBad++;
        }
    }

    int nMoreIterations;
    if(nBad>0)
        nMoreIterations=10;
    else
        nMoreIterations=5;

    if(nCorrespondences-nBad<10)
        return 0;

    // Optimize again only with inliers

    optimizer.initializeOptimization();
    optimizer.optimize(nMoreIterations);

    int nIn = 0;
    for(size_t i=0; i<vpEdges12.size();i++)
    {
        g2o::EdgeSim3ProjectXYZ* e12 = vpEdges12[i];
        g2o::EdgeInverseSim3ProjectXYZ* e21 = vpEdges21[i];
        if(!e12 || !e21)
            continue;

        if(e12->chi2()>th2 || e21->chi2()>th2)
        {
            size_t idx = vnIndexEdge[i];
            vpMatches1[idx]=static_cast<MapPoint*>(NULL);
        }
        else
            nIn++;
    }

    // Recover optimized Sim3
    g2o::VertexSim3Expmap* vSim3_recov = static_cast<g2o::VertexSim3Expmap*>(optimizer.vertex(0));
    g2oS12= vSim3_recov->estimate();

    return nIn;
}

参考文献

1.Horn, Berthold, K, et al. Closed-form solution of absolute orientation using unit quaternions[J]. Journal of the Optical Society of America A, 1987.
2.Strasdat H . Local Accuracy and Global Consistency for Efficient SLAM[D]. PhD thesis, Imperial College London, 2012.
3.视觉SLAM十四讲–高翔
4.ORB-SLAM2 源码

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ORB-SLAM3 是一种基于单目相机的稠密三维重建算法。它是从原始的 ORB-SLAM 系统发展而来,具有更高的精度和鲁棒性。ORB-SLAM3 使用了 ORB 特征点描述子和 Bag of Words 方法来进行特征匹配和地图构建。它还引入了位姿图优化和稠密重建模块,可以生成更准确的三维地图。 具体而言,ORB-SLAM3 的工作流程如下: 1. 特征提取与描述:首先,对输入的图像序列进行特征提取,使用 ORB 特征点检测器和描述子生成器获得关键点和描述子。 2. 建立初始化地图:通过对关键帧之间的特征匹配进行三角化,估计相机的初始位姿,并初始化地图点的位置。 3. 位姿估计与优化:使用非线性优化方法(例如,基于捆绑调整(bundle adjustment)的方法)对关键帧之间的相对位姿进行优化,同时优化地图点的位置。 4. 闭环检测与回环优化:通过检测到与先前访问过的关键帧之间的回环,进行闭环检测,并通过非线性优化来优化回环帧之间的位姿和地图点的位置。 5. 增量式地图更新:根据新的关键帧和它们与地图点之间的匹配,更新地图点的位置,并添加新的地图点。 6. 稠密重建:使用关键帧之间的视差信息,通过三角化和深度滤波,进行稠密地图重建。 通过这样的流程,ORB-SLAM3 能够实现单目相机下的三维重建。它在许多实际应用中被广泛使用,如机器人导航、增强现实和虚拟现实等领域。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值