IMU和视觉融合学习笔记

研究相机和IMU坐标系变换 刚开始录制的数据时没有考虑相机和IMU之间的坐标变换,但是后来发现跟踪效果不好,去查验imu数据时,发现,我采集保存的imu数据格式没有和euroc数据集中的imu数据保存格式统一,所以需要研究的是在euroc用于数据采集的设备中的相机和imu之间的坐标系变换关系,我的设备相机和imu之间的坐标系变换关系,然后按照euroc数据保存格式来保存我采集的数据。 下面是euroc数据集使用的数据采集设... 阅读详情
AI摘要

文章探讨了视觉与IMU融合在位姿估计中的重要性,特别是在高速运动和光照干扰等条件下提高定位鲁棒性。介绍了融合的两种方式——松耦合和紧耦合,以及它们在优化纯视觉SLAM中的作用,如单目尺度优化和视觉惯性联合优化。同时,文章还提及了初始化参数估计和IMU标定的过程。

利用纯视觉信息进行位姿估计,对运动物体、光照干扰、场景纹理缺失等情况,定位效果不够鲁棒。当下,视觉与IMU融合(VI-SLAM)逐渐成为常见的多传感器融合方式。视觉信息与IMU 数据进行融合,根据融合方式同样可分为基于滤波器和基于优化两类。按照是否把图像特征信息加入状态向量,又可以分为松融合与紧融合两类。

双目相机加入imu模块的的好处,是解决了高速运动中间数据处理的问题。相机运动过程中间出现模糊,两帧之间重叠区域太少,因此很难做特征点匹配。相机自己又能在慢速运动中间解决 imu的漂移问题,这两者是互补的。在之前研究视觉和IMU的基础上,开展IMU和视觉融合学习,并做记录。

在这里插入图片描述

一、IMU和视觉融合的方法

1、IMU

IMU以高频率(100HZ或200HZ)输出载体的角速度w和线加速度a,解算出高频率(100HZ或200HZ)的载体速度V、位置P以及旋转R

2、相机

零偏和噪声会比较大,以至于长时间使用后偏移的就很快,但是如果使用高精惯导,这个漂移误差会降低些,因为它是一种积分状态,从开始时间一直在持续积分,积分到不再使用为止,也就是它的V、P、R,他们分别每一个时刻都有一个误差,这个误差会产生迭代,所以长时间使用后就会漂移,

相机以30Hz或20Hz获得场景中的图像信息,利用图像中的特征信息,解算载体的旋转和平移
相机可以获得丰富的环境信息,并在长时间内的漂移误差较小,但在快速运动或旋转的环境中容易发生跟踪丢失的情况,且在面对挑战环境时定位精度会明显下降

3、融合的目标

融合的目标就是进行一个相互的补偿,主要有三个目标可以进行相互的补偿:

  • 利用视觉里程计对IMU的累积漂移进行补偿,降低惯导的漂移误差
  • 对于单目视觉传感器,可以利用IMU进行场景深度的校正,缓解单目相机尺度不确定性问题
  • IMU的输出与环境无关,不受环境变化的约束,利用IMU与视觉进行补偿可以提高视觉里程计位姿估计的鲁棒性

在这里插入图片描述

note:IMU加进视觉里面,并不是提高了视觉的定位精度,比如ORB-SLAM3的纯视觉定位精度是高于视觉+IMU的定位精度的,纯视觉是更高精度的里程计,过程相当于一个高精度融合低精度的,所以最后多模态的定位精度没有纯视觉的高,所以视觉+IMU提高的是系统的鲁棒性,并非提高精度

4、IMU如何和视觉传感器实现融合

(1)松耦合

松耦合是每一个传感器算出一个轨迹,对算出的结果进行再一次的融合

(2)紧耦合

