在上一篇文章(https://blog.csdn.net/weixin_38636815/article/details/109495227)中我写了如何使用ceres,根据一系列的点来拟合一个平面,很难保证ORB-SLAM输出的轨迹严格与某一个坐标平面平行,所以这篇文章我我将说一下,如何将不与任何一个

坐标平面平行的三维平面绕着一个轴,旋转一个角度,使得其与某一个坐标平面平行。

一、原理分析

实现步骤:

1. 获得拟合出的平面的法向量

2. 找到参考向量,如要与XOY平面平行,参考向量为(0,0,1)的转置,与XOZ平面平行,参考向量为(0,1,0)的转置,与YOZ平面平行,参考向量为(1, 0, 0)的转置。

因为SLAM中的相机坐标系为(水平朝前放置相机时)Z轴朝前,X轴朝右, Y轴朝下,也就是说Z轴就是相机朝着的方向,我们在实际安装时也多数水平朝前安装

有时会有一些俯仰角,所以一般情况下我会把XOZ平面作为参考面,那么参考向量就是(0,1,0)向量的转置。

3. 根据“向量的点积”来求解两个向量之间的夹角。

4. 根据“向量的叉乘”求解垂直于两个向量构成的平面的法向量。因为两个向量的叉乘得到的新向量

就是一个同时垂直于两个向量的向量。

上面第4步求解出的向量就是我们的旋转轴,第三步求解出来的角度就是需要旋转的角度,我们将拟合出来的

平面绕着这个轴,旋转一个角度,就可以将这个平面旋转到与参考平面平行的地方。

注意:(1)上面求解出的角度是以弧度为单位,(2)上面求解出的旋转轴向量要进行归一化处理,否则后续求解的结果会出错。

二、代码讲解

下面有两种方法可以进行求解,一种是使用opencv的方法,一种是使用Eigen的方法。

1. 首先求解出旋转轴和旋转向量

//a,b,c为求解出的拟合平面的法向量,是进行归一化处理之后的向量。
cv::Point3d plane_norm(a, b, c);
//xz_norm是参考向量,也就是XOZ坐标平面的法向量
cv::Point3d xz_norm(0.0, 1.0, 0.0);
std::cout<<"plane_norm: "<<plane_norm<<std::endl;
std::cout<<"xz_norm: "<<xz_norm<<std::endl;
//求解两个向量的点乘
double v1v2 = plane_norm.dot(xz_norm);
//计算平面法向量和参考向量的模长,因为两个向量都是归一化之后的,所以这里的结果都是1.
double v1_norm = plane_norm.x*plane_norm.x + plane_norm.y*plane_norm.y + plane_norm.z*plane_norm.z;
double v2_norm = xz_norm.x*xz_norm.x + xz_norm.y*xz_norm.y + xz_norm.z*xz_norm.z;
//计算两个向量的夹角
double theta  = std::acos(v1v2/(std::sqrt(v1_norm)*std::sqrt(v2_norm)));//根据向量的叉乘求解同时垂直于两个向量的法向量。
cv::Point3d axis_v1v2 = xz_norm.cross(plane_norm);//对旋转向量进行归一化处理
double v1v2_2 = axis_v1v2.x*axis_v1v2.x + axis_v1v2.y*axis_v1v2.y + axis_v1v2.z*axis_v1v2.z;
double v1v2_n = std::sqrt(v1v2_2);
axis_v1v2 = axis_v1v2/v1v2_n;
std::cout<<"axis_v1v2: "<<axis_v1v2<<std::endl;
std::cout<<"theta: "<<theta<<std::endl;

2. 根据旋转轴和旋转向量求解旋转矩阵

(opencv法)

//-theta表示改变旋转的方向
cv::Point3d axis_v1v2_cv = -theta* axis_v1v2;//轴角,角度乘以方向
std::cout<<"axis_v1v2_cv: "<<axis_v1v2_cv<<std::endl;cv::Mat R_vec=(cv::Mat_<double>(3,1)<<axis_v1v2_cv.x,axis_v1v2_cv.y,axis_v1v2_cv.z);cv::Mat rotation;
cv::Rodrigues(R_vec,rotation);
std::cout<<" rotation  CV::"<<rotation<<std::endl;for (size_t i = 0; i < locations.size(); i++)
{cv::Mat Pt=(cv::Mat_<double>(3,1)<<locations[i][0],locations[i][1],locations[i][2]);cv::Mat afterPt=rotation*Pt;std::cout<<"orignal     "<<locations[i].transpose()<<"      cv  Translate   "<<afterPt.t()<<std::endl;}

(Eigen法)

Eigen::AngleAxisd ro_vector(-theta, Eigen::Vector3d(axis_v1v2.x, axis_v1v2.y, axis_v1v2.z));
Eigen::Matrix3d ro_matrix = ro_vector.toRotationMatrix();
std::cout<<"rotation eigen "<<ro_matrix<<std::endl;std::vector<Eigen::Vector3d> new_points;
double sum  = 0;
for(int i = 0; i<locations.size(); i++)
{Eigen::Vector3d newP;//计算点到平面上的距离,只处理那些距离平面距离很小的点。double x = locations[i].x();double y = locations[i].y();double z = locations[i].z();//计算每一个真实点到拟合出来的平面的距离,求出总距离,求解平均距离double dist2 = (a*x + b*y + c*z+d)*(a*x + b*y + c*z+d);sum =+ std::sqrt(dist2); newP.x()=x;newP.y()=y;newP.z()=z;Eigen::Vector3d new_point = ro_matrix*newP;std::cout<<"newP: "<<new_point.transpose()<<"    orignal"<<newP.transpose()<<std::endl;      }

根据上面的步骤,我们就可以将拟合出来的平面旋转到参考平面。

如何验证我们的旋转对不对呢,你可以检验经过旋转之后的3D点是不是某一个维度上的值全部稳定在一个值附近。下图中y轴的值稳定在比较小的值,这样

我们取出x和z轴上的两维坐标去画图,得到的轨迹失真性更小。

三、将旋转后的3D平面画在图像上

将轨迹点画到图像上,其中一个重要的思想就是点坐标的转换。

在下面段代码中,将原始轨迹点按照求解出来的旋转变换,进行旋转,旋转到平行于指定的坐标平面。并且求出实际点的最大值和最小值(作用在下文中有体现)

double min_x = 10e5;
double max_x = 10e-5;
double min_y = 10e5;
double max_y = 10e-5;
for (size_t i = 0; i < locations.size(); i++){cv::Mat Pt=(cv::Mat_<double>(3,1)<<locations[i][0],locations[i][1],locations[i][2]);cv::Mat afterPt=rotation*Pt;double x = afterPt.at<double>(1,0);double y = afterPt.at<double>(2,0);plane_points.push_back(cv::Point2d(x, y));if(min_x > x) min_x = x;if(max_x < x) max_x = x;if(min_y > y) min_y = y;if(max_y < y) max_y = y;//求出这些点中的水平方向的最大值最小值和竖直方向的最大值最小值std::cout<<"orignal     "<<locations[i].transpose()<<"      after rotated   "<<afterPt.t()<<std::endl;}

step1: 根据实际点的大小计算生成图像的长宽

step2: 将实际点中为负的点,通过减去最小值,转换到正点上

step3: 然后就是把转换后的实际点按照原始比例画在图像上

    //计算实际点水平方向与竖直方向的长宽比值double wh = (max_x-min_x)/( max_y-min_y);//只设置高度,然后根据实际比利计算长度,这样可以确保画出来的轨迹图不失真。int img_height = 720;int img_width = img_height * wh;cv::Mat img(img_height, img_width, CV_8UC3, cv::Scalar(255, 255, 255));std::cout<<"img.size(): "<<img.size()<<std::endl;double scale_ = max(max_x-min_x, max_y-min_y);std::cout<<"scale_: "<<scale_<<std::endl;cv::Point2i center_p;std::vector<cv::Point2i> pixels;for(int i = 0; i<plane_points.size(); i++){//首先将负数部分都转换到正数部分double x = plane_points[i].x - min_x;double y = plane_points[i].y - min_y;//std::cout<<"x: "<<x<<"   y:"<<y<<std::endl;// int u = img_width*x/(scale_);// int v = img_height*y/(scale_);int u = img_width*x/(max_x-min_x);int v = img_height*y/(max_y-min_y);std::cout<<"u: "<<u<<"  v:"<<v<<std::endl;cv::Point2i center_p(u,v);pixels.push_back(center_p);cv::circle(img, center_p, 2, cv::Scalar(0, 0, 255), -1, 8);}for(int i = 1; i<pixels.size(); i++){cv::Point2i start_point(pixels[i-1].x, pixels[i-1].y);cv::Point2i end_point(pixels[i].x, pixels[i].y);cv::line(img, start_point, end_point, cv::Scalar(0, 0, 255), 2);}

旋转三维平面与某一坐标平面平行相关推荐

  1. 【飞控理论】【惯性导航基础】二维平面的旋转如何用代数表示?三维平面的旋转如何用代数表示?什么是四元数?四元数、欧拉角、方向余弦之间有什么关系?

    上一篇欧拉角 由于欧拉角在描述三维空间物体旋转问题时存在万向节死锁问题(详情戳这里),所以引入四元数概念. 目录 1.二维平面的旋转 2.三维平面的旋转(什么是四元数) 3.<捷联惯性导航> ...

  2. R语言绘图学习1(pairs,cpairs,饼图,三维饼图,高密度散点图,三维图,旋转三维图)

    本人正在学习R语言,像利用这个平台记录自己一些自己的学习情况,方便以后查找,也想分享出来提供一些资料给同样学习R语言的同学们.(如果内容有错误,欢迎大家批评指正) 1.pairs和cpairs的区别 ...

  3. 如何做出好看的三维平面地图?

    阿里云DataV最近上新了全新三维地形可视化神器-三维平面地图组件~ 一.为何要推出新版三维平面地图? DataV以前就有三维平面世界与三维平面中国两个组件,差不多长这样 从现在的技术水平来看旧版三维 ...

  4. matlab中如何转动三维图_MATLAB小技巧之:绕任意空间轴旋转三维图形

    x=1:0.1:10; y=sin(x); c=cosd(15); s=sind(15); X=x*c-y*s; Y=x*s+y*c; plot(x,y) hold on plot(X,Y) 这就是新 ...

  5. 西门子1200三轴打胶机程序。 三轴运动控制定位,进行三维平面打胶

    西门子1200三轴打胶机程序. 三轴运动控制定位,进行三维平面打胶,平面打胶,Z轴上下使能,PLC与工业相机进行TCP通信来读取定位偏差,传送数据通过比较完成精准定位. 程序支持博图15及以上. 程序 ...

  6. pyqtgraph:GLSurfacePlotItem如何在三维地形表面自定义颜色分布(读取图片给三维平面分区域着色)

    pyqtgraph:GLSurfacePlotItem如何在3D高度图上自定义颜色分布(读取图片给三维平面分区域着色) 一.关于pyqtgraph 二.本文要实现的功能 1.默认效果 2.使用matp ...

  7. python三维图视角旋转_如何在python中旋转3d图? (或作为动画)使用鼠标旋转三维视图...

    我有这段代码,其中包含一个3D图.我在Spyder中运行代码;我想知道是否可以使这个绘图旋转(360度)并保存. 谢谢! P.s.对不起,如果这是一个愚蠢的问题,但我是Python的newby.如何在 ...

  8. python 实现三维平面拟合 附纸质版最小二乘法的推导与程序的实现

    1.公式推导 2.程序的实现 2.1数据集格式 程序的数据集格式如下,只要数据集格式相同,只要修改程序的文件路径即可运行. 2.2程序 import pandas as pd import numpy ...

  9. 银河系三维平面全景图

    昨天想了一下,地球在太阳系,太阳系在银河系内部,假定都在一个平面内圆周运动,地球每天旋转360度,可以买个望远镜指向银河的方向,定时拍摄一系列照片,通过拼接.计算应该可以计算出一个银河系的三维全景图. ...

最新文章

  1. python pycurl_简单谈谈Python的pycurl模块_python
  2. Webpack实现按需打包Lodash的几种方法详解
  3. 博图v15.1 安装步骤
  4. MySQL单表数据不要超过500万行:是经验数值,还是黄金铁律?
  5. Spark Structured SQL : NumberFormatException: Zero length BigInteger
  6. CSV文件在Python中的几种处理方式
  7. negative binomial(Pascal) distribution —— 负二项式分布(帕斯卡分布)
  8. kettle预览点两下才出数_谁才是娱乐圈油腻王中王?
  9. LeetCode之翻转字符串里的单词
  10. 计算机辅助设计rhino试题,计算机辅助设计基础期末复习试题(完整版).doc
  11. 第一个彻底解决!微信公众号数学公式排版
  12. 【嗅探工具】wireshark初步认识
  13. U盘防病毒从七方面做起
  14. C++判断一个数是否是质数(极简版)
  15. PS案例提升 【第1节】抠图--薄、透、露的朦胧美 案例二:扣取婚纱新娘
  16. MacBook 安装win7双系统、2013款MacBook air安装双系统教程
  17. mysql安装教程5.1_mysql 5.1安装教程详解
  18. nginx转发post请求
  19. Verilog学习笔记(06)
  20. 04.第五章、范围管理

热门文章

  1. macbook使用和养护技巧,心得
  2. require js之define 函数
  3. NSArray与NSPredicate使用
  4. NSArray (API详解)
  5. FLYMCU之串口下载到stm32单片机注意点
  6. C# DataRow 比较
  7. maven常见面试题
  8. html分享标题描述图片尺寸,微信分享网页时自定义标题、描述和图片
  9. FPGA数字信号处理(六)直接型IIR滤波器Verilog设计
  10. 系统思考:智猪博弈(关键决策)