ORB-SLAM3:初始化

ORB-SLAM3的初始化分为纯视觉、纯IMU和VI联合优化三阶段。首先,通过单目ORB-SLAM进行视觉初始化,使用BA进行MAP估计并计算IMU预积分。接着,利用IMU数据对视觉轨迹进行校准,获取尺度、速度等信息。然后,通过图像和IMU数据的联合处理,实现更精确的初始化。整个过程中涉及的关键步骤包括特征匹配、变换关系计算和全局BA。
摘要由CSDN通过智能技术生成

在这里插入图片描述
参考:https://blog.csdn.net/weixin_46363611/article/details/113445503?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.nonecase
ORB-SLAM3的初始化分为三步,纯视觉的初始化、纯IMU的初始化、VI联合优化。
(1)纯视觉的初始化:使用单目ORB-SLAM的视觉初始化并在一段短时间内(比如两秒)使用BA进行纯视觉MAP估计。同时,计算相邻关键帧之间的IMU预积分和协方差。
(2)纯IMU的初始化:把IMU轨迹和视觉轨迹对齐,并得到尺度、关键帧的速度、重力向量和IMU偏差。我觉得这里和vins-mono的初始化差不太多,vins-mono的初始化是松耦合,先用三角化和PnP得到滑动窗口中pose和landmark,然后在和IMU轨迹对齐。orb-slam3是在得到视觉轨迹的时候也用了优化,然后还是和IMU轨迹对齐,这么看来应该也是个松耦合。但是orb3又加了一步

ORB3是这样的流程:
从main函数开始,先把图像路径和imu数据读进来。然后初始化SLAM对象,初始化所有线程并准备处理frame。假设IMU测量先开始,先找到第一张图片时间戳对应的IMU数据。设图片的数量为n,则下面的内容循环n次,也就是每读取一张图片,就读取与前一张图片中间的所有IMU数据(如果是第一张图片,ni==0,不会读取)。然后执行SLAM.TrackMonocular()函数。
TrackMonocular函数中,主要包括GrabImuData和GrabImageMonocular。
GrabImuData就只是把之前读取的IMU数据放到mlQueueImuData队列中,以供预积分等处理。
GrabImageMonocular函数首先处理图片颜色格式,创建当前帧mCurrentFrame。并执行
Track函数。接下来所有内容都在这个函数中完成。
首先把上一个关键帧的imuBias设置到当前帧,然后预积分。如果没有完成初始化,执行初始化函数MonocularInitialization(单目imu情况,双目imu和rgbd执行StereoInitialization)。
由于现阶段我只关心初始化的问题,后面暂且不看,直接进入初始化函数。
以单目imu为例,MonocularInitialization中:
首先找一个关键点很多(大于100)的图片作为参考帧,用这个帧作为初始帧,并用这个帧创建初始化器Initializer。如果使用惯导,则新建一个预积分对象Preintegrated,用bias和calib进行初始化,主要包括预积分变量的创建和初始化。

Preintegrated::Preintegrated(const Bias &b_, const Calib &calib)
{
    Nga = calib.Cov.clone();
    NgaWalk = calib.CovWalk.clone();
    Initialize(b_);
}

如果已经有了初始化器,用当前帧和初始帧进行orb特征点的匹配,执行ReconstructWithTwoViews函数得到两帧之间的变换关系Tcw,并设置在当前帧中。这个过程目前也不是重点,继续看后面的CreateInitialMapMonocular函数。
CreateInitialMapMonocular函数中,先以初始帧和当前帧作为两个关键帧。注意,关键帧Keyframe类中有很多数据,其中包括了预积分对象的指针Preintegrated和imu的Calib。把初始帧的预积分对象指针指向null,因为初始帧没有前一帧,没有预积分数据。然后分别为两个关键帧计算BoW,并把这两个关键帧加到地图中去。然后做了一步全局BA,后面有需要再展开。

// Bundle Adjustment
    Verbose::PrintMess("New Map created with " + to_string(mpAtlas->MapPointsInMap()) + " points", Verbose::VERBOSITY_QUIET);
    Optimizer::GlobalBundleAdjustemnt(mpAtlas->GetCurrentMap(),20);

