VINS-FUSION代码解读【1】——程序入口

2023-05-16

VINS-FUSION代码解读【1】——程序入口

本人基础不太扎实所以会把代码注释的比较详细,也会适当添加对应知识点的解读。
vins-fusion不像mono那样有三个node,它只有一个node,在rosNodeTest.cpp里。并且这个文件是整个vins-fusion的程序入口。所以我先从这个文件看起。
mian函数如下所示:

int main(int argc, char **argv)
{
	//初始化ros节点
    ros::init(argc, argv, "vins_estimator");
    ros::NodeHandle n("~");

    /*
    * 设置消息日志级别,默认有Debug,Info,Warn,Error,Fatal
    * Debug : 输出程序正常运行需要的信息
    * Info : 输出大量用户需要的信息
    * Warn : 输出警告,或许影响程序的应用,但系统仍处于可控的预期状态
    * Error : 输出严重错误(但错误可恢复)
    * Fatal : 输出不可恢复的崩溃式错误
    */
    ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME, ros::console::levels::Info);
    
    //启动程序时参数缺少,给予错误提示
    if(argc != 2)
    {
        printf("please intput: rosrun vins vins_node [config file] \n"
               "for example: rosrun vins vins_node "
               "~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_imu_config.yaml \n");
        return 1;
    }
    //很明显就是在启动程序的时候将参数文件路径作为参数
    //启动程序 : rosrun vins vins_node ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_mono_imu_config.yaml 
    string config_file = argv[1];
    printf("config_file: %s\n", argv[1]);

    // 读取YAML配置参数
    readParameters(config_file);
    estimator.setParameter();

#ifdef EIGEN_DONT_PARALLELIZE
    ROS_DEBUG("EIGEN_DONT_PARALLELIZE");
#endif

    ROS_WARN("waiting for image and imu...");

    // 注册vins_estimator节点,在次节点下发布话题
    registerPub(n);

    // 订阅IMU
    ros::Subscriber sub_imu = n.subscribe(IMU_TOPIC, 2000, imu_callback, ros::TransportHints().tcpNoDelay());
    // 订阅一帧跟踪的特征点,没有找到对应的发布者,实际上应该是没用到,实际上是使用featureBuf进行传递
    ros::Subscriber sub_feature = n.subscribe("/feature_tracker/feature", 2000, feature_callback);
    // 订阅左图 
    ros::Subscriber sub_img0 = n.subscribe(IMAGE0_TOPIC, 100, img0_callback);
    // 订阅右图
    ros::Subscriber sub_img1 = n.subscribe(IMAGE1_TOPIC, 100, img1_callback);
     //  这三个订阅可以通过外部的程序发布ROS消息进行调整
    // 订阅,重启节点,实际上应该是没用到
    ros::Subscriber sub_restart = n.subscribe("/vins_restart", 100, restart_callback);
    // 订阅,双目下,IMU开关。实际上并没有用到,实际上是根据参数文件的参数读取进行的切换
    ros::Subscriber sub_imu_switch = n.subscribe("/vins_imu_switch", 100, imu_switch_callback);
    // 订阅,单双目切换。实际上并没有用到,实际上是根据参数文件的参数读取进行的切换
    ros::Subscriber sub_cam_switch = n.subscribe("/vins_cam_switch", 100, cam_switch_callback);

    // 从两个图像队列中取出最早的一帧,并从队列删除,双目相机的话要求两帧时差不得超过0.003s
    std::thread sync_thread{sync_process};
    //  等待回调
    ros::spin();

    return 0;
}

rosNodeTest.cpp的文件级变量

queue<sensor_msgs::ImuConstPtr> imu_buf;//imu缓存队列
queue<sensor_msgs::PointCloudConstPtr> feature_buf;//特征点缓存队列
queue<sensor_msgs::ImageConstPtr> img0_buf;//左目/单目缓存队列
queue<sensor_msgs::ImageConstPtr> img1_buf;//右目图像缓存队列
std::mutex m_buf;   // 操作缓存队列需要上锁,防止数据出现异常

