Eigen线性代数库学习大全

14 篇文章 5 订阅

        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库结构导图

1、Eigen库安装

        参考文章1.4节(链接

2、Eigen库矩阵基础(参考

2.1 矩阵模板函数       

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

template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>

         参数说明:

  • typename _Scalar:矩阵元素的类型;
  • int _Rows:矩阵的行数;
  • int _Cols:矩阵的列数;
  •  int _Options:矩阵是行主序(RowMajor)或列主序(ColMajor),默认是列主序;
  • int _MaxRows:矩阵的最大行数;
  • int _MaxCols:矩阵的最大列数;
Eigen::Matrix<int, 3, 4> mat1;              //  3x4 的 int 类型的矩阵 mat1
Eigen::Matrix<double, 3, Dynamic> mat2;     //  3x? 的 double 类型的矩阵 mat2
Eigen::Matrix<float, Dynamic, 4> mat3;      //  ?x4 的 float 类型的矩阵 mat3
Eigen::Matrix<long, Dynamic, Dynamic> 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<std::complex<double>, 2, 2> Eigen::Matrix2cd;            //  2x2 的 cd 类型的矩阵
typedef Matrix<double, 2, 2> Eigen::Matrix2d;                           //  2x2 的 d 类型的矩阵
typedef Matrix<std::complex<double>, 2, Dynamic> Eigen::Matrix2Xcd;     //  2x? 的 cd 类型的矩阵
typedef Matrix<std::complex<float>, Dynamic, 2> Eigen::MatrixX2cf;      //  ?x2 的 cf 类型的矩阵
typedef Matrix<std::complex<double>, Dynamic, Dynamic> Eigen::MatrixXcd;//  ?x? 的 cd 类型的矩阵
typedef Matrix<int, Dynamic, Dynamic> Eigen::MatrixXi;                  //  ?x? 的 i 类型的矩阵

        同时,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(); //初始化为零

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

// 但是在Eigen里你不能混合两种不同类型的矩阵,像这样是错的
// Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d;
// 应该显式转换,matrix_23.cast<double>(),然后进行计算
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;

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<p, q>(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<p,q>()
左下角 pxq  mat.bottomLeftCorner(p,q)  mat.bottomLeftCorner<p,q>()
右上角 pxq mat.topRightCorner(p,q) mat.topRightCorner<p,q>()
右下角 pxq mat.bottomRightCorner(p,q) mat.bottomRightCorner<p,q>()
前 p 行      mat.topRows(p) mat.topRows<p>()
后 p 行      mat.bottomRows(p) mat.bottomRows<p>()
前 q 列       mat.leftCols(q) mat.leftCols<q>()
后 q 列      mat.rightCols(q)  mat.rightCols<q>()

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<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN= MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
// 保证半正定,矩阵和矩阵的转置相乘可以保证半正定
matrix_NN = matrix_NN * matrix_NN.transpose();  
// 定义v_Nd
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;

3、Eigen库的向量基础

3.1 类型与存储

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

typedef Matrix<std::complex<float>, 2, 1> Eigen::Vector2cf; //  cf 类型的 2 向量
typedef Matrix<int, 2, 1> Eigen::Vector2i;                  //  i 类型的 2 向量
typedef Matrix<double, 4, 1> Eigen::Vector4d;               //  d 类型的 4 向量
typedef Matrix<float, Dynamic, 1> Eigen::VectorXf;          //  f 类型的 ? 向量

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

typedef Matrix<float, 1, 2> Eigen::RowVector2f;                 //  f 类型的 2 向量(行向量)
typedef Matrix<std::complex<double>, 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>()
后 n 个元素vec.tail(n)vec.tail<n>()
从 i 开始取 n 个元素vec.segment(i,n)vec.segment<n>(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(参考文章

        <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 <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;
}

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 <iostream>
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
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 —— 参考指南 | 码农家园

  • 5
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
### 回答1: Eigen是一个C++模板,用于线性代数计算。通过学习Eigen的源码,可以深入了解其内部实现原理,从而更好地利用该进行编程。 首先,学习Eigen的源码可以帮助我们理解它是如何实现矩阵和向量的表示和运算的。Eigen采用模板编程的方式,可以适应不同的矩阵和向量类型,包括动态大小和静态大小,以及不同的数据类型,如浮点数、整数等。通过查看源码,我们可以了解Eigen是如何使用模板来实现通用的矩阵和向量类型,并提供相应的运算功能。 其次,学习Eigen的源码还可以帮助我们了解其高效的实现方式。Eigen在设计和实现时,考虑了性能和内存使用效率。它采用矩阵和向量的表达形式,可以有效地进行运算,并尽量减少内存的分配和拷贝。通过分析源码,我们可以学习Eigen是如何通过优化算法和数据结构来提高计算性能的。 此外,学习Eigen的源码还有助于我们了解其丰富的功能和用法。Eigen提供了大量的线性代数计算功能,包括矩阵乘法、矩阵分解、特征值分解、线性方程求解等。通过深入学习Eigen的源码,我们可以理解每个功能的实现细节,进而更好地应用于实际问题中。 综上所述,通过学习Eigen的源码,我们可以深入了解其内部实现原理,掌握其高效的计算方式,同时了解其丰富的功能和用法。这将有助于我们更好地利用Eigen进行线性代数计算。 ### 回答2: Eigen是一个用于线性代数C++模板,包含了各种矩阵运算、向量运算和线性方程求解等功能。通过学习Eigen的源码,可以深入理解矩阵运算的原理和实现细节,提高代码效率和准确性。 首先,Eigen源码使用模板实现了矩阵和向量的各种运算操作,这意味着可以使用不同的数据类型和维度来进行运算,使得代码更加灵活和通用。学习源码可以了解到模板元编程的技巧和实现方式,提高代码设计的灵活性。 其次,Eigen的源码包含了矩阵和向量的基本运算方法,如加减乘除、转置、求逆等,可以通过源码学习到这些运算方法的具体实现原理和优化技巧。了解这些细节可以帮助我们更好地理解算法的本质和性能优化的方法,提高代码的效率。 此外,Eigen的源码还包含了线性方程求解的方法,如LU分解、QR分解和特征值分解等。学习源码可以了解到这些求解方法的具体实现过程和数学原理,加深对数学建模和求解问题的理解。 最后,学习Eigen源码还可以帮助我们理解矩阵和向量的内存布局和访问方式。Eigen使用列优先的内存布局方式,了解源码可以了解到这种布局方式的原理和优势,避免内存访问的冗余和低效。 总之,通过学习Eigen的源码,可以深入理解线性代数的各种运算方法和数学原理,提高代码效率和准确性。同时,还可以学习到模板元编程的技巧和实现方式,提高代码设计的灵活性。 ### 回答3: Eigen是一个用于线性代数运算的C++模板,由于其高性能和易用性,被广泛应用于科学计算、机器学习等领域。学习Eigen的源码可以帮助我们深入理解其内部实现原理,进而更好地使用该进行开发。 在学习Eigen源码时,首先我们可以从它的模板结构和设计思想入手。Eigen使用了大量模板元编程的技术,利用C++的模板特性实现了高度的灵活性和通用性。通过阅读Eigen的模板实现,我们可以学习到如何利用模板编程来实现复杂的数学运算和算法,以及如何提高程序性能和可维护性。 其次,我们可以关注Eigen的矩阵和向量运算部分的源码。Eigen提供了丰富的矩阵和向量运算接口,包括基本的加减乘除运算、转置、求逆、特征值分解等等。通过深入研究其源码,我们可以了解到这些运算是如何实现的,了解它们的算法和优化方法,从而更好地掌握Eigen的使用技巧。 此外,学习Eigen源码还可以让我们了解到一些底层的计算优化技术。Eigen使用了一些特殊的技巧和优化策略,如内存对齐、向量化指令、并行计算等等,以提高运算性能。通过学习这些优化技术,我们可以将其应用到自己的程序中,提高程序的执行效率。 总之,学习Eigen的源码可以帮助我们更好地理解其内部实现原理,并且提高我们对线性代数的理解和运用能力。通过深入研究Eigen的源码,我们可以获得更多关于高性能计算和模板编程的经验,为我们日后的程序开发和科学计算提供有益的借鉴。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

几度春风里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值