目录

vins_estimator

vins_estimator

rosNodeTest.cpp

读取配置文件的参数

readParameters(config_file);

  • readParameters()将配置文件的参数读取到parameters.h中

estimator.setParameter()

  • 设置estimator的外参、td、g
  • 设置FeatureManager的旋转外参
  • 设置ProjectionFactor的协方差(此处是协方差的开方)
  • 设置FeatureTracker的内参
  • 如果是多线程,processMeasurements()会一直工作

让发布者注册话题

registerPub(ros::NodeHandle &n)

订阅者订阅话题

  • imu_callback

    • 从消息中获取时间戳、线加速度和角加速度

    • 将imu信息输入到estimator中estimator.inputIMU(t, acc, gyr)

      • 将值加到accBuf和gyrBuf中

      • 根据上一帧的pvq和imu的输入来更新此时的pvq fastPredictIMU(t, linearAcceleration, angularVelocity)

	- 发布最新的pvq信息

pubLatestOdometry(latest_P, latest_Q, latest_V, t)

  • feature_callback

    • 获得点云的id、cameraid、3d坐标、像素坐标、像素速度、时间戳信息

    • 将特征点信息输入到estimator中 estimator.inputFeature(t, featureFrame)

      • 将值加入到featureBuf中
      • processMeasurements()
  • img0_callback

    • 获得左目的图片消息,存到rosNodeTest的img0_buf中
  • img1_callback

    • 获得右目的图片消息,存到rosNodeTest的img1_buf中
  • restart_callback 重启estimator,重新设置参数

    • estimator.clearState() 把buf清空,参数设置成初始值
    • estimator.setParameter()
  • imu_switch_callback

    • 更改estimator中是否使用imu选项 estimator.changeSensorType()

      • 如果现在使用了imu,要重启estimator
  • cam_switch_callback

    • 更改estimator中是否使用imu选项 estimator.changeSensorType()

将图像送给estimator

std::thread sync_thread{sync_process} 不断执行

  • 双目

    • 从img0_buf和img1_buf中判断两帧的时间差不超过0.003s
    • 通过getImageFromMsg()获得左右目的cv::Mat图像
    • estimator.inputImage(time, image0, image1)
  • 单目

    • 从img0_buf中取第一帧,getImageFromMsg()获得图像

    • estimator.inputImage(time, image)

      • featureFrame = featureTracker.trackImage(t, _img)

        • cv::calcOpticalFlowPyrLK()

        • 反向追踪

        • reduceVector()把没追踪到的点除去

        • 使特征点分布均匀 setMask()

          • 设置mask图
          • 将当前追踪的点按追踪次数降序排
          • 清空cur_pts、ids、track_cnt
          • 通过画圈的方式使特征点均匀,重新填充cur_pts、ids、track_cnt
        • cv::goodFeaturesToTrack()提取角点

        • 把新角点增加进cur_pts等

        • undistortedPts(cur_pts, m_camera[0])将像素坐标恢复成归一化坐标放到cur_un_pts中

        • ptsVelocity(ids, cur_un_pts, cur_un_pts_map, prev_un_pts_map)求像素速度(实际上是在归一化平面求)把结果放入pts_velocity

          • 把id和当前帧像素坐标对应
          • 通过前后两帧像素差除以时间求速度
        • 左右目光流追踪

        • 反向左右目光流追踪

        • 和单目相似,更新右目信息

        • drawTrack()

        • prev=cur hasPrediction = false

        • 将归一化坐标、真正的像素坐标、归一化平面xy的速度封装成featureFrame,如果有右目,再来一次,返回featureFrame

      • 获得左目图像,pubTrackImage(imgTrack, t)发布消息用于可视化

      • featureBuf.push(make_pair(t, featureFrame))

      • processMeasurements()

        • feature为当前帧的特征点,加上时间戳 curTime是当前时间

        • 循环等待当前时刻的imu数据到来

        • 把前一帧和当前帧之间的buf数据取出放Vector中 getIMUInterval(prevTime, curTime, accVector, gyrVector)

        • initFirstIMUPose(accVector)

          • 取这段时间的平均加速度作为重力
          • 让加速度和重力对齐,修正Rs[0]
        • 根据上一图像帧的位姿和之间的imu数据进行粗略的预积分得到现在图像帧的pvq processIMU(accVector[i].first, dt, accVector[i].second, gyrVector[i].second)

          • 把dt,加速度、角速度加到当前帧的pre_integrations中,再加入到当前帧的对应的buf中
		- processImage(feature.second, feature.first)

			- addFeatureCheckParallax(frame_count, image, td)判断边缘化最老帧还是次新帧

				- 更新feature数组
				- compensatedParallax2(it_per_id, frame_count)算视差

					- 在归一化平面计算点的距离

				- 通过视差判断是否为关键帧

			- 将当前帧封装成ImageFrame,装进all_image_frame
			- 获得特征点在两帧下的归一化坐标