接着,我们单独看一下每个订阅函数:

/*
 * 订阅左目图像,缓存
*/
void img0_callback(const sensor_msgs::ImageConstPtr &img_msg)
{
    m_buf.lock();
    img0_buf.push(img_msg);
    m_buf.unlock();
}

/*
 * 订阅右目图像,缓存
*/
void img1_callback(const sensor_msgs::ImageConstPtr &img_msg)
{
    m_buf.lock();
    img1_buf.push(img_msg);
    m_buf.unlock();
}

//*
 * 订阅IMU,交给estimator处理
*/
void imu_callback(const sensor_msgs::ImuConstPtr &imu_msg)
{
    double t = imu_msg->header.stamp.toSec();
    double dx = imu_msg->linear_acceleration.x;
    double dy = imu_msg->linear_acceleration.y;
    double dz = imu_msg->linear_acceleration.z;
    double rx = imu_msg->angular_velocity.x;
    double ry = imu_msg->angular_velocity.y;
    double rz = imu_msg->angular_velocity.z;
    Vector3d acc(dx, dy, dz);
    Vector3d gyr(rx, ry, rz);
    /*
    *  inputIMU函数:
    * 1. 积分预测当前帧状态,latest_time,latest_Q,latest_P,latest_V,latest_acc_0,latest_gyr_0
    * 2. 发布里程计
    */
    estimator.inputIMU(t, acc, gyr);
    return;
}

/*
 * 订阅,重启节点
*/
void restart_callback(const std_msgs::BoolConstPtr &restart_msg)
{
    if (restart_msg->data == true)
    {
        ROS_WARN("restart the estimator!");
        estimator.clearState();
        estimator.setParameter();
    }
    return;
}

/*
 * 订阅一帧跟踪的特征点,包括3D坐标、像素坐标、速度,交给estimator处理
*/
void feature_callback(const sensor_msgs::PointCloudConstPtr &feature_msg)
{
    map<int, vector<pair<int, Eigen::Matrix<double, 7, 1>>>> featureFrame;
    for (unsigned int i = 0; i < feature_msg->points.size(); i++)
    {
        int feature_id = feature_msg->channels[0].values[i];
        int camera_id = feature_msg->channels[1].values[i];
        //  3D坐标
        double x = feature_msg->points[i].x;
        double y = feature_msg->points[i].y;
        double z = feature_msg->points[i].z;
        //  像素坐标
        double p_u = feature_msg->channels[2].values[i];
        double p_v = feature_msg->channels[3].values[i];
        //  速度
        double velocity_x = feature_msg->channels[4].values[i];
        double velocity_y = feature_msg->channels[5].values[i];
        if(feature_msg->channels.size() > 5)
        {
            double gx = feature_msg->channels[6].values[i];
            double gy = feature_msg->channels[7].values[i];
            double gz = feature_msg->channels[8].values[i];
            pts_gt[feature_id] = Eigen::Vector3d(gx, gy, gz);
            //printf("receive pts gt %d %f %f %f\n", feature_id, gx, gy, gz);
        }
        // 检查是不是归一化
        ROS_ASSERT(z == 1); 
        Eigen::Matrix<double, 7, 1> xyz_uv_velocity;
        xyz_uv_velocity << x, y, z, p_u, p_v, velocity_x, velocity_y;
        featureFrame[feature_id].emplace_back(camera_id,  xyz_uv_velocity);
    }
    double t = feature_msg->header.stamp.toSec();
    estimator.inputFeature(t, featureFrame);
    return;
}
/*
 * 订阅,重启节点
*/
void restart_callback(const std_msgs::BoolConstPtr &restart_msg)
    {
        ROS_WARN("restart the estimator!");
        /*
        *  清理状态,缓存数据、变量、滑动窗口数据、位姿等
        *  系统重启或者滑窗优化失败都会调用
        */
        estimator.clearState();
        //为求解器设置参数
        estimator.setParameter();
    }
    return;
}

