旋轉矩陣和歐拉角相互轉換及代碼實現(八十九)

2021-03-02 馬少爺

一、相關概念介紹

1.1. 旋轉矩陣與旋轉向量

旋轉矩陣(Rotation Matrix)

旋轉向量(Rotation Vector)

旋轉向量到旋轉矩陣的轉換是通過羅德裡格斯公式(Rodrigues's Formula),轉換公式如下:

其中,  為旋轉軸單位向量,  為旋轉角度,  為向量到反對稱矩陣的運算符。

旋轉矩陣到旋轉向量的轉換過程如下:

可得轉角  :

對於轉角  ,旋轉軸上的向量在旋轉後不發生改變:

因此,轉軸  是矩陣  特徵值 1 對應的特徵向量。

#include <vector>#include <Eigen/Core>#include <Eigen/Geometry>#include <opencv2/opencv.hpp>
// 1. Eigen 實現

// 旋轉向量轉旋轉矩陣
Eigen::Vector3d rvec (r_x, r_y, r_z);
double n_norm = rvec.norm();
Eigen::AngleAxisd rotation_vector (n_norm, rvec/n_norm);
Eigen::Matrix3d rotm;
rotm = rotation_vector.toRotationMatrix();

// 旋轉矩陣轉旋轉向量
Eigen::Matrix3d rotation_matrix;
rotation_matrix << x_00,x_01,x_02,x_10,x_11,x_12,x_20,x_21,x_22;
Eigen::AngleAxisd rotation_vector;
rotation_vector.fromRotationMatrix(rotation_matrix);

// 2. OpenCV 實現

// 旋轉向量轉旋轉矩陣
cv::Mat rvec = (cv::Mat_<double>(3,1) << r_x, r_y, r_z);
cv::Mat rotm;
cv::Rodrigues(rvec, rotm);

// 旋轉矩陣轉旋轉向量
cv::Mat rvec;
cv::Mat rotm = (cv::Mat_<double>(3,3) << x_00,x_01,x_02,x_10,x_11,x_12,x_20,x_21,x_22);
cv::Rodrigues(rotm, rvec);

1.2. 歐拉角

旋轉矩陣和旋轉向量描述旋轉時非常不直觀,而歐拉角提供了一種比較直觀的表示法:將旋轉分解成3次繞不同軸的旋轉。由於分解方式有多種,歐拉角也存在不同的定義方法,大致可分為兩類:

比較常用的一種歐拉角為 RPY(「偏航 - 俯仰 - 翻轉」/ Yaw - Pitch - Roll),等價於 ZYX 軸的旋轉:

繞物體的 Z 軸旋轉,得到偏航角 Yaw;

繞旋轉後的 Y 軸旋轉,得到俯仰角 Pitch;

繞旋轉後的 X 軸旋轉,得到翻轉角 Roll。

歐拉角雖然比較直觀,但存在萬向節死鎖(Gimbal Lock)問題:在俯仰角為正負90° 時,第一次旋轉和第三次旋轉將使用一個軸,使得系統丟失一個自由度,會發生奇異。因此,歐拉角不適於插值和迭代。

1.3. 四元數

四元數(Quaternion)既是緊湊的,又沒有奇異性,缺點是不夠直觀,運算比較複雜。

四元數  有一個實部和三個虛部,如下所示:

三個虛部  滿足以下關係式:

(1)四元數與旋轉向量

(2)四元數與旋轉矩陣

或者:

二、歐拉角轉換成旋轉矩陣

對於六軸機械臂而言,在控制器端通常會有六個參數表徵當前機械臂末端的狀態(x, y, z, rx, ry, rz),其中(x, y, z)表示以機械臂的基座為原點(0, 0, 0)的OXYZ坐標系下機械臂工具的坐標值,其中地面為xoy面,垂直方向為z軸,而(rx, ry, rz)表示末端工具分別圍繞基座的x、y以及z軸旋轉的角度,即歐拉角(eular)。

此時我們定義機械臂末端存在另一個坐標系O'X'Y『Z',可以知道從OXYZ坐標系經過Rt的坐標變換會得到O'X'YZ'。
其中t=[x, y, z], 為一個3*1的列向量。
對於旋轉矩陣R而言,可以通過歐拉角轉換成旋轉矩陣,

Euler 角 ψ, θ, 和 φ

橫滾角 ψ

俯仰角 θ

航向角 φ

旋轉角 R

根據 Euler 角 ψ, θ, 和 φ 計算旋轉角 R

使用opencv以及eigen程序實現如下:

2.1. opencv實現

Mat eulerAnglesToRotationMatrix(Vec3d &theta){            Mat R_x = (Mat_<double>(3, 3) <<    1, 0, 0,    0, cos(theta[0]), -sin(theta[0]),    0, sin(theta[0]), cos(theta[0])    );
       Mat R_y = (Mat_<double>(3, 3) <<    cos(theta[1]), 0, sin(theta[1]),    0, 1, 0,    -sin(theta[1]), 0, cos(theta[1])    );
       cv::Mat R_z = (cv::Mat_<double>(3, 3) <<    cos(theta[2]), -sin(theta[2]), 0,    sin(theta[2]), cos(theta[2]), 0,    0, 0, 1);
   Mat R = R_z * R_y * R_x;    return R;}

2.2. eigen實現

  cout << "R = " << endl << " " << Eigen::Vector3d::UnitZ() << endl << endl;  cout << "R = " << endl << " " << Eigen::Vector3d::UnitY() << endl << endl;  cout << "R = " << endl << " " << Eigen::Vector3d::UnitX() << endl << endl;
   Matrix3d rotation;  Vector3d eular_angle(123 * PI / 180, 45 * PI / 180, 126 * PI / 180);
 rotation = Eigen::AngleAxisd(eular_angle[2], Eigen::Vector3d::UnitZ())  * Eigen::AngleAxisd(eular_angle[1], Eigen::Vector3d::UnitY())  * Eigen::AngleAxisd(eular_angle[0], Eigen::Vector3d::UnitX());  cout << rotation << endl;

通常我們會將R和t組合成一個齊次坐標,形式如下:

r11    r12    r13    t1

r21    r22    r23    t2

r31    r32    r33    t3

0        0        0      1

使用eigen實現如下:

Eigen::Matrix3d rotation_matrix = Eigen::Matrix3d::Identity();    Eigen::AngleAxisd rotation_vector(PI / 4, Eigen::Vector3d(0, 1, 0));       Eigen::Isometry3d T = Eigen::Isometry3d::Identity();                  T.rotate(rotation_matrix);                                       T.pretranslate(Eigen::Vector3d(1, 3, 4));                       cout << "Transform matrix = " << T.matrix() << endl;

假設機械臂末端坐標原點在基坐標系下的坐標值為(134, 324, 45, 123, 45, 126),可以計算出t=[134, 324, 45], R的值如下:

Mat R; Vec3d eular(123 *  PI/ 180, 45 * PI / 180, 126 * PI / 180);  R = eulerAnglesToRotationMatrix(eular);

假設在機械臂末端坐標系下有坐標值( 12, 4 , 45),那麼在基坐標系下的坐標值計算如下:

R*([12, 4 , 45].transpose()) + t


三、從旋轉矩陣計算歐拉角


旋轉矩陣和歐拉角之間的正向轉換關係比較好推理,而逆向變換就顯得不是那麼容易了。這篇博客介紹由旋轉矩陣計算歐拉角的方法,參考了一篇Paper:Computing Euler angles from a rotation matrix。

3.1MATLAB實現
function eulerAngles = rotationMatrix2eulerAngles(R)
% eulerAngles = rotationMatrix2eulerAngles(R)%% This function returns the rotation angles in degrees about the x, y and z axis for a% given rotation matrix% % Copyright : This code is written by david zhao from SCUT,1257650237@qq,com. The code% may be used, modified and distributed for research purposes with% acknowledgement of the author and inclusion this copyright information.%% Disclaimer : This code is provided as is without any warrantly.
if abs(R(3,1)) ~= 1 theta1 = -asin(R(3,1)); theta2 = pi - theta1; psi1 = atan2(R(3,2)/cos(theta1), R(3,3)/cos(theta1)); psi2 = atan2(R(3,2)/cos(theta2), R(3,3)/cos(theta2)); pfi1 = atan2(R(2,1)/cos(theta1), R(1,1)/cos(theta1)); pfi2 = atan2(R(2,1)/cos(theta2), R(1,1)/cos(theta2)); theta = theta1; % could be any one of the two psi = psi1; pfi = pfi1;else phi = 0; delta = atan2(R(1,2), R(1,3)); if R(3,1) == -1 theta = pi/2; psi = phi + delta; else theta = -pi/2; psi = -phi + delta; endend
%psi is along x-axis.theta is along y-axis...pfi is along z%axis% eulerAngles = [psi theta pfi]; %for rad;eulerAngles = [psi*180/pi theta*180/pi pfi*180/pi]; %for degree;

測試:

R =

    -0.4156    0.0920    0.9048

    0.5720    0.7999    0.1814

   -0.7071    0.5930   -0.3851

eulerAngles =

  122.9994   44.9995  126.0012

R =

   -0.4156    0.0920    0.9049

    0.5721    0.7999    0.1814

   -0.7071    0.5930   -0.3851

eulerAngles =

  122.9999   45.0000  125.9999

3.2 C++實現
需要math.h,矩陣R的類型,用的Eigen庫,如果你不用這個直接換成二維數組vector<vector<float>>就可以,完全不影響。
std::vector<float> computeEularAngles(Eigen::Matrix4f& R, bool israd){  std::vector<float> result(3, 0);  const float pi = 3.14159265397932384626433;  float theta = 0, psi = 0, pfi = 0;  if (abs(R(2, 0)) < 1 - FLT_MIN || abs(R(2, 0)) > 1 + FLT_MIN){     float theta1 = -asin(R(2, 0));    float theta2 = pi - theta1;    float psi1 = atan2(R(2,1)/cos(theta1), R(2,2)/cos(theta1));    float psi2 = atan2(R(2,0)/cos(theta2), R(2,2)/cos(theta2));    float pfi1 = atan2(R(1,0)/cos(theta1), R(0,0)/cos(theta1));    float pfi2 = atan2(R(1,0)/cos(theta2), R(0,0)/cos(theta2));    theta = theta1;    psi = psi1;    pfi = pfi1;  } else{    float phi = 0;    float delta = atan2(R(0,1), R(0,2));    if (R(2, 0) > -1 - FLT_MIN && R(2, 0) < -1 + FLT_MIN){       theta = pi / 2;      psi = phi + delta;    } else{      theta = -pi / 2;      psi = -phi + delta;    }  }
if (israd){ result[0] = psi; result[1] = theta; result[2] = pfi; } else{ result[0] = psi * 180 / pi; result[1] = theta * 180 / pi; result[2] = pfi * 180 / pi; } return result;}
Matrix4f R(4,4);R << -0.4156, 0.0920, 0.9048, 1,0.5720, 0.7999, 0.1814, 1,-0.7071, 0.5930, -0.3851, 1,0, 0, 0, 1;cout << "R=" << endl << R << endl;bool RIGHT =false;computeEularAngles(R, RIGHT);    

測試:

參考文獻

【1】https://zhuanlan.zhihu.com/p/93563218

【2】blog.csdn.net/yuzhongch

【3】http://nghiaho.com/?page_id=846

【4】http://www.soi.city.ac.uk/~sbbh653/publications/euler.pdf

【5】

https://d3cw3dd2w32x2b.cloudfront.net/wp-content/uploads/2012/07/euler-angles.pdf

推薦:機器人實時糾偏系統(一)

在C++中調用Matlab函數(二十八)

機器人手眼標定MATLAB及C++實現

機器人位姿運算及Eigen的使用(三十)

OpenCV與Eigen矩陣運算(三十一)

VS中數據讀寫及OpenCV擬合(三十二)

VS2013配置OpenGL庫(三十三)

曲線擬合/插值(三十四)

曲線擬合繪製濾波及機器人平移(三十五)

DX200操作要領—示教1(三十六)

直接打開與平移變換(三十七)
PAM與鏡像平移變換(三十八)

修改與編輯程序(三十九)

YRC1000 宏程序命令(四十)

程序編輯與試運行(四十一)

程序編輯與再現(四十二)

再現(四十三)

程序管理(四十四)

便捷功能(四十五)

便捷功能(四十六)

橢圓擬合(四十七)

RANSAC直線擬合(四十八)

讀寫CSV文件類(四十九)

RANSAC直線擬合(五十)

法向量、旋轉矩陣計算(五十一)

機器人手眼標定與變量設置調試(五十二)

20200715調試記錄(五十三)

20200717調試記錄(五十四)

C++~RANSAC擬合圓/隨機數產生(五十五)

安川機器人外部軸協調(五十六)

安川機器人Motoplus編程(五十七)

安川機器人Motoplus編程(五十八)

安川機器人Motoplus編程(五十九)

安川機器人Motoplus編程(六十)

安川機器人Motoplus編程(六十一)

機器人調試(六十二)
機器人調試(六十三)

機器人調試(六十四)

機器人調試(六十五)

機器人調試(六十六)

機器人調試(六十七)

高斯卷積的可分離性(六十八)

機器人焊縫識別及軌跡規劃研究(六十九)

安川機器人同步設置與檢驗(七十)

OpenCV+Eigen上位機程序移植(七十一)

機器人調試(七十二)

雷射焊縫跟蹤技術(七十三)
工業機器人編程技術及趨勢(七十四)

20201130記錄(七十五)

結構相似性(SSIM)原理及其實現(七十六)

機器人調試(七十七)

20201209調試記錄(七十八)

機器人調試(七十九)

機器人調試(八十)

機器人調試(八十一)
KUKA 機器人系統(八十二)

KUKA機器人運動控制(八十三)
KUKA機器人運行(八十四)

KUKA機器人運行(八十五)

B樣條曲線擬合原理及代碼實現(八十六)

卡爾曼濾波及其代碼實現(八十七)

執行KUKA機器人程序(八十八)

相關焦點

  • 剛體旋轉知識
    三維空間中剛體旋轉實質是對所定義的剛體坐標系的旋轉,如相機坐標系的旋轉。目前,剛體旋轉的表示有軸角(旋轉向量)、四元數、歐拉角、旋轉矩陣等方法。本文主要闡述這些表示方法以及優缺點。對於坐標系的旋轉,任意旋轉都可以採用一個旋轉軸(單位向量)n 和一個旋轉角 θ 表示,稱之為軸角法。
  • 坐標系與相機旋轉表示
    >在講旋轉矩陣前,先介紹下向量的概念,因為旋轉矩陣的推導是根據同一向量在不同坐標系下的坐標表示得出的。等式兩邊同乘[e,e,e]T,得:R即為旋轉矩陣,可以看出其是行列式為1的正交矩陣(滿足AAT=I的矩陣,其行列式detA=±1)2.2旋轉向量與歐拉角
  • Unity中BVH骨骼動畫驅動的可視化理論與實現
    CMU提供的BVH骨骼動畫中,第一幀通常就是Tpose,所以直接計算出第一幀每個關節的全局旋轉矩陣就是對應的Tpose變換矩陣T1,記住:unity中骨骼動畫因為導入以後通常就是T姿勢,所以直接獲取所有關節最開始的全局旋轉矩陣就是對應的Tpose變換矩陣T2變換矩陣 T3
  • HALCON高級篇:3D坐標之剛性轉換和齊次轉換矩陣
    剛性轉換和齊次轉換矩陣點的剛性轉換如果你想將旋轉和平移結合在一起,你就可以得到所謂的剛性轉換。這樣的轉換可以用下面的方程來描述:(1)對於多次轉換,這樣的方程很快就會變得令人困擾,就像下面的兩個轉換:一個更加明了的選擇就是使用所謂的齊次轉換矩陣和對應的齊次向量。一個齊次轉換矩陣H包含了旋轉矩陣和平移向量。
  • 姿態解算進階之理解旋轉矩陣(也稱方向餘弦矩陣)
    算法類推文比技能類推文難寫,Sugar 本來想姿態解算的算法系列在入門後就不再寫了,但有讀者下載了 Sugar 在 5 篇入門系列共享的 MATLAB 代碼之後表示對其中一些方法不理解,所以 Sugar 再開個進階系列。注意:深入算法無終點,打算進階的讀者需要自行補一點數學基礎。旋轉矩陣為何五花八門關於旋轉矩陣的基本原理 Sugar 就不再重寫了,網上非常多。
  • Python代碼閱讀(第41篇):矩陣轉置
    本篇閱讀的代碼實現了將一個使用嵌套列表表示的矩陣進行轉置的功能。
  • 用Python 代碼實現簡單圖片人像識別換臉
    旋轉、縮放、平移和第二張圖片,以配合第一步。調整第二張圖片的色彩平衡,以適配第一張圖片。把第二張圖像的特性混合在第一張圖像中。1.使用 dlib 提取面部標記該腳本使用 dlib 的 Python 綁定來提取面部標記:
  • 教程| 從特徵分解到協方差矩陣:詳細剖析和實現PCA算法
    機器之心編譯參與:蔣思源本文先簡要明了地介紹了特徵向量和其與矩陣的關係,然後再以其為基礎解釋協方差矩陣和主成分分析法的基本概念,最後我們結合協方差矩陣和主成分分析法實現數據降維。本文不僅僅是從理論上闡述各種重要概念,同時最後還一步步使用 Python 實現數據降維。首先本文的特徵向量是數學概念上的特徵向量,並不是指由輸入特徵值所組成的向量。
  • 手把手:用OpenCV親手給小扎、Musk等科技大佬們做一張「平均臉」(附Python代碼)
    因此我們可以利用這兩個點計算圖像的相似變換矩陣(旋轉、變換和縮放),將輸入坐標系統的點進行轉換為輸出坐標系統。(不想學數學的同學可以簡單略過)如果你想對一個正方形作出轉換,使正方形在x和y方向上分別縮放s_x和s_y,同時將它旋轉theta角度,再在x和y方向上平移t_x和t_y,對應的相似變換矩陣就是
  • 如何實現將C#字符串轉換為大寫或小寫?數組怎麼轉換為字符串?
    C#字符串大小寫轉換完整代碼如下:using System;using System.Collections.Generic;using System.Linq; Console.ReadKey(); } }}在圖1中,將str字符串的所有字母分別全部轉換為大寫和小寫
  • ...貨幣市場基金開通同一基金不同類別份額相互轉換業務的提示性公告
    浙商日添利A : 關於旗下浙商日添利貨幣市場基金開通同一基金不同類別份額相互轉換業務的提示性公告 時間:2020年12月16日 09:10:42&nbsp中財網 原標題:浙商日添利A : 關於旗下浙商日添利貨幣市場基金開通同一基金不同類別份額相互轉換業務的提示性公告
  • PDF和DWG的相互轉換看完你就會了
    、列印、交流這幾個流程是相互的,甚至有時候我們希望是可逆的。因此,對於兩種格式的相互轉換,最理想的狀態是無差別完美轉換。今天,我們不妨整理一下,PDF和DWG格式的相互轉換到底有哪些方法。截圖來自CAD2020版經常有入門小夥伴會問,為什麼我的CAD無論是另存為還是輸出,都沒有找到PDF格式?這裡先科普一下小知識:另存為和輸出到底有什麼分別?
  • 【程序猿】Transform組件的關於旋轉轉向方面的API解析
    1.eulerAngles/localEulerAngles指使用歐拉角的方式圍繞穿過遊戲物體的自身位置點並且平行於遊戲物體的「父級」的世界/局部坐標軸(X/Y/Z)方向的「隱形軸」旋轉一定的角度。通過eulerAngle/localEulerAngle設置新值時,要同時設置x/y/z的值transform.localEulerAngles = newVector3(0, yRotation, 45);最好不要單個設置某個軸的值transform.localEulerAngles.y= yRotation,它很可能會產生偏移和不希望的旋轉
  • 小 200 行 Python 代碼做了一個換臉程序
    旋轉、縮放、平移和第二張圖片,以配合第一步。調整第二張圖片的色彩平衡,以適配第一張圖片。把第二張圖像的特性混合在第一張圖像中。1.使用 dlib 提取面部標記該腳本使用 dlib 的 Python 綁定來提取面部標記:
  • 小 200 行 Python 代碼就可以做一個換臉程序
    Dlib 實現了 Vahid Kazemi 和 Josephine Sullivan 的《使用回歸樹一毫秒臉部對準》論文中的算法。2.用 Procrustes 分析調整臉部現在我們已經有了兩個標記矩陣,每行有一組坐標對應一個特定的面部特徵(如第30行的坐標對應於鼻頭)。我們現在要解決如何旋轉、翻譯和縮放第一個向量,使它們儘可能適配第二個向量的點。一個想法是可以用相同的變換在第一個圖像上覆蓋第二個圖像。將這個問題數學化,尋找T,s 和 R,使得下面這個表達式:
  • 張先軼博士:OpenBLAS項目與矩陣乘法優化
    此外,還有很多科學和工程的模擬,在轉換後都變成了一種矩陣上的操作。如果你把矩陣優化的特別好的話,對整個應用的提升,都是非常有幫助的。我想只要學過《線性代數》之類的,這種矩陣乘法,是一個非常簡單的問題,如果轉換成C代碼來做的話,就是一個三重循環,我在這張圖裡列出了一個【i j k】的三重循環,這裡面矩陣乘法的代碼就已經是,它實現的功能就是矩陣A*矩陣B,加到矩陣C裡面,C是結果矩陣,這裡面C的代碼,和在課本上看到的累加的公式是一樣的。
  • 非線性回歸中的Levenberg-Marquardt算法理論和代碼實現
    這就是為什麼我們的函數f取決於xi和aj的原因:我們有x的i值和a的j值。我們可以將所有這些導數彙編成一個稱為Jacobian的術語。雅可比行列式是一個矩陣,其中包含一個函數相對於每個參數的所有一階偏導數。記住,下標i代表一個特定的數據點。如果數據包含100個點那麼雅可比矩陣就有100行3列因為我們有3個參數。
  • Word、PPT、Excel怎麼相互轉換​
    因為它們之間的相互轉換都很麻煩,比如一份十幾頁的Word文檔轉換為PPT演示文件,基本就要用一個下午的時間來作複製粘貼了…… 其實呢,Word、PPT、Excel之間的轉換並不需要用到「複製粘貼」哦,只是平時我們都沒有找到那條它們「友好」進行互轉互換的途徑。