Eigen线性代数库学习大全

        Eigen是C++的线性代数库,能提供有关矩阵的线性代数运算,还包含解方程等功能。

目录

0、Eigen库结构导图

1、Eigen库安装

2、Eigen库矩阵基础(参考)

2.1 矩阵模板函数       

2.2 类型

2.3 赋值与访问

2.4 调整与操作 

2.4 运算

2.5 解方程 

3、Eigen库的向量基础

3.1 类型与存储

3.2 访问与赋值

3.3 运算

4、Eigen/Geometry(参考文章)

4.1 旋转向量:Eigen::AngleAxis

4.2 欧拉角:Eigen::eulerAngles

4.3 欧式变换:Eigen::Isometry

4.4 四元数:Eigen::Quaternion

4.5 转化:

5、完整代码

5.1 高翔《视觉SLAM十四讲》程序1

5.2  高翔《视觉SLAM十四讲》程序1

6、参考文章


0、Eigen库结构导图

Eigen线性代数库学习大全_第1张图片

1、Eigen库安装

        参考文章1.4节(链接)

2、Eigen库矩阵基础(参考)

2.1 矩阵模板函数       

        Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类,矩阵模板函数共包含六个参数.

template

         参数说明:

  • typename _Scalar:矩阵元素的类型;
  • int _Rows:矩阵的行数;
  • int _Cols:矩阵的列数;
  •  int _Options:矩阵是行主序(RowMajor)或列主序(ColMajor),默认是列主序;
  • int _MaxRows:矩阵的最大行数;
  • int _MaxCols:矩阵的最大列数;
Eigen::Matrix mat1;              //  3x4 的 int 类型的矩阵 mat1
Eigen::Matrix mat2;     //  3x? 的 double 类型的矩阵 mat2
Eigen::Matrix mat3;      //  ?x4 的 float 类型的矩阵 mat3
Eigen::Matrix mat4; //  ?x? 的 long 类型的矩阵 mat4

        动态矩阵:在 Eigen 中可以用 Dynamic 表示行或者列数未知,所以在定义一个矩阵时并不能确定矩阵的大小,只有在运行时才可以确定大小,然后进行动态分配。

        静态矩阵:在定义时便明确给定了行数以及列数,在编译时就可以分配好内存。

 

2.2 类型

        在 EigenEigen 中 typedef 了很多矩阵的类型,通常命名为 Matrix前缀加一个长度为 1∼4 的字符串 S 的命名——MatrixS。

        其中 S 可以用来判断该矩阵类型,数字 n 表示 n ∗ n,n 的范围是2∼4,字母 d、f、i、cd、f、i、c 表示 double、float、int、complexdouble、float、int、complex,另外 X 表示行或者列数未知的矩阵。

typedef Matrix, 2, 2> Eigen::Matrix2cd;            //  2x2 的 cd 类型的矩阵
typedef Matrix Eigen::Matrix2d;                           //  2x2 的 d 类型的矩阵
typedef Matrix, 2, Dynamic> Eigen::Matrix2Xcd;     //  2x? 的 cd 类型的矩阵
typedef Matrix, Dynamic, 2> Eigen::MatrixX2cf;      //  ?x2 的 cf 类型的矩阵
typedef Matrix, Dynamic, Dynamic> Eigen::MatrixXcd;//  ?x? 的 cd 类型的矩阵
typedef Matrix Eigen::MatrixXi;                  //  ?x? 的 i 类型的矩阵

        同时,Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix,

//例如 Vector3d 实质上是 Eigen::Matrix,即三维向量
Vector3d v_3d;
// 这是一样的
Matrix vd_3d;
// Matrix3d 实质上是 Eigen::Matrix
Matrix3d matrix_33 = Matrix3d::Zero(); //初始化为零

        但是在Eigen里你不能混合两种不同类型的矩阵,同样你不能搞错矩阵的维度,需要注意如下的问题。