getCorresponding(frame_count - 1, frame_count) - CalibrationExRotation(corres, pre_integrations[frame_count]->delta_q, calib_ric)

				- 根据对极约束求位姿,8点法

solveRelativeR(corres)

					- 通过两帧的像素坐标(归一化坐标XY)求本质矩阵
					- 将E(-E)分解成R和t

decomposeE()

						- 

					- 检验四组解

testTriangulation(ll, rr, R1, t1)获得真正的R

						- 通过cv::triangulatePoints()检验深度为正的点的比例

				- 求qbc,详细看《手写VIO》第七讲第10页

				- 如果成功把结果给calib_ric_result

			- 初始化

				- 单目+imu

					- initialStructure()

						- imu激励是否足够

							- 计算所有帧的加速度的标准差

						- 创建Q、T、sfm_f、sfm_tracked_points
						- 把feature信息填充到sfm_f中
						- 确定参考帧l,求最新帧到它的位姿

relativePose(relative_R, relative_T, l)

							- 遍历滑窗,获得第i帧和最新帧的共视点
							- 计算共视点的平均视差并判断
							- 对极约束,五点法求位姿

solveRelativeRT(corres, relative_R, relative_T)

								- RANSAC求解本质矩阵E
								- 对极约束恢复位姿

cv::recoverPose(E, ll, rr, cameraMatrix, rot, trans, mask)

						- SFM

sfm.construct(frame_count + 1, Q, T, l, relative_R, relative_T,sfm_f, sfm_tracked_points)

							- 创建c_XXX数组表示第l帧相机在别的帧相机系下的表示
							- 三角化l和最新帧

triangulateTwoFrames()

								- 遍历特征点,如果是这两个帧的共视点

triangulatePoint(),求出在l相机系的空间坐标,更新sfm_f 《手写VIO》

							- pnp求l+1帧的位姿

solveFrameByPnP()

								- 获得特征点的3d坐标和2d坐标
								- 将cv::Eigen转化为cv::Mat
								- cv::solvePnP()求旋转向量和平移向量

							- 三角化l+1帧和最新帧

triangulateTwoFrames() - 三角化l帧和l+1帧 - pnp求l-1帧 - 三角化l-1帧和l帧 - triangulatePoint()三角化剩余点,更新sfm_f - 全局ba

								- 用ceres求解,添加参数块,将先验设为恒定
								- ReprojectionError3D定义残差

									- 重投影-光流

								- 添加残差块,ceres求解
								- 更新滑窗每一帧的q和T,填充sfm_tracked_points

						- pnp求所有帧

							- 如果当前帧在滑窗内,更新ImageFrame的R和T
							- 如果当前帧不在滑窗内,找这个帧被三角化的特征点,获得3d、2d坐标
							- cv::solvePnP()求解该帧的位姿
							- 更新该帧ImageFrame的R和T

						- 视觉imu对齐

visualInitialAlign()

							- VisualIMUAlignment(all_image_frame, Bgs, g, x)

								- solveGyroscopeBias(all_image_frame, Bgs)

									- 
									- ldlt求解

