pts_i和pts_j:具体指什么含义?(分别为第l个路标点在第i, j个相机归一化相机坐标系中的观察到的坐标,P¯¯¯cil \bar{P}^{c_i}_l
P
ˉ

l
c
i


和 P¯¯¯cjl \bar{P}^{c_j}_l
P
ˉ

l
c
j


);
tangent_base:正切平面上的任意两个正交基(在构造函数中通过计算?被赋值);
静态数据成员sqrt_info和sum_t:在何时被赋值的呢?

class ProjectionFactor : public ceres::SizedCostFunction<2, 7, 7, 7, 1>
{
public:
ProjectionFactor(const Eigen::Vector3d &_pts_i, const Eigen::Vector3d &_pts_j);
virtual bool Evaluate(double const *const *parameters, double *residuals, double **jacobians) const;
void check(double **parameters);

Eigen::Vector3d pts_i, pts_j;
Eigen::Matrix<double, 2, 3> tangent_base;
static Eigen::Matrix2d sqrt_info;
static double sum_t;
};

1
2
3
4
5
6
7
8
9
10
11
12
13
步入正题,ProjectionFactor类中的Evaluate成员函数:
优化变量:[pwbi,qwbi],[pwbj,qwbj],[pbc,qbc],λl [p^w_{b_i}, q^w_{b_i}], [p^w_{b_j}, q^w_{b_j}], [p^b_c, q^b_c], \lambda_l[p
b
i

w

,q
b
i

w

],[p
b
j

w

,q
b
j

w

],[p
c
b

,q
c
b

],λ
l

分别对应:[Pi, Qi], [Pj, Qj], [tic, qic], inv_dep_i
(此处的λ \lambdaλ表示什么含义?逆深度inv_dep_i)(w,b w, bw,b代表的坐标系具体指什么?)

解析中(27)式:
Pcjl=Rcb{Rbjw[Rwbi(Rbc1λlP¯¯¯cil+pbc)+pwbi−pwbj]−pbc} P^{c_j}_l=R^c_b\{R^{b_j}_w[R^w_{b_i}(R^b_c\frac{1}{\lambda_l}\bar{P}^{c_i}_l+p^b_c)+p^w_{b_i}-p^w_{b_j}]-p^b_c\}P
l
c
j


=R
b
c

{R
w
b
j


[R
b
i

w

(R
c
b

λ
l

1

P
ˉ

l
c
i


+p
c
b

)+p
b
i

w

−p
b
j

w

]−p
c
b

}
1λlP¯¯¯cil \frac{1}{\lambda_l}\bar{P}^{c_i}_l
λ
l

1

P
ˉ

l
c
i


==>Eigen::Vector3d pts_camera_i = pts_i / inv_dep_i;
(因此,pts_i表示P¯¯¯cil \bar{P}^{c_i}_l
P
ˉ

l
c
i


,为第l个路标点在第i个相机归一化相机坐标系中的观察到的坐标);
(Rbc∗+pbc) (R^b_c*+p^b_c)(R
c
b

∗+p
c
b

) ==>Eigen::Vector3d pts_imu_i = qic * pts_camera_i + tic;
(此处可以看出来,imu与b bb系是一个系?)
Rwbi∗+pwbi R^w_{b_i}*+p^w_{b_i}R
b
i

w

∗+p
b
i

w

==>Eigen::Vector3d pts_w = Qi * pts_imu_i + Pi;
Rbjw(∗−pwbj) R^{b_j}_w(*-p^w_{b_j})R
w
b
j


(∗−p
b
j

w

) ==>Eigen::Vector3d pts_imu_j = Qj.inverse() * (pts_w - Pj);
Rcb(∗−pbc) R^c_b(*-p^b_c)R
b
c

(∗−p
c
b

) ==>Eigen::Vector3d pts_camera_j = qic.inverse() * (pts_imu_j - tic);
因此,pts_camera_j表示Pcjl P^{c_j}_lP
l
c
j


,是由P¯¯¯cil \bar{P}^{c_i}_l
P
ˉ

l
c
i


计算得来的。

解析中(25)式:
rc(zˆcjl,X)=[b1⃗ b2⃗ ](Pcjl∣∣Pcjl∣∣−P¯¯¯cjl) r_c(\hat{z}^{c_j}_l,X)=\begin{bmatrix}\vec{b_1}\\ \vec{b_2} \end{bmatrix}(\frac{P^{c_j}_l}{||P^{c_j}_l||}-\bar{P}^{c_j}_l)r
c