紧耦合是在估计的时候,就是把IMU的状态,视觉传感器的状态等传感器的状态放在一起进行位姿估计,最后融合出来是只存在一个轨迹误差,其实不需要单独计算各个传感器的轨迹。
在这里插入图片描述
在这里插入图片描述
现在大多数研究使用的都是紧耦合的状态,如果系统特别大,视觉惯性SLAM所有导航模块中间的一个小分支,那么更多的用的是松耦合的情况,因为要避免单个模块失效的情况。

现在大多数系统上看到的问题都是由基于优化的,比如VINS、ORB-SLAM,都是基于优化算法的紧耦合。滤波只考虑当前的状态,相当于“得过且过”,想要得到当前的状态,只需要直达搜上一个状态,是一步一步推算出来的;优化过程是长期的一个过程,想要得到当前的状态,从过去很久之前到当前的所有的一个状态变化

以前的情况是基于优化的算法计算量比较大,基于滤波的计算量稍小一些,但是发展中,基于滤波的种方法也不仅仅值依靠前一个状态,也涉及了一个滑动窗模型,而基于优化的方法,使用稀疏边缘化的方式,实现了计算量的降低,从而减负,优化算法的实时性还是不错的

5、IMU如何优化纯视觉SLAM

(1)单目尺度优化

单目尺度可以理解为比例尺,计算视觉的轨迹需要特征的匹配,特征匹配就是每两个图像帧之间非常快速的运动,纯视觉开始运动的时候比较依赖前几帧选择出来的尺度,慢慢进行迭代。

遇到大场景的情况下,运动速度非常的剧烈,这个时候尺度是非常的不准的,这个时候引入IMU,IMU预积分的过程中是可以解算出PRV,IMU单独的模块可以解算出载体的平移旋转和速度,IMU解算出来的平移不是靠特征匹配的方式得到的,和场景是没有关系的,所以IMU在短时间内效果还是不错的,长时间就很很容易漂移(比如说100Hz,也就是0.01秒之内,对平移来说还是相对而言比较准确的)。

载体上是固连的,相机和IMU两个模块,那么我们就可以通过这两个模块,一个是已知的、计算的还算比较准确的平移,一个是通过图像匹配估计出来的平移,这样就可以计算出来这两个之间的一个尺度,
在这里插入图片描述

尺度的大小主要与相机的平移运动有关,IMU可以解算出PRV,因此可以利用IMU估计得到的平移优化纯视觉的平移尺度

(2)视觉惯性联合优化

当我们视觉的信息在模糊的情况下,整个的联合优化的过程,可以保证轨迹估计继续进行,IMU预积分会存在一些估计的误差,而视觉存在投影误差,PnP、3D到3D投影误差,2D到3D的投影误差,把这两个同时放到一个优化方程里面,我们想要缩小所有的残差,进一步的到当前更好的当前状态X,相当于实现了扩大考虑的因素,

在这里插入图片描述
如果是一个纯视觉的里程计,那就是C这一部分,
在这里插入图片描述

IMU的话就是加了一个B,用了一个边缘化的操作。
在这里插入图片描述

二、IMU和视觉的初始化及参数估计方法

1、VI初始化需要初始化那些参数?

(1)视觉

确定载体的初始位姿、初始关键帧以及初始空间点的位置

保留在算法中的始终都是关键帧,确定载体的初始位姿为母关键帧,第一个关键帧设置为i,当前的位姿为单位阵,设置为初始关键,此刻确定的是初始关键帧所对应的初始空间点的位置确定好着三个量之后,完成的视觉初始化

(2)IMU

估计IMU的偏置、异构传感器的时延、重力方向对准

IMU的偏置分为两个:陀螺仪的偏置、加速度计的偏置
现在的研究大部分没有考虑到异构传感器的很长的时延

(3)VI

传感器旋转平移外参、单目尺度因子

传感器旋转平移外参就是所谓的标定

标定算法现在还是再出很多好论文,标定所发也就是这视觉、IMU和VI这三方面的内容,也可以从这几方面入手,写一下自己的标定算法

2、IMU标定

(1)陀螺仪

