BA模型 第10章
1.BA模型
BA模型就是世界坐标到像素坐标的转换过程。这里多了一个去畸变。因为归一化平面坐标在转成像素坐标的过程中会出现畸变。这里只处理了径向畸变,径向畸变包括桶形失真和枕形失真,都是由于图像放大率随着与光轴之间的距离改变而增大或减小造成的。切向畸变是由于透镜与成像平面没有严格平行造成的。处理径向畸变就是拿归一化平面坐标比如uc,vc乘以(1+k1*rc*rc+k2*rc*rc*rc*rc)得到的,这里的k1,k2是外参,rc=sqrt(uc*uc+vc*vc).而切向畸变就是用到uc*vc了。
世界坐标P到相机坐标P'
P'=Rp+t=[X',Y',Z'].t
相机坐标到归一化坐标
[uc,vc,1].t=[X'/Z',Y'/Z',1].t
归一化坐标去畸变
uc'=uc(1+k1*rc*rc+k2*rc*rc*rc*rc)
vc'=vc(1+k1*rc*rc+k2*rc*rc*rc*rc)
归一化坐标uc',vc'到像素坐标us,vs
us=fx*uc'+cx
vs=fy*vc'+cy
观测方程中,z=h(x,y),x就是相机位姿,y就是路标,即这里的三维点p.求得的z就是观测数据z,也就是像素坐标[us,vs].t.所以h(x,y)也可以写成h(ep,p)
而误差就为e=z-h(x,y)=z-h(ep,p)
那BA模型就是在位姿epi处观测到了路标pj,产生观测数据zi,j.
就是1/2||zi,j-h(epi,pj)||的平方。
2.求解BA
BA的求解简单。用非线性优化方向的话,就是要求一个deltax了。
也就是Hdeltax=g
如果是高斯牛顿法的话,就是
J(x).t*J(x)=-J(x).t*f(x)
这里的J(x)是f(x)对x的导数
令x={ep1,ep2,...,epm,p1,p2,...,pn}.t
那么原来的BA模型就可以定义为f(x)
令xc=[ep1,ep2,..,epm].t
xp=[p1,p2,..,pm].t
F为误差函数对位姿的导数,E为误差函数对路标点的导数,一个为2*6矩阵,du/dp*[I,-P的反对称矩阵】一个是2*3矩阵,du/dp*R。
那么
1/2||f(x+delatx||的平方=1/2||eij+F*deltaxc+E*deltaxp||的平方
那么J=[F,E]
如果高斯牛顿,H=J(x).t*J(x)
如果列文伯格-马夸而特的话H=J(x).t*J(x)+lamda*I
以高斯牛顿为例
H=[H1,H2].t
H1=[F.t*F,F.t*E]
H2=[E.t*F,E.t*E]
其实是要解方程,H*delatx=g.如果直接求H矩阵的逆,非常麻烦,但H是有特殊结构的。也就是它的稀疏性。
3.稀疏性和边缘化
因为eij只和epi,pj有关,跟其他的位姿和路标点无关。所以
Jij=[0,0,..,epi,0,..,0,pj,0,...,0】
那么Jij只在i,i+j处有非零块,而Jij.t*Jij只是(i,i)处,(i,i+j),(i+j,i),(i+j,i+j)处有非零块。
而H就为所有的Jij.t*Jij的和。
那么如果把H按照F.t*F,F.t*E,E.t*F,E.t*E来分,H11=F.t*F,H22=E.t*E。那么H11,H22就是对角阵,只在对角线处有非零块来着。而且H11比较小,H22则比较大。
而H12=F.t*E,H21=E.t*F,可能为稀疏阵也可能为稠密阵,视具体的观测矩阵来定。
邻接矩阵:比如说W.Wij,如果节点i和节点j存在一条边,那么Wij=1,否则为0.
这是图论中的内容,而H当中的H12,H21确实是这样的。如果它们当中存在非零块,说明这个非零块对应的两个变量在图中存在着一条边,也就是位姿epi和路标pj存在着一条边,这可以称之为约束。
H矩阵又称为镐头矩阵和箭头形矩阵,因为它的形状很像箭头和一把镐子。
3.1Schur消元
在slam称之为边缘化(Marginalization)
把H看成是B,E,E.t,C组成的矩阵。其中B其实就是F.t*F,C其实就是E.t*E.此E非彼E.
Hdeltax=g可以分解为
B*deltaxc+E*deltaxp=v
E.t*deltaxc+C*deltaxp=w
其实就是对H矩阵求逆比较难,但是对它里面的对角线矩阵B,C求逆简单啊。这里消的是deltaxp.
把第二个等式两边同乘E*C.n
则
E*C.n*E.t*deltaxc+E*deltaxp=E*C.n*w
两个相减得
(B-E*C.n*E.t)*deltaxc=v-E*C.n*w
因为C.n比较容易求,而B比较小,B-E*C.n*E.t的逆也比较好求,比较困惑的是B-E*C.n*E.t也不是对角线矩阵啊。同样是要对矩阵求逆啊。
deltaxp也可以很容易得出。
记S=B-E*C.n*E.t,则S*deltaxc=v-E*Cn*w
而S矩阵的稀疏性是这么决定的,S矩阵的非对角线的上的非零对角块,表明两个相机之间有共同观测的部分,也称共视。
比如S14,S24不为0,表明相机1,2和相机4有共同观测部分。
S矩阵取决于实际观测的结构,是无法提取预知的。
ORB_SLAM在local_mapping环节,刻意选择具有共同观测的帧作为关键帧,这样S矩阵是稠密矩阵。DSO,OKVIS等用了滑动窗口法,对每一帧都要做一次BA,所以要用一些技巧保持S矩阵的稀疏性。
之所以称为边缘化是从概率的角度讲,求deltaxc,deltaxp变成了
P(xc,xp)=P(xp|xc)*P(xc)
也可以用cholesky分解来进行消元。也可以选择一部分进行消元。
4.鲁棒核函数
问题:如果出现误匹配,会出现一条误差很大的边,算法会专注于调节一个错误的值。
解决:鲁棒核函数。给二范数度量加一个限制 sigma,保证每条边的误差不会太大而掩盖其他的边。
常见的Huber核
H(e),变量是e.
当|e|<=sigma的时候,H(e)=1/2*e*e
其他,H(e)=sigma(|e|-1/2sigma)
ta是光滑连续的。当|e|=sigma的时候,H(e)=1/2*sigma*sigma,另外一个分段也趋向于它。
除了Huber核之外,还有Cauchy核,Tukey核。
5.实践
我们只需要设置BA问题,然后设置Schur消元,然后调用稠密或稀疏矩阵求解器对变量进行优化就可以了。
g2o/ceres都可以用来做BA.
6.BA实际求解 ,g2o
6.1数据信息
data/problem-16-22106-pre.txt
第一行:相机数量,路标数量,观测数据数量
这里是 16 22016 83718
从第二行开始,第i个相机观测j路标所得到的像素坐标
6.2求预测值得几个h文件
6.2.1 rotation.h
这里面定义了轴角和四元数,空间点经轴角旋转之后得到的坐标函数。具体实现看轴角和四元数和空间点.note
这里的轴角变成了angle_axis=[nx,ny,nz].t*theta
(1)轴角变成四元数
那么[q1,q2,q3]=[a0,a1,a2]*sin(theta/2)/theta
这里theta=sqrt(a0*a0+a1*a1+a2*a2)
定义sin(theta/2)/theta为k
当角不为0的时候,如常计算k.
当角趋近于0的时候,k趋近于0.5.
(2)四元数变轴角
[a0,a1,a2]=[q1,q2,q3]*theta/sin(theta/2)
定义theta/sin(theta/2)为k
这里sin(theta/2)为sqrt(q1*q1+q2*q2+q3*q3)
阈值用sin(theta/2)的平方。
如果不为0,theta为2*arctan(-sin(theta/2),-q0)或者2*arctan(sin(theta/2),q0).
如果为0,k直接趋近于2.
(3)求旋转后的点,用轴角算的。
这里theta=sqrt(a0*a0+a1*a1+a2*a2),用theta的平方做了一个阈值判断。
如果不为0.计算w=[a0,a1,a2]/theta,也就是n.
计算w cross pt结果为w_cross_pt.(1-cos(theta))*w*pt为tmp.tmp是一个数值来着。
result[0]=cos(theta)*pt[0]+sin(theta)*w_cross_pt[0]+w[0]*tmp
如果为0.
cos(theta)=1,sin(theta)~theta
所以R=I+hat(w)*theta=I+hat(angle_axis)
令w_cross_pt为angle_axis和pt的叉乘结果
所以result[0]=pt[0]+w_cross_pt[0]
6.2.2 projection.h
整个主题就是函数CamProjectionWithDistortion函数,变量camera,point,predictions,都是指针。
这里的camera代表9个未知量,依次是轴角,位移,焦距,外参l1,l2.
计算跟公式相同,不同的是
归一化平面坐标xp=-p[0]/p[2],yp=-p[1]/p[2]
最后预测值为focal*distortion*xp,focal*distortion*yp,后面并没有加上cx,cy.
camera应该是一个单独的类来着。point为什么也用指针形式,还有预测值,这个还只是归一化平面上的值吧。
cmera,point是指针形式,指针形式[0],[1],[2]也可以这样吗?
还有经过平移的
还有一个问题是为什么xp取负值。
projection.h
就是定义了一个CamProjectionWithDistortion,三个变量,camera,point,predictions,三个都是指针。把camera当成了9个未知量。前3个是轴角,然后平移,然后focal,l1,l2就是去畸变的时候用到的。
但是归一化平面坐标xp=-p[0]/p[2],yp=-p[1]/p[2].
预测结果是fx*xp*distortion,没有加cx,cy.
6.3g2o求解用到的h文件
6.3.1 g2o_bal_class.h
顶点是两个,一个是相机位姿camera,这里变成了9个量,和之前没什么不同,但是重写了oplusImpl函数,原来是_estimate+=Eigen::Vector3d(update);这里变成了+=v,v是
Eigen::VectorXd::ConstMapType v(update,VertexCameraBAL::Dimension);
typedef const Eigen::Map<const Derived, Unaligned> ConstMapType;
一个就是路标点,就是3*1的,也重写了oplusImpl函数,和camera不同,它的v是
Eigen::Vector3d::ConstMapType v(update),这里没有加路标点的维度。
边的计算误差函数computeError被重写了,这里它定义
cam=static_cast<const VertexCameraBAL*>(vertex(0));
里面的变量变成了vertex(0).point对应的是vertex(1).
由cam的estimate().data(),point的estimate().data(),_error.data(),这3个数据是变量,然后用括号运算符来计算了误差。
下面定义了operator()的具体形式。
cam的估计值和point的估计值代入到CamProjectionWithDistortion函数,得到预测值。
误差也可以说是残差residual[0]=predictions[0]-T(measurement()(0))
就是预测值减去测量值。
接下来看边的定义导数的函数linearizeOplus,它也被重写了。
同样先定义cam,point.
定义bal的自动求导类型 BALAutoDiff
typedef ceres::internal::AutoDiff<EdgeObervationBAL,double,VertexCameraBAL::Dimension,VertexPointBAL::Dimension> BALAutoDiff;
其实就是边对两个顶点进行求导。边的数据可以当成是误差数据对位姿和路标的求导,也可以看成是预测值对位姿和路标的求导。
定义矩阵dError_Camera,dError_dPoint,维度分别为它们自己本身的维度*Eigen::RowMajor
参数由camera和point的估计值组成,
雅克比矩阵由dError_Camera,dError_dPoint组成。
value为Dimension维的向量。
BALAutoDiff有一个函数Differentiate,放入变量*this,parameters,Dimension,value,jacobians,返回bool值diffState.
如果返回值为true,_jacobianOplusXi就为dError_Camera,Xj就为dError_Point.
否则,导数设为0.
BA模型 第10章的更多相关文章
- g2o求解BA 第10章
1.g2o_bal_class.h1.1 projection.hg2o还是用图模型和边,顶点就是相机和路标,边就是观测,就是像素坐标.只不过这里的相机是由旋转(3个参数,轴角形式,就是theta*n ...
- 敏捷软件开发:原则、模式与实践——第10章 LSP:Liskov替换原则
第10章 LSP:Liskov替换原则 Liskov替换原则:子类型(subtype)必须能够替换掉它们的基类型(base type). 10.1 违反LSP的情形 10.1.1 简单例子 对L ...
- 高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化
高性能Linux服务器 第10章 基于Linux服务器的性能分析与优化 作为一名Linux系统管理员,最主要的工作是优化系统配置,使应用在系统上以最优的状态运行.但硬件问题.软件问题.网络环境等 ...
- 《Android开发艺术探索》读书笔记 (10) 第10章 Android的消息机制
第10章 Android的消息机制 10.1 Android消息机制概述 (1)Android的消息机制主要是指Handler的运行机制,其底层需要MessageQueue和Looper的支撑.Mes ...
- 《C#从现象到本质》读书笔记(八)第10章反射
<C#从现象到本质>读书笔记(八)第10章反射 个人感觉,反射其实就是为了能够在程序运行期间动态的加载一个外部的DLL集合,然后通过某种办法找到这个DLL集合中的某个空间下的某个类的某个成 ...
- 《构建之法》第8,9,10章读后感&sprint1个人总结
第8章 这一章主要介绍需求分析.具体说明了软件需求的类型.利益相关者,获取用户需求分析的常用方法与步骤.竞争性需求分析的框架NABCD,四象限方法以及项目计划和估计的技术.需求分析是决定一个软件的使用 ...
- SQL Server2012 T-SQL基础教程--读书笔记(8 - 10章)
SQL Server2012 T-SQL基础教程--读书笔记(8 - 10章) 示例数据库:点我 CHAPTER 08 数据修改 8.1 插入数据 8.1.1 INSERT VALUES 语句 8.1 ...
- 《Python学习手册 第五版》 -第10章 Python语句简介
前面在开始讲解数据类型的时候,有说过Python的知识结构,在此重温一下 Python知识结构: 程序由模块组成 模块包含语句 语句包含表达式 表达式创建并处理对象 关于知识结构,前面已经说过我自己的 ...
- 《构建之法》之第8、9、10章读后感 ,以及sprint总结
第8章: 主要介绍了软件需求的类型.利益相关者,获取用户需求分析的常用方法与步骤.竞争性需求分析的框架NABCD,四象限方法以及项目计划和估计的技术. 1.软件需求:人们为了解决现实社会和生活中的各种 ...
随机推荐
- 【GLSL教程】(一)图形流水线 【转】
http://blog.csdn.net/racehorse/article/details/6593719 这是一些列来自lighthouse3d的GLSL教程,非常适合入门.我将边学习边翻译该教程 ...
- DailyMasalaCMS升级记录
手头上是一个比较老的工程,Jdk1.7 + Tomcat7.0 + Spring 3.x + Hibernate 3.x + Elasticseach 2.x 最近Elasticsearch升级,ja ...
- Netty4.0 用户指南
原文链接http://netty.io/wiki/user-guide-for-4.x.html 前言 Nowadays we use general purpose applications or ...
- Mockito图书馆
转载:https://static.javadoc.io/org.mockito/mockito-core/2.12.0/org/mockito/Mockito.html#42 org.mockito ...
- 用PROXYCHAINS实现SSH全局代理
NUX下可以实现SSH全局代理的软件有tsocks和proxychains两种,但是个人感觉proxychains要更加稳定简单. $ yum install proxychains # vim /e ...
- 每天一个 Linux 命令(57):ss命令
ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信 ...
- 聚合数据Android SDK 12306火车票查询订票演示示例
1.聚合SDK是聚合数据平台,为移动开发者提供的免费数据接口.使用前请先到聚合平台(http://www.juhe.cn/)注册,申请相关数据. 2.下载聚合数据SDK,将开发包里的juhe_sdk_ ...
- 统一建模语言(UML,Unified Modeling Language)
Something about UML: 统一建模语言(UML,英语:Unified Modeling Language)是非专利的第三代建模和规约语言.UML是一种开放的方法,用于说明.可视化.构建 ...
- 如何修改eclipse的默认字符集和修改中文乱码
转载,以供以后学习.谢谢 有时候 java代码,导入eclipse中会出现 乱码的问题,通过修改字符集就可以解决. 看下面图片演示过程. 发表在 使用教程 | 标签为 eclipse, 乱码 | 留下 ...
- 浅谈Generator和Promise原理及实现
Generator 熟悉ES6语法的同学们肯定对Generator(生成器)函数不陌生,这是一个化异步为同步的利器. 栗子: function* abc() { let count = 0; whil ...