// 但是在Eigen里你不能混合两种不同类型的矩阵,像这样是错的
// Matrix result_wrong_type = matrix_23 * v_3d;
// 应该显式转换,matrix_23.cast(),然后进行计算
Matrix result = matrix_23.cast() * v_3d;
cout << "[1,2,3;4,5,6]*[3,2,1]=" << result.transpose() << endl;

Matrix result2 = matrix_23 * vd_3d;
cout << "[1,2,3;4,5,6]*[4,5,6]: " << result2.transpose() << endl;

// 同样你不能搞错矩阵的维度
// 试着取消下面的注释,看看Eigen会报什么错
// Eigen::Matrix result_wrong_dimension = matrix_23.cast() * v_3d;

 

2.3 赋值与访问

(1)Eigen::Matrix 使用 ()访问赋值数据,矩阵之间通过 = 来进行赋值(拷贝)。

x = mat(a, b);  //  获取到矩阵 mat 的 a 行 b 列的元素并赋值给 x
mat(b, a) = x;  //  将 x 赋值给矩阵 mat 的 b 行 a 列
mat1 = mat2;    //  将矩阵 mat2 赋值(拷贝)给矩阵 mat1

         注意: 通过 = 进行矩阵之间的拷贝时,如果左右两侧矩阵尺寸不一样并且左侧矩阵为动态矩阵,那么会将左侧矩阵的尺寸修改为与右侧一致。

(2)在 Eigen 中重载了 << 可以用来赋值矩阵,也可以用来 cout 输出矩阵。

MatrixXf m(4, 4);   //  定义一个 4x4 的 float 类型的动态矩阵
m << 1, 2, 3, 4,
     5, 6, 7, 8,
     9, 10, 11, 12,
     13, 14, 15, 16;//  赋值
std::cout << m;     //  输出 m

(3)Eigen 矩阵可以进行分块操作,通过成员函数 block() 获取某一部分矩阵。

mat = mat1.block(i, j, p, q);   //  从矩阵 mat1 的 i 行 j 列开始获取一个 p 行 q 列的子矩阵
mat = mat1.block(i, j);   //  从矩阵 mat1 的 i 行 j 列开始获取一个 p 行 q 列的子矩阵(动态矩阵)

 (4)Eigen 矩阵还可以使用成员函数 fill() 进行统一赋值。

mat.fill(n);    //  将 mat 的所有元素均赋值为 n

2.4 调整与操作 

(1)调整大小

mat.resize(nb_rows, nb_cols);             // 调整成具有指定行列数的矩阵
mat.resize(Eigen::NoChange, nb_cols);     // 只调整列
mat.resize(nb_rows, Eigen::NoChange);     // 只调整行
mat.resizeLike(other_matrix);             // 按other_matrix的大小调整矩阵
mat.conservativeResize(nb_rows, nb_cols); // 矩阵重排,resize()函数会改变原有矩阵中的值。

(2)获取矩阵信息

mat.rows();             // 获取矩阵的行数
mat.cols();             // 获取矩阵的列数
mat.innerSize();        // 按列存储的矩阵返回其行数,接行存储的矩阵返回其列数
mat.outerSize();        // 按列存储的矩阵返回其列数,按行存储的矩阵返回其行数
mat.innerStride();      // 返回内部实际存储时相邻元素之间的指针增量
mat.outerStride();      // 返回按逻辑顺序存储时的指针增量
mat.data();             // 获取矩阵的数据指针

(3)Eigen 提供了从边角开始提取子矩阵的方法

描述        

动态矩阵 静态矩阵
左上角 pxq   mat.topLeftCorner(p,q) mat.topLeftCorner()
左下角 pxq   mat.bottomLeftCorner(p,q)   mat.bottomLeftCorner()
右上角 pxq  mat.topRightCorner(p,q)  mat.topRightCorner()
右下角 pxq  mat.bottomRightCorner(p,q)  mat.bottomRightCorner()
前 p 行       mat.topRows(p)  mat.topRows

()

后 p 行       mat.bottomRows(p)  mat.bottomRows

()

前 q 列        mat.leftCols(q)  mat.leftCols()
后 q 列       mat.rightCols(q)   mat.rightCols()

2.4 运算

