最近接到一个任务,在激光检测回环失败时,比如黑色物体多,场景大等,可否利用视觉进行回环检测。如果只是检测回环,现有的许多框架都可以使用。ORB-SLAM本身就有单目模式,且效果不错。但是发现ORB在检测回环时,必须要进行pose计算,产生地图点,然后根据地图点和回环之间的关系进行回环检测。这样就比较耗费资源,可否只检测回环,并不计算位姿与地图点。然后想到VINS也是有单目检测回环功能,就着手从VINS开始。

1. feature_tracker模块

这部分模块无需较大改动,只需要在节点里增加激光数据触发信号,让激光关键帧与图象帧保持数据同步

2. estimator模块

此部分会融合IMU做pose计算,所以这个节点在回环检测中不需要使用。需要在launch文件中,删去此节点,不运行。

3. pose_graph模块

3.1 此部分为回环检测核心模块

首先修改订阅的topic,只需要订阅图像与feature_tracker发布的特征点信息。

   ros::Subscriber sub_image = n.subscribe(IMAGE_TOPIC, 2000, image_callback);

   ros::Subscriber sub_point = n.subscribe("/feature_tracker/feature", 2000, point_callback);

3.2 修改KeyFrame构造函数

以前的需要特征点对应的3D点,此时为了减少计算量,只需要特征点图像坐标与归一化坐标,以及特征点光流平均速度