/*
 * 订阅,双目,IMU开关
*/
void imu_switch_callback(const std_msgs::BoolConstPtr &switch_msg)
{
    if (switch_msg->data == true)
    {
        //ROS_WARN("use IMU!");
        //  改变传感器使用状态
        estimator.changeSensorType(1, STEREO);
    }
    else
    {
        //ROS_WARN("disable IMU!");
        //  改变传感器使用状态
        estimator.changeSensorType(0, STEREO);
    }
    return;
}

/*
 * 订阅,单双目切换
*/
void cam_switch_callback(const std_msgs::BoolConstPtr &switch_msg)
{
    if (switch_msg->data == true)
    {
        //ROS_WARN("use stereo!");
        //  改变传感器使用状态
        estimator.changeSensorType(USE_IMU, 1);
    }
    else
    {
        //ROS_WARN("use mono camera (left)!");
        //  改变传感器使用状态
        estimator.changeSensorType(USE_IMU, 0);
    }
    return;
}

还有一个将ROS中的图像数据格式转换为OpenCV的数据格式的函数

/*
 * ROS图像转换成CV格式
*/
cv::Mat getImageFromMsg(const sensor_msgs::ImageConstPtr &img_msg)
{
    cv_bridge::CvImageConstPtr ptr;
    // 如果编码格式不是MONO8的话将编码格式进行转换
    if (img_msg->encoding == "8UC1")
    {
        sensor_msgs::Image img;
        img.header = img_msg->header;
        img.height = img_msg->height;
        img.width = img_msg->width;
        img.is_bigendian = img_msg->is_bigendian;
        img.step = img_msg->step;
        img.data = img_msg->data;
        img.encoding = "mono8";
        ptr = cv_bridge::toCvCopy(img, sensor_msgs::image_encodings::MONO8);
    }
    else
        ptr = cv_bridge::toCvCopy(img_msg, sensor_msgs::image_encodings::MONO8);

    cv::Mat img = ptr->image.clone();
    return img;
}

接下来解析sync_process线程

/*
 * 从图像队列中取出最早的一帧,并从队列删除
 * 双目的话要求两帧时差不得超过0.003s
*/
void sync_process()
{
    while(1)
    {
        if(STEREO)
        {
            cv::Mat image0, image1;
            std_msgs::Header header;
            double time = 0;
            m_buf.lock();
            if (!img0_buf.empty() && !img1_buf.empty())
            {
                double time0 = img0_buf.front()->header.stamp.toSec();
                double time1 = img1_buf.front()->header.stamp.toSec();
                // 双目相机左右图像时差不得超过0.003s
                if(time0 < time1 - 0.003)
                {
                    img0_buf.pop();
                    printf("throw img0\n");
                }
                else if(time0 > time1 + 0.003)
                {
                    img1_buf.pop();
                    printf("throw img1\n");
                }
                else
                {
                    // 提取缓存队列中最早一帧图像,并从队列中删除
                    time = img0_buf.front()->header.stamp.toSec();
                    header = img0_buf.front()->header;
                    image0 = getImageFromMsg(img0_buf.front());
                    img0_buf.pop();
                    image1 = getImageFromMsg(img1_buf.front());
                    img1_buf.pop();
                }
            }
            m_buf.unlock();
            if(!image0.empty())
                estimator.inputImage(time, image0, image1);
        }
        else
        {
            cv::Mat image;
            std_msgs::Header header;
            double time = 0;
            m_buf.lock();
            if(!img0_buf.empty())
            {
                time = img0_buf.front()->header.stamp.toSec();
                header = img0_buf.front()->header;
                image = getImageFromMsg(img0_buf.front());
                img0_buf.pop();
            }
            m_buf.unlock();
            if(!image.empty())
                estimator.inputImage(time, image);
        }
        //  线程休眠2ms
        std::chrono::milliseconds dura(2);
        std::this_thread::sleep_for(dura);
    }
}