(1)Eigen 重载了 +、−(减)、∗、/、−(负)、+=、−=、∗=、/=+、−(减)、∗、/、−(负)、+=、−=、∗=、/=。

mat = mat1 + mat2;  //  +
mat = mat1 - mat2;  //  -(减)
mat = mat1 * mat2;  //  *
mat = mat1 * n;     //  *
mat = mat1 / n;     //  /
mat = -mat1;        //  -(负)
mat += mat1;        //  +=
mat -= mat1;        //  -=
mat *= mat1;        //  *=
mat *= n;           //  *=
mat /= n;           //  /=

(2)对于 MatrixMatrix 的转置矩阵、共轭矩阵、伴随矩阵、对角矩阵可以通过成员函数 transpose()、conjugate()、adjoint()、diagonal()来获得。如果想要在原矩阵上进行转换,则需要通过成员函数 transposeInPlace()、conjugateInPlace()、adjointInPlace()。

mat1.transpose(); //  获取 mat1 的转置矩阵
mat1.conjugate(); //  获取 mat1 的共轭矩阵
mat1.adjoint();   //  获取 mat1 的伴随矩阵
mat1.diagonal();  //  获取 mat1 的对角矩阵
mat1.transposeInPlace();//  mat1 转换为对应的转置矩阵
mat1.conjugateInPlace();//  mat1 转换为对应的共轭矩阵
mat1.adjointInPlace();  //  mat1 转换为对应的伴随矩阵
mat1.diagonalInPlace(); //  mat1 转换为对应的对角矩阵
mat1.sum();            // mat1各元素和
mat1.trace()         // mat1迹
mat1.inverse()         //mat1 逆
mat1.determinant()     // mat1行列式
mat1.transpose().colwise().reverse();   //  mat1 Rot90
mat.minCoeff();              // 求矩阵的最小元素
mat.colwise().minCoeff();   // 求每列的最小元素,所以输出为行
mat.rowwise().minCoeff();   // 求每行的最小元素,所以输出为列

(3)特殊矩阵

mat = MatrixXd::Identity(rows, cols);   //  生成 rows x cols 的单位阵
mat.setIdentity(rows, cols);            //  将 mat 设置为 rows x cols 的单位阵
mat = MatrixXd::Zero(rows, cols);       //  生成 rows x cols 的零矩阵
mat.setZero(rows, cols);                //  将 mat 设置为 rows x cols 的零矩阵
mat = MatrixXd::Ones(rows, cols);       //  生成 rows x cols 的壹矩阵
mat.setOnes(rows, cols);                //  将 mat 设置为 rows x cols 的壹矩阵
mat = MatrixXd::Random(rows, cols);     //  生成 rows x cols 的随机矩阵
mat.setRandom(rows, cols);              //  将 mat 设置为 rows x cols 的随机矩阵

2.5 解方程 

        我们求解 matrix_NN * x = v_Nd 这个方程为例直接求逆自然是最直接的,但是求逆运算量大。

// 求解的方程matrix_NN * x = v_Nd
// 定义matrix_NN矩阵  
Matrix matrix_NN= MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
// 保证半正定,矩阵和矩阵的转置相乘可以保证半正定
matrix_NN = matrix_NN * matrix_NN.transpose();  
// 定义v_Nd
Matrix v_Nd = MatrixXd::Random(MATRIX_SIZE, 1);

clock_t time_stt = clock(); // 计时
// 方法一:直接求逆
Matrix 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;

3、Eigen库的向量基础

3.1 类型与存储

        类似于矩阵的类型,不过前缀从 Matrix 改成 Vector。

typedef Matrix, 2, 1> Eigen::Vector2cf; //  cf 类型的 2 向量
typedef Matrix Eigen::Vector2i;                  //  i 类型的 2 向量
typedef Matrix Eigen::Vector4d;               //  d 类型的 4 向量
typedef Matrix Eigen::VectorXf;          //  f 类型的 ? 向量

        上述的 Vector 默认的都是列向量,如果是行向量,在命名上则需要加上 Row 前缀。

