使用g2o求解PnP的步骤:

1、定义顶点和边的数据类型

2、提取ORB特征、匹配点对

3、构造顶点、边

4、定义、设置求解器

5、调用求解器的 initializeOptimization 函数求解问题

如果无法链接g2o动态库,可以参考这篇文章的注意事项

https://blog.csdn.net/floatinglong/article/details/116159431
#include <iostream>
using namespace std;#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/calib3d/calib3d.hpp>
using namespace cv;#include <Eigen/Core>
using namespace Eigen;#include <chrono>#include <sophus/se3.hpp>#include <g2o/core/base_vertex.h>
#include <g2o/core/base_unary_edge.h>
#include <g2o/core/sparse_optimizer.h>
#include <g2o/core/block_solver.h>
#include <g2o/core/solver.h>
#include <g2o/core/optimization_algorithm_gauss_newton.h>
#include <g2o/solvers/dense/linear_solver_dense.h>//提取ORB特征并匹配
void findAndMatchOrbFeature(const Mat &img1, const Mat &img2, vector<KeyPoint> &kp1, vector<KeyPoint> &kp2, vector<DMatch> &matches)
{   Ptr<FeatureDetector> detector = ORB::create();Ptr<DescriptorExtractor> desc = ORB::create();Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");//检测角点detector->detect(img1, kp1);detector->detect(img2, kp2);//计算描述子Mat desc1, desc2;    desc->compute(img1, kp1, desc1);desc->compute(img2, kp2, desc2);//匹配点对vector<DMatch> allMatches;matcher->match(desc1, desc2, allMatches);//检测误匹配double minDist = 10000;for(auto m : allMatches)if(m.distance < minDist)  minDist = m.distance;double useDist = max(2*minDist, 30.0);for(auto m : allMatches)   if(m.distance <= useDist)matches.push_back(m);
}// 顶点,模板参数:优化变量维度和数据类型
class Vertex : public g2o::BaseVertex<6, Sophus::SE3d> {
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW// 重置virtual void setToOriginImpl() override {    //override保留字表示当前函数重写了基类的虚函数_estimate = Sophus::SE3d();}// 更新virtual void oplusImpl(const double *update) override {Eigen::Matrix<double, 6, 1> updateVector;updateVector << update[0], update[1], update[2], update[3], update[4], update[5]; _estimate = Sophus::SE3d::exp(updateVector) * _estimate;}// 存盘和读盘:留空virtual bool read(istream &in) {}virtual bool write(ostream &out) const {}
};// 误差模型 模板参数:观测值维度,类型,连接顶点类型
class Edge : public g2o::BaseUnaryEdge<2, Eigen::Vector2d, Vertex> {
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEWEdge(const Eigen::Vector3d &p3d, const Eigen::Matrix3d &k) :  _p3d(p3d), _k(k) {}// 计算误差virtual void computeError() override {        const Vertex *v = static_cast<const Vertex *> (_vertices[0]);    //static_cast将_vertices[0]的类型强制转换为const Edge *const Sophus::SE3d T = v->estimate();    //取出顶点优化变量Eigen::Vector3d tp3d = T * _p3d;  //旋转后的空间坐标tp3d = _k * tp3d / tp3d[2];  //映射到像素平面,前两维是像素坐标,因为这里用到了k的运算,因此k要定义为Eigen库里的类型   Eigen::Vector2d tp2d = tp3d.head<2>();_error = _measurement - tp2d;    //计算误差}// 计算雅可比矩阵
virtual void linearizeOplus() override {const Vertex *v = static_cast<const Vertex *> (_vertices[0]);const Sophus::SE3d T = v->estimate();   //取出上次估计的TEigen::Vector3d tp3d = T * _p3d; //3d点经过旋转double invZ = 1 / tp3d[2], invZ2 = invZ * invZ;double fx = _k(0, 0);double fy = _k(1, 1);double cx = _k(0, 2);double cy = _k(1, 2);_jacobianOplusXi << -fx * invZ,0,fx * tp3d[0] * invZ2,fx * tp3d[0] * tp3d[1] * invZ2,-fx - fx * tp3d[0] * tp3d[0] * invZ2,fx * tp3d[1] * invZ,0,-fy * invZ,fy * tp3d[1] * invZ2,fy + fy * tp3d[1] * tp3d[1] * invZ2,-fy * tp3d[0] * tp3d[1] * invZ2,-fy * tp3d[0] * invZ;}virtual bool read(istream &in) {}virtual bool write(ostream &out) const {}private:Eigen::Vector3d _p3d;Eigen::Matrix3d _k;
};int main(int argc, char **argv)
{if(argc != 4){cout << "error!" << endl;return 0;}Mat img1 = imread(argv[1], CV_LOAD_IMAGE_COLOR);Mat img2 = imread(argv[2], CV_LOAD_IMAGE_COLOR);vector<KeyPoint> kp1, kp2;vector<DMatch> matches;findAndMatchOrbFeature(img1, img2, kp1, kp2, matches);//g2otypedef g2o::BlockSolver<g2o::BlockSolverTraits<6, 2>> BlockSolverType;  //优化变量维度为6,误差维度为2typedef g2o::LinearSolverDense<BlockSolverType::PoseMatrixType> LinearSolverType; // 线性求解器类型//梯度下降方法auto solver = new g2o::OptimizationAlgorithmGaussNewton(g2o::make_unique<BlockSolverType>(g2o::make_unique<LinearSolverType>()));g2o::SparseOptimizer optimizer;     // 图模型optimizer.setAlgorithm(solver);   // 设置求解器optimizer.setVerbose(true);       // 打开调试输出//构造顶点Vertex *v = new Vertex();v->setEstimate(Sophus::SE3d());v->setId(0);optimizer.addVertex(v);//取3d-2d点对Eigen::Matrix3d k;k << 520.9, 0, 325.1, 0, 521.0, 249.7, 0, 0, 1;vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> ps2d;vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d>> ps3d;    for(auto m : matches){Point2d p2d = kp1[m.queryIdx].pt;int x = p2d.x, y = p2d.y;unsigned short pixel = img3.ptr<unsigned short>(y)[x];if(pixel == 0)    continue;double dPixel = pixel / 5000.0;Point2d pNor2d = pixelToNor(p2d, k);ps3d.push_back(Eigen::Vector3d(pNor2d.x*dPixel, pNor2d.y*dPixel, dPixel));p2d = kp2[m.trainIdx].pt;x = p2d.x;y = p2d.y;ps2d.push_back(Eigen::Vector2d(x, y));}//构造边for (int i = 0; i < (int)ps3d.size(); i++) {Edge *edge = new Edge(ps3d[i], k);edge->setId(i);edge->setVertex(0, v);                // 设置连接的顶点edge->setMeasurement(ps2d[i]);      // 观测值edge->setInformation(Eigen::Matrix2d::Identity()); // 信息矩阵:协方差矩阵之逆,这里没有,填单位阵,维度与误差变量一致optimizer.addEdge(edge);}// 执行优化optimizer.initializeOptimization();optimizer.optimize(10);  //迭代次数// 输出优化值Sophus::SE3d T = v->estimate();cout << "estimated model: " << endl << T.matrix() << endl;return 0;
}

视觉SLAM实践入门——(15)使用g2o求解PnP相关推荐

