旋轉三維平面與某一坐標平面平行

在上一篇文章(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);}

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/252335.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/252335.shtml
英文地址,請注明出處:http://en.pswp.cn/news/252335.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

elasticsearch的插件安裝

目前使用的是2.4.5版本的es 安裝的時候注意以下幾點 : 1.如果想所有的ip都能訪問es,需要修改config下的elasticsearch.yml.修改如下 network.host0.0.0.02.安裝查詢插件 : 進入es的安裝目錄,執行以下命令 ./bin/plugin install mobz/elasticsearch-head3.安裝刪除插件 目前不支…

let const緩存for循環的中間變量

es5中使用在for-in for循環中注冊異步事件&#xff0c;異步事件中的i總是最后一個值。使用es6的let const可以解決 let obj {a: 1,b: 1,c: 1 }// es5 for循環中 var聲明 i let funcs [] for (var key in obj) {funcs.push(() > {console.log(key)}) } funcs.forEach(func …

BZOJ1439 : YY的問題

考慮容斥&#xff0c;枚舉哪些不存在的邊選中了&#xff0c;剩下的不管&#xff0c;則可以用組合數計算方案數。 時間復雜度$O(m2^mnm)$。 #include<cstdio> const int N550,B10000,MAXL350; int n,m,S,i,j,e[N][2],g[N],f[N]; inline int max(int a,int b){return a>…

windows下配置opencv

我的windows下是使用的一個鏡像安裝的vs2015&#xff0c;然后在vs上編譯工程需要使用opencv時&#xff0c;需要在工程中配置opencv 新建一個C工程&#xff0c;按照下面的步驟進行配置。 設置opencv的環境變量 “此電腦”右鍵點擊“屬性”-->選擇“高級系統設置”-->選…

關于spring MVC中加載多個validator的方法。

首先講下什么叫做validator&#xff1a; validator是驗證器&#xff0c;可以驗證后臺接受的數據&#xff0c;對數據做校驗。 SpringMVC服務器驗證有兩種方式,一種是基于Validator接口,一種是使用Annotaion JSR-303標準的驗證。 1.使用Annotaion JSR-303標準的驗證 使用這個需要…

面試時,面試官到底在考察什么?

作者&#xff1a;白海飛出處&#xff1a;極客時間《面試現場》專欄 先看一段面試對話&#xff0c;“大面”是一位久經沙場的面試官&#xff0c;小明就是今天的應聘者。一通面試下來&#xff0c;前面的技術問題小明都對答如流&#xff0c;雙方相談甚歡&#xff0c;接下來面試官“…

NoSQL-MongoDB with python

前言&#xff1a; MongoDB&#xff0c;文檔存儲型數據庫&#xff08;document store&#xff09;。NoSQL數據庫中&#xff0c;它獨占鰲頭&#xff0c;碾壓其他的NoSQL數據庫。 使用C開發的&#xff0c;性能僅次C。與redis一樣&#xff0c;開源、高擴展、高可用。 基于分布式文件…

RHCS

云計算與大數據 黑洞 RHCS(概念篇) 一、 什么是RHCS RHCS是Red Hat Cluster Suite的縮寫&#xff0c;也就是紅帽子集群套件&#xff0c;RHCS是一個能夠提供高可用性、高可靠性、負載均衡、存儲共享且經濟廉價的集群工具集合&#xff0c;它將集群系統中三大集群架構融合一體&…

深度圖壓縮之-高低8位拆分保存

使用kinect相機保存數據&#xff0c;為了減少保存的數據集量&#xff0c;對圖像進行壓縮。將彩色圖像直接壓縮成.mp4格式&#xff0c;此時圖像上的一些高頻信息會被損失掉。 為了能夠讓深度圖有比較高的保真度&#xff0c;減少深度圖上高頻信息的損失&#xff0c;我們將16位的…

linux 一個超簡單的makefile

2019獨角獸企業重金招聘Python工程師標準>>> makefile 自動化變量&#xff1a; $ : 規則的目標文件名 例如&#xff1a;main:main.o test.o g -Wall -g main.o test.o -o main 可以寫成&#xff1a; main:main.o test.o g -Wall -g main.o test.o -o $ $< : …

