IMU Preintegration Started.?33[0m" ); // 打印消息 ros::MultiThreadedSpinner spinner( 4 ); // 開四個(gè)線程 通過并發(fā)的方式使得速度得到提升 spinner.spin(); // 程序執(zhí)行到這個(gè)地方 則等待 topic 回調(diào)函數(shù)執(zhí)行 return 0 ;} main函數(shù)部分很簡(jiǎn)潔,功能主要完成部分都在定義的兩個(gè)類中進(jìn)行。 在main函數(shù)中進(jìn)行 節(jié)點(diǎn)初始" />
0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

IMU預(yù)積分功能數(shù)據(jù)初始化代碼解讀

麥辣雞腿堡 ? 來源:古月居 ? 作者:月照銀海似蛟龍 ? 2023-11-22 15:18 ? 次閱讀

代碼解讀

int main(int argc, char** argv){    ros::init(argc, argv, "roboat_loam");    IMUPreintegration ImuP;//IMUPreintegration 類的實(shí)例    TransformFusion TF;//TransformFusion 類的實(shí)例    ROS_INFO("?33[1;32m---- > IMU Preintegration Started.?33[0m");//打印消息    ros::MultiThreadedSpinner spinner(4);//開四個(gè)線程 通過并發(fā)的方式使得速度得到提升    spinner.spin();//程序執(zhí)行到這個(gè)地方 則等待 topic 回調(diào)函數(shù)執(zhí)行    return 0;}

main函數(shù)部分很簡(jiǎn)潔,功能主要完成部分都在定義的兩個(gè)類中進(jìn)行。

在main函數(shù)中進(jìn)行

  • 節(jié)點(diǎn)初始化
  • IMUPreintegration 類的實(shí)例
  • TransformFusion 類的實(shí)例
  • 打印消息
  • 開四個(gè)線程 通過并發(fā)的方式使得速度得到提升
  • 等待 topic 回調(diào)函數(shù)執(zhí)行
  • 之后則看 IMUPreintegration 這個(gè)類,先看構(gòu)造函數(shù)部分

在里面首先進(jìn)行了 訂閱imu信息

subImu      = nh.subscribe< sensor_msgs::Imu >  (imuTopic,2000, &IMUPreintegration::imuHandler,this, ros::TransportHints().tcpNoDelay());

imuTopic 為 imu_correct, imu原始數(shù)據(jù),這個(gè)imuTopic是從參數(shù)服務(wù)器讀取的,具體的配置在prams.yaml中

圖片

如果你的imu的topic和默認(rèn)的不一致則需要修改

然后可以看其具體的回調(diào)函數(shù) imuHandler

void imuHandler(const sensor_msgs::Imu::ConstPtr& imu_raw)    {        std::lock_guard< std::mutex > lock(mtx);        //首先把imu的狀態(tài)做一個(gè)簡(jiǎn)單的轉(zhuǎn)換        sensor_msgs::Imu thisImu = imuConverter(*imu_raw);        // 注意這里有兩個(gè)imu隊(duì)列,作用不相同,一個(gè)用來執(zhí)行預(yù)積分和位姿變換的優(yōu)化,一個(gè)用來更新最新imu狀態(tài)          imuQueOpt.push_back(thisImu);        imuQueImu.push_back(thisImu);        // 如果沒有發(fā)生過優(yōu)化 則 return        if (doneFirstOpt == false)            return;

回調(diào)函數(shù)先把imu的狀態(tài)做一個(gè)簡(jiǎn)單的轉(zhuǎn)換,轉(zhuǎn)到lidar坐標(biāo)系 下

將轉(zhuǎn)換后的imu數(shù)據(jù)存入兩個(gè)隊(duì)列中,注意這里有兩個(gè)imu隊(duì)列,作用不相同,一個(gè)用來執(zhí)行預(yù)積分和位姿變換的優(yōu)化,一個(gè)用來更新最新imu狀態(tài)

如果沒有發(fā)生過優(yōu)化 則 retur,doneFirstOpt這個(gè)標(biāo)志位,在接受到幀間里程計(jì)信息后,則至為true,imuConverter函數(shù)在utility.h文件中

sensor_msgs::Imu imuConverter(const sensor_msgs::Imu& imu_in)    {        sensor_msgs::Imu imu_out = imu_in;        // rotate acceleration  //進(jìn)行加速度坐標(biāo)旋轉(zhuǎn)        Eigen::Vector3d acc(imu_in.linear_acceleration.x, imu_in.linear_acceleration.y, imu_in.linear_acceleration.z);        acc = extRot * acc;        imu_out.linear_acceleration.x = acc.x();        imu_out.linear_acceleration.y = acc.y();        imu_out.linear_acceleration.z = acc.z();        // rotate gyroscope  // 進(jìn)行陀螺儀坐標(biāo)旋轉(zhuǎn)        Eigen::Vector3d gyr(imu_in.angular_velocity.x, imu_in.angular_velocity.y, imu_in.angular_velocity.z);        gyr = extRot * gyr;        imu_out.angular_velocity.x = gyr.x();        imu_out.angular_velocity.y = gyr.y();        imu_out.angular_velocity.z = gyr.z();        // rotate roll pitch yaw // 進(jìn)行姿態(tài)角坐標(biāo)旋轉(zhuǎn)        Eigen::Quaterniond q_from(imu_in.orientation.w, imu_in.orientation.x, imu_in.orientation.y, imu_in.orientation.z);        Eigen::Quaterniond q_final = q_from * extQRPY;        imu_out.orientation.x = q_final.x();        imu_out.orientation.y = q_final.y();        imu_out.orientation.z = q_final.z();        imu_out.orientation.w = q_final.w();        //檢測(cè)姿態(tài)數(shù)據(jù)是否正常        if (sqrt(q_final.x()*q_final.x() + q_final.y()*q_final.y() + q_final.z()*q_final.z() + q_final.w()*q_final.w()) &lt; 0.1)        {            ROS_ERROR("Invalid quaternion, please use a 9-axis IMU!");            ros::shutdown();        }        return imu_out;//返回變換后的imu數(shù)據(jù)    }};

進(jìn)行加速度坐標(biāo)旋轉(zhuǎn)、進(jìn)行陀螺儀坐標(biāo)旋轉(zhuǎn)、進(jìn)行姿態(tài)角坐標(biāo)旋轉(zhuǎn)、檢測(cè)姿態(tài)數(shù)據(jù)是否正常、返回變換后的imu數(shù)據(jù)

在進(jìn)行加速度和陀螺儀變換的時(shí)候,使用的是extRot,該參數(shù)的根源來源于prams.yaml中

圖片

進(jìn)行姿態(tài)角坐標(biāo)旋轉(zhuǎn),使用的是extQRPY,該參數(shù)的根源來源于prams.yaml中

圖片

所有終于明白為什么在配置文件中有兩個(gè)外參了!

imuHandler這個(gè)回調(diào)函數(shù),先看到這部分,后面的之后再看,需要回到上面的IMUPreintegration的構(gòu)造函數(shù),看訂閱到幀間里程計(jì)信息做了哪些事情。

subOdometry = nh.subscribe< nav_msgs::Odometry >("lio_sam/mapping/odometry_incremental", 5,    &IMUPreintegration::odometryHandler, this, ros::TransportHints().tcpNoDelay());

訂閱雷達(dá)里程計(jì)信息,lio_sam/mapping/odometry_incremental 是mapOptmization發(fā)出的,odometryHandler回調(diào)函數(shù),走起

double currentCorrectionTime = ROS_TIME(odomMsg);

通過ROS_TIME函數(shù)把消息中的時(shí)間戳取了出來

if (imuQueOpt.empty())            return;

保證imu隊(duì)列中有數(shù)據(jù)

float p_x = odomMsg- >pose.pose.position.x;        float p_y = odomMsg- >pose.pose.position.y;        float p_z = odomMsg- >pose.pose.position.z;        float r_x = odomMsg- >pose.pose.orientation.x;        float r_y = odomMsg- >pose.pose.orientation.y;        float r_z = odomMsg- >pose.pose.orientation.z;        float r_w = odomMsg- >pose.pose.orientation.w;

通過里程計(jì)話題獲得位置信息 四元數(shù) 獲得雷達(dá)里程計(jì)位姿

bool degenerate = (int)odomMsg- >pose.covariance[0] == 1 ? true : false;

該位姿是否出現(xiàn)退化 pose.covariance[0] 為1 則 雷達(dá)里程計(jì)有退化風(fēng)險(xiǎn),該幀位姿精度有一定程序下降

gtsam::Pose3 lidarPose = gtsam::Pose3(gtsam::Rot3::Quaternion(r_w, r_x, r_y, r_z), gtsam::Point3(p_x, p_y, p_z));

把位姿轉(zhuǎn)成 gtsam的格式,進(jìn)入系統(tǒng)的初始化,下面部分僅執(zhí)行一次

resetOptimization();

在函數(shù)內(nèi)部 初始化gtsam的一些量

while (!imuQueOpt.empty())            {                if (ROS_TIME(&imuQueOpt.front()) < currentCorrectionTime - delta_t)                {                    lastImuT_opt = ROS_TIME(&imuQueOpt.front());                    imuQueOpt.pop_front();                }                else                    break;            }

將這個(gè)雷達(dá)里程計(jì)之前的imu信息全部扔掉,整個(gè)LIO-SAM中作者對(duì)時(shí)間同步這塊的思想都是這樣的,保證imu與odometry消息時(shí)間同步 因?yàn)閕mu是高頻數(shù)據(jù)所以這是必要的

prevPose_ = lidarPose.compose(lidar2Imu);

將lidar的位姿移到imu坐標(biāo)系下,lidar2Imu 是lidar到imu的外參,compose是gtsam的一個(gè)功能函數(shù),VIO和LIO的框架都在在IMU坐標(biāo)系下進(jìn)行的

gtsam::PriorFactor< gtsam::Pose3 > priorPose(X(0), prevPose_, priorPoseNoise);            graphFactors.add(priorPose);

設(shè)置其初始位姿和置信度,約束加入到因子中

  • gtsam::PriorFactor 模塊涉及到的變量結(jié)點(diǎn)
  • gtsam::Pose3 表示六自由度位姿
  • gtsam::Vector3 表示三自由度速度
  • gtsam::imuBias::ConstantBias 表示IMU零偏

以上也是預(yù)積分模型中涉及到的三種狀態(tài)變量

gtsam::PriorFactor 為先驗(yàn)因子,表示對(duì)某個(gè)狀態(tài)量T的一個(gè)先驗(yàn)估計(jì),約束某個(gè)狀態(tài)變量的狀態(tài)不會(huì)離該先驗(yàn)值過遠(yuǎn)。

其中的X(0)的,初始定義如下。事先的符號(hào)

圖片

priorPoseNoise 是先驗(yàn)位姿的噪聲,該值為

priorPoseNoise  = gtsam::noiseModel::Diagonal::Sigmas((gtsam::Vector(6) < < 1e-2, 1e-2, 1e-2, 1e-2, 1e-2, 1e-2).finished()); // rad,rad,rad,m, m, m

初始 位姿 置信度 設(shè)置 比較高 后面構(gòu)成協(xié)方差矩陣 值越小 表示 置信度越高

prevVel_ = gtsam::Vector3(0, 0, 0);            gtsam::PriorFactor< gtsam::Vector3 > priorVel(V(0), prevVel_, priorVelNoise);               graphFactors.add(priorVel);

和上面位姿基本一樣初始化速度,這里直接賦 0 了,將速度約束加到因子圖中,其中priorVelNoise 速度的噪聲是

priorVelNoise   = gtsam::noiseModel::Isotropic::Sigma(3, 1e4); // m/s

初始化速度 置信度 設(shè)置 差些 因?yàn)樗俣纫婚_始設(shè)置的是0,不知道是多少

prevBias_ = gtsam::imuBias::ConstantBias();            gtsam::PriorFactor< gtsam::imuBias::ConstantBias > priorBias(B(0), prevBias_, priorBiasNoise);                  graphFactors.add(priorBias);

初始化IMU 零偏 ,將零偏約束加到因子圖中,gtsam::imuBias::ConstantBias()是gtsam做好的一個(gè)imu零偏,其中都是0,所以對(duì)應(yīng)bias的噪聲置信度也要設(shè)置的高些

priorBiasNoise  = gtsam::noiseModel::Isotropic::Sigma(6, 1e-3); // 1e-2 ~ 1e-3 seems to be good

以上把約束加入完畢,下面就開始添加狀態(tài)量

graphValues.insert(X(0), prevPose_);            graphValues.insert(V(0), prevVel_);            graphValues.insert(B(0), prevBias_);

給各個(gè)狀態(tài)量賦成初始值

optimizer.update(graphFactors, graphValues);

約束和狀態(tài)量更新 進(jìn)isam優(yōu)化器

graphFactors.resize(0);            graphValues.clear();

進(jìn)優(yōu)化器之后 保存約束和狀態(tài)量的變量就清零

imuIntegratorImu_-&gt;resetIntegrationAndSetBias(prevBias_);            imuIntegratorOpt_-&gt;resetIntegrationAndSetBias(prevBias_);

預(yù)積分的接口,使用初始零偏進(jìn)行初始化 之前imu有兩個(gè)隊(duì)列,每個(gè)隊(duì)列對(duì)應(yīng)預(yù)積分處理器

key = 1;
            systemInitialized = true;//系統(tǒng)初始化完成
            return;

系統(tǒng)初始化完成

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62604
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4788

    瀏覽量

    68601
  • 激光雷達(dá)
    +關(guān)注

    關(guān)注

    968

    文章

    3972

    瀏覽量

    189917
  • IMU
    IMU
    +關(guān)注

    關(guān)注

    6

    文章

    312

    瀏覽量

    45746
  • 3D激光
    +關(guān)注

    關(guān)注

    0

    文章

    30

    瀏覽量

    7473
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    LINUX系統(tǒng)引導(dǎo)和初始化-LINUX內(nèi)核解讀

    Linux 的系統(tǒng)引導(dǎo)和初始化 ----------Linux2.4.22內(nèi)核解讀之一 一、 系統(tǒng)引導(dǎo)和初始化概述 相關(guān)代碼(引導(dǎo)扇區(qū)的程序及其輔助程序,以 x86體系為例): \li
    發(fā)表于 11-03 22:31 ?53次下載

    RM68171配BOE3.97玻璃的初始化代碼

    RM68171配BOE3.97玻璃的初始化代碼,測(cè)試OK,確定沒問題!
    發(fā)表于 06-17 17:07 ?2次下載

    使用QCS生成DPAA初始化代碼

    本文檔指導(dǎo)用戶如何使用從qc DPAA生成的代碼,用戶應(yīng)用程序的上下文中利用USDPAA司機(jī)。該文檔解釋了反射器應(yīng)用程序,并演示了幫助用戶快速插入QCS的步驟,這些插件在應(yīng)用程序中生成DPAA初始化代碼。
    發(fā)表于 09-07 17:21 ?0次下載
    使用QCS生成DPAA<b class='flag-5'>初始化</b><b class='flag-5'>代碼</b>

    如何排除代碼編寫器Studio 2和2.10的初始化問題

    此應(yīng)用程序報(bào)告使讀者熟悉XDSPo探測(cè)實(shí)用程序。這個(gè)實(shí)用程序可能是用于排除代碼編寫器Studio 2和2.10的初始化問題通常表現(xiàn)為指示目標(biāo)DSP的錯(cuò)誤消息。無法初始化。此問題可能是由于配置或硬件不正確造成的。
    發(fā)表于 04-25 09:59 ?4次下載
    如何排除<b class='flag-5'>代碼</b>編寫器Studio 2和2.10的<b class='flag-5'>初始化</b>問題

    8259a初始化的步驟及代碼介紹

    本文首先介紹了8259a工作初始化的步驟及程序,其次介紹了通過OCW對(duì)8259A進(jìn)行操作方法,最后介紹了8259A初始化編程。
    的頭像 發(fā)表于 05-23 14:24 ?3w次閱讀
    8259a<b class='flag-5'>初始化</b>的步驟及<b class='flag-5'>代碼</b>介紹

    lcd NT35512的手冊(cè)及初始化代碼免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是lcd NT35512的手冊(cè)及初始化代碼免費(fèi)下載
    發(fā)表于 06-29 08:00 ?95次下載
    lcd NT35512的手冊(cè)及<b class='flag-5'>初始化</b><b class='flag-5'>代碼</b>免費(fèi)下載

    IIC總線初始化基本驅(qū)動(dòng)參考程序應(yīng)用代碼免費(fèi)下載

    本文檔的主要內(nèi)容詳細(xì)介紹的是IIC總線初始化基本驅(qū)動(dòng)參考程序應(yīng)用代碼免費(fèi)下載。程序功能:為IIC總線的基本驅(qū)動(dòng)程序(此程序沒有寫主函數(shù),只是一些IIC必備的子函數(shù))
    發(fā)表于 01-16 08:00 ?5次下載
    IIC總線<b class='flag-5'>初始化</b>基本驅(qū)動(dòng)參考程序應(yīng)用<b class='flag-5'>代碼</b>免費(fèi)下載

    STM32執(zhí)行代碼初始化卡住,或者上電卡住,或者復(fù)位卡住,導(dǎo)致代碼不執(zhí)行

    STM32的板子上電或者復(fù)位,接有顯示屏或者LED指示燈的都會(huì)卡住解決:1、檢查自己的代碼是否有中斷,有中斷的話,其初始化放在其他硬件初始化之后 即:中斷的初始化放在進(jìn)入while
    發(fā)表于 12-09 09:21 ?22次下載
    STM32執(zhí)行<b class='flag-5'>代碼</b><b class='flag-5'>初始化</b>卡住,或者上電卡住,或者復(fù)位卡住,導(dǎo)致<b class='flag-5'>代碼</b>不執(zhí)行

    BF533 AD73360初始化程序代碼

    BF533 AD73360初始化程序代碼源碼分享
    發(fā)表于 10-08 14:59 ?0次下載

    初始化的if和switch語(yǔ)句詳解

    在上面的代碼中,初始化語(yǔ)句是int s = check()。s的生命周期是整個(gè)if語(yǔ)句,這里也包含else語(yǔ)句。
    的頭像 發(fā)表于 10-14 10:50 ?1386次閱讀

    DB2163_STM32配置和初始化C代碼生成

    DB2163_STM32配置和初始化C代碼生成
    發(fā)表于 11-23 20:29 ?0次下載
    DB2163_STM32配置和<b class='flag-5'>初始化</b>C<b class='flag-5'>代碼</b>生成

    使用STM32CubeMX生成初始化代碼

    我使用STM32CubeMX生成初始化代碼,使用LL庫(kù),這里只介紹跟i2c相關(guān)的部分,其他必要的初始化需要自己完成。芯片使用stm32f042。本文的代碼不能到手即用,只提供思路。
    的頭像 發(fā)表于 03-22 15:26 ?2995次閱讀

    CPU CACHE策略的初始化

    build_mem_type_table()函數(shù)的功能是獲取當(dāng)前CPU的CACHE類型,據(jù)此初始化mem_type。
    的頭像 發(fā)表于 06-05 15:03 ?1432次閱讀
    CPU CACHE策略的<b class='flag-5'>初始化</b>

    rt-thread線程棧初始化參數(shù)分析

    RT-Thread 在線程初始化代碼內(nèi)有一段初始化線程堆棧的代碼
    的頭像 發(fā)表于 08-14 16:50 ?1747次閱讀
    rt-thread線程棧<b class='flag-5'>初始化</b>參數(shù)分析

    MCU單片機(jī)GPIO初始化該按什么順序配置?為什么初始化時(shí)有電平跳變?

    GPIO初始化時(shí)有時(shí)鐘配置、模式配置、輸出配置、復(fù)用配置,那么在編寫初始化代碼時(shí),到底該按什么順序執(zhí)行呢?如果順序不當(dāng)那初始化過程可能會(huì)出現(xiàn)短暫的電平跳變。
    的頭像 發(fā)表于 02-22 11:07 ?1540次閱讀
    MCU單片機(jī)GPIO<b class='flag-5'>初始化</b>該按什么順序配置?為什么<b class='flag-5'>初始化</b>時(shí)有電平跳變?