VINS-mono代码笔记
feature_tracker_node.cpp:
一、通过roslaunch文件的参数服务器获得配置参数
二、获得相机的内参
三、订阅图像,img_callback:
1、第一帧图像只记录时间戳
2、与之前时间戳比较一下,判断是否要发布当前帧,避免高频率发送(PUB_THIS_FRAME),不管发不发布,都要做光流跟踪,这样跟踪间隔时间才短,跟踪才稳定
3、根据是否多目相机,把图像分割出来
4、根据曝光配置来决定是否对图像直方图均衡化
5、prev_img(上上帧)、cur_img(上帧)、 forw_img(当前帧)都有图后才进行光流追踪
6、如果上一帧有特征点,则对上一帧和当前帧进行光流追踪,在当前帧找到上一帧对应的特征点(https://blog.csdn.net/ergevv/article/details/137981026?spm=1001.2014.3001.5502)
7、剔除新特征点超出当前帧位置的所有对应数据
8、记录被跟踪的次数track_cnt,对仍然存在的特征点数量加1
9、通过对级约束来剔除异常点:
(1)将特征点从像素坐标转到归一化坐标,先将像素投影到归一化相机坐标系(未去畸变),
x_image = fx * x/z + cx(fx为焦距*缩放率,cx为宽度/2)。基于相机的畸变程度离中心越远,畸变越大的原理,将畸变点代入畸变模型计算出该点的畸变程度,然后畸变点A再减去该值,得到新畸变点,通过新畸变点得到畸变程度,再由畸变点A再减去该值,如此迭代几轮后完成去畸变,因为迭代过程,畸变程度越来越小,点会越来越接近真值。
(2)将归一化坐标点转到设定好的图像坐标系,这样好处就是无论是什么相机坐标系都可以得到一样的图像坐标系,这样进行对极约束得到基础矩阵时,内外点的判断可以使用同一个参数
(3)将当前帧和上一帧去畸变后,利用cv::findFundamentalMat进行对极约束得到基础矩阵,将是外点的特征点去除。(对极约束原理:P、p1、p2、O1、O2五点共面。cv::findFundamentalMat输入归一化或者图像坐标都行,因为基础矩阵包含K,不同的坐标系基础矩阵值不一样而已)
10、特征点均匀化:如果是鱼眼相机,去除边缘的点,保留特征点被跟踪数量较多的点,其旁边的特征点去除
11、如果目前剩余的特征点少于设定的阈值,则对当前帧提取角点,补充数量到forw_pts,id为-1,跟踪次数为1。这也是一开始特征点的由来。
12、帧间图像和特征点传递
13、去除当前帧的畸变,放到cur_un_pts,id+归一化坐标放到cur_un_pts_map
14、对id不为-1的特征点,计算其当前帧与上一帧的去畸变后归一化坐标下的速度pts_velocity
15、更新特征点id,每个特征点id都是不一样的,每次加1
16、发布数据:将归一化坐标、速度、id、像素坐标发送到estimator_node.cpp("/feature_tracker/feature")
17、可视化数据
estimator_node.cpp
feature_callback:
1、接收feature_tracker_node.cpp计算得到的特征点数据("/feature_tracker/feature")
2、将特征点数据放入feature_buf
3、唤醒process检查数据
imu_callback:
1、接收imu数据
2、将imu数据放入imu_buf
3、唤醒process检查数据
relocalization_callback:
1、
2、
线程process:
1、等待被唤醒,检查数据是否合适,保证特征点前后皆有imu,返回数据个数需要不为0(estimator.td这是预测的时延)
(1)feature_buf和imu_buf不为空
(2)imu_buf最后一个值时间要大于特征点第一个值
(3)imu_buf第一个值时间要小于特征点第一个值
2、将小于特征点时间的所有imu和一个时间大于等于特征点放入IMUs
3、将IMUs和特征点打包到measurements
4、遍历IMUs,第一次进去,frame_count为0,只是定义一个预积分器,所以frame_count为0时没有用。滑窗里的每一帧都建了一个预积分器,初始化各个变量,记录了一开始的角速度和线加速度,方便中值积分:
(1)midPointIntegration函数计算:
1)将上一次线加速度转到相机坐标,un_acc_0 = delta_q * (_acc_0 - linearized_ba);
2)计算角速度中值,un_gyr = 0.5 * (_gyr_0 + _gyr_1) - linearized_bg;
3)积分角速度,delta_q * Quaterniond(1, un_gyr(0) * _dt / 2, un_gyr(1) * _dt / 2, un_gyr(2) * _dt / 2);这里使用了近似计算,假设角速度接近0,从而将sinx转为x
4)将当前线加速度转到相机坐标,un_acc_1 = result_delta_q * (_acc_1 - linearized_ba);
5)计算线加速度中值,un_acc = 0.5 * (un_acc_0 + un_acc_1);
6)积分位移,result_delta_p = delta_p + delta_v * _dt + 0.5 * un_acc * _dt * _dt;
7)积分速度,result_delta_v = delta_v + un_acc * _dt;
8)零偏不变,result_linearized_ba = linearized_ba; result_linearized_bg = linearized_bg;
9)X_k+1 = F * X_k + V * noise (noise为一开始定义,后续不更新),计算转移矩阵F,以及噪声矩阵V,
10)计算残差对变量的导数,即雅可比矩阵,jacobian = F * jacobian;jacobian初始值为单位矩阵,因为一开始时,各变量只与自身有关
11)预测估计协方差矩阵,covariance = F * covariance * F.transpose() + V * noise * V.transpose(); 这个矩阵的逆用于后续作为残差的信息矩阵
(2)更新预积分值与累计时间
(3)tmp_pre_integration记录每一帧之间的预积分,而不是只记录关键帧之间的预积分,另一个积份就是只记录关键帧之间的
(4)记录每一次预积分的值Rs、Ps、Vs
5、取出回环帧处理
6、channels数据顺序:id、像素坐标x、y、去畸变的归一化坐标下的速度x、y,point为去畸变的归一化相机坐标系,提取出特征点数据到image
7、特征点feature添加(feature是f_manager的变量):
(1)如果是新特征点,则新创建一个特征点id,这里的feature_id是id,frame_count就是该特征点在滑窗中的当前位置,作为这个特征点的起始位置。在feature_per_frame记录特征点信息,坐标、速度
(2)如果已经存在,则直接在feature_per_frame后叠加特征点信息
8、关键帧判断:
(1)前两帧为关键帧
(2)特征点数量少于20为关键帧
(3)计算归一化坐标系下,对应特征点在相邻帧下的平均位移(视差),需要大于设定的阈值
9、将特征点、时间、和预积分得到的帧间约束记录到all_image_frame
10、tmp_pre_integration清零
11、外参初始化:
(1)ESTIMATE_EXTRINSIC为2时,没有先验外参,需要自己计算;为1时,表示有可信的外参,但是仍需要优化,为0时则是精确的,不需要优化。
(2)获取当前帧和上一帧的所有归一化坐标对
(3)使用cv::findFundamentalMat计算基础矩阵,因为是归一化坐标,K为单位矩阵,因此这个求的也是本质矩阵E = t * R
(4)根据本质矩阵求解R:https://www.cnblogs.com/weihao-ysgs/p/epipolar-constriant-nature.html
(5)使用预积分和R求解外参:https://blog.csdn.net/ergevv/article/details/139201880?spm=1001.2014.3001.5501
(6)求解一次后,外参已经具有可信度,将ESTIMATE_EXTRINSIC设为1
12、初始化:
(1)一开始solver_flag 为 INITIAL
(2)累积关键帧,如果不够WINDOW_SIZE帧,则frame_count++,直到关键帧足够,则开始初始化。
(3)计算相邻帧的线加速度方差,若方差较小,则代表运动状态变化较小,不宜初始化
(4)遍历特征点,将各帧下的归一化坐标放到一起再放入sfm_f
(5)从滑窗的第一帧开始计算与最后一帧的共视特征点,需要大于20个。计算对应特征点在不同帧之间坐标的平均距离(视差),需要大于设定阈值。然后根据两帧匹配对求解E矩阵,再调用recoverPose通过E矩阵与R、t的关系求解R和带尺度的t(非真实的),记录枢纽帧为l
(6)以枢纽帧为世界系原点,则滑窗最后一帧位姿为(5)所求的R和t
(7)通过sfm求出每一帧的位姿,通过三角化(求深度)、PnP(求位姿)计算出所有特征点的坐标,再通过BA优化
(8)零偏求解:利用预积分和视觉求得的相对位姿对零偏约束列出方程,使用Cholesky求解
(9)惯性对齐:求解imu坐标系下速度、世界坐标下g、s
(10)使用(9)求得的g作为方向向量,模为设定好的固定值。类似第(9)步再优化g
(11)将(7)求得每一帧的位姿用来更新关键帧位姿
(12)将特征点深度estimated_depth设为-1,设置之前计算出来的旋转外参
(13)使用三角化计算特征点在被观测的第一帧带尺度深度
(14)使用计算的零偏重新积分预积分(这里不使用雅可比修正,第一次积分误差较大)
(15)目前原点是在枢纽帧下,将所有数据转到第0帧关键帧下(在优化过程中,因为重力的参与,pitch和roll已经被修正,但是yaw不可观,求出重力的时候,只需要更新yaw即可)
13、
(1)设solver_flag 为NON_LINEAR
后端优化:
1、预积分约束:之前计算的相对变换和预积分之间的残差
2、重投影约束:将第i帧下的特征归一化的坐标投影到第j帧得到归一化坐标与光流追踪得到的归一化坐标的差作为残差
边缘化:
1、求出第0帧和第一帧的雅可比矩阵和残差,约束关系和后端优化是一致的
边缘化作用,去除了旧帧之后得到新的约束关系,然后重新求解得到J^T*J在后端优化时仍然使用
回环检测:重投影约束
改进:
1、使用因子图代替滑窗边缘化
相关文章:
VINS-mono代码笔记
feature_tracker_node.cpp: 一、通过roslaunch文件的参数服务器获得配置参数 二、获得相机的内参 三、订阅图像,img_callback: 1、第一帧图像只记录时间戳 2、与之前时间戳比较一下,判断是否要发布当前帧,避免高频率发送ÿ…...
Maven下载安装IDEA使用MavenJava在pom.xml配置教程
一、Maven 简介 Maven 是一个强大的项目管理和构建工具,主要用于 Java 项目的构建、依赖管理和文档生成等。它通过一个统一的 XML 文件(pom.xml)来管理项目的整个生命周期,包括编译、测试、打包、发布等环节。 二、Maven 下载与…...
NAT(网络地址转换)技术详解:网络安全渗透测试中的关键应用与防御策略
目录 NAT的作用 NAT类型 NAT工作流程示例 NAT 转换技术的原理 源地址转换(SNAT,Source NAT): 目标地址转换(DNAT,Destination NAT): 端口地址转换(PAT,…...
newgrp docker需要每次刷新问题
每次都需要运行 newgrp docker 的原因: 当用户被添加到 docker 组后,当前会话并不会立即更新组信息,因此需要通过 newgrp docker 切换到新的用户组以使权限生效 如果不想每次都手动运行 newgrp docker,可以在终端中配置一个自动刷新的脚本。…...
【FastAPI 使用FastAPI和uvicorn来同时运行HTTP和HTTPS的Python应用程序】
在本文中,我们将介绍如何使用 FastAPI和uvicorn来同时运行HTTP和HTTPS的 Python应用程序。 简介 FastAPI是一个高性能的Web框架,可以用于构建快速、可靠的API。它基于Python的类型提示和异步支持,使得开发者可以轻松地编写出安全且高效的代…...
容器化部署Kafka的最佳实践:基于KRaft模式的无ZooKeeper方案
一、docker 部署kafka单节点 1.1安装docker 可以参考这篇CentOS 7安装docker并配置镜像加速 1.3 运行kafka(注意修改zookeeper,kafka地址) docker run -d --name kafka -e KAFKA_ADVERTISED_LISTENERSPLAINTEXT://172.16.10.180:9092 -p …...
20250214 随笔 线程安全 线程不安全
1. 什么是线程安全 & 线程不安全? 线程安全(Thread-Safe):在多线程环境下访问同一个对象时,不会产生数据竞争、不会出现数据不一致的问题。线程不安全(Not Thread-Safe):在多线…...
rem、em、vw区别
在前端开发里,rem、em、vw都是用来设置元素大小的单位,下面就用大白话讲讲它们的区别。 参考标准不一样 rem:就像大家都用同一把“大尺子”来量东西,这把“大尺子”就是网页里根元素(也就是 <html> 标签&#…...
【PHP】php+mysql 活动信息管理系统(源码+论文+数据库+数据库文件)【独一无二】
👉博__主👈:米码收割机 👉技__能👈:C/Python语言 👉专__注👈:专注主流机器人、人工智能等相关领域的开发、测试技术。 【PHP】php 活动信息管理系统(源码论文…...
cURL请求与Javascript请求转换工具
cURL请求与Javascript请求在线转换工具(如 curlconverter) 首先,看看各个证据中关于curl的定义。提到cURL是“Client for URLs”的缩写,最初全大写是为了方便记忆,社区也将其解释为“Client URL Request Library”或递归的“Curl URL Request Library”。同时,还指出cURL…...
rv1103b编译opencv
opencv-3.4.16,png的neon会报错,如果想开可以参考 https://blog.csdn.net/m0_60827485/article/details/137561429 rm -rf build mkdir build cd build cmake -DCMAKE_BUILD_TYPERELEASE \ -DCMAKE_C_COMPILERxxx/arm-rockchip831-linux-uclibcgnueabih…...
thingboard告警信息格式美化
原始报警json内容: { "severity": "CRITICAL","acknowledged": false,"cleared": false,"assigneeId": null,"startTs": 1739801102349,"endTs": 1739801102349,"ackTs": 0,&quo…...
OpenHarmonry 5.0.1源码下载与编译
预置环境:硬盘500G、内存32G、Ubuntu 20.04.6 LTS Ubuntu系统下载路径:ubuntu-releases安装包下载_开源镜像站-阿里云 一、必需环境 sudo apt-get update && sudo apt-get install binutils binutils-dev git git-lfs gnupg flex bison gperf…...
【Python深入浅出㊸】解锁Python3中的TensorFlow:开启深度学习之旅
目录 一、TensorFlow 简介1.1 定义与背景1.2 特点 二、Python 3 与 TensorFlow 的关系2.1 版本对应2.2 为何选择 Python 3 三、安装 TensorFlow3.1 安装步骤3.2 验证安装 四、TensorFlow 基本概念与使用方法4.1 计算图(Graph)4.2 会话(Sessio…...
STM32 外部中断和NVIC嵌套中断向量控制器
目录 背景 外部中断/事件控制器(EXTI) 主要特性 功能说明 外部中断线 嵌套向量中断控制器 特性 中断线(Interrupt Line) 中断线的定义和作用 STM32中断线的分类和数量 优先级分组 抢占优先级(Preemption Priority) …...
string类详解(上)
文章目录 目录1. STL简介1.1 什么是STL1.2 STL的版本1.3 STL的六大组件 2. 为什么学习string类3. 标准库中的string类3.1 string类3.2 string类的常用接口说明 目录 STL简介为什么学习string类标准库中的string类string类的模拟实现现代版写法的String类写时拷贝 1. STL简介 …...
DeepSeek教unity------Dotween
1、命名法 Tweener(补间器):一种控制某个值并对其进行动画处理的补间。 Sequence(序列):一种特殊的补间,它不直接控制某个值,而是控制其他补间并将它们作为一个组进行动画处理。 Tw…...
AIP-146 泛化域
编号146原文链接AIP-146: Generic fields状态批准创建日期2019-05-28更新日期2019-05-28 API中的大多数域,无论是在请求、资源还是自定义应答中,都有具体的类型或模式。这个模式是约定的一部分,开发者依此约定进行编码。 然而,偶…...
【Go并发编程】Goroutine 调度器揭秘:从 GMP 模型到 Work Stealing 算法
每天一篇Go语言干货,从核心到百万并发实战,快来关注魔法小匠,一起探索Go语言的无限可能! 在 Go 语言中,Goroutine 是一种轻量级的并发执行单元,它使得并发编程变得简单高效。而 Goroutine 的高效调度机制是…...
【前端】Vue组件库之Element: 一个现代化的 UI 组件库
文章目录 前言一、官网1、官网主页2、设计原则3、导航4、组件 二、核心功能:开箱即用的组件生态1、丰富的组件体系2、特色功能亮点 三、快速上手:三步开启组件化开发1、安装(使用Vue 3)2、全局引入3、按需导入(推荐&am…...
第十五天 学习并实践HarmonyOS应用的基本结构、页面导航和状态管理
HarmonyOS应用开发入门:从基本结构到状态管理实战指南 前言 (约300字,说明HarmonyOS的发展前景,应用开发的市场需求,以及本教程的核心价值。强调手把手教学特点,降低学习门槛) 一、HarmonyOS应…...
Cursor生成JAVA相关的关键词提示规则
在项目根目录创建一个.curstorrules文件(注意有个小数点),之后在该文件内填入下面内容 你是 Java 编程、Spring Boot、Spring Framework、Maven、JUnit 及相关 Java 技术的专家。 代码风格与结构 编写整洁、高效且文档完善的 Java 代码&am…...
数据结构:队列(Queue)及其实现
队列(Queue)是一种广泛使用的线性数据结构,它遵循先进先出(FIFO,First In, First Out)的原则。也就是说,最早插入队列的元素会最先被移除。队列是一种典型的顺序存取结构,它与栈&…...
MoE架构中的专家选择门控机制:稀疏激活如何实现百倍效率突破?
技术原理(数学公式与核心逻辑) 核心公式 门控网络输出: G ( x ) Softmax ( W g ⋅ x b g ) G(x) \text{Softmax}(W_g \cdot x b_g) G(x)Softmax(Wg⋅xbg) 最终输出: y ∑ i 1 n G i ( x ) ⋅ E i ( x ) (仅保留Top-…...
坐井说天阔---DeepSeek-R1
前言 DeepSeek-R1这么火,虽然网上很多介绍和解读,但听人家的总不如自己去看看原论文。于是花了大概一周的时间,下班后有进入了研究生的状态---读论文。 DeepSeek这次的目标是探索在没有任何监督数据的情况下训练具有推理能力的大模型&#…...
UART(一)——UART基础
一、定义 UART(Universal Asynchronous Receiver/Transmitter)是一种广泛使用的串行通信协议,用于在设备间通过异步方式传输数据。它无需共享时钟信号,而是依赖双方预先约定的参数(如波特率)完成通信。 功能和特点 基本的 UART 系统只需三个信号即可提供稳健的中速全双工…...
DeepSeek 的创新融合:多行业应用实践探索
引言 在数字化转型的浪潮中,技术的融合与创新成为推动各行业发展的关键力量。蓝耘平台作为行业内备受瞩目的创新平台,以其强大的资源整合能力和灵活的架构,为企业提供了高效的服务支持。而 DeepSeek 凭借先进的人工智能技术,在自然…...
C语言中的常量与只读变量,#define与const的区别
#include中的#表明C处理器需要在编译器接手工作之前先处理这条指令。 #define 这条定义宏的语句,是不是很熟悉,这条预处理指令会在编译器编译前把源文件中使用到这个宏的地方都先展开。 #define NUM 12 这个定义了一个宏常量,它的处理发生编…...
Python常见面试题的详解6
1. 按字典 value 值排序 要点:对于给定字典,使用 sorted() 函数结合 items() 方法,依据 value 进行排序,也可以定义一个通用函数,支持按 value 升序或降序排序。示例: python d {a: 1, b: 2, c: 3, d: …...
CentOS 7超详细安装教程(含镜像)
1. 安装前准备 1.1 CentOS简介 CentOS(Community Enterprise Operating System,中文意思是:社区企业操作系统)是一种基于 Red Hat Enterprise Linux(RHEL)源代码构建的免费开源操作系统。它在稳定性、安全…...
