萌新学VINS-Fusion(三)------双目和GPS融合

2023-05-16

虽然要过年了,而且每天还要在家里小店打工,但是网红之路不能停对吧,这篇博客写关于VINS-Fusion和GPS的融合。其实我之前改出过一个加了GPS的VIO,并且也开源了,之前没有找到合适的数据集,自己造的数据,效果一般,近期我准备跑一下看看效果

最近我在之前代码基础上做了修改,还在测试效果,之后可能会开源,希望大家关注、点赞、收藏,素质三连。

我的开源代码地址
https://github.com/grn213331/remove_ros_VINS-position-constraint

Vins-Fusion源码:https://github.com/HKUST-Aerial-Robotics/VINS-Fusion

主函数文件

与GPS融合的程序入口在KITTIGPSTest.cpp文件中,数据为KITTI数据集
数据集为 http://www.cvlibs.net/datasets/kitti/raw_data.php
以 [2011_10_03_drive_0027_synced]为例
https://s3.eu-central-1.amazonaws.com/avg-kitti/raw_data/2011_10_03_drive_0027/2011_10_03_drive_0027_sync.zip

main函数与之前的main函数相似,都要进行ros节点初始化,然后读取参数,区别在于该数据集的图像和gps数据均为文件读取方式,将gps数据封装进ros 的sensor_msgs::NavSatFix 数据类型里,以gps为topic播发出去,而双目图像则直接放到estimator进行vio,将vio得到的结果再播发出去,方便后面的订阅和与GPS的融合。

global_fusion

所有的与gps的融合在global_fusion文件夹中,该部分的文件入口是一个rosnode文件globalOptNode.cpp,主函数很短,如下

int main(int argc, char **argv)
{
    ros::init(argc, argv, "globalEstimator");
    ros::NodeHandle n("~");

    global_path = &globalEstimator.global_path;

    ros::Subscriber sub_GPS = n.subscribe("/gps", 100, GPS_callback);
    ros::Subscriber sub_vio = n.subscribe("/vins_estimator/odometry", 100, vio_callback);
    pub_global_path = n.advertise<nav_msgs::Path>("global_path", 100);
    pub_global_odometry = n.advertise<nav_msgs::Odometry>("global_odometry", 100);
    pub_car = n.advertise<visualization_msgs::MarkerArray>("car_model", 1000);
    ros::spin();
    return 0;
}

代码很简单,订阅topic(/gps)在GPS_callback回调函数中接收并处理gps数据,订阅topic(/vins_estimator/odometry)在vio_callback回调函数中接收并处理vio的定位数据。
并且生成了三个发布器,用于将结果展示在rviz上。

GlobalOptimization类

所有的融合算法都是在GlobalOptimization类中,对应的文件为globalOpt.h和globalOpt.cpp,并且ceres优化器的相关定义在Factors.h中。
GlobalOptimization类中的函数和变量如下

class GlobalOptimization
{
public:
	GlobalOptimization();
	~GlobalOptimization();
	void inputGPS(double t, double latitude, double longitude, double altitude, double posAccuracy);
	void inputOdom(double t, Eigen::Vector3d OdomP, Eigen::Quaterniond OdomQ);
	void getGlobalOdom(Eigen::Vector3d &odomP, Eigen::Quaterniond &odomQ);
	nav_msgs::Path global_path;

private:
	void GPS2XYZ(double latitude, double longitude, double altitude, double* xyz);
	void optimize();
	void updateGlobalPath();

	// format t, tx,ty,tz,qw,qx,qy,qz
	map<double, vector<double>> localPoseMap;
	map<double, vector<double>> globalPoseMap;
	map<double, vector<double>> GPSPositionMap;
	bool initGPS;
	bool newGPS;
	GeographicLib::LocalCartesian geoConverter;
	std::mutex mPoseMap;
	Eigen::Matrix4d WGPS_T_WVIO;
	Eigen::Vector3d lastP;
	Eigen::Quaterniond lastQ;
	std::thread threadOpt;

};

