矩阵求逆c语言实现_[V-SLAM] Bundle Adjustment 实现

72aa55fee0e6d5393cacc547a8938de5.png
SLAM问题的后端有主要有滤波和优化两种方案。目前,普遍认为优化的方法在精度上要超过滤波方法,因为它可以进行多次的线性化。近年来出现的SLAM算法也大都是基于优化的算法(如ORB-SLAM、DSO等)。优化问题的核心便是Bundle Adjustment。本文对V-SLAM中纯视觉的Bundle Adjustment问题进行了介绍,给出了简单的实现,并利用仿真数据进行了测试。

1. 基本原理

关于Bundle Adjustment是什么,大家应该都比较熟悉,这里就不多做介绍了(可以参考文献[1][2])。本文主要给出一些实现细节。在基于特征点的视觉SLAM中,我们一般通过最小化重投影误差,来优化相机的位姿和地图点的位置。下面针对这个问题展开。

1.1 地图点的参数化方式

地图点的参数化方式主要有两种:一种是用三维向量

表达;另一种采用逆深度表达。为了简单和直观,我们这里还是使用比较传统的
三维向量表达

1.2 相机模型

相机模型也有很多啦,这里我们同样是使用最简单的一种:针孔相机模型。一个3D地图点

投影到图像上形成2D像素点
3D-2D投影)可以表示为

其中

为内参矩阵,
为相机的位姿,
为3D地图点的齐次坐标。我们设定摄像机内参数是已知的,将上述方程简写为

1.3 误差、最小二乘问题

我们需要求解的最小二乘优化问题为

需要优化的量为相机的位姿

和地图点的位置
包含了所有的
3D-2D投影。
cost function
为鲁棒核函数,我们这里也是用最常用的
Huber函数

为了便于操作,这里我们将其转换为一个权重

(这是g2o的做法),使得

那么,权重

至此,我们需要求解的最小二乘优化问题,变为

1.4 雅克比Jacobian

求解最小二乘问题,需要提供cost function:

相对于优化量
雅克比。(可参考高博十四讲)

1)cost function关于相机位姿

的雅克比,我们采用李代数扰动模型计算,为

其中,

2)cost function关于地图点

的雅克比为

1.5 增量方程的求解

无论使用Gauss-Netwon还是Levenburg-Marquadt方法求解最小二乘问题(7),都会求解一个关于增量的线性方程:

Bundle Adjustment中的

矩阵,具有特殊的稀疏结构,如下图所示。利用这种稀疏结构可以加速求解过程。我们这里的实现使用了舒尔补的方法求解。(TODO,其他求解方法)

3188984fbf2be8544c2f2bb297274210.png
H矩阵的稀疏结构

其中只与相机位姿相关的矩阵

,以及只与地图点相关的矩阵块
均为对角块矩阵。将(11)式写成分块形式。

利用舒尔补消元,我们首先求解

然后,再去求解

:

由于

为对角块矩阵,求逆比较快。上述过程就是利用了这一特性,实现了求解的加速。

1.6 固定部分状态

对于一个Bundle Adjustment问题,我们必须固定一部分状态,或者给一部分状态一个先验。不然,就会有无穷多解。可以想象一下,一个网络的误差已经达到了最小后,可以整体在空间内任意移动,而不会对误差造成任何影响。我们只要固定这个网络中的任意1个以上的节点,其他的节点都会有确定的状态。(我们通常会把第一个位姿固定)

怎么固定呢?一种可以加一个先验约束,就是加一个先验cost function。另外一种就是直接把状态fix住,不让它参与优化就好了。 我们采用后一种方法。

2. 实现

我们的实现比较简单:
1. 仅针对上述优化问题;
2. 不考虑扩展性;
3. 不考虑内存问题(不考虑稀疏矩阵存储,十分占内存,只能用来解小规模问题。TODO);
4. 不考虑稀疏矩阵的加速;
5. 只实现了LM优化算法;
6. 不支持先验约束,有motion only BA, struct only BA 和 Full BA三种形式。
7. 没有考虑鲁棒性的问题,错误的输入可能直接就崩掉了。
8. 速度慢(TODO)
实现的过程我们参考了g2o的做法。全部工程代码请见:
ydsf16/vslam​github.com
1f00a292ba3d5624f2d581558f01de45.png