(
z
^

l
c
j


,X)=[
b
1

b
2


](
∣∣P
l
c
j


∣∣
P
l
c
j



P
ˉ

l
c
j


)
residual = tangent_base * (pts_camera_j.normalized() - pts_j.normalized());
residual = sqrt_info * residual;

最后,计算Jacobian,解析中(28)式:
注:此处解析上有一些矩阵维数上的错误,应该为3×∗ 3\times *3×∗,而非3×∗ 3\times *3×∗。
以其中一个为例,分析公式与代码对应关系:
J[0]2×7=[∂rc∂pwbi,∂rc∂qwbi] J[0]^{2\times 7}=[\frac{\partial r_c}{\partial p^w_{b_i}}, \frac{\partial r_c}{\partial q^w_{b_i}}]J[0]
2×7
=[
∂p
b
i

w

∂r
c


,
∂q
b
i

w

∂r
c


]
代码中首先定义了一个jaco_i为3×6 3\times 63×6,然后用一个reduce2×3 2\times 32×3去乘,得到的2×6 2\times 62×6的结果作为J[0] J[0]J[0]的左边6列,最后一列为0;具体如下:
Eigen::Matrix<double, 3, 6> jaco_i;
RcbRbjw R^c_bR^{b_j}_wR
b
c

R
w
b
j

jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose();
−RcbRbjwRwbi(Rbc1λlP¯¯¯cil+pbc) -R^c_bR^{b_j}_wR^w_{b_i}(R^b_c\frac{1}{\lambda_l}\bar{P}^{c_i}_l+p^b_c)−R
b
c

R
w
b
j


R
b
i

w

(R
c
b

λ
l

1

P
ˉ

l
c
i


+p
c
b

)
jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * -Utility::skewSymmetric(pts_imu_i);

Eigen::Matrix<double, 2, 3> reduce(2, 3);
reduce = tangent_base * norm_jaco;此处norm_jaco表达什么含义?对应公式?
reduce = sqrt_info * reduce;

Eigen::Map的用法?
Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);
jacobian_pose_i.leftCols<6>() = reduce * jaco_i;
jacobian_pose_i.rightCols<1>().setZero();

J[1]2×7 J[1]^{2\times 7}J[1]
2×7
、J[2]2×7 J[2]^{2\times 7}J[2]
2×7
和J[3]2×1 J[3]^{2\times 1}J[3]
2×1
的计算类似。

至此,视觉约束暂时告一段落。