  1. 视觉SLAM实践入门——(3)运动的可视化演示

    旋转矩阵等数学表示方式不直观,考虑将相机的运动轨迹画到一个窗口中. 假设轨迹文件已经存储在trajectory.txt,每一行用下面格式存储: time,tx,ty,tz,qx,qy,qz,qw, 其 ...

  2. 视觉SLAM实践入门——(12)三角测量

    前面通过对极约束估计了相机的 R,t,这一节通过三角测量可以恢复深度值,得到特征点的空间位置(估计值) 利用opencv进行三角测量的步骤: 1.定义旋转矩阵和平移向量组成的增广矩阵 2.计算特征点在 ...

  3. 视觉SLAM笔记(15) 李群与李代数

    视觉SLAM笔记(15) 李群与李代数 1. 前言 2. 基础 3. 群 4. 李代数的引出 5. 李代数的定义 6. 李代数so(3) 7. 李代数se(3) 1. 前言 之前介绍了三维世界中刚体运 ...

  4. 视觉SLAM笔记(53) g2o 操作后端优化

    视觉SLAM笔记(53) g2o 操作后端优化 1. BA 数据集 2. g2o 求解 BA 3. 求解 1. BA 数据集 目录下的 common 文件夹是实验的数据集部分 它的布局如图所示 其中, ...