我们首先定义几个类,分别是地图点,相机位姿,CostFunction。地图点和相机位姿类用来存储需要优化的状态,通过id进行标识。CostFunction用来存储代价函数、观测,计算雅克比、残差等。

2.1 地图点类

存储待优化的地图点,同时定义了位置的加法。如果设置了fixed_标志,则不加入状态向量。

class MapPoint{
public:MapPoint(const Eigen::Vector3d& position, int id, bool fixed = false);const Eigen::Vector3d& getPosition();void setPosition(const Eigen::Vector3d& position);int getId();void setId(int id);void setFixed();bool isFixed();void addDeltaPosition(const Eigen::Vector3d& delta_position);int state_index_;
private:Eigen::Vector3d position_; int id_;bool fixed_; 	
}; //class MapPoint

2.2 相机类

存储待优化的相机,同时定义了位姿的加法,其实就是左乘啦。如果设置了fixed_标志,则不加入状态向量。

class Camera{
public:Camera(const Sophus::SE3& pose, int id, bool fixed = false);const Sophus::SE3& getPose();void setPose(const Sophus::SE3& pose);int getId();void setId(int id);void setFixed();bool isFixed();void addDeltaPose(const Eigen::Matrix<double, 6, 1>& delta_pose);void addDeltaPose(const Sophus::SE3& delta_pose);int state_index_;
private:Sophus::SE3 pose_;int id_;bool fixed_;
}; // class CameraPose

2.3 CostFunction类

用来存储cost function,计算cost, 计算雅克比,计算Huber weight等作用。总之,跟cost function相关的操作都放在了这里。

class CostFunction{
public:CostFunction(MapPoint* map_point, Camera* camera, double fx, double fy, double cx, double cy, const Eigen::Vector2d& ob_z);void setHuberParameter(double b = 1.0);void setCovariance(const Eigen::Matrix2d& cov);void computeInterVars(Eigen::Vector2d& e, Eigen::Matrix2d& weighted_info, double& weighted_e2);void computeJT(Eigen::Matrix<double, 2, 6>& JT);void computeJX(Eigen::Matrix<double, 2, 3>& JX);MapPoint* map_point_;Camera* camera_;
private:double fx_, fy_, cx_, cy_;Eigen::Vector2d ob_z_;Eigen::Matrix2d info_matrix_;double huber_b_;                                      
}; // class CostFunction

2.4 BundleAdjustment类

整个BA问题的类。利用map结构存储地图点和相机,主要是为了方便快速查找。利用set结构存储cost function。在这个类里面,将所有cost function的雅克比、信息矩阵整合成大的雅克比、信息矩阵、Hassian矩阵等。在这里面,我们实现了LM优化算法。

class BundleAdjustment{
public:BundleAdjustment();~BundleAdjustment(); // free memory.void addMapPoint(MapPoint* mpt);void addCamera(Camera* cam);void addCostFunction(CostFunction* cost_func);MapPoint* getMapPoint(int id);Camera* getCamera(int id);void setConvergenceCondition(int max_iters, double min_delta, double min_error);void setVerbose(bool flag);void optimize();private:void optimizationInit();    // init for the optimization.void computeStateIndexes(); // compute index for every state.void computeHAndbAndError();void solveNormalEquation();void inverseM(const Eigen::MatrixXd& M, Eigen::MatrixXd& M_inv);void updateStates();void recoverStates();std::map<int, MapPoint*> mappoints_; // all mappointsstd::map<int, Camera*> cameras_; // all cameras.std::set<CostFunction*> cost_functions_; // all cost functions.Eigen::MatrixXd J_; // Jocabian matrix.Eigen::MatrixXd JTinfo_;Eigen::MatrixXd H_; // Hassian matrix.Eigen::MatrixXd r_; // residual vector.Eigen::MatrixXd b_;Eigen::MatrixXd info_matrix_; // information matrix.Eigen::MatrixXd Delta_X_;     // Delta_X_Eigen::MatrixXd I_;int n_cam_state_; // number of cameras in the state vector.int n_mpt_state_; // number of map points in the state vector./* Convergence condition */int max_iters_;double min_delta_;double min_error_;double sum_error2_;double last_sum_error2_;bool verbose_;
}; // BundleAdjustment	

2.5 BA问题的构建

这个过程跟g2o几乎一样。