KeyFrame::KeyFrame(double _time_stamp, int _index, cv::Mat &_image,vector<cv::Point2f> &_point_2d_uv, vector<cv::Point2f> &_point_2d_normal, double _aver_velocity,  int _sequence)
{
time_stamp = _time_stamp;
index = _index;
image = _image.clone();
cv::resize(image, thumbnail, cv::Size(80, 60));
std::cout<<"this is resize"<<std::endl;
point_2d_uv = _point_2d_uv;
point_2d_norm = _point_2d_normal;
has_loop = false;
loop_index = -1;
has_fast_point = false;
loop_info << 0, 0, 0, 0, 0, 0, 0, 0;
sequence = _sequence;
computeWindowBRIEFPoint();
computeBRIEFPoint();
aver_velocity = _aver_velocity;
//这里可以把图像清除了,但是为了显示图像,可以暂且留着 }
3.3 改动的还有addkeyFrame

为了减少计算量,当机器人静止时,可以不需要进行回环检测,这里判断条件就是特征点的平均光溜速度,对于这个阈值可以根据最小视差来决定。

void PoseGraph::addKeyFrame(KeyFrame* cur_kf, bool flag_detect_loop)
{
//先判断是否关键帧
std::cout<<"this is function of addKeyFrame"<<std::endl;
if(cur_kf->aver_velocity<=0.2&&keyframelist.size()>0)
{
std::cout<<"loop index unchanged"<<last_loop_index<<std::endl;
}else{ if (sequence_cnt != cur_kf->sequence)
{
sequence_cnt++;
sequence_loop.push_back(0);
}
cur_kf->index = global_index;
last_index= global_index;
int loop_index = -1;
if (flag_detect_loop)
{
TicToc tmp_t;
loop_index = detectLoop(cur_kf, cur_kf->index);
last_loop_index = loop_index;
std::cout<<"loop index is "<<loop_index<<std::endl;
KeyFrame * old_kf = getKeyFrame(loop_index);
if(loop_index !=-1)
{
if(cur_kf->findConnection(old_kf))
{
std::cout<<"this is true loop with index of : "<< loop_index<<std::endl;
}
}
}
global_index++;
keyframelist.push_back(cur_kf);
}
}

detectLoop函数没有做改动,对与findConnection函数做了如下改动。一开始以为可以不需要findConnection函数,直接用detectLoop得出的结果就能使用了,但是发现误匹配很多,VINS之前是使用

  PnPRANSAC(matched_2d_old_norm, matched_3d, status, PnP_T_old, PnP_R_old);

函数进行两帧之间的真实相似度检测。但是此函数需要3D点,且计算了相对位姿,并不适合我的任务,正好VINS的老版本仅仅使用了两帧图像的2d点进行验证,虽然精度略微降低,但是无需3D点,减少了计算量。

bool KeyFrame::findConnection(KeyFrame* old_kf)
{
std::cout<<"this is function of findConnection"<<std::endl;
TicToc tmp_t;
//printf("find Connection\n");
vector<cv::Point2f> matched_2d_cur, matched_2d_old;
vector<cv::Point2f> matched_2d_cur_norm, matched_2d_old_norm;
vector<uchar> status; matched_2d_cur = point_2d_uv;
matched_2d_cur_norm = point_2d_norm; TicToc t_match;
std::cout<<"old_kf keypoint : "<<old_kf->keypoints.size()<<std::endl;
searchByBRIEFDes(matched_2d_old, matched_2d_old_norm, status, old_kf->brief_descriptors, old_kf->keypoints, old_kf->keypoints_norm); reduceVector(matched_2d_cur, status);
reduceVector(matched_2d_old, status);
reduceVector(matched_2d_cur_norm, status);
reduceVector(matched_2d_old_norm, status); //printf("search by des finish\n");
status.clear();
if ((int)matched_2d_cur.size() > MIN_LOOP_NUM)
{
status.clear();
FundmantalMatrixRANSAC(matched_2d_cur_norm,matched_2d_old_norm,status);
reduceVector(matched_2d_cur, status);
reduceVector(matched_2d_old, status);
reduceVector(matched_2d_cur_norm, status);
reduceVector(matched_2d_old_norm, status); int gap = 10;
cv::Mat gap_image(ROW, gap, CV_8UC1, cv::Scalar(255, 255, 255));
cv::Mat gray_img, loop_match_img;
cv::Mat old_img = old_kf->image;
cv::hconcat(image, gap_image, gap_image);
cv::hconcat(gap_image, old_img, gray_img);
cvtColor(gray_img, loop_match_img, CV_GRAY2RGB);
for(int i = 0; i< (int)matched_2d_cur.size(); i++)
{
cv::Point2f cur_pt = matched_2d_cur[i];
cv::circle(loop_match_img, cur_pt, 5, cv::Scalar(0, 255, 0));
}
for(int i = 0; i< (int)matched_2d_old.size(); i++)
{
cv::Point2f old_pt = matched_2d_old[i];
old_pt.x += (COL + gap);
cv::circle(loop_match_img, old_pt, 5, cv::Scalar(0, 255, 0));
}
for (int i = 0; i< (int)matched_2d_cur.size(); i++)
{
cv::Point2f old_pt = matched_2d_old[i];
old_pt.x += (COL + gap) ;
cv::line(loop_match_img, matched_2d_cur[i], old_pt, cv::Scalar(0, 255, 0), 2, 8, 0);
}
cv::Mat notation(50, COL + gap + COL, CV_8UC3, cv::Scalar(255, 255, 255));
putText(notation, "current frame: " + to_string(index) + " sequence: " + to_string(sequence), cv::Point2f(20, 30), CV_FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 3); putText(notation, "previous frame: " + to_string(old_kf->index) + " sequence: " + to_string(old_kf->sequence), cv::Point2f(20 + COL + gap, 30), CV_FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255), 3);
cv::vconcat(notation, loop_match_img, loop_match_img); if ((int)matched_2d_cur.size() > MIN_LOOP_NUM)
{ cv::imshow("loop connection",loop_match_img);
cv::waitKey(100);
return true;
}
} //printf("loop final use num %d %lf--------------- \n", (int)matched_2d_cur.size(), t_match.toc());
return false;
}

效果如图所示。