inputGPS和inputOdom两个函数将回调函数中的gps和vio数据导入,getGlobalOdom为获取融合后位姿函数。
GPS2XYZ函数是将GPS的经纬高坐标转换成当前的坐标系的函数,updateGlobalPath顾名思义更新全局位姿函数。
融合算法的实现主要就是在optimize函数中,接下来进行详细介绍。

注意其中几个变量localPoseMap中保存着vio的位姿,GPSPositionMap中保存着gps数据,globalPoseMap中保存着优化后的全局位姿。

融合算法(optimize函数)

void GlobalOptimization::optimize()
{
    while(true)
    {
        if(newGPS)
        {
            newGPS = false;
            printf("global optimization\n");
            TicToc globalOptimizationTime;

            ceres::Problem problem;
            ceres::Solver::Options options;
            options.linear_solver_type = ceres::SPARSE_NORMAL_CHOLESKY;
            //options.minimizer_progress_to_stdout = true;
            //options.max_solver_time_in_seconds = SOLVER_TIME * 3;
            options.max_num_iterations = 5;
            ceres::Solver::Summary summary;
            ceres::LossFunction *loss_function;
            loss_function = new ceres::HuberLoss(1.0);
            ceres::LocalParameterization* local_parameterization = new ceres::QuaternionParameterization();

            //add param
            mPoseMap.lock();
            int length = localPoseMap.size();
            // w^t_i   w^q_i
            double t_array[length][3];
            double q_array[length][4];
            map<double, vector<double>>::iterator iter;
            iter = globalPoseMap.begin();
            for (int i = 0; i < length; i++, iter++)
            {
                t_array[i][0] = iter->second[0];
                t_array[i][1] = iter->second[1];
                t_array[i][2] = iter->second[2];
                q_array[i][0] = iter->second[3];
                q_array[i][1] = iter->second[4];
                q_array[i][2] = iter->second[5];
                q_array[i][3] = iter->second[6];
                problem.AddParameterBlock(q_array[i], 4, local_parameterization);
                problem.AddParameterBlock(t_array[i], 3);
            }

            map<double, vector<double>>::iterator iterVIO, iterVIONext, iterGPS;
            int i = 0;
            for (iterVIO = localPoseMap.begin(); iterVIO != localPoseMap.end(); iterVIO++, i++)
            {
                //vio factor
                iterVIONext = iterVIO;
                iterVIONext++;
                if(iterVIONext != localPoseMap.end())
                {
                    Eigen::Matrix4d wTi = Eigen::Matrix4d::Identity();
                    Eigen::Matrix4d wTj = Eigen::Matrix4d::Identity();
                    wTi.block<3, 3>(0, 0) = Eigen::Quaterniond(iterVIO->second[3], iterVIO->second[4], 
                                                               iterVIO->second[5], iterVIO->second[6]).toRotationMatrix();
                    wTi.block<3, 1>(0, 3) = Eigen::Vector3d(iterVIO->second[0], iterVIO->second[1], iterVIO->second[2]);
                    wTj.block<3, 3>(0, 0) = Eigen::Quaterniond(iterVIONext->second[3], iterVIONext->second[4], 
                                                               iterVIONext->second[5], iterVIONext->second[6]).toRotationMatrix();
                    wTj.block<3, 1>(0, 3) = Eigen::Vector3d(iterVIONext->second[0], iterVIONext->second[1], iterVIONext->second[2]);
                    Eigen::Matrix4d iTj = wTi.inverse() * wTj;
                    Eigen::Quaterniond iQj;
                    iQj = iTj.block<3, 3>(0, 0);
                    Eigen::Vector3d iPj = iTj.block<3, 1>(0, 3);

                    ceres::CostFunction* vio_function = RelativeRTError::Create(iPj.x(), iPj.y(), iPj.z(),
                                                                                iQj.w(), iQj.x(), iQj.y(), iQj.z(),
                                                                                0.1, 0.01);
                    problem.AddResidualBlock(vio_function, NULL, q_array[i], t_array[i], q_array[i+1], t_array[i+1]);
                }
                //gps factor
                double t = iterVIO->first;
                iterGPS = GPSPositionMap.find(t);
                if (iterGPS != GPSPositionMap.end())
                {
                    ceres::CostFunction* gps_function = TError::Create(iterGPS->second[0], iterGPS->second[1], 
                                                                       iterGPS->second[2], iterGPS->second[3]);
                    //printf("inverse weight %f \n", iterGPS->second[3]);
                    problem.AddResidualBlock(gps_function, loss_function, t_array[i]);

                }

            }
            //mPoseMap.unlock();
            ceres::Solve(options, &problem, &summary);
            //std::cout << summary.BriefReport() << "\n";

            // update global pose
            //mPoseMap.lock();
            iter = globalPoseMap.begin();
            for (int i = 0; i < length; i++, iter++)
            {
            	vector<double> globalPose{t_array[i][0], t_array[i][1], t_array[i][2],
            							  q_array[i][0], q_array[i][1], q_array[i][2], q_array[i][3]};
            	iter->second = globalPose;
            	if(i == length - 1)
            	{
            	    Eigen::Matrix4d WVIO_T_body = Eigen::Matrix4d::Identity(); 
            	    Eigen::Matrix4d WGPS_T_body = Eigen::Matrix4d::Identity();
            	    double t = iter->first;
            	    WVIO_T_body.block<3, 3>(0, 0) = Eigen::Quaterniond(localPoseMap[t][3], localPoseMap[t][4], 
            	                                                       localPoseMap[t][5], localPoseMap[t][6]).toRotationMatrix();
            	    WVIO_T_body.block<3, 1>(0, 3) = Eigen::Vector3d(localPoseMap[t][0], localPoseMap[t][1], localPoseMap[t][2]);
            	    WGPS_T_body.block<3, 3>(0, 0) = Eigen::Quaterniond(globalPose[3], globalPose[4], 
            	                                                        globalPose[5], globalPose[6]).toRotationMatrix();
            	    WGPS_T_body.block<3, 1>(0, 3) = Eigen::Vector3d(globalPose[0], globalPose[1], globalPose[2]);
            	    WGPS_T_WVIO = WGPS_T_body * WVIO_T_body.inverse();
            	}
            }
            updateGlobalPath();
            //printf("global time %f \n", globalOptimizationTime.toc());
            mPoseMap.unlock();
        }
        std::chrono::milliseconds dura(2000);
        std::this_thread::sleep_for(dura);
    }
	return;
}