step 1. 构造地图点、相机,加入到BundleAdjustment类。
step 2. 构造cost function,加入到BundleAdjustment类。
step 3. 开始迭代优化
step 4. 根据id将结果取出
        BundleAdjustment ba_mba;ba_mba.setConvergenceCondition(20, 1e-5, 1e-10);ba_mba.setVerbose(true);// add mappointsfor(size_t i = 0; i < noise_mappoints.size(); i ++){const Eigen::Vector3d& npt = noise_mappoints.at(i);MapPoint* mpt = new MapPoint(npt, i);mpt->setFixed(); // fixed all mappoints.ba_mba.addMapPoint(mpt);} // add mappoints// add cameras.for(size_t i = 0; i < noise_cameras.size(); i ++){const Sophus::SE3& ncam = noise_cameras.at(i);Camera* cam = new Camera(ncam, i);ba_mba.addCamera(cam);} // add cameras.// add observationsfor(size_t i = 0; i < noise_observations.size(); i ++){const Observation& ob = noise_observations.at(i);MapPoint* mpt = ba_mba.getMapPoint(ob.mpt_id_);Camera* cam = ba_mba.getCamera(ob.cam_id_);CostFunction* cost_func = new CostFunction(mpt, cam, fx, fy, cx, cy, ob.ob_);ba_mba.addCostFunction(cost_func);} // add observations.// Optimizeba_mba.optimize();// Compute pose Errordouble sum_rot_error = 0.0;double sum_trans_error = 0.0;for(size_t i = 0; i < cameras.size(); i ++){Camera* cam = ba_mba.getCamera(i);const Sophus::SE3& opt_pose = cam->getPose();const Sophus::SE3& org_pose = cameras.at(i);Sophus::SE3 pose_err = opt_pose * org_pose.inverse();sum_rot_error += pose_err.so3().log().norm();sum_trans_error += pose_err.translation().norm();}std::cout << "Mean rot error: " << sum_rot_error / (double)(cameras.size())<< "tMean trans error: " <<  sum_trans_error / (double)(cameras.size()) << std::endl;

3. 实验

为了能够与真值对比,我采用了仿真实验。就是人为的仿真出m个相机,n个地图点和一些观测。然后给数据加一些噪声,测试算法的有效性。仿真实验有明确的ground truth,调试程序非常好用。下面给出一组数据的结果。

相机数量:6地图点数量:275观测数量:1650

3.1 motion only BA实验(固定地图点,只优化位姿)

首先,我们可以不添加任何噪声进行实验,可以发现优化后的结果与真值完全一样。

然后,添加如下所示的噪声,进行测试

mpt_noise = 0.01; // m
cam_trans_noise = 0.1; // m
cam_rot_noise = 0.1; // rad
ob_noise = 1.0; // pixel

d7522c7c409e9c42c9f078152cf6af8e.png

3.2 struct only BA实验(固定相机,只优化地图点)

添加如下所示的噪声,进行测试

mpt_noise = 0.1;
cam_trans_noise = 0.0;
cam_rot_noise = 0.00;
ob_noise = 1.0;

9d66f6a6800d61ac61fee1ae2f222956.png

3.3 Full BA实验(只固定第一个相机,优化其余相机和地图点)

添加如下所示的噪声,进行测试

mpt_noise = 0.05;
cam_trans_noise = 0.1;
cam_rot_noise = 0.1;
ob_noise = 1.0;

9f638c85fecd17b3f4814e42c5403f36.png

实测发现,算法可以很好的收敛。

相机位姿的估计精度会比地图点的精度更高,因为一个相机可以看到几百个地图点,而一个地图点只能被几个相机看到;相机的约束方程数量远大于地图点的约束方程数量。

还发现,计算速度很慢,主要的原因在于

1. 内存频繁拷贝
2. 增量方程求解速度很慢
3. 没有利用稀疏矩阵的性质
4. Eigen的特性不熟悉
......

总之,还有一大堆问题需要解决。这只是一个很基础的版本。以后有时间再搞吧。

参考资料

[1] Triggs B . Bundle Adjustment - A Modern Synthesis[M]// Vision Algorithms: Theory and Practice. Springer Berlin Heidelberg, 1999.

[2] 视觉SLAM十四讲

[3] Methods for Non-Linear Least Squares Problems

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/504010.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos安装 mysql_Linux centos 安装 mysql 5.6