VINS 检测回环辅助激光建图的更多相关文章

  1. ​综述 | SLAM回环检测方法

    本文作者任旭倩,公众号:计算机视觉life成员,由于格式原因,公式显示可能出问题,建议阅读原文链接:综述 | SLAM回环检测方法 在视觉SLAM问题中,位姿的估计往往是一个递推的过程,即由上一帧位姿 ...

  2. cartographer环境建立以及建图测试(详细级)

  3. 第五篇 openvslam建图与优化模块梳理

    建图模块 mapping_module在初始化系统的时候进行实例化,在构建实例的时候会实例化local_map_cleaner和local_bundle_adjuster.系统启动的时候会在另外一个线 ...

  4. java下蛇形回环矩阵的实现

    前文废话:这个问题据说是腾讯之前的一道笔试题,由于当时没认真看,现在记不清这种矩阵是不是叫"蛇形回环矩阵"......请大家直接看图1,就是那个样子的矩阵. 问题描述:输入一个N, ...

  5. VINS 回环检测与全局优化

    回环检测 VINS回环检测与全局优化都在pose_graph.cpp内处理.首先在pose_graph_node加载vocabulary文件给BriefDatabase用,如果要加载地图,会loadP ...

  6. segMatch:基于3D点云分割的回环检测

    该论文的地址是:https://arxiv.org/pdf/1609.07720.pdf segmatch是一个提供车辆的回环检测的技术,使用提取和匹配分割的三维激光点云技术.分割的例子可以在下面的图 ...

  7. ORB-SLAM(六)回环检测

    上一篇提到,无论在单目.双目还是RGBD中,追踪得到的位姿都是有误差的.随着路径的不断延伸,前面帧的误差会一直传递到后面去,导致最后一帧的位姿在世界坐标系里的误差有可能非常大.除了利用优化方法在局部和 ...

  8. Code Reading: ORB-SLAM回环检测源码阅读+注释

    之前研究过一些回环检测的内容,首先要看的自然是用词袋回环的鼻祖和正当继承人(没有冒犯VINS和LDSO的意思)ORB-SLAM.下面是我的代码注释.因为代码都是自己手打的,不是在源码上注释的,所以一些 ...

  9. 一个基于深度学习回环检测模块的简单双目 SLAM 系统

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12634631.html 写在前面 最近在搞本科毕设,关于基于深度学 ...

随机推荐

  1. Dev控件

    在DevExpress程序中使用PopupContainerEdit和PopupContainer实现数据展示 使用PopupContainerEdit和PopupContainerControl制作 ...

  2. pycharm 断点跟踪

    F8 下一步 F7 step info F9 直接运行

  3. ThinkPHP import 类库导入 include PHP文件

    ThinkPHP 模拟了 Java 的类库导入机制,统一采用 import 方法进行类文件的加载.import 方法是 ThinkPHP 内建的类库和文件导入方法,提供了方便和灵活的文件导入机制,完全 ...

  4. 01.springboot入门--启用自动配置注解EnableAutoConfiguration

    springboot入门 <parent> <groupId>org.springframework.boot</groupId> <artifactId&g ...

  5. python代码整体左移或右移

    IDE 是  PyCharm 选中代码块: 1)右移:直接 Tab 2)左移:Shift + Tab

  6. 理解Java构造器中的"this"

    Calling Another Constructor if the first statement of a constructor has the form this(...), then the ...

  7. C#基础提升系列——C#异步编程

    C#异步编程 关于异步的概述,这里引用MSDN的一段文字: 异步编程是一项关键技术,使得能够简单处理多个核心上的阻塞 I/O 和并发操作. 如果需要 I/O 绑定(例如从网络请求数据或访问数据库),则 ...

  8. HTTP Error 500.30 - ANCM In-Process Start Failure

    环境 windown 10 IIS 10 net core 2.2 vs2019 背景 在vs2019使用net core 2.2发布到IIS 10上(见在 ASP.NET Core 中使用多个环境) ...

  9. JAVA学习笔记--赋值(“=”)

    参考来源:<java编程思想(第四版)> 见第三章3.4节 基本数据类型存储了实际的数值,并非指向一个对象的引用,故其赋值,就是直接将一个地方的内容复制到了另一个地方.例如,对基本数据类型 ...

  10. Vue学习笔记-组件通信-父传子(props中的驼峰标识)

    在组件中,使用选项props来声明需要从父级接收到的数据.props的值有两种方式:方式一:字符串数组,数组中的字符串就是传递时的名称.方式二:对象,对象可以设置传递时的类型,也可以设置默认值等. & ...