ω ~ m 是测量量; ω t 是真值; b ω 是零偏; \tilde{ω}_{m} 是测量量;{ω}_{t} 是真值;{b}_{ω} 是零偏; ω~m​是测量量;ωt​是真值;bω​是零偏;

n ω 是高斯白噪声,出场预设的,不会去拿来标定 {n}_{ω}是高斯白噪声,出场预设的,不会去拿来标定 nω​是高斯白噪声,出场预设的,不会去拿来标定
在这里插入图片描述

(2)加速度计测得的线加速度

a ω ~ 是测得的值, R W B ( a w t − a w g + b a + n a ) 是真值, \tilde{a _{ω} }是测得的值,R_W^B ( a_{wt}-a_{wg}+ b_{a}+n_{a} )是真值, aω​~​是测得的值,RWB​(awt​−awg​+ba​+na​)是真值,
加速度计测的是重力方向的加速度,需要把世界坐标系转化到 B 系, 加速度计测的是重力方向的加速度,需要把世界坐标系转化到B系, 加速度计测的是重力方向的加速度,需要把世界坐标系转化到B系,
后面是 b a 是零偏; n a 是高斯白噪声 后面是{b}_{a} 是零偏; {n}_{a}是高斯白噪声 后面是ba​是零偏;na​是高斯白噪声
在这里插入图片描述

3、ORB-SLAM3的IMU初始化

先进行视觉初始化,然后IMU状态变量是和Map紧密关联的,而这在一定程度上也就导致了IMU和Map的耦合,不易分开