一、mysql下载1、方式一(简单粗暴)直接在linux 目录下wget https://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.43-linux-glibc2.12-x86_64.tar.gz2、方式二(官方下载)浏览器打开网址&#xff1a;https://www.mysql.com如下图依次点击1、2、3、4步如下图设置对应版本点击…

tracepro杂散光分析例子_光刻机的蜕变过程及专利分析

来源&#xff1a;芯通社近两年&#xff0c;中国芯片产业受到了严重打击&#xff0c;痛定思痛之余也让国人意识到芯片自主研发的重要性。从2008年以来&#xff0c;十年间&#xff0c;芯片都是我国第一大宗进口商品&#xff0c;进口额远超于排名第二的石油。2018年我国进口集成电…

docker mysql日志_面试官问:了解Mysql主从复制原理么?我呵呵一笑

搭建Mysql主从同步之前&#xff0c;我们先来说他们之间同步的过程与原理&#xff1a;同步复制过程献上一张图&#xff0c;这张图诠释了整个同步过程主从复制过程&#xff1a;slave节点与主节点进行连接&#xff0c;建立主从关系&#xff0c;并把从哪开始同步&#xff0c;及哪个…

查看socket缓冲区数据_什么是socket缓冲区?

Socket 就是发送和接收网络数据&#xff0c;Socket 有发送缓冲也有接收缓冲&#xff0c;这些缓冲区有什么作用&#xff1f;1、什么是Socket缓冲区&#xff1f;熟悉 Socket 的读者都知道&#xff0c;Socket 的发送和接收&#xff0c;就是调用 send 和 recv 函数。实际操作中&…

哈工大大数据实验_科研常用 | 实验大数据分析方法

对于每个科研工作者而言&#xff0c;对实验数据进行处理是在开始论文写作之前十分常见的工作之一。但是&#xff0c;常见的数据分析方法有哪些呢&#xff1f;常用的数据分析方法有&#xff1a;聚类分析、因子分析、相关分析、对应分析、回归分析、方差分析。1、聚类分析(Cluste…

搭建github服务器_搭建一个属于自己的公网博客

相信每一位程序员都喜欢拥有一个属于自己的博客。当然&#xff0c;在我认为&#xff0c;内容以及模块都要自己进行可扩展定义才是真正属于自己的。那么想要一个博客就必须要有一个服务器和一个域名&#xff0c;这样的话才能让自己的博文内容发扬光大&#xff0c;但是服务器的性…

mysql修改级联表数据_MySQL数据库 外键,级联, 修改表的操作

1.外键: 用来建立两张表之间的关系- 一对多- 多对多- 一对一研究表与表之间的关系:1.定义一张 员工部门表id, name, gender, dep_name, dep_desc- 将所有数据存放在一张表中的弊端:1.结构不清晰 ---> 不致命2.浪费空间 ---> 不致命3.可扩展性极差 ---> 不可忽视的弊端…

OpenCV学习笔记 - 使用密集光流检测运动的简单方法

一、简述 使用光流进行运动检测的方法与帧间差分方法类似。主要区别在于第一步,我们将从光流而不是帧差分中获取初始运动信息(一些神经网络模型也是基于光流和原始图像进行运动识别训练的)。 该算法概述如下: 1、计算密集光流 2、获得运动掩模的阈值光流 3、在运动蒙版中查…

mysql 5.6.27安装图解_Linux下MySQL 5.6.27 安装教程

本文实例为大家分享了Linux下MySQL 5.6.27 安装教程&#xff0c;供大家参考&#xff0c;具体内容如下1、下载地址2、将压缩包上传到服务器3、解压tar -zxf mysql-5.6.27-linux-glibc2.5-x86_64.tar.gz4、移动压缩包至mysql文件夹下mp mysql-5.6.27-linux-glibc2.5-x86_64 /usr…

linux部署tomcat项目404_Tomcat部署项目的几种常见方式

点击蓝字“程序员考拉”欢迎关注&#xff01;1 /直接将web项目文件件拷贝到webapps目录中这是最常用的方式&#xff0c;Tomcat的Webapps目录是Tomcat默认的应用目录&#xff0c;当服务器启动时&#xff0c;会加载所有这个目录下的应用。如果你想要修改这个默认目录&#xff0c;…

mysql bug_MySQL 记一次 Bug发现过程