更新滑窗内的Bgs数组 - 所有帧重新预积分 repropagate(Vector3d::Zero(), Bgs[0])

								- LinearAlignment(all_image_frame, g, x)

									- 
									- RefineGravity(all_image_frame, g, x)

										- 

							- 用尺度、优化后的速度更新滑窗内的Ps、Rs、Vs,并把坐标系从第l帧相机系转到世界系

								- 对滑窗内的帧重新预积分

repropagate(Vector3d::Zero(), Bgs[i]) - 求将第l帧g变换到世界系的g的旋转,把参考系从第l帧变到第0帧body系(世界系),现在的Rs、Ps、Vs是第i帧imu在第0帧body系 - clearDepth() 清楚feature数组FeaturePerId的深度 - 重新求深度(相对于世界系) triangulate(frame_count, Ps, Rs, tic, ric)

									- 获得首次观测到这个特征点的帧

获得左目右目的像素坐标 triangulatePoint()三角化求世界系的空间坐标 - feature数组的FeaturePerId的深度是在首次观测到该特征点的帧的相机系的z - 三角化的两帧是首次观测到该特征点的帧和它的下一帧 - 详细看手写VIO第6讲25页 - SVD求解

					- optimization()
					- updateLatestStates()
					- slideWindow()
					- slideWindow()

				- 双目+imu

					- initFramePoseByPnP(frame_count, Ps, Rs, tic, ric)

						- 遍历滑窗特征点,获得世界系3d坐标和最新帧的像素坐标
						- solvePoseByPnP()求位姿

							- 主要是格式转换

旋转向量和旋转矩阵转换 cv::solvePnP()

						- 更新最新帧的Ps、Rs

					- triangulate(frame_count, Ps, Rs, tic, ric)
					- 更新all_image_frame的RT(all_image_frame都在滑窗内)
					- solveGyroscopeBias(all_image_frame, Bgs)
					- repropagate(Vector3d::Zero(), Bgs[i])重新预积分
					- optimization()
					- updateLatestStates()
					- slideWindow()

				- 仅双目

					- initFramePoseByPnP(frame_count, Ps, Rs, tic, ric)
					- triangulate(frame_count, Ps, Rs, tic, ric)
					- optimization()
					- updateLatestStates()
					- slideWindow()

				- 把当前帧信息pvqb给下一帧

			- initFramePoseByPnP(frame_count, Ps, Rs, tic, ric)
			- 估计滑窗特征点的深度

三角化滑窗中的特征点,把结果给feature数组featureperid的estimated_depth,这个深度是对于首次观测到它的帧的相机系 triangulate(frame_count, Ps, Rs, tic, ric),把 - optimization()

				- vector2double()

					- 填充para_Pose、para_SpeedBias、para_Ex_Pose、para_Feature、para_Td