  5. 视觉SLAM笔记(29) g2o

    视觉SLAM笔记(29) g2o 1. 图优化 2. g2o 的编译与安装 3. 拟合曲线 1. 图优化 图优化是一种将非线性优化与图论结合起来的理论 因此在使用它之前,需要花一点篇幅介绍一个图优化理 ...

  6. 2d与2.5d坐标转换_视觉SLAM:搞定坐标系、三角测量、PnP

    1.正文 四个坐标系:世界坐标系.相机坐标系.图像坐标系.像素坐标系. 世界坐标系:机器人或相机运动过程中,肯定需要知道它的位置,因此需要设定世界坐标系,认定固定不动,作为 参考坐标系,描述世界中的任 ...

  7. 视觉SLAM十四讲 ch3 Ubuntu18.04 KDevelop的使用及Eigen实践 入门笔记

    视觉SLAM十四讲 ch3 Ubuntu18.04 KDevelop的使用及Eigen实践 入门笔记 一.创建KDevelop项目 二.编写程序 一.创建KDevelop项目 你的电脑上如果还没有安装 ...

  8. 视觉SLAM十四讲学习笔记-第七讲-视觉里程计-特征点法和特征提取和匹配实践

    专栏系列文章如下: 视觉SLAM十四讲学习笔记-第一讲_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习笔记-第二讲-初识SLAM_goldqiu的博客-CSDN博客 视觉SLAM十四讲学习 ...

  9. 【slam十四讲第二版】【课本例题代码向】【第九讲~后端Ⅰ】【安装Meshlab】【BAL数据集格式】【ceres求解BA】【g2o求解BA】

    [slam十四讲第二版][课本例题代码向][第九讲~后端Ⅰ][安装Meshlab][BAL数据集格式][ceres求解BA][g2o求解BA] 0 前言 1 安装Meshlab: 三维几何网格处理 2 ...

最新文章

  1. unittest 框架学习
  2. 日期在数据库的存储和取出
  3. 桶排序算法(基于Java实现)
  4. MapXtrem2004经典代码:asp.net鹰眼
  5. python的字符串内建函数
  6. 中南大学 oracle试卷,数据库原理期末复习(中南大学)数据库原理、技术及应用2.ppt...
  7. 支付宝上市,让我损失了2000万(盘点这些年错过的机会)
  8. IPC 进程间通信方式——管道
  9. 【Kafka】Kafka The valid options based on currently configured listeners are PLAINTEXT,SSL
  10. kubeadm一键搭建kubernetes1.14.1高可用集群
  11. 观看Tudou视频更流畅
  12. 基于Android的健康打卡系统,基于Android平台的个人健康管理系统
  13. cmd从网站上下载指定文件
  14. LeetCode1818:绝对差值和
  15. Android 屏幕适配tips
  16. Codeforces 596D Wilbur and Trees dp (看题解)
  17. 百词斩个人中心功能测试
  18. wincc实现手机APP远程监控
  19. 在网页中发起QQ临时对话的方法
  20. 二叉树的遍历——层序遍历

热门文章

  1. Oracle-标识符无效问题
  2. 硬件视频编解码基本知识
  3. 创意被盗用,这3个加水印方法,让照片刻上我们专属印记
  4. EPIC无法登录 无法连接服务器解决办法
  5. 【职场篇】游戏开发社招求职面试指南①——前期准备
  6. Excel 2010 SQL应用003修改数据源的连接路径
  7. IT风云15年的那些人、那些事(七)
  8. 使用Python实现基于人脸识别的上课考勤系统(一):数据录入端
  9. 前阿里架构师钟华,最新分享:中台战略驱动企业生产力生产关系再变革
  10. 可供货华为!ARM 十年来首次发布最新架构 Armv9