为何初始化外参

当外参完全不知道的时候,VINS也可以在线对其进行估计(rotation),先在processImage内进行初步估计,然后在后续优化时,会在optimize函数中再次优化。

如何初始化外参

外参校准函数为:
    if(ESTIMATE_EXTRINSIC == 2)
{
ROS_INFO("calibrating extrinsic par am, rotation movement is needed");
if (frame_count != 0)
{
vector<pair<Vector3d, Vector3d>> corres = f_manager.getCorresponding(frame_count - 1, frame_count);
Matrix3d calib_ric;
if (initial_ex_rotation.CalibrationExRotation(corres, pre_integrations[frame_count]->delta_q, calib_ric))
{
ROS_WARN("initial extrinsic rotation calib success");
ROS_WARN_STREAM("initial extrinsic rotation: " << endl << calib_ric);
//有几个相机,就有几个ric,目前单目情况下,ric内只有一个值
ric[0] = calib_ric;
RIC[0] = calib_ric;
//如果校准成功就设置flag为1
ESTIMATE_EXTRINSIC = 1;
}
}
}

核心函数在initial_ex_rotaion.cpp内

bool InitialEXRotation::CalibrationExRotation(vector<pair<Vector3d, Vector3d>> corres, Quaterniond delta_q_imu, Matrix3d &calib_ric_result)
{
frame_count++;
//计算前后两帧的旋转矩阵,加到Rc向量内,直到校准成功
Rc.push_back(solveRelativeR(corres));
Rimu.push_back(delta_q_imu.toRotationMatrix());
Rc_g.push_back(ric.inverse() * delta_q_imu * ric); Eigen::MatrixXd A(frame_count * 4, 4);
A.setZero();
int sum_ok = 0;
for (int i = 1; i <= frame_count; i++)
{
Quaterniond r1(Rc[i]);
Quaterniond r2(Rc_g[i]); double angular_distance = 180 / M_PI * r1.angularDistance(r2);
ROS_DEBUG(
"%d %f", i, angular_distance); double huber = angular_distance > 5.0 ? 5.0 / angular_distance : 1.0;
++sum_ok;
Matrix4d L, R; double w = Quaterniond(Rc[i]).w();
Vector3d q = Quaterniond(Rc[i]).vec();
L.block<3, 3>(0, 0) = w * Matrix3d::Identity() + Utility::skewSymmetric(q);
L.block<3, 1>(0, 3) = q;
L.block<1, 3>(3, 0) = -q.transpose();
L(3, 3) = w; Quaterniond R_ij(Rimu[i]);
w = R_ij.w();
q = R_ij.vec();
R.block<3, 3>(0, 0) = w * Matrix3d::Identity() - Utility::skewSymmetric(q);
R.block<3, 1>(0, 3) = q;
R.block<1, 3>(3, 0) = -q.transpose();
R(3, 3) = w; A.block<4, 4>((i - 1) * 4, 0) = huber * (L - R);
} JacobiSVD<MatrixXd> svd(A, ComputeFullU | ComputeFullV);
Matrix<double, 4, 1> x = svd.matrixV().col(3);
Quaterniond estimated_R(x);
ric = estimated_R.toRotationMatrix().inverse();
//cout << svd.singularValues().transpose() << endl;
//cout << ric << endl;
Vector3d ric_cov;
ric_cov = svd.singularValues().tail<3>();
if (frame_count >= WINDOW_SIZE && ric_cov(1) > 0.25)
{
calib_ric_result = ric;
return true;
}
else
return false;
}

通过SVD解旋转外参原理如下:



SVD的原理与应用可参考博客