其中para_Feature是逆深度,取自于featurePerId.estimated_depth的倒数,且只有4帧及以上追踪到这个特征点才考虑

				- 添加参数块
				- 添加残差块(imu、视觉、先验)
				- ceres::Solve()
				- double2vector()

					- 把double数组填进Ps、Rs等
					- setDepth(dep)

						- 设置featurePerId的estimated_depth,通过深度正负设置featurePerId的solve_flag为1或2,为之后的removeFailures()使用

				- 边缘化

					- 边缘化最老帧

						- vector2double()
						- 把最老帧有关的因子 (先验因子、imu因子、视觉因子和drop_set增加进marginalization_info中
						- 求雅克比和残差

marginalization_info->preMarginalize() - 构建H和b,反解出J和b marginalization_info->marginalize() - 填充addr_shift - 新值赋给旧值

					- 边缘化次新帧

						- vector2double()
						- 边缘化先验因子中和次新帧有关的变量,通过drop_set的形式添加进marginalization_info中
						- marginalization_info->preMarginalize()
						- marginalization_info->marginalize()
						- 填充addr_shift
						- 新值赋给旧值

			- 外点检测

outliersRejection(removeIndex)

				- 获得这个特征点首次观测的帧和共视帧
				- 求重投影误差

reprojectionError - 如果是双目,要增加和共视帧右目的重投影误差 - 平均误差大于阈值,认为是外点

			- 去除外点

removeOutlier(removeIndex)

				- 根据removeIndex把feature中对应的特征点去除

			- removeOutliers(removeIndex)

				- 在featureTracker中把prev_pts、ids、track_cnt对应的点去除

			- 预测下一帧特征点出现的位置

predictPtsInNextFrame()

				- 获得的当前帧和上一帧的位姿

getPoseInWorldFrame() - 假设下一帧的位姿和当前帧到前一帧的位姿相同,进而求下一帧位姿 - 通过位姿重投影,形成预测点 - setPrediction(predictPts)

					- 填充featuretracker的predict_pts,存的是真正的像素坐标,用于下一帧光流追踪

			- slideWindow()

				- 最老帧

					- 信息前移

Ps、Rs、Headers、pre_integrations、dt_buf、linear_acceleration_buf、angular_velocity_buf、Vs、Bas、Bgs - 第10帧信息赋值给第11帧,重新创建第11帧的预积分,清空第11帧的buf - 从all_image_frame中删除最老帧之前的帧 - slideWindowOld()

						- removeBackShiftDepth(R0, P0, R1, P1)

							- start_frame--
							- 更新featurePerId和featurePerFrame
							- 重新计算featurePerId的estimated_depth

						- removeBack()

							- start_frame--
							- 更新featurePerId和featurePerFrame

				- 次新帧

					- 用最新帧信息覆盖次新帧
					- 对于imu,最新帧的buf直接拼接到次新帧上(此时的次新帧是最新帧,信息已被覆盖)
					- 对第11帧重新创建预积分,清空第11帧的buf
					- slideWindowNew()

						- removeFront(frame_count)

							- 更新featurePerId和featurePerFrame

			- 把滑窗中深度为负的点移除

removeFailures() - 将滑窗中的Ps填充进key_poses数组中,用于可视化 - 更新last_RP、last_R0P0 - updateLatestStates()

				- 更新latest_xxx变量,用于fastpredictimu()
				- 用最新的数据不断fastpredictimu()

		- printStatistics()输出数据
		- 向rziv发布

IntegrationBase

repropagate()

  • 把预积分相关的变量设为初始值

  • 遍历所有imu帧,预积分 propagate(dt_buf[i], acc_buf[i], gyr_buf[i])

    • midPointIntegration(_dt, acc_0, gyr_0, _acc_1, _gyr_1, delta_p, delta_q, delta_v, linearized_ba, linearized_bg, result_delta_p, result_delta_q, result_delta_v, result_linearized_ba, result_linearized_bg, 1)

- 新值赋给旧值

evaluate()

IMUFactor

evaluate()

  • 计算残差 pre_integration->evaluate(Pi, Qi, Vi, Bai, Bgi, Pj, Qj, Vj, Baj, Bgj)

ProjectionFactor

MarginalizationFactor

ResidualBlockInfo

  • Evaluate()

    • 调用对应残差的Evalua()函数求残差和雅克比 cost_function->Evaluate(parameter_blocks.data(), residuals.data(), raw_jacobians)
    • 如果有核函数,残差和雅克比需要缩放

MarginalizationInfo

  • addResidualBlockInfo()

    • 填充parameter_block_size和parameter_block_idx,先记为0
  • getParameterBlocks()

    • 将边缘化保留的变量存入到keep_block_xxx中
  • preMarginalize()

    • 调用对应的Evaluate()求残差和雅克比
    • 填充parameter_block_data
  • marginalize()

    • 把边缘化掉的变量排在前面,在parameter_block_idx中体现

    • 构造H和b,舒尔补求新的H和b

- 将H和b分解出J和e

	- 

MarginalizationFactor

  • Evaluate()

    • 计算残差
    • 雅克比就是marginalization_info的雅克比