bool ProjectionFactor::Evaluate(double const *const *parameters, double *residuals, double **jacobians) const
{
TicToc tic_toc;
Eigen::Vector3d Pi(parameters[0][0], parameters[0][1], parameters[0][2]);
Eigen::Quaterniond Qi(parameters[0][6], parameters[0][3], parameters[0][4], parameters[0][5]);

Eigen::Vector3d Pj(parameters[1][0], parameters[1][1], parameters[1][2]);
Eigen::Quaterniond Qj(parameters[1][6], parameters[1][3], parameters[1][4], parameters[1][5]);

Eigen::Vector3d tic(parameters[2][0], parameters[2][1], parameters[2][2]);
Eigen::Quaterniond qic(parameters[2][6], parameters[2][3], parameters[2][4], parameters[2][5]);

double inv_dep_i = parameters[3][0];

Eigen::Vector3d pts_camera_i = pts_i / inv_dep_i;
Eigen::Vector3d pts_imu_i = qic * pts_camera_i + tic;
Eigen::Vector3d pts_w = Qi * pts_imu_i + Pi;
Eigen::Vector3d pts_imu_j = Qj.inverse() * (pts_w - Pj);
Eigen::Vector3d pts_camera_j = qic.inverse() * (pts_imu_j - tic);
Eigen::Map<Eigen::Vector2d> residual(residuals);

#ifdef UNIT_SPHERE_ERROR
residual = tangent_base * (pts_camera_j.normalized() - pts_j.normalized());
#else
double dep_j = pts_camera_j.z();
residual = (pts_camera_j / dep_j).head<2>() - pts_j.head<2>();
#endif

residual = sqrt_info * residual;

if (jacobians)
{
Eigen::Matrix3d Ri = Qi.toRotationMatrix();
Eigen::Matrix3d Rj = Qj.toRotationMatrix();
Eigen::Matrix3d ric = qic.toRotationMatrix();
Eigen::Matrix<double, 2, 3> reduce(2, 3);
#ifdef UNIT_SPHERE_ERROR
double norm = pts_camera_j.norm();
Eigen::Matrix3d norm_jaco;
double x1, x2, x3;
x1 = pts_camera_j(0);
x2 = pts_camera_j(1);
x3 = pts_camera_j(2);
norm_jaco << 1.0 / norm - x1 * x1 / pow(norm, 3), - x1 * x2 / pow(norm, 3), - x1 * x3 / pow(norm, 3),
- x1 * x2 / pow(norm, 3), 1.0 / norm - x2 * x2 / pow(norm, 3), - x2 * x3 / pow(norm, 3),
- x1 * x3 / pow(norm, 3), - x2 * x3 / pow(norm, 3), 1.0 / norm - x3 * x3 / pow(norm, 3);
reduce = tangent_base * norm_jaco;
#else
reduce << 1. / dep_j, 0, -pts_camera_j(0) / (dep_j * dep_j),
0, 1. / dep_j, -pts_camera_j(1) / (dep_j * dep_j);
#endif
reduce = sqrt_info * reduce;

if (jacobians[0])
{
Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_i(jacobians[0]);

Eigen::Matrix<double, 3, 6> jaco_i;
jaco_i.leftCols<3>() = ric.transpose() * Rj.transpose();
jaco_i.rightCols<3>() = ric.transpose() * Rj.transpose() * Ri * -Utility::skewSymmetric(pts_imu_i);

jacobian_pose_i.leftCols<6>() = reduce * jaco_i;
jacobian_pose_i.rightCols<1>().setZero();
}

if (jacobians[1])
{
Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_pose_j(jacobians[1]);

Eigen::Matrix<double, 3, 6> jaco_j;
jaco_j.leftCols<3>() = ric.transpose() * -Rj.transpose();
jaco_j.rightCols<3>() = ric.transpose() * Utility::skewSymmetric(pts_imu_j);

jacobian_pose_j.leftCols<6>() = reduce * jaco_j;
jacobian_pose_j.rightCols<1>().setZero();
}
if (jacobians[2])
{
Eigen::Map<Eigen::Matrix<double, 2, 7, Eigen::RowMajor>> jacobian_ex_pose(jacobians[2]);
Eigen::Matrix<double, 3, 6> jaco_ex;
jaco_ex.leftCols<3>() = ric.transpose() * (Rj.transpose() * Ri - Eigen::Matrix3d::Identity());
Eigen::Matrix3d tmp_r = ric.transpose() * Rj.transpose() * Ri * ric;
jaco_ex.rightCols<3>() = -tmp_r * Utility::skewSymmetric(pts_camera_i) + Utility::skewSymmetric(tmp_r * pts_camera_i) +
Utility::skewSymmetric(ric.transpose() * (Rj.transpose() * (Ri * tic + Pi - Pj) - tic));
jacobian_ex_pose.leftCols<6>() = reduce * jaco_ex;
jacobian_ex_pose.rightCols<1>().setZero();
}
if (jacobians[3])
{
Eigen::Map<Eigen::Vector2d> jacobian_feature(jacobians[3]);
#if 1
jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i * -1.0 / (inv_dep_i * inv_dep_i);
#else
jacobian_feature = reduce * ric.transpose() * Rj.transpose() * Ri * ric * pts_i;
#endif
}
}
sum_t += tic_toc.toc(http://www.my516.com);

return true;
}

---------------------

VINS-Fusion代码阅读(四)的更多相关文章

  1. 代码阅读分析工具Understand 2.0试用

    Understand 2.0是一款源代码阅读分析软件,功能强大.试用过一段时间后,感觉相当不错,确实可以大大提高代码阅读效率.由于Understand功能十分强大,本文不可能详尽地介绍它的所有功能,所 ...

  2. 40 网络相关函数(八)——live555源码阅读(四)网络

    40 网络相关函数(八)——live555源码阅读(四)网络 40 网络相关函数(八)——live555源码阅读(四)网络 简介 15)writeSocket向套接口写数据 TTL的概念 函数send ...

  3. 29 GroupSock(NetAddressList)——live555源码阅读(四)网络

    29 GroupSock(NetAddressList)——live555源码阅读(四)网络 29 GroupSock(NetAddressList)——live555源码阅读(四)网络 简介 Net ...

  4. 28 GroupSock(NetAddress)——live555源码阅读(四)网络

    28 GroupSock(NetAddress)——live555源码阅读(四)网络 28 GroupSock(NetAddress)——live555源码阅读(四)网络 简介 1) NetAddre ...

  5. 27 GroupSock概述(一)——live555源码阅读(四)网络

    27 GroupSock概述(一)——live555源码阅读(四)网络 27 GroupSock概述(一)——live555源码阅读(四)网络 简介 1.网络通用数据类型定义 2.Tunnel隧道封装 ...

  6. Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  7. [置顶] Linux协议栈代码阅读笔记(一)

    Linux协议栈代码阅读笔记(一) (基于linux-2.6.21.7) (一)用户态通过诸如下面的C库函数访问协议栈服务 int socket(int domain, int type, int p ...

  8. Bleve代码阅读(二)——Index Mapping

    引言 Bleve是Golang实现的一个全文检索库,类似Lucene之于Java.在这里通过阅读其代码,来学习如何使用及定制检索功能.也是为了通过阅读代码,学习在具体环境下Golang的一些使用方式. ...

  9. [置顶] Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  10. 【新人赛】阿里云恶意程序检测 -- 实践记录11.10 - XGBoost学习 / 代码阅读、调参经验总结

    XGBoost学习: 集成学习将多个弱学习器结合起来,优势互补,可以达到强学习器的效果.要想得到最好的集成效果,这些弱学习器应当"好而不同". 根据个体学习器的生成方法,集成学习方 ...

随机推荐

  1. liunx操作系统安装<一>

    一:磁盘分区类型(1)主分区(最多四个主分区,比如window系统的C盘,D盘)(2)扩展分区,逻辑分区(为了能让磁盘多分出几个区域而存在)(3)交换分区(虚拟内存,当物理内存不足时候,作为应急存在)

  2. CMake学习记录--list(列表操作命令)

    CMake是一个跨平台的工程管理工具,能方便的把工程转换为vs各个版本.Borland Makefiles.MSSYS Makefiles.NMake Makefiles等工程,对于经常在不同IDE下 ...

  3. C++初学(1) 简单的加减乘除取余运算代码

    //---------------+-*/%算法---------------------------------------------------------- #include <iost ...

  4. ASP.NET Core MVC 2.x 全面教程_ASP.NET Core MVC 24. Logging

    常用的诊断中间件: UseDeveloperExceptionPage UseStatusCodePages:返回 400~600 的状态码 UseExceptionHandler 自定义异常的处理器 ...

  5. E20170415-ms

    opaque adj 不透明的 n 不透明 adapter n 配适器

  6. Swift3.0 UITextView写反馈界面

    效果图 适配用的 SnapKit 使用介绍:  http://www.hangge.com/blog/cache/detail_1097.html private func creationTextV ...

  7. Tenka1 Programmer Beginner Contest D - IntegerotS(位运算)

    传送门 题意 给出N,K,给出N对数a[i],b[i],选择一些数使得or和小于k且\(max\sum b[i]\) 分析 枚举k的每一个1位,将其删去并让低位全为1,对于每一个这样的数c,如果a[i ...

  8. python list生成表达式

    列表生成式即List Comprehensions,是Python内置的非常简单却强大的可以用来创建list的生成式.运用列表生成式,可以写出非常简洁的代码. >>> list(ra ...

  9. wpf 中AxShockwaveFlash重写以及屏蔽鼠标右键

    在wpf中需要用到flash播放swf或者图片,需要使用 AxShockwaveFlashObjects.dll和ShockwaveFlashObjects.dll 在项目中使用的时候遇到 问题1.使 ...

  10. C#中自定义类数组和结构数组的使用

    如有雷同,不胜荣幸,若转载,请注明 C#中自定义类数组和结构数组的使用 最近在很多项目中发现很多时候给定的数组要实现某个逻辑或处理很是麻烦,一维数组,二维数组,,,等等需要经过n多转换,还不如自己写一 ...