首先呢,判断是否有gps数据,整体的算法就是在ceres架构下的优化算法。
所以总体的步骤就是ceres优化的步骤,首先添加优化参数块(AddParameterBlock函数),参数为globalPoseMap中的6维位姿(代码中旋转用四元数表示,所以共7维)。
之后添加CostFunction,通过构建factor重载operator方法实现(具体实现需要学习ceres库)。该部分有两个factor,一个是位姿图优化,另一个则是利用gps进行位置约束。
将factor添加后,进行ceres求解,更新此时gps和vio间的坐标系转换参数,之后再利用updateGlobalPath函数更新位姿。

总而言之,VF的和GPS的融合也是一个优化框架下的松组合,利用GPS的位置约束,使得位姿图优化可以不依赖回环,这是一大优势。

谢谢大家,比心(有理解不到位或错误,欢迎指正,虚心学习)

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

萌新学VINS-Fusion(三)------双目和GPS融合 的相关文章

  • Android上的“覆盖”尺寸可以与谷歌地图一起缩放吗?

    我已经能够使用 MapActivity 和 ItemizedOverlay 通过 Eclipse 在 Android 上的谷歌地图上绘制叠加层 但是当地图放大和缩小时 叠加层的大小不会改变 我希望叠加层 固定 在地图上 并随地图一起放大和缩
  • 如何在 python 中创建自己的数据类型以便覆盖算术运算符?

    我目前正在使用 Python Numpy 处理地理 GPS 数据 喜欢它 并且我面临着计算由坐标对定义的地理点之间的距离的重复任务pn lon lat 我有一个这样使用的函数 dist geodistance p1 p2 它类似于线性代数中
  • Android 手机和模拟器中的mapView不同

    关于应用程序 这是一个简单的应用程序 可以查找用户当前位置 问题 该应用程序在模拟器上运行良好 请参见图片 但在手机中它没有显示MapView 请看图片 请告诉我手机出了什么问题 在手机中 它只下载巨大的 20 MB 数据 但不显示实际地图
  • 如何通过 GPS 检查距 x,y 位置 10 米的半径

    我从 GPS 得到这个位置 40 715192 74 005795 如何判断i是否在10米半径范围内 提前致谢 使用半正矢公式http en wikipedia org wiki Haversine formula http en wiki
  • 位置侦听器从服务工作,但不是 IntentService

    我有一个应用程序 我试图定期获取用户位置并将其发送到服务器 我有一项服务附加到AlarmManager每分钟执行一次 用于测试 该服务正确找到用户位置并注销 GPS 坐标 一旦出现 GPS 锁定 我就会取消位置请求并停止服务 当我请求位置更
  • C#:GPS跟踪系统[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何在 C net 中构建带有移动设备 带 GPS 的 GPS 跟踪系统 场景是 通过支持 GPS 的手机跟踪用户 服务工程师 这里没
  • 根据 GPS 坐标计算平均速度的最佳实践

    我这里有一个可以给我 GPS 坐标的设备 我可以定义的时间间隔 我想用它来计算驾驶或驾车旅行时的平均速度 实际上 我使用了正交公式来计算两点之间的距离 然后将其除以给定的时间间隔 通过我遵循的实施这个词 http de wikipedia
  • Android GPS 的准确度如何? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我好像在某处读过Android的GPS精度约为10厘米 任何人都可以验证或更正这个吗 原因是我正在尝试开发的应用程序会跟踪用户访问过的位置 这将极大地
  • Android 应用无法获取位置权限

    我的清单中有以下内容
  • 如何将timestamp_t转换为实际时间?

    我见过很多使用 time t 的例子 但 timestamp t 让我感到困惑 我正在做一个作业 我们需要打印出 GPS 数据 并且 GPS 设备返回一个类型 timestamp t 来表示它的时间戳和纪元时间 我尝试过使用 gmtime
  • 如何从 Android 手机获取 GPS 数据?

    有没有办法将 Android 手机的 GPS 数据连接 USB 有线 到 PC 我目前正在使用基于 gpsd 项目的 GPSTether 应用程序 我正在寻找比该应用程序提供更多控制且错误更少的替代方案 另外 是否有另一种方法可以在不使用任
  • 如何知道jar文件是否已经在运行?

    经过谷歌研究后 我找到了很好的答案 例如 1 using jps or jps l让 jars 在 JVM 下运行 这个答案可以 但是如果用户根本没有安装java并且我使用例如 bat文件和带有java JRE的文件夹运行我的jar 另外
  • 谷歌地图定位是如何工作的?

    我的问题是谷歌地图或移动 GPS 如何找到我的当前位置 读完本文后我的高层次理解article http www physics org article questions asp id 55就是 GPS接收器通过这些卫星获取位置坐标 该位
  • 使用 iPhone 版 gmap 中的经纬度计算两个地点之间的距离 [重复]

    这个问题在这里已经有答案了 可能的重复 GPS 坐标 以度为单位 来计算距离 https stackoverflow com questions 6994101 gps coordinates in degrees to calculate
  • iPhone 网络应用程序可以获取 GPS 位置吗?

    有没有一种简单的方法来设计一个网站来促进iphone用户提供gps网站坐标 我想知道表单字段是否有命名约定 例如 让用户以自动方式输入 我正在考虑建立一个基于位置的网站 并希望为 iPhone 和其他移动用户 量身定制 我意识到 iPhon
  • 如何找到特定路线上两点之间的距离?

    我正在为我的大学开发一个 Android 应用程序 可以帮助学生跟踪大学巴士的当前位置 并为他们提供巴士到达他们的预计时间 截至目前 我获取了公交车的当前位置 通过公交车上的设备 和学生的位置 我陷入了必须找到两个 GPS 坐标之间的距离的
  • 如何请求用户开启定位服务

    我需要我的应用程序来访问用户的当前位置 它在应用程序开始时检查用户是否已设置 如果没有 我需要应用程序显示提示以使其使用位置服务 就像警报视图一样 点击按钮 它应该会带您进入 iPhone 上的位置服务屏幕 您可以通过以下代码检查 loca
  • 如何在Android中获取当前位置[重复]

    这个问题在这里已经有答案了 我在使用 Android 定位系统的网络提供程序获取当前位置坐标时遇到麻烦 已经阅读了大量教程并在我的项目中实现了 4 或 5 个现有类 所有这些类都给了我最后的坐标 但不是当前的坐标 我很确定这个问题是我所缺少
  • Android:CellID 不适用于所有运营商?

    当我请求 Cell ID 和 LAC 信息时 在某些设备上我无法检索它们 我使用这段代码 TelephonyManager tm TelephonyManager getSystemService Context TELEPHONY SER
  • 我可以使用手机信号塔的信息在没有 GPS 服务的 j2me 中获取移动设备的位置吗

    我可以通过 j2me 编程获取未安装 GPS 装置的移动设备的位置吗 我可以使用手机信号塔信息获取位置吗 我听说过三角测量 https stackoverflow com a 9820133 839601 method 并且经历了http

