13-[LVI-SAM]visual

13-[LVI-SAM]visual,第1张

13-[LVI-SAM]visual

2021SC@SDUSC

visual_odometry_初探

​ 上一章已经把visual_feature的大部分代码和功能,虽然没有完全把那一个节点讲明白,但是已经大致清楚整一份代码的思路。至于更多的细枝末节的算法思想,这些涉及比较多的数学知识,脱离了写博客的目的,就不再赘述了,留下了链接,等有空可以自己去探索。

​ 而我们团队其他人还没有完成自己节点部分的分析,于是,经过商量,我来帮另外一个@同学分析他的部分。由于他还在分析initial文件夹下的内容,于是,我从这个节点本身出发,开始分析,避免分析冲突。

文章目录
  • visual_odometry_初探
    • 1. 概览
    • 2. 输入输出
      • a. 输入
      • b. 输出
    • 3. Main函数
      • a. readParameters()
      • b. estimator.setParameter()
      • c. registerPub()
      • d. odomRegister类
    • 附录:引用

1. 概览

​ 这个节点其实就是通过视觉特征点输出里程计了,与激光有互动的地方主要体现在两点,一是带有激光雷达深度图获取到深度的视觉特征点,二是把之前激光IMU参与的IMUPreIntegration模块输出的odometry结果作为初始化的值。

​ visual_odometry模块其实就是下面的lvi_sam_visual_odometry节点。但在源代码中,这个模块的名字其实是visual_estimator。在图中,可以看到,这个节点有着众多的输入和输出。可以说,是LVI-SAM的视觉核心模块。其中,有和前面的lidar模块中的lvi_sam_mapOptmization和lvi_sam_PreIntegration,以及视觉的其他模块相关联。

​ 这里可以再穿插一个小知识,是我在看这个节点收获到的和ROS相关的知识。有人可能很好奇,为什么节点源代码的文件名和这个图中实际运行时的节点名不一致?在前面blog的源码分析体会中,我写过是因为launch中定义了节点的名字,但是如果看过launch文件可以知道(见下文),它并没有明确指定节点存放在哪个位置。只有在type参数的时候写明了节点。其实,这是在编译的时候就已经指明了节点的type,也可以说是节点的唯一名字。编译的时候,CMakeLists已经列好了编译的源代码文件位置和节点名称(见下文)。

launch文件:



    
    
    
    
    
    
    
    
    
    
    

    
    
    
    

    
    


CMakeLists 文件(节选):

file(GLOB visual_odometry_files
    "src/visual_odometry/visual_estimator
    ESTIMATE_EXTRINSIC = fsSettings["estimate_extrinsic"];
    if (ESTIMATE_EXTRINSIC == 2) // 不提供
    {
        ROS_INFO("have no prior about extrinsic param, calibrate extrinsic param");
        RIC.push_back(Eigen::Matrix3d::Identity());
        TIC.push_back(Eigen::Vector3d::Zero());
        EX_CALIB_RESULT_PATH = pkg_path + "/config/extrinsic_parameter.csv";

    }
    else 
    {
        if ( ESTIMATE_EXTRINSIC == 1) // 不准确
        {
            ROS_INFO(" Optimize extrinsic param around initial guess!");
            EX_CALIB_RESULT_PATH = pkg_path + "/config/extrinsic_parameter.csv";
        }
        if (ESTIMATE_EXTRINSIC == 0) // 准确
            ROS_INFO(" Fix extrinsic param.");

        
        cv::Mat cv_R, cv_T;
        fsSettings["extrinsicRotation"] >> cv_R;
        fsSettings["extrinsicTranslation"] >> cv_T;
        Eigen::Matrix3d eigen_R;
        Eigen::Vector3d eigen_T;
        cv::cv2eigen(cv_R, eigen_R);
        cv::cv2eigen(cv_T, eigen_T);
        Eigen::Quaterniond Q(eigen_R);
        eigen_R = Q.normalized(); // 归一化
        RIC.push_back(eigen_R);
        TIC.push_back(eigen_T);
        ROS_INFO_STREAM("Extrinsic_R : " << std::endl << RIC[0]);
        ROS_INFO_STREAM("Extrinsic_T : " << std::endl << TIC[0].transpose());
        
    } 

    INIT_DEPTH = 5.0;
    BIAS_ACC_THRESHOLD = 0.1;
    BIAS_GYR_THRESHOLD = 0.1;

    
    TD = fsSettings["td"];
    ESTIMATE_TD = fsSettings["estimate_td"];
    if (ESTIMATE_TD)
        ROS_INFO_STREAM("Unsynchronized sensors, online estimate time offset, initial td: " << TD);
    else
        ROS_INFO_STREAM("Synchronized sensors, fix time offset: " << TD);

    ROLLING_SHUTTER = fsSettings["rolling_shutter"];
    if (ROLLING_SHUTTER)
    {
        TR = fsSettings["rolling_shutter_tr"];
        ROS_INFO_STREAM("rolling shutter camera, read out time per line: " << TR);
    }
    else
    {
        TR = 0;
    }
    
    fsSettings.release();
    usleep(100);
}
b. estimator.setParameter()

