当前位置: 首页 > news >正文

VINS-Fusion源码逐行解析:除单目+imu模式外的位姿初始化函数initFramePoseByPnP()及其内部函数

通过之前看processImage函数,我们发现只有单目+imu模式的位姿初始化函数用的是sfm那种,也就是initialStructure()函数,而除了这个模式外的别的模式,如双目+imu、仅双目、和不是初始化过程中位姿初始化函数都使用initFramePoseByPnP()

这个函数十分简单,相信不用看笔者的注释大家也都能理解,主要就是对所有深度大于0的特征点,找到它是否在当前帧被观测到,然后把对应的2D点、3D点取出来放进一个容器里,然后结合之前imu估计的初始位姿调用solvePoseByPnP()函数来优化并更新到imu坐标系下的位姿,我们来看一下源码:

//虽然已经有imu提供的初始位姿了,但还是要调用此函数优化
void FeatureManager::initFramePoseByPnP(int frameCnt, Vector3d Ps[], Matrix3d Rs[], Vector3d tic[], Matrix3d ric[])
{// 如果当前帧数大于0if(frameCnt > 0){vector<cv::Point2f> pts2D;// 用于存储2D特征点vector<cv::Point3f> pts3D;// 用于存储3D特征点// 遍历所有特征点for (auto &it_per_id : feature){   // 如果特征点的深度估计值大于0if (it_per_id.estimated_depth > 0){// 计算特征点在当前帧的索引int index = frameCnt - it_per_id.start_frame;// 如果特征点在当前帧存在if((int)it_per_id.feature_per_frame.size() >= index + 1){// 将特征点从相机坐标系转换到世界坐标系Vector3d ptsInCam = ric[0] * (it_per_id.feature_per_frame[0].point * it_per_id.estimated_depth) + tic[0];Vector3d ptsInWorld = Rs[it_per_id.start_frame] * ptsInCam + Ps[it_per_id.start_frame];// 将世界坐标系下的3D点和图像平面上的2D点存入对应的容器中cv::Point3f point3d(ptsInWorld.x(), ptsInWorld.y(), ptsInWorld.z());cv::Point2f point2d(it_per_id.feature_per_frame[index].point.x(), it_per_id.feature_per_frame[index].point.y());pts3D.push_back(point3d);pts2D.push_back(point2d); }}}Eigen::Matrix3d RCam;Eigen::Vector3d PCam;// trans to w_T_cam// 将当前帧的旋转和平移转换到相机坐标系RCam = Rs[frameCnt - 1] * ric[0];PCam = Rs[frameCnt - 1] * tic[0] + Ps[frameCnt - 1];// 如果通过PnP算法求解位姿成功if(solvePoseByPnP(RCam, PCam, pts2D, pts3D)){// trans to w_T_imu// 将相机坐标系下的旋转和平移转换到IMU坐标系下Rs[frameCnt] = RCam * ric[0].transpose(); Ps[frameCnt] = -RCam * ric[0].transpose() * tic[0] + PCam;Eigen::Quaterniond Q(Rs[frameCnt]);//cout << "frameCnt: " << frameCnt <<  " pnp Q " << Q.w() << " " << Q.vec().transpose() << endl;//cout << "frameCnt: " << frameCnt << " pnp P " << Ps[frameCnt].transpose() << endl;}}
}

而对于solvePoseByPnP()函数,这个函数主要就是将eigen形式的矩阵等变成opencv的格式,然后调用opencv提供给的底层函数solvePnP()函数来求解位姿,这两个函数都十分简单,我们来看下源码:

bool FeatureManager::solvePoseByPnP(Eigen::Matrix3d &R, Eigen::Vector3d &P, vector<cv::Point2f> &pts2D, vector<cv::Point3f> &pts3D)
{Eigen::Matrix3d R_initial;Eigen::Vector3d P_initial;// w_T_cam ---> cam_T_w // 将当前帧的世界坐标系下的旋转矩阵和平移向量转换到相机坐标系下R_initial = R.inverse();// 旋转矩阵取逆得到相机坐标系到世界坐标系的转换P_initial = -(R_initial * P);// 平移向量取负并转换到相机坐标系//printf("pnp size %d \n",(int)pts2D.size() );// 检查2D特征点的数量是否足够if (int(pts2D.size()) < 4){// 如果2D特征点数量不足,则提示用户缓慢移动设备printf("feature tracking not enough, please slowly move you device! \n");return false;}// 定义用于PnP求解的变量cv::Mat r, rvec, t, D, tmp_r;// 将Eigen的旋转矩阵转换为OpenCV的格式cv::eigen2cv(R_initial, tmp_r);// 将旋转矩阵转换为旋转向量cv::Rodrigues(tmp_r, rvec);// 将Eigen的平移向量转换为OpenCV的格式cv::eigen2cv(P_initial, t);// 定义相机内参矩阵,这里假设为单位矩阵cv::Mat K = (cv::Mat_<double>(3, 3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);  // 调用solvePnP函数求解位姿bool pnp_succ;pnp_succ = cv::solvePnP(pts3D, pts2D, K, D, rvec, t, 1);//pnp_succ = solvePnPRansac(pts3D, pts2D, K, D, rvec, t, true, 100, 8.0 / focalLength, 0.99, inliers);// 如果PnP求解失败,输出错误信息并返回falseif(!pnp_succ){printf("pnp failed ! \n");return false;}// 将旋转向量转换回旋转矩阵cv::Rodrigues(rvec, r);//cout << "r " << endl << r << endl;// 将OpenCV的矩阵格式转换回Eigen的格式Eigen::MatrixXd R_pnp;cv::cv2eigen(r, R_pnp);Eigen::MatrixXd T_pnp;cv::cv2eigen(t, T_pnp);// cam_T_w ---> w_T_cam// 将求解得到的相机坐标系下的位姿转换回世界坐标系下R = R_pnp.transpose();// 旋转矩阵取转置得到世界坐标系到相机坐标系的转换P = R * (-T_pnp);// 平移向量取负并转换到世界坐标系return true;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 科普文:Redis一问一答
  • 特斯拉的选择:.NET技术栈的工业级魅力
  • 第三方配件也能适配苹果了,iOS 18与iPadOS 18将支持快速配对
  • Vue1-Vue核心
  • 斐讯N1盒子刷入Armbian并安装Docker拉取网络下行流量教程
  • docker安装nginx并配置https
  • html5——列表、表格
  • JDK14新特征最全详解
  • Linux rsync文件同步工具
  • 顶顶通呼叫中心中间件-私有化asrproxy配置热词模型
  • 【机器学习】之旅——线性回归
  • React Native
  • 【HTML入门】第十二课 - iframe框架
  • Kubernetes(k8s)和Docker Compose本质区别
  • 暑期oc后必看:一线城市实习,如何攒出一个小金库?
  • go append函数以及写入
  • java多线程
  • OpenStack安装流程(juno版)- 添加网络服务(neutron)- controller节点
  • 高度不固定时垂直居中
  • 理解在java “”i=i++;”所发生的事情
  • 适配mpvue平台的的微信小程序日历组件mpvue-calendar
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • (0)Nginx 功能特性
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (3)nginx 配置(nginx.conf)
  • (JS基础)String 类型
  • (剑指Offer)面试题34:丑数
  • (转) ns2/nam与nam实现相关的文件
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (转)编辑寄语:因为爱心,所以美丽
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .NET 材料检测系统崩溃分析
  • .net 简单实现MD5
  • .net 使用ajax控件后如何调用前端脚本
  • .NET/C# 编译期能确定的字符串会在字符串暂存池中不会被 GC 垃圾回收掉
  • .NET命名规范和开发约定
  • .net下简单快捷的数值高低位切换
  • @media screen 针对不同移动设备
  • []使用 Tortoise SVN 创建 Externals 外部引用目录
  • [023-2].第2节:SpringBoot中接收参数相关注解
  • [BZOJ4566][HAOI2016]找相同字符(SAM)
  • [C#]winform部署yolov5-onnx模型
  • [C++][基础]1_变量、常量和基本类型
  • [FZSZOJ 1223] 上海红茶馆
  • [git] windows系统安装git教程和配置
  • [HarmonyOS]第一课:从简单的页面开始
  • [HNOI2008]玩具装箱toy
  • [JavaWeb学习] Spring Ioc和DI概念思想
  • [JavaWeb学习] tomcat简介、安装及项目部署
  • [Leetcode 51][Hard]-n皇后问题-回溯
  • [LeetCode] Copy List with Random Pointer 拷贝带有随机指针的链表
  • [Linux] 系统管理
  • [Linux]于Mac在配置Linuxserver安装Nginx+PHP
  • [LLM]大模型八股知识点(一)
  • [macOS] Mojave10.14 夜神安卓模拟器启动问题