void LocalMapping::InitializeIMU(float priorG, float priorA, bool bFIBA)
{
    if (mbResetRequested)
        return;

    float minTime;
    int nMinKF;
    if (mbMonocular)
    {
        minTime = 2.0;
        nMinKF = 10;
    }
    else
    {
        minTime = 1.0;
        nMinKF = 10;
    }


    if(mpAtlas->KeyFramesInMap()<nMinKF)
        return;

    // Retrieve all keyframe in temporal order
    list<KeyFrame*> lpKF;
    KeyFrame* pKF = mpCurrentKeyFrame;
    while(pKF->mPrevKF)
    {
        lpKF.push_front(pKF);
        pKF = pKF->mPrevKF;
    }
    lpKF.push_front(pKF);
    vector<KeyFrame*> vpKF(lpKF.begin(),lpKF.end());

    if(vpKF.size()<nMinKF)
        return;

    mFirstTs=vpKF.front()->mTimeStamp;
    if(mpCurrentKeyFrame->mTimeStamp-mFirstTs<minTime)
        return;

    bInitializing = true;

    while(CheckNewKeyFrames())
    {
        ProcessNewKeyFrame();
        vpKF.push_back(mpCurrentKeyFrame);
        lpKF.push_back(mpCurrentKeyFrame);
    }

    const int N = vpKF.size();
    IMU::Bias b(0,0,0,0,0,0);

    // Compute and KF velocities mRwg estimation
    if (!mpCurrentKeyFrame->GetMap()->isImuInitialized())
    {
        Eigen::Matrix3f Rwg;
        Eigen::Vector3f dirG;
        dirG.setZero();
        for(vector<KeyFrame*>::iterator itKF = vpKF.begin(); itKF!=vpKF.end(); itKF++)
        {
            if (!(*itKF)->mpImuPreintegrated)
                continue;
            if (!(*itKF)->mPrevKF)
                continue;

            dirG -= (*itKF)->mPrevKF->GetImuRotation() * (*itKF)->mpImuPreintegrated->GetUpdatedDeltaVelocity();
            Eigen::Vector3f _vel = ((*itKF)->GetImuPosition() - (*itKF)->mPrevKF->GetImuPosition())/(*itKF)->mpImuPreintegrated->dT;
            (*itKF)->SetVelocity(_vel);
            (*itKF)->mPrevKF->SetVelocity(_vel);
        }

        dirG = dirG/dirG.norm();
        Eigen::Vector3f gI(0.0f, 0.0f, -1.0f);
        Eigen::Vector3f v = gI.cross(dirG);
        const float nv = v.norm();
        const float cosg = gI.dot(dirG);
        const float ang = acos(cosg);
        Eigen::Vector3f vzg = v*ang/nv;
        Rwg = Sophus::SO3f::exp(vzg).matrix();
        mRwg = Rwg.cast<double>();
        mTinit = mpCurrentKeyFrame->mTimeStamp-mFirstTs;
    }
    else
    {
        mRwg = Eigen::Matrix3d::Identity();
        mbg = mpCurrentKeyFrame->GetGyroBias().cast<double>();
        mba = mpCurrentKeyFrame->GetAccBias().cast<double>();
    }

    mScale=1.0;

    mInitTime = mpTracker->mLastFrame.mTimeStamp-vpKF.front()->mTimeStamp;

    std::chrono::steady_clock::time_point t0 = std::chrono::steady_clock::now();
    Optimizer::InertialOptimization(mpAtlas->GetCurrentMap(), mRwg, mScale, mbg, mba, mbMonocular, infoInertial, false, false, priorG, priorA);

    std::chrono::steady_clock::time_point t1 = std::chrono::steady_clock::now();

    if (mScale<1e-1)
    {
        cout << "scale too small" << endl;
        bInitializing=false;
        return;
    }

    // Before this line we are not changing the map
    {
        unique_lock<mutex> lock(mpAtlas->GetCurrentMap()->mMutexMapUpdate);
        if ((fabs(mScale - 1.f) > 0.00001) || !mbMonocular) {
            Sophus::SE3f Twg(mRwg.cast<float>().transpose(), Eigen::Vector3f::Zero());
            mpAtlas->GetCurrentMap()->ApplyScaledRotation(Twg, mScale, true);
            mpTracker->UpdateFrameIMU(mScale, vpKF[0]->GetImuBias(), mpCurrentKeyFrame);
        }

        // Check if initialization OK
        if (!mpAtlas->isImuInitialized())
            for (int i = 0; i < N; i++) {
                KeyFrame *pKF2 = vpKF[i];
                pKF2->bImu = true;
            }
    }

    mpTracker->UpdateFrameIMU(1.0,vpKF[0]->GetImuBias(),mpCurrentKeyFrame);
    if (!mpAtlas->isImuInitialized())
    {
        mpAtlas->SetImuInitialized();
        mpTracker->t0IMU = mpTracker->mCurrentFrame.mTimeStamp;
        mpCurrentKeyFrame->bImu = true;
    }

    std::chrono::steady_clock::time_point t4 = std::chrono::steady_clock::now();
    if (bFIBA)
    {
        if (priorA!=0.f)
            Optimizer::FullInertialBA(mpAtlas->GetCurrentMap(), 100, false, mpCurrentKeyFrame->mnId, NULL, true, priorG, priorA);
        else
            Optimizer::FullInertialBA(mpAtlas->GetCurrentMap(), 100, false, mpCurrentKeyFrame->mnId, NULL, false);
    }

    std::chrono::steady_clock::time_point t5 = std::chrono::steady_clock::now();

    Verbose::PrintMess("Global Bundle Adjustment finished\nUpdating map ...", Verbose::VERBOSITY_NORMAL);

    // Get Map Mutex
    unique_lock<mutex> lock(mpAtlas->GetCurrentMap()->mMutexMapUpdate);

    unsigned long GBAid = mpCurrentKeyFrame->mnId;

    // Process keyframes in the queue
    while(CheckNewKeyFrames())
    {
        ProcessNewKeyFrame();
        vpKF.push_back(mpCurrentKeyFrame);
        lpKF.push_back(mpCurrentKeyFrame);
    }

    // Correct keyframes starting at map first keyframe
    list<KeyFrame*> lpKFtoCheck(mpAtlas->GetCurrentMap()->mvpKeyFrameOrigins.begin(),mpAtlas->GetCurrentMap()->mvpKeyFrameOrigins.end());

    while(!lpKFtoCheck.empty())
    {
        KeyFrame* pKF = lpKFtoCheck.front();
        const set<KeyFrame*> sChilds = pKF->GetChilds();
        Sophus::SE3f Twc = pKF->GetPoseInverse();
        for(set<KeyFrame*>::const_iterator sit=sChilds.begin();sit!=sChilds.end();sit++)
        {
            KeyFrame* pChild = *sit;
            if(!pChild || pChild->isBad())
                continue;

            if(pChild->mnBAGlobalForKF!=GBAid)
            {
                Sophus::SE3f Tchildc = pChild->GetPose() * Twc;
                pChild->mTcwGBA = Tchildc * pKF->mTcwGBA;

                Sophus::SO3f Rcor = pChild->mTcwGBA.so3().inverse() * pChild->GetPose().so3();
                if(pChild->isVelocitySet()){
                    pChild->mVwbGBA = Rcor * pChild->GetVelocity();
                }
                else {
                    Verbose::PrintMess("Child velocity empty!! ", Verbose::VERBOSITY_NORMAL);
                }

                pChild->mBiasGBA = pChild->GetImuBias();
                pChild->mnBAGlobalForKF = GBAid;

            }
            lpKFtoCheck.push_back(pChild);
        }

        pKF->mTcwBefGBA = pKF->GetPose();
        pKF->SetPose(pKF->mTcwGBA);

        if(pKF->bImu)
        {
            pKF->mVwbBefGBA = pKF->GetVelocity();
            pKF->SetVelocity(pKF->mVwbGBA);
            pKF->SetNewBias(pKF->mBiasGBA);
        } else {
            cout << "KF " << pKF->mnId << " not set to inertial!! \n";
        }

        lpKFtoCheck.pop_front();
    }

    // Correct MapPoints
    const vector<MapPoint*> vpMPs = mpAtlas->GetCurrentMap()->GetAllMapPoints();

    for(size_t i=0; i<vpMPs.size(); i++)
    {
        MapPoint* pMP = vpMPs[i];

        if(pMP->isBad())
            continue;

        if(pMP->mnBAGlobalForKF==GBAid)
        {
            // If optimized by Global BA, just update
            pMP->SetWorldPos(pMP->mPosGBA);
        }
        else
        {
            // Update according to the correction of its reference keyframe
            KeyFrame* pRefKF = pMP->GetReferenceKeyFrame();

            if(pRefKF->mnBAGlobalForKF!=GBAid)
                continue;

            // Map to non-corrected camera
            Eigen::Vector3f Xc = pRefKF->mTcwBefGBA * pMP->GetWorldPos();

            // Backproject using corrected camera
            pMP->SetWorldPos(pRefKF->GetPoseInverse() * Xc);
        }
    }

    Verbose::PrintMess("Map updated!", Verbose::VERBOSITY_NORMAL);

    mnKFs=vpKF.size();
    mIdxInit++;

    for(list<KeyFrame*>::iterator lit = mlNewKeyFrames.begin(), lend=mlNewKeyFrames.end(); lit!=lend; lit++)
    {
        (*lit)->SetBadFlag();
        delete *lit;
    }
    mlNewKeyFrames.clear();

    mpTracker->mState=Tracking::OK;
    bInitializing = false;

    mpCurrentKeyFrame->GetMap()->IncreaseChangeIndex();

    return;
}