通过计算匹配图像之间的的本质矩阵得到旋转矩阵
Matrix3d InitialEXRotation::solveRelativeR(const vector<pair<Vector3d, Vector3d>> &corres)
{
if (corres.size() >= 9)
{
vector<cv::Point2f> ll, rr;
for (int i = 0; i < int(corres.size()); i++)
{
ll.push_back(cv::Point2f(corres[i].first(0), corres[i].first(1)));
rr.push_back(cv::Point2f(corres[i].second(0), corres[i].second(1)));
}
cv::Mat E = cv::findFundamentalMat(ll, rr);
cv::Mat_<double> R1, R2, t1, t2;
decomposeE(E, R1, R2, t1, t2); if (determinant(R1) + 1.0 < 1e-09)
{
E = -E;
decomposeE(E, R1, R2, t1, t2);
}
//选出合适的R和T
double ratio1 = max(testTriangulation(ll, rr, R1, t1), testTriangulation(ll, rr, R1, t2));
double ratio2 = max(testTriangulation(ll, rr, R2, t1), testTriangulation(ll, rr, R2, t2));
cv::Mat_<double> ans_R_cv = ratio1 > ratio2 ? R1 : R2; Matrix3d ans_R_eigen;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
ans_R_eigen(j, i) = ans_R_cv(i, j);
return ans_R_eigen;
}
return Matrix3d::Identity();
}
double InitialEXRotation::testTriangulation(const vector<cv::Point2f> &l,
const vector<cv::Point2f> &r,
cv::Mat_<double> R, cv::Mat_<double> t)
{
cv::Mat pointcloud;
cv::Matx34f P = cv::Matx34f(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0);
cv::Matx34f P1 = cv::Matx34f(R(0, 0), R(0, 1), R(0, 2), t(0),
R(1, 0), R(1, 1), R(1, 2), t(1),
R(2, 0), R(2, 1), R(2, 2), t(2));
cv::triangulatePoints(P, P1, l, r, pointcloud);
int front_count = 0;
for (int i = 0; i < pointcloud.cols; i++)
{
double normal_factor = pointcloud.col(i).at<float>(3); cv::Mat_<double> p_3d_l = cv::Mat(P) * (pointcloud.col(i) / normal_factor);
cv::Mat_<double> p_3d_r = cv::Mat(P1) * (pointcloud.col(i) / normal_factor);
if (p_3d_l(2) > 0 && p_3d_r(2) > 0)
front_count++;
}
ROS_DEBUG("MotionEstimator: %f", 1.0 * front_count / pointcloud.cols);
return 1.0 * front_count / pointcloud.cols;
}
//请参考视觉slam14讲第145页
void InitialEXRotation::decomposeE(cv::Mat E,
cv::Mat_<double> &R1, cv::Mat_<double> &R2,
cv::Mat_<double> &t1, cv::Mat_<double> &t2)
{
cv::SVD svd(E, cv::SVD::MODIFY_A);
cv::Matx33d W(0, -1, 0,
1, 0, 0,
0, 0, 1);
cv::Matx33d Wt(0, 1, 0,
-1, 0, 0,
0, 0, 1);
R1 = svd.u * cv::Mat(W) * svd.vt;
R2 = svd.u * cv::Mat(Wt) * svd.vt;
t1 = svd.u.col(2);
t2 = -svd.u.col(2);
}