​ 这里其中一个是设置计时器的参数。另外是,设置视觉测量残差的协方差矩阵

void Estimator::setParameter()
{
    for (int i = 0; i < NUM_OF_CAM; i++)
    {
        tic[i] = TIC[i];
        ric[i] = RIC[i];
    }
    
    f_manager.setRic(ric);
    ProjectionFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
    ProjectionTdFactor::sqrt_info = FOCAL_LENGTH / 1.5 * Matrix2d::Identity();
    td = TD;
}
c. registerPub()
void registerPub(ros::NodeHandle &n)
{
    
    pub_latest_odometry     = n.advertise               (PROJECT_NAME + "/vins/odometry/imu_propagate", 1000);
    pub_latest_odometry_ros = n.advertise               (PROJECT_NAME + "/vins/odometry/imu_propagate_ros", 1000);
    pub_path                = n.advertise                   (PROJECT_NAME + "/vins/odometry/path", 1000);
    pub_odometry            = n.advertise               (PROJECT_NAME + "/vins/odometry/odometry", 1000);
    pub_point_cloud         = n.advertise          (PROJECT_NAME + "/vins/odometry/point_cloud", 1000);
    pub_margin_cloud        = n.advertise          (PROJECT_NAME + "/vins/odometry/history_cloud", 1000);
    pub_key_poses           = n.advertise       (PROJECT_NAME + "/vins/odometry/key_poses", 1000);
    pub_camera_pose         = n.advertise               (PROJECT_NAME + "/vins/odometry/camera_pose", 1000);
    pub_camera_pose_visual  = n.advertise  (PROJECT_NAME + "/vins/odometry/camera_pose_visual", 1000);
    pub_keyframe_pose       = n.advertise               (PROJECT_NAME + "/vins/odometry/keyframe_pose", 1000);
    pub_keyframe_point      = n.advertise          (PROJECT_NAME + "/vins/odometry/keyframe_point", 1000);
    pub_extrinsic           = n.advertise               (PROJECT_NAME + "/vins/odometry/extrinsic", 1000);

    
    cameraposevisual.setScale(1);
    cameraposevisual.setLineWidth(0.05);
    
    keyframebasevisual.setScale(0.1);
    keyframebasevisual.setLineWidth(0.01);
}
d. odomRegister类

​ 这个类可以把里程计信息从lidar帧的坐标系转到VINS视觉图像帧的坐标系,在initial的文件夹下。这个类原本是可以是发布相关的信息的,但是,作者把相关的东西注释掉了,估计是写程序时的调试信息。因此,这个类其实并不需要把NodeHandle传进去作为参数,只剩下坐标系转换的功能了。这里的内容,也可以看看另外一个同学的blog。

附录:引用

LVI-SAM代码– xuwuzhou的SLAM之路 – 静心,慎思,明辨,笃学

ManiiXu/VINS-Mono-Learning: VINS-Mono

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5658634.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存