1.ICP

假设有一组配对好的3D点, \(P={P_{1}, ..., P_{N}}\) , \(P^{'}={P_{1}^{'}, ..., P_{N}^{'}}\)。

有一个欧式变换R,t,使得: \(p_{i} = Rp^{'}_{i} + t\)

该问题可以用迭代最近点(ICP)来求解。注意考虑两组3D点的变换时,和相机没有关系。

ICP求解线性代数的求解(SVD)和非线性优化方式求解(类似于BA)

2.SVD求解:

定义误差项: \(e_{i} = p_{i} - ( Rp^{'}_{i} + t )\)

构建最小二乘问题,使误差平方和达到极小的R,t

定义两组点的质心:

\(p = \frac{1}{n} \sum^{n}_{i=1} (p_{i}), p^{'} = \frac{1}{n} \sum^{n}_{i=1} (p_{i}^{'}),\)

步骤:

  • 计算两组点的质心位置 \(p,p^{'}\),然后再计算每个点的去质心坐标:

    \(q_{i} = p_{i} - p, q_{i}^{'} = p_{i}^{'} - p^{'}\)

  • 计算 \(R^{*} = argmin \frac{1}{2} \sum^{n}_{i=1} || q_{i} - Rq_{i}^{'} ||^{2}\)

  • 将上式展开,优化函数变为求解 \(-tr( R \sum^{n}_{i=1} q_{i}^{'} q_{i}^{T} )\)

    定义 \(W=\sum^{n}_{i=1} q_{i}^{'} q_{i}^{T}\),对W进行SVD分解,得到\(W=U \Sigma V^{T}\)

  • \Sigma 为奇异值组成的对角矩阵,对角线元素从大到小排列,而U和V为正交矩阵。当W满秩时,\(R=UV^{T}\)

  • 根据求出的R,计算t: \(t^{*} = p - Rp^{'}\)

3.代码:

void pose_estimation_3d3d(const vector<Point3f> &pts1,
const vector<Point3f> &pts2,
Mat &R, Mat &t) {
Point3f p1, p2; // center of mass
//求质心
int N = pts1.size();
for (int i = 0; i < N; i++) {
p1 += pts1[i];
p2 += pts2[i];
} p1 = Point3f(Vec3f(p1) / N);
p2 = Point3f(Vec3f(p2) / N);
vector<Point3f> q1(N), q2(N); // remove the center
//去质心
for (int i = 0; i < N; i++) {
q1[i] = pts1[i] - p1;
q2[i] = pts2[i] - p2;
} // compute q1*q2^T
Eigen::Matrix3d W = Eigen::Matrix3d::Zero();
for (int i = 0; i < N; i++) {
W += Eigen::Vector3d(q1[i].x, q1[i].y, q1[i].z) * Eigen::Vector3d(q2[i].x, q2[i].y, q2[i].z).transpose();
}
cout << "W=" << W << endl; // SVD on W
Eigen::JacobiSVD<Eigen::Matrix3d> svd(W, Eigen::ComputeFullU | Eigen::ComputeFullV);
Eigen::Matrix3d U = svd.matrixU();
Eigen::Matrix3d V = svd.matrixV(); cout << "U=" << U << endl;
cout << "V=" << V << endl; Eigen::Matrix3d R_ = U * (V.transpose());
if (R_.determinant() < 0) {
R_ = -R_;
}
Eigen::Vector3d t_ = Eigen::Vector3d(p1.x, p1.y, p1.z) - R_ * Eigen::Vector3d(p2.x, p2.y, p2.z); // convert to cv::Mat
//推导是按第二张图到第一张图的变化,
//此处进行逆变换,即为第一张图到第二张图的变化
R = (Mat_<double>(3, 3) <<
R_(0, 0), R_(0, 1), R_(0, 2),
R_(1, 0), R_(1, 1), R_(1, 2),
R_(2, 0), R_(2, 1), R_(2, 2)
);
t = (Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
}

4.非线性优化方法:

/// 节点,优化变量维度和数据类型
class VertexPose : public g2o::BaseVertex<6, Sophus::SE3d> {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW; //初始化
virtual void setToOriginImpl() override {
_estimate = Sophus::SE3d();
} //更新估计值
/// left multiplication on SE3
virtual void oplusImpl(const double *update) override { Eigen::Matrix<double, 6, 1> update_eigen;
update_eigen << update[0], update[1], update[2], update[3], update[4], update[5]; _estimate = Sophus::SE3d::exp(update_eigen) * _estimate;
} virtual bool read(istream &in) override {} virtual bool write(ostream &out) const override {}
}; /// 边,误差模型 观测维度,观测数据类型, 链接节点类型
class EdgeProjectXYZRGBDPoseOnly : public g2o::BaseUnaryEdge<3, Eigen::Vector3d, VertexPose> {
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW; EdgeProjectXYZRGBDPoseOnly(const Eigen::Vector3d &point) : _point(point) {} virtual void computeError() override {
//获取姿态估计值
const VertexPose *pose = static_cast<const VertexPose *> ( _vertices[0] );
//计算误差,测量值-转换值
_error = _measurement - pose->estimate() * _point;
} //计算雅可比矩阵
//雅可比矩阵为[I,-P'^]
virtual void linearizeOplus() override {
VertexPose *pose = static_cast<VertexPose *>(_vertices[0]);
Sophus::SE3d T = pose->estimate();
Eigen::Vector3d xyz_trans = T * _point;
//单位矩阵
_jacobianOplusXi.block<3, 3>(0, 0) = -Eigen::Matrix3d::Identity();
//向量到反对称矩阵
_jacobianOplusXi.block<3, 3>(0, 3) = Sophus::SO3d::hat(xyz_trans);
} bool read(istream &in) {} bool write(ostream &out) const {} protected:
Eigen::Vector3d _point;
}; //将顶点和边加入g2o
oid bundleAdjustment(
const vector<Point3f> &pts1,
const vector<Point3f> &pts2,
Mat &R, Mat &t) {
// 构建图优化,先设定g2o
typedef g2o::BlockSolverX BlockSolverType;
typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; // 线性求解器类型
// 梯度下降方法,可以从GN, LM, DogLeg 中选
auto solver = new g2o::OptimizationAlgorithmLevenberg(
g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));
g2o::SparseOptimizer optimizer; // 图模型
optimizer.setAlgorithm(solver); // 设置求解器
optimizer.setVerbose(true); // 打开调试输出 // vertex
VertexPose *pose = new VertexPose(); // camera pose
pose->setId(0);
pose->setEstimate(Sophus::SE3d());
optimizer.addVertex(pose); // edges
for (size_t i = 0; i < pts1.size(); i++) {
EdgeProjectXYZRGBDPoseOnly *edge = new EdgeProjectXYZRGBDPoseOnly(
Eigen::Vector3d(pts2[i].x, pts2[i].y, pts2[i].z));
edge->setVertex(0, pose);
edge->setMeasurement(Eigen::Vector3d(
pts1[i].x, pts1[i].y, pts1[i].z));
edge->setInformation(Eigen::Matrix3d::Identity());
optimizer.addEdge(edge);
} chrono::steady_clock::time_point t1 = chrono::steady_clock::now();
optimizer.initializeOptimization();
optimizer.optimize(10);
chrono::steady_clock::time_point t2 = chrono::steady_clock::now();
chrono::duration<double> time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
cout << "optimization costs time: " << time_used.count() << " seconds." << endl; cout << endl << "after optimization:" << endl;
cout << "T=\n" << pose->estimate().matrix() << endl; // convert to cv::Mat
Eigen::Matrix3d R_ = pose->estimate().rotationMatrix();
Eigen::Vector3d t_ = pose->estimate().translation();
R = (Mat_<double>(3, 3) <<
R_(0, 0), R_(0, 1), R_(0, 2),
R_(1, 0), R_(1, 1), R_(1, 2),
R_(2, 0), R_(2, 1), R_(2, 2)
);
t = (Mat_<double>(3, 1) << t_(0, 0), t_(1, 0), t_(2, 0));
}

视觉十四讲:第七讲_3D-3D:ICP估计姿态的更多相关文章

  1. 视觉slam十四讲第七章课后习题6

    版权声明:本文为博主原创文章,转载请注明出处: http://www.cnblogs.com/newneul/p/8545450.html 6.在PnP优化中,将第一个相机的观测也考虑进来,程序应如何 ...

  2. 视觉slam十四讲第七章课后习题7

    版权声明:本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/newneul/p/8544369.html  7.题目要求:在ICP程序中,将空间点也作为优化变量考虑进来 ...

  3. 视觉slam学习之路(一)看高翔十四讲所遇到的问题

      目前实验室做机器人,主要分三个方向,定位导航,建图,图像识别,之前做的也是做了下Qt上位机,后面又弄红外识别,因为这学期上课也没怎么花时间在项目,然后导师让我们确定一个方向来,便于以后发论文什么. ...

  4. 浅读《视觉SLAM十四讲:从理论到实践》--操作1--初识SLAM

    下载<视觉SLAM十四讲:从理论到实践>源码:https://github.com/gaoxiang12/slambook 第二讲:初识SLAM 2.4.2 Hello SLAM(书本P2 ...

  5. 高翔《视觉SLAM十四讲》从理论到实践

    目录 第1讲 前言:本书讲什么:如何使用本书: 第2讲 初始SLAM:引子-小萝卜的例子:经典视觉SLAM框架:SLAM问题的数学表述:实践-编程基础: 第3讲 三维空间刚体运动 旋转矩阵:实践-Ei ...

  6. 高博-《视觉SLAM十四讲》

    0 讲座 (1)SLAM定义 对比雷达传感器和视觉传感器的优缺点(主要介绍视觉SLAM) 单目:不知道尺度信息 双目:知道尺度信息,但测量范围根据预定的基线相关 RGBD:知道深度信息,但是深度信息对 ...

  7. 《视觉SLAM十四讲》第2讲

    目录 一 视觉SLAM中的传感器 二 经典视觉SLAM框架 三 SLAM问题的数学表述 注:原创不易,转载请务必注明原作者和出处,感谢支持! 本讲主要内容: (1) 视觉SLAM中的传感器 (2) 经 ...

  8. 《视觉SLAM十四讲》第1讲

    目录 一 视觉SLAM 注:原创不易,转载请务必注明原作者和出处,感谢支持! 一 视觉SLAM 什么是视觉SLAM? SLAM是Simultaneous Localization and Mappin ...

  9. 视觉SLAM十四讲:从理论到实践 两版 PDF和源码

    视觉SLAM十四讲:从理论到实践 第一版电子版PDF 链接:https://pan.baidu.com/s/1SuuSpavo_fj7xqTYtgHBfw提取码:lr4t 源码github链接:htt ...

  10. 《SLAM十四讲》个人学习知识点梳理

    0.引言 从六月末到八月初大概一个月时间一直在啃SLAM十四讲[1]这本书,这本书把SLAM中涉及的基本知识点都涵盖了,所以在这里做一个复习,对这本书自己学到的东西做一个梳理. 书本地址:http:/ ...

随机推荐

  1. HTTPS详解二

    前言 在上篇文章中,我已经为大家介绍了 HTTPS 的详细原理和通信流程,但总感觉少了点什么,应该是少了对安全层的针对性介绍,那么这篇文章就算是对HTTPS 详解一的补充吧.还记得这张图吧. HTTP ...

  2. Centos 7.6 安装部署 openGauss 3.1.0 企业版一主两备集群

    一.安装环境设置 1.1 硬件环境 名称 最低配置 建议配置 测试配置 服务器数量 3 略 略 硬盘 * 至少1GB用于安装openGauss的应用程序.* 每个主机需大约300MB用于元数据存储.* ...

  3. 理解MySQL事务

    事务是什么 百度百科是这么定义的: 事务(Transaction),一般是指要做的或所做的事情.在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元.在关系数据库中,一个事务可以是一条 ...

  4. 从 Numpy+Pytorch 到 TensorFlow JS:总结和常用平替整理

    demo展示 这是一个剪刀石头布预测模型,会根据最近20局的历史数据训练模型,神经网络输入为最近2局的历史数据. 如何拥有较为平滑的移植体验? 保持两种语言,和两个框架的API文档处于打开状态,并随时 ...

  5. Lakehouse架构指南

    你曾经是否有构建一个开源数据湖来存储数据以进行分析需求? 数据湖包括哪些组件和功能? 不了解 Lakehouse 和 数据仓库 之间的区别? 或者只是想管理数百到数千个文件并拥有更多类似数据库的功能但 ...

  6. MyEclipse连接MySQL

    在官网http://www.mysql.com/downloads/下载数据库连接驱动 本文中使用驱动版本为mysql-connector-java-5.1.40 一.创建一个java测试项目MySQ ...

  7. 【JVM故障问题排查心得】「内存诊断系列」JVM内存与Kubernetes中pod的内存、容器的内存不一致所引发的OOMKilled问题总结(上)

    背景介绍 在我们日常的工作当中,通常应用都会采用Kubernetes进行容器化部署,但是总是会出现一些问题,例如,JVM堆小于Docker容器中设置的内存大小和Kubernetes的内存大小,但是还是 ...

  8. USB限流IC,限流开关保护芯片

    PW1503和PW1502是超低RDS(ON)开关,具有可编程电流限制的USB限流IC,以保护电源于过电流负载和正极负极短路的保护.它具有过温保护以及反向闭锁功能. PW1503,PW1502均采用S ...

  9. mybatis 之定义拦截器 控制台SQL的打印

    类型 先说明Mybatis中可以被拦截的类型具体有以下四种: 1.Executor:拦截执行器的方法.2.ParameterHandler:拦截参数的处理.3.ResultHandler:拦截结果集的 ...

  10. Proxyless Mesh 在 Dubbo 中的实践

    背景 随着 Dubbo 3.1 的 release,Dubbo 在云原生的路上又迈出了重要的一步.在这个版本中添加了 Proxyless Mesh 的新特性,Dubbo Proxyless Mesh ...