typedef Matrix Eigen::RowVector2f;                 //  f 类型的 2 向量(行向量)
typedef Matrix, 1, 4> Eigen::RowVector4cd; //  cd 类型的 4 向量(行向量)

        动态向量:不指定向量的尺寸,只有在运行时才会分配对应的内存;

        静态向量:一开始就定义好了大小,在编译时分配好内存。

3.2 访问与赋值

(1)类似于 Matrix 的操作,不过向量也可以使用 [],另外向量对前四个元素的访问与赋值还可以通过成员函数 x()、y()、z()、w()来操作。

x = vec(n);     //  获取向量 vec 的第 n 个元素并赋值给 x
x = vec[n];
x = vec.x();    //  获取向量 vec 的第一个元素并赋值给 x
y = vec.y();    //  获取向量 vec 的第二个元素并赋值给 y
z = vec.z();    //  获取向量 vec 的第三个元素并赋值给 z
w = vec.w();    //  获取向量 vec 的第四个元素并赋值给 w
vec(n) = x;     //  将 x 赋值给 vec 的第 n 个元素

(2)Eigen 向量也提供了一些分块操作来获取子向量。

描述 静态向量 动态向量
前 n 个元素 vec.head(n) vec.head()
后 n 个元素 vec.tail(n) vec.tail()
从 i 开始取 n 个元素 vec.segment(i,n) vec.segment(i)

3.3 运算

(1)Eigen 向量提供了 norm()、squareNorm()、dot()、cross()等成员函数。

vec.norm()        //  返回向量的二范数
vec.squaredNorm() //  返回向量所有元素的平方和
vec.dot(y)        //  返回向量点积
vec.cross(y)      //  返回向量叉积

(2)向量其他成员函数

vector.size();             // 获取向量的大小,即元素个数
vector.innerStride();      // 沿数据存储的方向移动一个位置,内存中需要移动的位置数
vector.data();             // 获取向量的数据

(3)调整大小

vector.resize(size);                         // 调整具有动态尺寸的向量大小
vector.resizeLike(other_vector);             // 按other_vector的尺寸调整具有动态尺寸的向量大小
vector.conservativeResize(size);

(4)特殊向量构造

Vector3f::UnitX() // 1 0 0
Vector3f::UnitY() // 0 1 0
Vector3f::UnitZ() // 0 0 1

 

4、Eigen/Geometry(参考文章)

        模块提供了各种旋转和平移的表示

4.1 旋转向量:Eigen::AngleAxis

(1)初始化

        旋转矩阵直接使用 Matrix3d 或 Matrix3f,旋转向量使用 AngleAxis。

AngleAxisd rotation_vector(alpha, Vector3d(x, y, z));  //alpha:旋转角度,(x, y, z):旋转轴

(2)转换为旋转矩阵

Matrix3d rotation_matrix;
rotation_matrix = rotation_vector.matrix();
// 或
rotation_matrix = rotation_vector.toRotationMatrix();

(3)转换为欧拉角(需要先转换为旋转矩阵)

euler_angles = rotation_vector.matrix().eulerAngles(2, 1, 0);

(4)转换为四元数

Quaternion q = Quaterniond(rotation_vector);

4.2 欧拉角:Eigen::eulerAngles

        Eigen::eulerAngles(a0, a1, a2)用法:a0, a1, a2从{0, 1, 2}中取值,0代表X轴,1代表Y轴,2代表Z轴,旋转顺序为从后到前(下一次旋转使用的旋转轴是上一次旋转之后的)。得到的Euler Angle角度值范围:[0, pi] x [-pi, pi] x [-pi, pi]。

Vector3f euler_angles = rotation_matrix.eulerAngles(2, 1, 0); 
// 等价于
rotation_matrix = AngleAxisd(euler_angles[0], Vector3f::UnitZ())
     * AngleAxisd(euler_angles[1], Vector3f::UnitY())
     * AngleAxisd(euler_angles[2], Vector3f::UnitX()); 

        均表示先绕X轴旋转euler_angles[2] rad,再绕Y轴旋转euler_angles[1] rad,再绕Z轴旋转euler_angles[0] rad。

        注意:坐标系:X轴朝前, Y轴朝左, Z轴朝上;XYZ轴并不一定总是对应roll, pitch, yaw,要根据实际情况判断。roll表示横滚,pitch表示俯仰,yaw表示偏航。

