IMU Preintegration Started.?33[0m" ); // 打印消息 ros::MultiThreadedSpinner spinner( 4 ); // 開四個線程 通過并發的方式使得速度得到提升 spinner.spin(); // 程序執行到這個地方 則等待 topic 回調函數執行 return 0 ;} main函數部分很簡潔,功能主要完成部分都在定義的兩個類中進行。 在main函數中進行 節點初始" />
<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

IMU預積分功能數據初始化代碼解讀

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

代碼解讀

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

main函數部分很簡潔,功能主要完成部分都在定義的兩個類中進行。

在main函數中進行

  • 節點初始化
  • IMUPreintegration 類的實例
  • TransformFusion 類的實例
  • 打印消息
  • 開四個線程 通過并發的方式使得速度得到提升
  • 等待 topic 回調函數執行
  • 之后則看 IMUPreintegration 這個類,先看構造函數部分

在里面首先進行了 訂閱imu信息

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

imuTopic 為 imu_correct, imu原始數據,這個imuTopic是從參數服務器讀取的,具體的配置在prams.yaml中

圖片

如果你的imu的topic和默認的不一致則需要修改

然后可以看其具體的回調函數 imuHandler

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

回調函數先把imu的狀態做一個簡單的轉換,轉到lidar坐標系 下

將轉換后的imu數據存入兩個隊列中,注意這里有兩個imu隊列,作用不相同,一個用來執行預積分和位姿變換的優化,一個用來更新最新imu狀態

如果沒有發生過優化 則 retur,doneFirstOpt這個標志位,在接受到幀間里程計信息后,則至為true,imuConverter函數在utility.h文件中

sensor_msgs::Imu imuConverter(const sensor_msgs::Imu& imu_in)    {        sensor_msgs::Imu imu_out = imu_in;        // rotate acceleration  //進行加速度坐標旋轉        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  // 進行陀螺儀坐標旋轉        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 // 進行姿態角坐標旋轉        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();        //檢測姿態數據是否正常        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數據    }};

進行加速度坐標旋轉、進行陀螺儀坐標旋轉、進行姿態角坐標旋轉、檢測姿態數據是否正常、返回變換后的imu數據

在進行加速度和陀螺儀變換的時候,使用的是extRot,該參數的根源來源于prams.yaml中

圖片

進行姿態角坐標旋轉,使用的是extQRPY,該參數的根源來源于prams.yaml中

圖片

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

imuHandler這個回調函數,先看到這部分,后面的之后再看,需要回到上面的IMUPreintegration的構造函數,看訂閱到幀間里程計信息做了哪些事情。

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

訂閱雷達里程計信息,lio_sam/mapping/odometry_incremental 是mapOptmization發出的,odometryHandler回調函數,走起

double currentCorrectionTime = ROS_TIME(odomMsg);

通過ROS_TIME函數把消息中的時間戳取了出來

if (imuQueOpt.empty())            return;

保證imu隊列中有數據

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;

通過里程計話題獲得位置信息 四元數 獲得雷達里程計位姿

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

該位姿是否出現退化 pose.covariance[0] 為1 則 雷達里程計有退化風險,該幀位姿精度有一定程序下降

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

把位姿轉成 gtsam的格式,進入系統的初始化,下面部分僅執行一次

resetOptimization();

在函數內部 初始化gtsam的一些量

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

將這個雷達里程計之前的imu信息全部扔掉,整個LIO-SAM中作者對時間同步這塊的思想都是這樣的,保證imu與odometry消息時間同步 因為imu是高頻數據所以這是必要的

prevPose_ = lidarPose.compose(lidar2Imu);

將lidar的位姿移到imu坐標系下,lidar2Imu 是lidar到imu的外參,compose是gtsam的一個功能函數,VIO和LIO的框架都在在IMU坐標系下進行的

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

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

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

以上也是預積分模型中涉及到的三種狀態變量

gtsam::PriorFactor 為先驗因子,表示對某個狀態量T的一個先驗估計,約束某個狀態變量的狀態不會離該先驗值過遠。

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

圖片

priorPoseNoise 是先驗位姿的噪聲,該值為

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

初始 位姿 置信度 設置 比較高 后面構成協方差矩陣 值越小 表示 置信度越高

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

初始化速度 置信度 設置 差些 因為速度一開始設置的是0,不知道是多少

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

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

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

以上把約束加入完畢,下面就開始添加狀態量

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

給各個狀態量賦成初始值

optimizer.update(graphFactors, graphValues);

約束和狀態量更新 進isam優化器

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

進優化器之后 保存約束和狀態量的變量就清零

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

預積分的接口,使用初始零偏進行初始化 之前imu有兩個隊列,每個隊列對應預積分處理器

key = 1;
            systemInitialized = true;//系統初始化完成
            return;

系統初始化完成

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 函數
    +關注

    關注

    3

    文章

    4117

    瀏覽量

    61463
  • 代碼
    +關注

    關注

    30

    文章

    4575

    瀏覽量

    67126
  • 激光雷達
    +關注

    關注

    963

    文章

    3731

    瀏覽量

    187337
  • IMU
    IMU
    +關注

    關注

    5

    文章

    275

    瀏覽量

    45363
  • 3D激光
    +關注

    關注

    0

    文章

    27

    瀏覽量

    7426
收藏 人收藏

    評論

    相關推薦

    字符型、指針型等變量該如何初始化

    在敲代碼的時候,我們會給變量一個初始值,以防止因為編譯器的原因造成變量初始值的不確定性。對于數值類型的變量往往初始化為0,但對于其他類型的變量,如字符型、指針型等變量等該如何
    發表于 09-23 11:50 ?2021次閱讀

    SPI模塊的初始化代碼

    第一次寫博客有點緊張哈哈哈所以話不多說先上代碼壓壓驚//以下是SPI模塊的初始化代碼,配置成主機模式//SPI口初始化//這里針是對SPI2的初始化
    發表于 08-04 07:17

    端口初始化初始化中斷

    目錄PA9(TX),PA10(RX)1、端口初始化2、初始化外設3、初始化中斷4、使能中斷5、使能外設5、發送數據PA9(TX),PA10(RX)1、端口
    發表于 08-16 06:54

    初始化封裝

    初始化封裝您可以在 Mask Editor 的 Initialization 窗格中添加 MATLAB? 代碼初始化封裝模塊。Simulink? 將執行這些初始化命令以便在關鍵時刻(
    發表于 08-27 07:17

    【原創分享】變量的初始化技巧

    由于在嵌入式系統中必須考慮程序規模的問題,因此,對程序中的變量的初始化也需要進行慎重的考慮。在C語言中,基本數據結構(字符型、整型)的初始化相對簡單;數組、結構體屬于C語言中的構造類型,其變量在
    發表于 09-08 15:28

    TF卡的初始化代碼是怎樣的?

    TF卡的初始化代碼是怎樣的?
    發表于 12-02 06:35

    USART2初始化代碼分享

    USART2初始化代碼分享
    發表于 12-10 07:48

    LED初始化部分的代碼編寫

    本文是我在復習階段的總結,算是寫復習筆記了,也很樂意把分享出來。小白也是可以看的,可以更快入門。#LED初始化初始化部分是比較簡單的,不是重點,直接貼代碼:voidLED_Init(void
    發表于 01-05 08:27

    STM32 HAL庫初始化函數的功能

    1、HAL_Init();初始化這個是主函數中首要處理的函數:主要用來初始化HAL庫,即用來初始化所有的外圍設備,Flash接口和系統定時器,系統中斷組,初始化低級別硬件。return
    發表于 01-13 06:35

    STM32執行代碼初始化卡住怎么解決

    STM32的板子上電或者復位,接有顯示屏或者LED指示燈的都會卡住解決:1、檢查自己的代碼是否有中斷,有中斷的話,其初始化放在其他硬件初始化之后即:中斷的初始化放在進入while()循
    發表于 02-14 06:16

    LINUX系統引導和初始化-LINUX內核解讀

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

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

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

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

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

    使用STM32CubeMX生成初始化代碼

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

    rt-thread線程棧初始化參數分析

    RT-Thread 在線程初始化代碼內有一段初始化線程堆棧的代碼
    的頭像 發表于 08-14 16:50 ?1057次閱讀
    rt-thread線程棧<b class='flag-5'>初始化</b>參數分析
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>