第三章主要是三維空間剛體運動描述方式:旋轉矩陣,變換矩陣,四元數、歐拉角以及Eigen庫的使用。由於本周時間比較緊,看的比較粗略,如有錯誤還請不吝賜教,不勝感激。
下面記錄以散碎的知識點為主,日后在行整理。
1.兩個向量的外積a X b可以看做一個矩陣和向量的乘法。其中a變為一個反對稱矩陣。 外積表示向量的旋轉
基坐標表示
2. 偉大的歐拉
(1)一個向量在機器人坐標系下的坐標到世界坐標系下坐標的轉換可以有一個旋轉加一個平移組成。(剛體運動)
(2)旋轉矩陣。(公式后面補)其各分量兩個坐標系基的內積。
一個向量a在旋轉后剛體不變,坐標描述變了
同時左乘
R就是旋轉矩陣。描述相機旋轉。為正交陣,其逆就是轉置
SO(N) 特殊正交群 。 n維。這個集合有n維空間的旋轉矩陣構成。
(3)變換矩陣與齊次坐標。
a'為變換后的矩陣。
在三維向量末尾添加1,變成四維向量,即為齊次坐標。然后根據a'=Ra+t,得出變換矩陣。
SE(N) 特殊歐氏群。 左上角為旋轉,右側平移向量,右下角為1;。
3.旋轉向量和歐拉角
(1) 問題 :SO(3)的旋轉矩陣有9個量,但一次旋轉只有3個自由度,因此該表達方式冗余。變換矩陣16個量表示6個自由度。
旋轉矩陣必須是正交矩陣,行列式為1.自身帶有約束,估計優化有困難。
解決:因此提出用一個旋轉軸和一個旋轉角刻畫變換。
一個三維向量方向與旋轉軸一致,長度為旋轉角,該向量即為旋轉向量。再用一個三維向量表示平移,正好對應變換矩陣的六維。
羅德里格斯公式:
描述了R旋轉矩陣與旋轉向量表示法θn 之間的關系
后面會用到
(2)歐拉角
yaw- pitch- roll;
偏航角,俯仰角,滾轉角
z軸 y軸 x軸
萬向鎖問題(待學,后面附教學視頻鏈接),即奇異性問題。
適用於2D場合,只用其中一個定位。
4.四元數
利用復數域乘法法則的性質。基礎性質略
某個旋轉繞單位向量n=[nx,ny,nx]T 進行角度為θ旋轉,四元數形式為
反求旋轉軸和夾角(證明過程先略過)
/* p' = qpq-1 其中p為空間中一個點,用虛四元數表示。
取出p'的虛部就可得到旋轉后點的坐標。*/
eigen庫使用:
#include <iostream> using namespace std; #include <ctime> // Eigen 核心部分 #include <Eigen/Core> // 稠密矩陣的代數運算(逆,特征值等) #include <Eigen/Dense> using namespace Eigen; #define MATRIX_SIZE 50 /**************************** * 本程序演示了 Eigen 基本類型的使用 ****************************/ int main(int argc, char **argv) { // Eigen 中所有向量和矩陣都是Eigen::Matrix,它是一個模板類。它的前三個參數為:數據類型,行,列 // 聲明一個2*3的float矩陣 Matrix<float, 2, 3> matrix_23; // 同時,Eigen 通過 typedef 提供了許多內置類型,不過底層仍是Eigen::Matrix // 例如 Vector3d 實質上是 Eigen::Matrix<double, 3, 1>,即三維向量 Vector3d v_3d; // 這是一樣的 Matrix<float, 3, 1> vd_3d; // Matrix3d 實質上是 Eigen::Matrix<double, 3, 3> Matrix3d matrix_33 = Matrix3d::Zero(); //初始化為零 // 如果不確定矩陣大小,可以使用動態大小的矩陣 Matrix<double, Dynamic, Dynamic> matrix_dynamic; // 更簡單的 MatrixXd matrix_x; // 這種類型還有很多,我們不一一列舉 // 下面是對Eigen陣的操作 // 輸入數據(初始化) matrix_23 << 1, 2, 3, 4, 5, 6; // 輸出 cout << "matrix 2x3 from 1 to 6: \n" << matrix_23 << endl; // 用()訪問矩陣中的元素 cout << "print matrix 2x3: " << endl; for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) cout << matrix_23(i, j) << "\t"; cout << endl; } // 矩陣和向量相乘(實際上仍是矩陣和矩陣) v_3d << 3, 2, 1; vd_3d << 4, 5, 6; // 但是在Eigen里你不能混合兩種不同類型的矩陣,像這樣是錯的 // Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d; // 應該顯式轉換 Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d; cout << "[1,2,3;4,5,6]*[3,2,1]=" << result.transpose() << endl; Matrix<float, 2, 1> result2 = matrix_23 * vd_3d; cout << "[1,2,3;4,5,6]*[4,5,6]: " << result2.transpose() << endl; // 同樣你不能搞錯矩陣的維度 // 試着取消下面的注釋,看看Eigen會報什么錯 // Eigen::Matrix<double, 2, 3> result_wrong_dimension = matrix_23.cast<double>() * v_3d; // 一些矩陣運算 // 四則運算就不演示了,直接用+-*/即可。 matrix_33 = Matrix3d::Random(); // 隨機數矩陣 cout << "random matrix: \n" << matrix_33 << endl; cout << "transpose: \n" << matrix_33.transpose() << endl; // 轉置 cout << "sum: " << matrix_33.sum() << endl; // 各元素和 cout << "trace: " << matrix_33.trace() << endl; // 跡 cout << "times 10: \n" << 10 * matrix_33 << endl; // 數乘 cout << "inverse: \n" << matrix_33.inverse() << endl; // 逆 cout << "det: " << matrix_33.determinant() << endl; // 行列式 // 特征值 // 實對稱矩陣可以保證對角化成功 SelfAdjointEigenSolver<Matrix3d> eigen_solver(matrix_33.transpose() * matrix_33); cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl; cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl; // 解方程 // 我們求解 matrix_NN * x = v_Nd 這個方程 // N的大小在前邊的宏里定義,它由隨機數生成 // 直接求逆自然是最直接的,但是求逆運算量大 Matrix<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN = MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE); matrix_NN = matrix_NN * matrix_NN.transpose(); // 保證半正定 Matrix<double, MATRIX_SIZE, 1> v_Nd = MatrixXd::Random(MATRIX_SIZE, 1); clock_t time_stt = clock(); // 計時 // 直接求逆 Matrix<double, MATRIX_SIZE, 1> x = matrix_NN.inverse() * v_Nd; cout << "time of normal inverse is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; cout << "x = " << x.transpose() << endl; // 通常用矩陣分解來求,例如QR分解,速度會快很多 time_stt = clock(); x = matrix_NN.colPivHouseholderQr().solve(v_Nd); cout << "time of Qr decomposition is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; cout << "x = " << x.transpose() << endl; // 對於正定矩陣,還可以用cholesky分解來解方程 time_stt = clock(); x = matrix_NN.ldlt().solve(v_Nd); cout << "time of ldlt decomposition is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; cout << "x = " << x.transpose() << endl; return 0; }