三、总结

双目相机加入imu模块的的好处,是解决了高速运动中间数据处理的问题。相机运动过程中间出现模糊,两帧之间重叠区域太少,因此很难做特征点匹配。相机自己又能在慢速运动中间解决 imu的漂移问题,这两者是互补的。

vio-视觉与IMU融合实现里程计(第三章)——基于优化的IMU和视觉信息融合 基于Bundle Adjustment的VIO融合 通过调整q,p,使得预测值与观测值的平方误差或带权重的平方误差最小。∑\sum∑范数是调节各个误差分量的权重的矩阵_BA问题 相机坐标系和IMU(body)坐标系之间可能有一个相对外参(R,t),图像采集的频率是30HZ,IMU采集到的频率是200HZ,使得相邻两张图像之间有多个IMU数据,需要通过预积分的方式,将两个相邻相机间多个IMU数据当作一个IMU来处理 2.最小二乘的求解 J代表梯度,Jd<0,最速下降法d的方向取J梯度的反方向.. 阅读详情

相关推荐

使用IMU进行状态估计及进阶

前言 本文章将汇总我这么长时间来,使用IMU进行姿态估计的总结,所以会很多内容,并且不能够一次性完成。我会分成很多次,一步步介绍下去。 目前的计划是,从零开始介绍姿态估算,然后进一步介绍姿态估算与滤波的关系,然后介绍各个我知道的进阶算法。 本文将采用状态估计,而不是姿态估计。因为姿态估计的范围比较狭小,是状态估计中的一部分,后续可能会介绍状态估计中的位置,速度等估计。 基本思想 状态估计=计算公式+数据 首先,我们需要知道的是,给你一些传感器的数据,我们怎么根据这些数据来计算得到姿态?这个就是计算公式的作