4.3 欧式变换:Eigen::Isometry

(1)定义

Isometry3d T = Isometry3d::Identity();  //T实际为4*4矩阵
T.rotate(rotation_vector);  //定义欧式变换的旋转矩阵
T.pretranslate(Vector3d(x, y, z));  //定义欧式变换的平移向量

(2)转换为4*4变换矩阵

Matrix4d transform_matrix = T.matrix();

(3)用欧式变换进行坐标变换

Vector3d v = (1, 2, 3);
Vector3d v_transformed = T * v;  //相当于R*v+t

4.4 四元数:Eigen::Quaternion

(1)初始化

// 直接初始化
// 注意:初始化时实部在前,虚部在后,但在内部存储顺序为[x, y, z, w]
Quaterniond q(w, x, y, z);

// 使用旋转向量和旋转矩阵初始化:
Quaterniond q1(rotation_vector);
Quaterniond q2(rotation_matrix);

(2)打印四元数

cout << q.coeffs() << endl;

(3)使用四元数旋转向量

Vector3d v_rotated = q * v;

4.5 转化:

​// 旋转向量->旋转矩阵:
rotationMatrix=rotation_vector.toRotationMatrix();
// 旋转向量->四元数:
Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_vector );
// 旋转矩阵->四元数:
Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_matrix );
// 四元素->旋转矩阵:
Eigen::Matrix3d Rx = q.toRotationMatrix();
// 旋转向量->欧拉角:
Eigen::Vector3d eulerAngle=rotationVector.matrix().eulerAngles(0,1,2);
// 旋转矩阵->欧拉角:
Eigen::Vector3d euler_angles = rotation_matrix.eulerAngles ( 2,1,0 ); // ZYX顺序,即roll pitch yaw顺序

​

5、完整代码

5.1 高翔《视觉SLAM十四讲》程序1

(1)CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(useEigen)

set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_FLAGS "-O3")

# 添加Eigen头文件
include_directories("/usr/include/eigen3")
add_executable(eigenMatrix eigenMatrix.cpp)

(2)eigenMatrix.cpp

#include 
using namespace std;
#include 
// Eigen 核心部分
#include 
// 稠密矩阵的代数运算(逆,特征值等)
#include 
using namespace Eigen;
#define MATRIX_SIZE 50

/****************************
* 本程序演示了 Eigen 基本类型的使用
****************************/

int main(int argc, char **argv) {
  // Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类。它的前三个参数为:数据类型,行,列
  // 声明一个2*3的float矩阵
  Matrix matrix_23;

  // 同时,Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix
  // 例如 Vector3d 实质上是 Eigen::Matrix,即三维向量
  Vector3d v_3d;
  // 这是一样的
  Matrix vd_3d;

  // Matrix3d 实质上是 Eigen::Matrix
  Matrix3d matrix_33 = Matrix3d::Zero(); //初始化为零
  // 如果不确定矩阵大小,可以使用动态大小的矩阵
  Matrix 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 result_wrong_type = matrix_23 * v_3d;
  // 应该显式转换
  Matrix result = matrix_23.cast() * v_3d;
  cout << "[1,2,3;4,5,6]*[3,2,1]=" << result.transpose() << endl;

  Matrix result2 = matrix_23 * vd_3d;
  cout << "[1,2,3;4,5,6]*[4,5,6]: " << result2.transpose() << endl;

  // 同样你不能搞错矩阵的维度
  // 试着取消下面的注释,看看Eigen会报什么错
  // Eigen::Matrix result_wrong_dimension = matrix_23.cast() * 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 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 matrix_NN
      = MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
  matrix_NN = matrix_NN * matrix_NN.transpose();  // 保证半正定
  Matrix v_Nd = MatrixXd::Random(MATRIX_SIZE, 1);

  clock_t time_stt = clock(); // 计时
  // 直接求逆
  Matrix 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;
}