随机推荐

  • Js 中的定时器

    定时器 前言一 定时器二 定时器具体内容三 定时器实例总结 前言 通过定时器自动的做一些事情 xff0c 例如发送网络请求 一 定时器 定时器 xff1a 定时器可以设定时间自动的做某件事情 定时器是一种方法 xff0c 不是对象 xff0
  • 【面经】常见机考类型汇总

    一 筑基篇 xff08 掌握数组 xff0c 字符串操作等基本操作 xff09 1 数组 目标 xff1a 掌握数组的正序遍历 xff0c 逆序遍历 xff0c 数组的排序等 典型例题 xff1a 提取不重复的整数 牛客题霸 牛客网 2 字
  • ONOS架构中的YANG、P4 Runtime

    本文总结自毛健炜的 ONOS架构中的YANG P4 Runtime 的演讲 ONOS xff0c 全称是开放网络操作系统 xff0c 通常以控制器的身份为人所知 它是由ON LAB组织发起的一个开源项目 xff0c 在与ONF组织合并之后
  • PID控制算法的原理剖析

    我的微信公众号 xff08 ID xff1a 00后开发者 xff09 从00后的角度出发 xff0c 专注但不局限于分享电气 嵌入式 机器视觉以及芯片行业的算法 技术文章和最新资讯 如果想查看更多内容 xff0c 可以关注我的微信公众号
  • flask 报错 sqlalchemy.exc.ArgumentError: Mapper mapped class BaseModel->base_model could not assemble

    报错信息 sqlalchemy exc ArgumentError Mapper mapped class BaseModel gt base model could not assemble any primary key columns
  • rospy-编写简单的服务端和客户端

    最近一直在实习公司做课程 xff0c 记录一些笔记和自己写的代码 基础知识和代码解释参考wiki 创客制造和其他博客 xff0c 侵删 背景知识 服务 service ROS中的通信方式有四种 主题 topic 服务 service 参数服
  • 树莓派 3B+ 蓝牙无法使用问题

    最近想用树莓派3B 43 做个小玩意 xff0c 结果发现蓝牙功能不可用 xff0c 图标消失且不能发现设备 xff0c 几乎试了网上所有方法都不行 最后参考了这个帖子恢复了蓝牙功能 xff1a SOLVED Bluetooth icon
  • 电源管理芯片之 Regulator用法。

    有问题请加 xff1a Q群 xff1a 241359063 共同走向创业学习之旅 原创 xff1a kylin zeng http blog chinaunix net uid 23795897 html 转载请注明原创出处 xff0c
  • 如何查看ssh登录日志信息

    less var log secure grep 39 Accepted 39 正常登录日志信息 正常退出 密码错误登录 三次密码登陆失败 输入密码时 xff0c 主动退出 为了安全期间 xff1a 我们只允许xxx xxx xxx xxx
  • C语言笔记-25-网络-TCP网络编程

    C语言笔记 25 网络 TCP网络编程 文章目录 C语言笔记 25 网络 TCP网络编程前言一 TCP编程模型概括二 inet工具htonlinet pton 三 TCP编程模型代码TCP服务端TCP客户端 总结 前言 自学笔记 xff0c
  • Failed to load nodelet [/camera/realsense2_camera] of type [realsense2_camera/RealSenseNodeFactory]

    输入 xff1a roslaunch realsense2 camera rs rgbd launch 报错 xff1a 报错原因 xff1a 是找不到realsense2 camera包 xff0c 在安装D435的包时 xff0c 以为
  • 十七、基于软核和CAN搭建FPGA在线升级系统设计

    1 系统搭建 系统主要包含 xff1a MicroBlaze软核处理器 xff0c Axi Can控制器 xff0c Axi lite user用户通信接口 xff0c MIG DDR3控制器 xff0c 中断控制器等 设计使用Can接收上
  • 【OpenCV】cv::Mat位深和通道,CV_8UC1等

    位深 每个像素由多少位来存储 是一个精度问题 xff0c 一般图片是8bit xff08 位 xff09 的 xff0c 则深度是8 通道 每个像素点能存放多少个数 类似于RGB彩色图中的每个像素点有三个值 xff0c 即三通道 一个图像的
  • darknet_ros编译报错以及在RVIZ中显示乱码

    最近在arm平台上部署 darknet ros 出现了一些错误 xff0c 这里记录一下 首先在ARM平台上部署和在AMD平台上部署是不同的 xff0c 首先应该考虑算力问题 xff0c 在ARM下 xff0c 加载yolov3 weigh
  • 萌新改代码系列(一)--VINS+GPS

    VINS与GPS组合 距离上次写博客过去了快一年了 xff0c 这一年来我一直忙于与SLAM方向几乎没有关系的科研工作 xff0c 说来惭愧 xff0c 最终也没研究出个啥 最近得空 xff0c 就把我之前开源的代码整理了一下 xff0c
  • GVINS论文阅读(VINS-MONO + gnss 紧耦合)

    原文 x1f517 https arxiv org pdf 2103 07899 pdf github x1f517 https github com HKUST Aerial Robotics GVINS 港科大 沈老师团队 在vins
  • 萌新学VINS-Fusion(一)------特征跟踪

    VINS FUSION代码心得 新人小白 xff0c 第一次写博客 xff0c 主要相当于自己做一个关于学习VINS的笔记 xff0c 不喜勿喷 xff0c 转载请注明出处 其实我之前也尝试着在VINS MONO基础上改写双目的 xff0c
  • 萌新学VINS-Fusion(二)------初始化

    最近在忙别的事情 xff0c 博客迟迟没更新 xff0c 现在终于放假了 xff0c 今天把初始化部分来分析一下 源码 xff1a https github com HKUST Aerial Robotics VINS Fusion pro
  • c 语言中 %d,%lu等区别

    转载至 xff1a http blog sina com cn s blog 7d94c35c01019f96 html d 有符号10进制整数 ld 长整型 hd短整型 hu 无符号短整形 u无符号整形 lu无符号长整形 i 有符号10进
  • 萌新学VINS-Fusion(三)------双目和GPS融合

    虽然要过年了 xff0c 而且每天还要在家里小店打工 xff0c 但是网红之路不能停对吧 xff0c 这篇博客写关于VINS Fusion和GPS的融合 其实我之前改出过一个加了GPS的VIO xff0c 并且也开源了 xff0c 之前没有