以上就是对rosNodeTest.cpp代码的解读
下一篇:
VINS-FUSION代码解读【2】——参数读取和求解器参数设置

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

VINS-FUSION代码解读【1】——程序入口 的相关文章

  • 树莓派安装realsense自己的步骤梳理

    树莓派3B 43 装上ubuntu 18 0 4的内核亲测是 4 15的 xff0c 跟ubuntu官网一致 目前最新的也就是4 15 xff0c 不知道realsense官网瞎扯什么4 16啥的干啥 xff1f 都还没有的东西 xff0c
  • Opencv快速入门(C++版)

    Opencv快速入门 C 43 43 版 Excerpt Opencv快速入门 C 43 43 版 xff09 前言1 图像的读取与显示所使用的API接口 xff1a 代码演示 xff1a 2 图像色彩空间转换所使用的API接口 xff1a
  • 搭建elsticsearch集群遇到的错误

    错误一 xff1a 报错with the same id but is a different node instance解决办法 原因 xff1a 是因为复制虚拟机时 xff0c elsticsearch时 xff0c 将elsticse
  • PX4无人机控制

    1 PX4安装 安装主要是配置环境 xff0c 我是按照官网配置的 xff1a PX4 自动驾驶用户指南 特别是对于英语不好的我来讲 xff0c 配置了好几天也没有配置成功 找到了比较靠谱的参考文献 xff1a Ubuntu18 04配置搭
  • 吃透计算机组成原理

    文章目录 博主杂谈计算机概论计算机发展历程计算机的分类计算机的结构体系冯诺依曼体系现代计算机体系 计算机的层次与编程语言程序翻译与程序解释计算机层次 计算机计算单位与字符编码计算机计算单位计算机字符编码字符编码种类与历程 计算机内部结构计算
  • 远程显示docker运行结果

    查找本地IP 服务器中的docker环境操作步骤 xff1a span class token function export span DISPLAY span class token operator 61 span 10 100 12
  • 笔试——Linux操作系统

    1 Linux内核的组成部分 Linux内核主要由五个子系统组成 xff1a 进程调度 内存管理 虚拟文件系统 网络接口 进程间通信 xff1b 2 Linux系统的组成部分 主要是4个部分 xff1a 内核 shell 文件系统和应用程序
  • WIN11打开照片 浏览器 office等软件启用硬件加速后总黑屏

    解决 WIN11打开照片 浏览器 office等软件启用硬件加速后总黑屏 问题描述 xff1a 电脑为暗影精灵6 xff08 OMEN 6 xff0c 在更新intel显卡驱动程序后或者更新win11后 xff08 自动更新最新驱动 xff
  • linux执行程序运行命令失败,提示找不到命令

    安装虚拟机时提示找不到命令 sudo VMware Workstation Full 17 0 0 20800274 x86 64 bundle xff1a 找不到命令 经检查 xff0c sudo命令正常 xff0c 该脚本文件的确存在于
  • 用自己的设备(realsense d435i)跑VINS-Fusion 还是有一些坑

    操作环境 设备 xff1a MacBook pro 2019 xff0c realsense d435i 系统 xff1a Ubuntu 16 04 lts 准备工作 标定 首先标定摄像头 xff0c 这里用kalibr对摄像头和imu进行
  • realsense d435i获取与rgb图像对应的深度值

    设置 realsense ros 的rs camera launch文件 lt arg name 61 34 align depth 34 default 61 34 true 34 gt 就能得到与图像对应的深度图 比如rgb对应的深度图
  • OpenVINS 配置,并用自己的设备跑起来~

    OpenVINS 下载编译 span class token function mkdir span p workspace catkin ws ov src span class token function cd span worksp
  • 用realsense d435i传感器在实际环境中跑ORB_SLAM3,顺带解决一部分编译问题

    是的ORB SLAM3 来了 xff0c 时隔五年 xff0c 它来带的惊喜到底是啥呢 xff1f 一个完全依赖于最大后验估计 xff08 MAP xff09 的单 双目惯导融合系统高回召的地点识别功能 xff08 High recall
  • ubuntu安装vnc server-x11vnc并设置开机自动启动

    安装x11vnc 打开终端 xff0c 使用如下命令女装x11vnc sudo apt get install x11vnc 手动启动x11vnc 按组合键CTRL 43 ALT 43 T打开终端 xff0c 输入 xff1a sudo u
  • Jetson nano 的三种供电方式

    1 首先最简单的就是USB供电 xff0c 使用数据线 xff0c 连接Jetson nano 的MicroUSB接口进行供电 xff0c 拔掉J48跳线帽 MicroUSB 的供电为 5V 2A xff0c 仅能支撑5W低功率模式 Mic
  • 在docker容器中运行Ubuntu桌面版,并通过vnc连接

    拉取镜像 xff1a docker pull dorowu span class token operator span ubuntu span class token operator span desktop span class to
  • keil5写入程序时显示Error.Flash Download failed -‘CortexM4’

    keil5写入程序时显示Error Flash Download failed CortexM4 我所遇到问题的芯片是STM32F429IGT6 xff0c 可能F1系列或者其他的STM芯片都可能遇到这样的问题 xff0c 网上目前比较多的
  • slam松耦合紧耦合调研

  • 深度相机进行目标检测

    1 介绍 国外博客 xff0c 这个博客还行 xff0c 可以下载源码 CSDN 目标检测采用YOLOv5 xff0c 已经搭建好 相机采用realsense d435i 官网有文档 xff0c 示例代码 xff0c 工具等 看官网是最好的
  • 手眼标定[Realsense+大象机器人]

    踩坑 手眼标定的算法网上是比较多的 xff0c 但是很多都不好用 github上高赞的easy handeye xff0c 试了一下 xff0c 但是mycobot600没有提供moveit的配置 xff0c 而我ROS基础不是很好 xff