poj2480(利用歐拉函數的積性求解)

題目鏈接: http://poj.org/problem?id2480 題意&#xff1a;∑gcd(i, N) 1<i <N&#xff0c;就這個公式&#xff0c;給你一個n&#xff0c;讓你求sumgcd(1,n)gcd(2,n)gcd(3,n)…………gcd(n-1,n)gcd(n,n),&#xff08;1<n<2^31&#xff09;是多少&#xff1f; 放…

跨域問題

一、為什么會有跨域問題&#xff1f; 是因為瀏覽器的同源策略是對ajax請求進行阻攔了&#xff0c;但是不是所有的請求都給做跨域&#xff0c;像是一般的href屬性&#xff0c;a標簽什么的都不攔截。 二、解決跨域問題的兩種方式 JSONPCORS 三、JSONP 先簡單來說一下JSONP&#x…

PAT A1052

這個需要注意的是相關的string轉整數或者double的函數&#xff1b;詳見這個鏈接blog #include <iostream> #include <string> using namespace std; bool isPrime(int n) {if (n 0 || n 1) return false;for (int i 2; i * i < n; i)if (n % i 0) return fa…

php審計學習:xdcms2.0.8注入

注入點Fields: 注冊頁面會引用如下方法: $fields 變量是從 $fields$_POST[fields]; 這里獲取&#xff0c; 在代碼里沒有過濾。 打印 fields 數據查看: 從代碼上看 $field_sql.",{$k}{$f_value}"; 最終會變成: ,truename111111,email12345 因為 $field_sql 最終會引入…

windows下安裝python和Python-opencv

背景&#xff1a;目前基于python的圖像處理和機器視覺的研究還挺多&#xff0c;最近不是在研究目標檢測和目標跟蹤的算法&#xff0c;由于檢測和跟蹤的環境比較簡單所以從不帶學習的跟蹤方法&#xff0c;在搜索資料時搜到這個網站&#xff0c;是對opencv中的目標跟蹤算法的一個…

捋一捋js面向對象的繼承問題

說到面向對象這個破玩意&#xff0c;曾經一度我都處于很懵逼的狀態&#xff0c;那么面向對象究竟是什么呢&#xff1f;其實說白了&#xff0c;所謂面向對象&#xff0c;就是基于類這個概念&#xff0c;來實現封裝、繼承和多態的一種編程思想罷了。今天我們就來說一下這其中繼承…

java8簡單入門

1、介紹 本片文章會從一下幾個知識點進行介紹&#xff1a; 函數式接口 FunctionalInterfaceLambda 表達式函數引用 Function ReferenceStream看了幾篇關于 java8 入門的例子&#xff0c;其中引入了許多令人期待已久的特性&#xff08;雖然我沒有過這樣的體會&#xff09;&#…

玩轉帶外觸發的單目相機之一

背景&#xff1a;去年開始研究vins,但是只是用了普通的相機&#xff0c;然后將IMU和相機粘在一起&#xff0c;然后就是聯合標定相機和IMU。VINS使用的相機是帶有外觸發的&#xff0c;還進行了相機和IMU的硬件時間同步。當時我特別想買個帶外觸發的相機&#xff0c;一直沒找到資…

基于django的視頻點播網站開發-step11-后臺用戶管理功能...

用戶管理功能&#xff0c;包含用戶添加、列表展示、編輯、刪除四大功能。下面我們一一揭曉。 用戶添加 我們先實現用戶添加功能&#xff0c;我們現在urls.py下添加相關的路由 path(user_add/, views.UserAddView.as_view(), nameuser_add), path(user_list/, views.UserListVie…

BZOJ 1070 拆點 費用流

1070: [SCOI2007]修車 Time Limit: 1 Sec Memory Limit: 128 MBSubmit: 5860 Solved: 2487[Submit][Status][Discuss]Description 同一時刻有N位車主帶著他們的愛車來到了汽車維修中心。維修中心共有M位技術人員&#xff0c;不同的技術人員對不同 的車進行維修所用的時間是不…