水平有限有误请谅解这个问题是一位朋友DBA-老庄的,他们使用的是PXC环境如下:MySQL:5.7.18-15wsrep:29.20os:Red Hat Enterprise Linux Server release 6.5实际上我对PXC并不是很熟&#xff0c;通过分析pstack还是找到了问题。并且提交Bug&#xff0c;percona确认了。虽然我不是…

正则表达式 任意数字_作为运维还不会正则表达式?赶快看这篇学习一下

概述正则表达式是很多运维薄弱的一项技能。大家很多时候都会觉得正则表达式难记、难学、难用&#xff0c;但不可否认的是正则表达式是一项很重要的技能&#xff0c;所有今天将学习和使用正则表达式时的关键点整理如下&#xff0c;仅供参考。什么是正则表达式&#xff1f;正则表…

vs xaml 语句完成 自动列出成员_数据传输 | mysqldiff/mysqldbcompare 实现 DTLE 自动化测试...

作者&#xff1a;张静文爱可生上海研发中心成员&#xff0c;测试工程师&#xff0c;负责 DMP 以及 DTLE 自动化测试。本文来源&#xff1a;原创投稿 *爱可生开源社区出品&#xff0c;原创内容未经授权不得随意使用&#xff0c;转载请联系小编并注明来源。任务&#xff1a;测试开…

raft算法_Raft算法与实现

强一致性、高可用的存储组件是构建现代分布式系统的必要条件&#xff0c;广泛应用于注册中心、配置中心等平台设施中&#xff0c;分布式锁、协调器等等各类场景需求也有相关需求&#xff0c;在该领域有众多知名的开源组件&#xff0c;如etcd、zookeeper、Tikv等等。共识算法是实…

静态ip ssh无法登录_识别动静态IP的技巧

动态IP&#xff0c;又称DHCP上网&#xff0c;即自动获取IP上网。动态IP这种上网方式&#xff0c;连接网络时即可自动获取IP地址来正常上网。在未使用路由器的情况下&#xff0c;只需要把宽带网线连接到电脑上&#xff0c;电脑上的IP地址设置为自动获得&#xff0c;电脑就可以实…

18awg线材最大电流_小米生态链拉车线:2.4A大电流,苹果MFi认证,高速充电不断裂...

对于经常使用苹果手机的用户来说&#xff0c;不随时准备几根充电线好像总感觉差点什么&#xff0c;苹果官方的电源线不耐用早已是公认的事实&#xff0c;其实最主要的还是因为苹果手机电池容量低&#xff0c;相对来说充电次数要比安卓手机多一些&#xff0c;电源线使用频率也就…

a股历史30年的大盘价_2020年7月30日大盘走势分析

2020年7月30日大盘走势分析严正声明&#xff1a;分析下面小程序炒股广告与本公众号zyh218642无关&#xff0c;纯属第三方平台自然生成&#xff0c;不要点开&#xff0c;谨防上当受骗。7月份大盘走势分析7月份大盘的多空压力与支撑位置&#xff1a;…第二压力&#xff1a;3139.0…

vue2.0 唤起百度地图app_开车选高德,出门靠百度,高德百度地图APP对比

高德和百度是在电子地图领域竞争的对手&#xff0c;但是&#xff0c;在同一领域他们的发展方向的侧重也存在差异。那么&#xff0c;他们究竟有什么不同呢&#xff1f;当然&#xff0c;他们的开发人员必须是不同的&#xff0c;肯定不用考虑。此外&#xff0c;在某些数据和功能上…

火力发电厂与变电站设计防火标准_真题—火力发电厂1

做真题&#xff0c;遇真题&#xff0c;解真题1、某燃煤火力发电厂&#xff0c;单机容量200MW&#xff0c;该发电厂火灾自动报警系统的下列设计方案中&#xff0c;正确的是()。A.运煤系统内的火灾探测器防护等级为IP65B.厂区设置集中报警系统C.消防控制室与集中控制室分别独立设…

捷波朗STORM耳机设置中文_2020年 除了Airpods pro以外无线降噪蓝牙耳机如何选?五款热门入耳式蓝牙降噪耳机推荐...

双十二红包&#xff0c;每日三次&#xff0c;手慢无2020 年除了Airpods pro 以外&#xff0c;五款热门入耳式无线蓝牙降噪耳机简评近期&#xff0c;Apple推出的新款无线耳机Air pods pro引起了一波数码控的热议&#xff0c;大致分为两个立场&#xff0c;我个人专门去苹果店试听…