逐日者 6374

传感器标定 坐标系旋转

坐标系定义 IMU系 右手系(右前上) 右x正,前y正,上z正 相机系 右手系(右前上) 右x正,下y正,前z正 相机外标定参数 R IMU绕自身轴旋转到相机坐标系 zyx T 相机光心在IMU坐标系下坐标 //坐标系旋转,IMU坐标系下点变换到相机系,变换顺序先平移,后旋转 //Pcam为1*3行矩阵,T为1*3行矩阵,R为3*3矩阵 Pcam = (Pimu - T)*R; /...

littleleafboy的博客 910

视觉SLAM融合IMU问题研究笔记

前言:视觉 SLAM 存在输出频率低、旋转运动时、或运动速率加快时定位易失败等问题,而 IMU 有输出频率高、能输出6DoF测量信息等优点。因此现阶段的一个研究热点是将视觉 SLAM 与 IMU 得到的位姿估计结果进行融合,得到更加鲁棒的输出结果。通过二者的融合,可以解决视觉位姿估计输出频率低的问题,同时位姿估计精度有一定的提高,整个系统也更加鲁棒。这也是一个 VIO (Visual Inertia

J.L 2万+

vins中imu融合_基于非线性优化算法—当视觉SLAM遇到VINS会碰撞出怎样的火花?

​今天来给大家分享一个视觉SLAM中比较综合且复杂的系统—VINS。VINS旨在通过融合两个传感器测量数据获得移动机器人的位姿和特征点在空间中的位置,在现代控制理论学科中属于最优估计问题。在移动智能机器定位导航技术中,VINS 的研究思路很多来自视觉SLAM,也有很多工程实现是直接基于视觉SLAM框架进行的二次优化。所以在了解VINS的研究现状之前,有必要先了解视觉SLAM的研究现状。视觉SLAM...

weixin_39787594的博客 488

视觉-惯导多传感器融合(1)IMU & ROS

IMU,全称inertial measurement unit,即惯性测量单元,主要用于测量自身位姿,位姿包含位置和姿态。

Techblog of HaoWANG 8992

单目/双目与imu的融合(一)

目前单目slam存在初始化的尺度问题和追踪的尺度漂移问题,而双目也存在精度不高和鲁棒性不好的问题。针对这些问题,提出了融合imu的想法。 那么imu的作用是什么呢? 单目 (1)解决初始化尺度问题 (2)追踪中提供较好的初始位姿。 (3)提供重力方向 (4)提供一个时间误差项以供优化 双目 (1)追踪中提供较好的初始位姿。 (2)提供重力方向 (3)提供一个时间误差项以供优

qq_18661939的博客 3万+

SLAM基础 —— 视觉与IMU融合(VIO基础理论)

1. 基于Bundle Adjustment的VIO融合 #&8195;#&8195; 2.最小二乘问题的求解 2.1 损失函数 2.1.1 定义   找到一个 nnn 维的变量 n∗∈Rnn^* \in \mathbb{R}^nn∗∈Rn,使得损失函数 F(x)F(x)F(x) 取局部最小值: F(x)=12∑i=1m(fi(x))2F(x) = \frac{1}{2}\sum_...

不世峰兄 的博客 1万+

slam之视觉+IMU

融合IMU是为了克服纯视觉SLAM的缺点,或者是说为了互补IMU的优势和纯视觉SLAM的优势。下面看看IMU能为纯视觉SLAM解决什么问题: 1)运动过快的场景下,相机会出现图像模糊,或者两帧图像之间的重叠区域太少甚至没有,造成无法基于两帧图像中相同点和不同点测算运动;这种情况下,IMU可以补充发挥作用,继续提供可靠的(R,t)估算。 2)纯视觉难以处理动态场景,环境变化时,会误认为自己在运动...