5.2  高翔《视觉SLAM十四讲》程序1

(1)CMakeLists.txt

cmake_minimum_required( VERSION 2.8 )
project( geometry )

# 添加Eigen头文件
include_directories( "/usr/include/eigen3" )

add_executable(eigenGeometry eigenGeometry.cpp)

(2)eigenGeometry.cpp

#include 
#include 
#include 
#include 
using namespace std;
using namespace Eigen;

// 本程序演示了 Eigen 几何模块的使用方法

int main(int argc, char **argv) {

  // Eigen/Geometry 模块提供了各种旋转和平移的表示
  // 3D 旋转矩阵直接使用 Matrix3d 或 Matrix3f
  Matrix3d rotation_matrix = Matrix3d::Identity();
  // 旋转向量使用 AngleAxis, 它底层不直接是Matrix,但运算可以当作矩阵(因为重载了运算符)
  AngleAxisd rotation_vector(M_PI / 4, Vector3d(0, 0, 1));     //沿 Z 轴旋转 45 度
  cout.precision(3);
  cout << "rotation matrix =\n" << rotation_vector.matrix() << endl;   //用matrix()转换成矩阵
  // 也可以直接赋值
  rotation_matrix = rotation_vector.toRotationMatrix();
  // 用 AngleAxis 可以进行坐标变换
  Vector3d v(1, 0, 0);
  Vector3d v_rotated = rotation_vector * v;
  cout << "(1,0,0) after rotation (by angle axis) = " << v_rotated.transpose() << endl;
  // 或者用旋转矩阵
  v_rotated = rotation_matrix * v;
  cout << "(1,0,0) after rotation (by matrix) = " << v_rotated.transpose() << endl;

  // 欧拉角: 可以将旋转矩阵直接转换成欧拉角
  Vector3d euler_angles = rotation_matrix.eulerAngles(2, 1, 0); // ZYX顺序,即yaw-pitch-roll顺序
  cout << "yaw pitch roll = " << euler_angles.transpose() << endl;

  // 欧氏变换矩阵使用 Eigen::Isometry
  Isometry3d T = Isometry3d::Identity();                // 虽然称为3d,实质上是4*4的矩阵
  T.rotate(rotation_vector);                                     // 按照rotation_vector进行旋转
  T.pretranslate(Vector3d(1, 3, 4));                     // 把平移向量设成(1,3,4)
  cout << "Transform matrix = \n" << T.matrix() << endl;

  // 用变换矩阵进行坐标变换
  Vector3d v_transformed = T * v;                              // 相当于R*v+t
  cout << "v tranformed = " << v_transformed.transpose() << endl;

  // 对于仿射和射影变换,使用 Eigen::Affine3d 和 Eigen::Projective3d 即可,略

  // 四元数
  // 可以直接把AngleAxis赋值给四元数,反之亦然
  Quaterniond q = Quaterniond(rotation_vector);
  cout << "quaternion from rotation vector = " << q.coeffs().transpose()
       << endl;   // 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
  // 也可以把旋转矩阵赋给它
  q = Quaterniond(rotation_matrix);
  cout << "quaternion from rotation matrix = " << q.coeffs().transpose() << endl;
  // 使用四元数旋转一个向量,使用重载的乘法即可
  v_rotated = q * v; // 注意数学上是qvq^{-1}
  cout << "(1,0,0) after rotation = " << v_rotated.transpose() << endl;
  // 用常规向量乘法表示,则应该如下计算
  cout << "should be equal to " << (q * Quaterniond(0, 1, 0, 0) * q.inverse()).coeffs().transpose() << endl;

  return 0;
}

6、参考文章

(1)高翔《视觉SLAM十四讲》

(2)官方文档:Eigen库官方文档

(3)博客:「 C++ 库 」“Eigen库”基础使用手册

(4)文章:Eigen —— 参考指南 | 码农家园

你可能感兴趣的:(C++,SLAM,线性代数,linux,c++)