参考:
https://www.cnblogs.com/CV-life/p/10286037.html
http://zhaoxuhui.top/blog/2018/04/10/g2o&bundle_adjustment.html#2g2o%E5%BA%93%E7%AE%80%E4%BB%8B%E4%B8%8E%E7%BC%96%E8%AF%91%E5%AE%89%E8%A3%85
https://blog.csdn.net/m0_37874102/article/details/114291951?spm=1001.2014.3001.5501
g2o的编程是从底层到顶层的搭建:
在这里插入图片描述
1、定义顶点
一般来说,我们使用图优化的情况各式各样,需要自行定义顶点,继承g2o::BaseVertex类,例如orbslam3中的位姿顶点:

// Optimizable parameters are IMU pose
class VertexPose : public g2o::BaseVertex<6,ImuCamPose>
{
public:
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW
    VertexPose(){}
    VertexPose(KeyFrame* pKF){
        setEstimate(ImuCamPose(pKF));
    }
    VertexPose(Frame* pF){
        setEstimate(ImuCamPose(pF));
    }


    virtual bool read(std::istream& is);
    virtual bool write(std::ostream& os) const;

    virtual void setToOriginImpl() {
        }

    virtual void oplusImpl(const double* update_){
        _estimate.Update(update_);
        updateCache();
    }
};

除了定义了该顶点的构造函数之外,还需要重写基类的虚函数,read,write,setToOriginImpl,和oplusImpl。
read和write用于读写操作,一般不做操作。
setToOriginImpl用于初始化顶点,给顶点一个初值。
oplusImpl最关键,用于更新顶点,比如上面的例子中,使用Update进行更新,这个函数在优化变量ImuCamPose中有定义:

void ImuCamPose::Update(const double *pu)
{
    Eigen::Vector3d ur, ut;
    ur << pu[0], pu[1], pu[2];
    ut << pu[3], pu[4], pu[5];

    // Update body pose
    twb += Rwb*ut;
    Rwb = Rwb*ExpSO3(ur);

    // Normalize rotation after 5 updates
    its++;
    if(its>=3)
    {
        NormalizeRotation(Rwb);
        its=0;
    }

    // Update camera poses
    const Eigen::Matrix3d Rbw = Rwb.transpose();
    const Eigen::Vector3d tbw = -Rbw*twb;

    for(int i=0; i<pCamera.size(); i++)
    {
        Rcw[i] = Rcb[i]*Rbw;
        tcw[i] = Rcb[i]*tbw+tcb[i];
    }

}
### ORB-SLAM3 中的 IMU 初始化方法 ORB-SLAM3 的 IMU 初始化旨在获取重力方向和 IMU 零偏的初始估计值。这一过程对于后续处理至关重要,因为只有当重力方向被正确识别后,才能有效去除加速度计测量中的重力影响,从而确保预积分数据的准确性[^1]。 #### 初始阶段的数据收集与分析 在初始化过程中,系统会先积累一段时间内的惯性传感器读数,并通过这些数据来估算重力矢量的方向。具体来说: - **陀螺仪偏差校准**:利用静止状态下角速度接近于零的特点,对陀螺仪的零偏进行初步估计。 - **加速度计观测建模**:假设设备处于静态或近似静态状态时,加速度计的主要输出应指向地球中心即重力场方向;因此可以通过统计平均或其他滤波手段提取出该主导分量作为重力向量g_dir的初估值[^2]。 ```cpp // Pseudo-code for collecting and processing sensor data during initialization phase. void collectSensorData() { while (notEnoughSamples()) { imuMeasurements.push_back(readIMUSensor()); } Vector3d avgAccel = computeAverageAcceleration(imuMeasurements); gravityDirectionEstimate = normalize(avgAccel); // Estimate of g_dir // Further refinement may involve more sophisticated filtering techniques... } ``` #### 联合优化框架下的参数调整 一旦获得了粗略的重力方向估计,接下来就是将其融入到更广泛的视觉-惯导融合体系之中。在此期间,不仅会对帧的姿态、速度等变量加以修正,还会同步更新IMU内部存在的各种误差源(如尺度因子、安装矩阵失配等),并通过一系列迭代操作不断逼近最优解集[^3]。 特别值得注意的是,在整个初始化流程结束之后的一段时间里(通常是前几秒内),算法将继续执行局部精细化调优工作,以进一步提升整体定位精度至可接受水平之内。 #### 实际应用建议 为了顺利完成上述各环节的工作,开发者应当注意如下几点事项: - 确认所使用的硬件平台能够提供稳定可靠的IMU信号输入; - 尽可能保持载体相对固定不动直至初始化完毕; - 如果条件允许的话,可以考虑引入外部辅助信息源(比如GNSS接收机)来进行交叉验证。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值