qq_24624539的博客 6849

视觉和imu融合的算法研究

融合方式概述 同SLAM发展过程类似,视觉融合IMU问题也可以分成基于滤波和基于优化两大类。 同时按照是否把图像特征信息加入状态向量来进行分类,可以分为松耦合和紧耦合两大类。 一.基于滤波的融合算法 1.1 松耦合 松耦合将视觉传感器和 IMU 作为两个单独的模块,两个模块均可以计算得到位姿信息,然后一般通过 EKF 进行融合。 可参考的算法有ethz的Step...

a7691176的博客 2063

最新综述!基于相机、LiDAR和IMU的多传感器融合SLAM

作者:萧炎 | 来源:3DCV 在公众号「3DCV」后台,回复「原论文」可获取论文pdf

2301_81515836的博客 5315

扫地机器人视觉+IMU融合导航路径规划技术详解

本文介绍了基于视觉-惯性里程计(VIO)的扫地机器人自主导航系统。系统硬件由RGB摄像头、IMU传感器和编码器组成,软件架构采用分层设计,包括传感器数据采集层、数据融合层、SLAM定位层和路径规划层。核心技术包括:1)融合视觉特征提取(ORB算法)与IMU预积分的VIO;2)基于栅格地图的SLAM构建;3)结合A*全局规划和动态窗口法(DWA)局部避障的路径规划。文章提供了完整的Python实现代码,涵盖传感器数据融合、特征匹配和运动估计等关键功能模块。该系统实现了扫地机器人的精准定位和环境感知能力。

电脑管家 1463

使用 robot_pose_ekf 对imu和odom进行融合

  robot_pose_ekf 是 ROS Navigation stack 中的一个包,通过扩展卡尔曼滤波器对 imu、里程计 odom、视觉里程计 vo 的数据进行融合,来估计平面移动机器人的真实位置姿态,输出 odom_combined 消息。robot_pose_ekf 只适用于平面上的轮式移动机器人,因此 odom 信息中的 z,pitch 和 roll 分量可以被忽略。IMU 可以提...

Qm的博客 2万+

低成本定位方案:纯视觉+IMU的自动驾驶实践

低成本定位方案:纯视觉+IMU的自动驾驶实践 ,人工智能,计算机视觉,大模型,AI,本文围绕纯视觉+IMU的低成本自动驾驶定位方案展开,介绍了系统组成与数据流程,解析了视觉定位、IMU工作原理及传感器融合算法。还阐述了硬件选型、校准、数据同步和融合算法实现步骤,分析常见问题及解决办法,给出测试结果、开源方案与二次开发建议,探讨未来优化方向,并提供实践建议。该方案能在低成本硬件平台实现较高精度定位,适用于多种低速场景。

不踩坑,不知道坑有多深;不总结,踩过的坑白踩。 15万+

视觉惯性里程计SVO融合IMU