VINS 估计器之外参初始化的更多相关文章

  1. VINS 估计器之结构初始化

    为什么要初始化 非线性VINS估计器的性能对于初始的速度,尺度,重力向量,空间点3D位置,以及外参等非常敏感.在很多场合中,能做到相机和IMU即插即用,线上自动校准与初始化,将会给用户带来极大的方便性 ...

  2. 关于map容器的元素被无参初始化

    使用C++中的map容器定义一个mp,当你执行if语句判断mp[3]是否为1时,那么如果mp[3]以前不存在,此时mp[3]就会被无参初始化,second赋值为0. 以下的程序可以证明这一点.执行了第 ...

  3. VINS(四)初始化与相机IMU外参标定

    和单目纯视觉的初始化只需要获取R,t和feature的深度不同,VIO的初始化话通常需要标定出所有的关键参数,包括速度,重力方向,feature深度,以及相机IMU外参$R_{c}^{b}$和$p_{ ...

  4. VINS(八)初始化

    首先通过imu预积分陀螺仪和视觉特征匹配分解Fundamental矩阵获取rotationMatrix之间的约束关系,联立方程组可以求得外参旋转矩阵: 接下来会检测当前frame_count是否达到W ...

  5. [VINS]IMU与相机之间旋转量的标定

    VINS-Mono[1]中IMU-Camera外参旋转量\(R_b^c\)的计算方法在他们实验室发的之前的论文有详细讲解[2].视觉利用匹配特征点中的基础矩阵求出相机坐标系下两帧的旋转量\(R_{c_ ...

  6. Objective-C对象初始化 、 实例方法和参数 、 类方法 、 工厂方法 、 单例模式

    1 重构Point2类 1.1 问题 本案例使用初始化方法重构Point2类,类中有横坐标x.纵坐标y两个属性,并且有一个能显示位置show方法.在主程序中创建两个Point2类的对象,设置其横纵坐标 ...

  7. Objective-c初始化和便利构造

    1.创建一个Student这个类 2.声明和实现 1).在Studnet.h文件中对属性和方法的声明 其中这个方法是带参初始化 而这个方法是便利构造.注意与上边的区别 2.在Studnet.m中实现 ...

  8. WMS—启动过程

    基于Android 6.0源码, 分析WMS的启动过程. 一. 概述 Surface:代表画布 WMS: 添加window的过程主要功能是添加Surface,管理所有的Surface布局,以及Z轴排序 ...

  9. JavaScript概述.pdf

    第1章 JavaScript概述 第2章 使用JavaScript 第3章 语法.关键保留字及变量 第4章 数据类型 第5章 运算符 第6章 流程控制语句 第7章 函数 //没有参数的函数 funct ...

随机推荐

  1. 【NOIP2016提高A组模拟7.17】锦标赛

    题目 403机房最近决定举行一场锦标赛.锦标赛共有N个人参加,共进行N-1轮.第一轮随机挑选两名选手进行决斗,胜者进入下一轮的比赛,第二轮到第N-1轮再每轮随机挑选1名选手与上一轮胜利的选手决斗,最后 ...

  2. vs 2010创建Windows服务定时timer程序

    vs 2010创建Windows服务定时timer程序: 版权声明:本文为搜集借鉴各类文章的原创文章,转载请注明出处:  http://www.cnblogs.com/2186009311CFF/p/ ...

  3. Redis学习:Redis的安装与配置

    Redis是新兴的一种内存数据库技术,在数据高速读写方面有着明显的优势.前几天,Redis3.0正式版本发布,为我们带来了Redis集群功能.这一功能很早就投入了开发,直到现在才真正走进我们的视野.可 ...

  4. SetwindowText 之线程阻塞

    示意代码: CriticalSection g_Section; CDialog g_Dlg; // 工作线程函数UINT TreadFunc_A(PVOID para){ Sleep(10); g_ ...

  5. 3D Computer Grapihcs Using OpenGL - 03 OpenGL Buffer Data

    本节绘制一个三角形,并讲解Buffer Object-缓冲对象 OpenGL的窗口坐标 屏幕中心为坐标原点,横向朝右为x正方向,纵向朝上为y正方向,最大值最小值分别为1,-1. Buffer Obje ...

  6. All-one Matrices

    All-one Matrices 单调栈 最大全一矩阵计数 #include<bits/stdc++.h> #define maxn 3005 using namespace std; # ...

  7. E. Compress Words

    E. Compress Words KMP #include<bits/stdc++.h> using namespace std; ]; int len; void getNext(ch ...

  8. [CSP-S模拟测试]:购物(柯朵莉树)

    题目描述 $visit_world$有一个商店,商店里卖$N$个商品,第$i$个的价格为$a[i]$我们称一个正整数$K$是美妙的,当且仅当我们可以在商店里选购若干个商品,使得价格之和落在区间$[K, ...

  9. jsp页面a标签URL转码问题

    简单的办法只有一句话,在后台对传过来的字符串(value)加一句: String value = new String(value.getBytes("ISO-8859-1"),& ...

  10. MySQL主从复制中replicate-ignore-db replicate-wild-ignore-table的应用

    MySQL主从复制中replicate-ignore-db replicate-wild-ignore-table的应用 replicate-ignore-dbreplicate-wild-ignor ...