随机推荐

  • 在Linux上用docker部署项目

    在Linux上用docker部署项目 一 用xftp5或FileZilla连接服务器 二 用上面工具在服务器上创建一个文件夹 xff08 或者用命令行创建 xff09 上面是在 usr 下创建了docker文件夹 xff0c 用来放需要更新
  • idea集成docker最新

    一 安装docker 1 卸载旧版本 sudo yum remove docker docker client docker client latest docker common docker latest docker latest l
  • docker命令删除容器和镜像

    1 查看所有正在运行容器 docker ps 2 停止容器 xff08 containerId是容器id xff09 docker stop containerId 3 删除容器 docker rm 容器id 4 查看镜像 docker i
  • 下拉多选框的实现

    这两天在写一个chrome插件 xff0c 需要在popup页使用下拉多选框 用select看起来非常原始 xff0c 多选还要按住ctrl键或者command键 html代码如下 xff1a span class token operat
  • 使用sed和awk分割部分文件内容

    目标 xff1a 文本的一部分内容 xff0c 需要进行比较 xff0c 其他的部分 xff0c 需要忽略掉 步骤 xff1a 1 cat 文件 xff1b 2 grep 起始 终止 字符串 xff1b 3 获取行数 xff1b 通过sed
  • docker安装、启动、卸载nginx,并配置修改nginx的配置文件

    一 第一种方式 1 使用docker 下载nginx 镜像 docker pull nginx 2 启动nginx docker run span class token operator span name nginx span clas
  • 更新docker镜像及容器,使用docker-compose命令

    第一种方法 xff1a docker stop 容器名或镜像id 下面一样 docker rm 容器名 docker rmi 镜像名 docker builder 镜像名 docker run d name 容器名 p 对外端口 内部端口
  • docker/overlay2磁盘占满——Linux定时清理docker日志脚本

    一 查看占用磁盘文件 df span class token operator span h 然后进入占用大的文件目录执行 du span class token operator span span class token operato
  • java3-5年面试题——数据库篇

    1 limit关键字从0到10与从100000到200000效率相差几个数量级 xff0c 为什么 xff1f 从100000到200000 这种分页查询方式会从数据库第一条记录开始扫描 xff0c 所以越往后 xff0c 查询速度越慢 x
  • java3-5年面试题——高级篇

    关于linux操作 xff1a 1 常用的文件操作命令 1 ls命令 xff1a 查看当前目录中的文件信息 ls span class token operator span a 查看所有文件列表 xff08 可查看隐藏文件 xff09 l
  • mysql行列转换

    一 行转列 建表语句 xff1a span class token keyword DROP span span class token keyword TABLE span span class token keyword IF span
  • Object Detection Api安装经测试后,出现“AlreadyExistsError: Another metric with the same name already exists.”

    在安装Object Detection Api后 xff0c 经过如下一行代码测试 xff1a span class token keyword from span object detection span class token pun
  • windows系统安装curl

    1 curl是什么 curl 是一种命令行工具 xff0c 作用是发出网络请求 xff0c 然后获取数据 xff0c 显示在 34 标准输出 34 xff08 stdout xff09 上面 安装教程 x1f447 xff1a 1 官网下载
  • 文件系统与LVM(mksf、mkswap、swapon、挂载、磁盘、UUID号)

    目录 mksfmkswapswapon挂载查看磁盘使用情况查看分区UUID号管理LVM逻辑卷 mksf 作用 创建文件系统 格式化 t 指定格式化文件类型 b 指定block大小 U 设置UUID号 mksf ext4 磁盘路径 格式化 m
  • 【踩坑记录】Slam-Gmapping建图失败--已解决

    目录 场景 仿真小车在Gazebo上进行Gmapping 步骤 xff1a 问题 xff1a 填坑过程 知识储备 解决办法 修改尝试 0 结论 0 odom由一个发布者发布 修改尝试 1 结论 1 odom信息由模型中的URDF决定 修改尝
  • 【踩坑记录】Gazebo启动慢,画面卡“Preparing your world”

    项目场景 xff1a ros kinetic 问题描述 xff1a 启动gazebo慢 xff0c 卡顿 xff0c 一直停留在Preparing your world的界面上 原因分析 xff1a gazebo在启动时需要将模型仿真出来
  • 【踩坑笔记】Linux系统无法启动,拷贝备份文件

    问题描述 xff1a Linux系统中安装输入法后 xff0c 重启后 xff0c 导致系统无法进入 xff0c 进入 recovery mode下的resume 也启动不了 xff0c 所以决定将需要的东西复制到U盘 解决方案 xff1a
  • FreeRTOS-任务基础知识

    xff08 一 xff09 多任务系统 1 单任务系统 在使用C51系列 STM32等单片机进行裸机编程时 xff0c 大都在main函数中写一个while或者for死循环函数 xff0c 用来无限轮询任务函数 很多时候会加入硬件中断来完成
  • 飞行控制全数字仿真

    在simulink中搭建飞行控制系统 基础工具是Aerospace Blockset模块 xff0c 该模块是集成的航天航空工具箱 xff0c 这个工具箱包含了在航空航天领域常用的需要模块 xff0c 共12个模块组 全数字仿真系统共由9大
  • VINS-FUSION代码解读【1】——程序入口

    VINS FUSION代码解读 1 程序入口 本人基础不太扎实所以会把代码注释的比较详细 xff0c 也会适当添加对应知识点的解读 vins fusion不像mono那样有三个node xff0c 它只有一个node xff0c 在rosN