基于视觉惯性融合的SVO 7月初的时候尝试把IMU作为视觉导航的辅助项,加入到SVO的框架中去,目前已完成基于半直接单目视觉里程计SVO 的 视觉惯性融合 的程序框架搭建。以下围绕 ‘IMU数据以什么样的方式参与视觉系统导航?’、‘IMU可作用于视觉导航的哪些部分?’、‘融合IMU数据可以得到怎样的结果?’几个方面进行讲述。 本文仅代表个人观点,并作为工作学习记录,若有错误或者不妥之处欢迎指出!!...

weixin_43793960的博客 3246

SLAM_相机与imu的融合基础知识

参考1.单目/双目与imu的融合(一) 目前单目slam存在初始化的尺度问题和追踪的尺度漂移问题,而双目也存在精度不高和鲁棒性不好的问题。针对这些问题,人们提出了融合imu的想法。 那么imu的作用是什么呢? 单目 (1)解决初始化尺度问题 (2)追踪中提供较好的初始位姿。 (3)提供重力方向 (4)提供一个时间误差项以供优化 双目 (1)追踪中提供较好的初始位姿。 (2)提供重力方向 (3)提供一个时间误差项以供优化 参考2.视觉和imu融合的算法研究 同SLAM发展过程类...

惊鸿一博 6336

GD32 TLI+IPA实战:5分钟搞定RGB888屏幕显示,并开启2D图形加速

本文详细介绍了如何使用GD32微控制器的TLI和IPA功能快速驱动RGB888屏幕并开启2D图形加速。通过硬件配置、时序设置和显存管理,开发者可以高效实现图形界面渲染,并利用IPA加速器提升性能,适用于嵌入式GUI开发等场景。

weixin_29163797的博客 231

VIO学习笔记(三)—— 基于优化的 IMU 与视觉信息融合

学习资料是深蓝学院的《从零开始手写VIO》课程,对课程做一些记录,方便自己以后查询,如有错误还请斧正。由于习惯性心算公式,所以为了加深理解,文章公式采用手写的形式。 基于优化的 IMU 与视觉信息融合基于 Bundle Adjustment 的 VIO 融合视觉 SLAM 里的 Bundle Adjustment 问题g2o or ceres 中采用如下的求解方式VIO 信息融合问题预积分的简单理...

梁小憨憨的博客 2261

数字加网(一)— —概念及分色合成

由于大多数图像复制设备是二值设备,因此在图像复制过程中,必须先把连续调的灰度图像或彩色图像处理成二值的半色调图像,用单位面积的网点覆盖率来表现图像的颜色深浅变化。这个过程被称之为加网(Screening) 数字加网就是根据人眼视觉的空间混色特性,用人眼无法分辨的细小黑白点的单位覆盖面积来模拟灰度图像或彩色图像的层次变化,使得到

Sin_Geek成长の迹 8593

IMU与里程计融合

官方卡尔曼滤波的包开启扩展卡尔曼滤波器生成机器人姿态,支持odom(编码器)imu_data(IMU)vo(视觉里程计) 还可以支持[GPS]

Hong_J_0826的博客 6225

RM3100应用笔记驱动程序SPI接口

PNI磁传感器套件RM3100磁传感器套件由2个Sen-XY-f(pn13104)地磁传感器,1个Sen-Z-f(pn13101)地磁传感器和MagI2C(pn13156)控制芯片组成,能够实现三维空间的磁场大小测量。

AIoT技术栈的博客 4923

【C++】开源:ncurses终端TUI文本界面库

ncurses配置使用(TUI)

DevFrank的博客 3168
上一篇: IMU参数标定学习笔记
下一篇: ORB-SLAM3数据集配置与评价
极客范儿
极客范儿 新星创作者: 游戏开发技术领域 新星创作者: 游戏开发技术领域 领域专家: 人工智能技术领域 领域专家: 人工智能技术领域
博客等级 码龄12年 1万+粉丝 342原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

极客范儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值