[TOC]

概述

文章参考:https://zhuanlan.zhihu.com/p/462494086

Eigen是一个C++语言中的开源的模板库,支持线性代数的运算,包括向量运算,矩阵运算,数值分析等相关算法。因为eigen只包含头文件,所以使用的话不需要进行编译,只需要在cpp文件开头写#include <Eigen>就好。

Eigen是C++中可以用来调用并进行矩阵计算的一个库,简单了说它就是一个c++版本的matlab包。

开源库安装

直接安装

apt-get方式(假设默认安装到/usr/local/include里(可在终端中输入locate eigen3查看位置),若实际中默认安装到了/usr/include的话,可以对应替换下面命令的相应部分)

1
2
// Ubuntu系统
sudo apt install libeigen3-dev

这种安装方式有一个缺点,因为apt包更新比较慢,安装的版本可能不是最新版,那么之后的一些依赖于eigen的库可能没有办法使用(如Sophus库要求必须选用3.3以上版本的eigen包),可以使用指令来查看apt包中eigen的版本

1
apt show libeigen3-dev

上述安装的问题,会将上面的依赖库安装到/usr/local/include。我们可以执行复制命令。将Eigen文件夹及其内容放在/usr/include

1
sudo cp -r /usr/local/include/eigen3 /usr/include 

源码编译安装

下载地址:http://eigen.tuxfamily.org/index.php?title=Main_Page#Download

克隆下载:

1
git clone https://gitlab.com/libeigen/eigen.git

编译步骤:

1
2
3
4
5
6
7
8
#编译
cd eigen-3.3.9
mkdir build
cd build
cmake ..

#安装
make install

安装完成之后,安装的头文件和静态库

1
2
头文件在:/usr/local/include/eigen3
静态库:/usr/local/lib/engines-3

上述安装的问题,会将上面的依赖库安装到/usr/local/include。我们可以执行复制命令。将Eigen文件夹及其内容放在/usr/include

1
sudo  cp  -r  /usr/local/include/eigen3  /usr/include 

框架集成

eigen库采用模板编程技术,仅由一些头文件组成,运行速度快。用cmake管理项目的时候,只需要在CMakeLists.txt里面头文件的路径即可:

CMakeList里面添加:

1
2
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})

模块与头文件

模块 头文件 内容
Core #include <Eigen/Core> 矩阵和数组 (向量) 类 (Matrix, Array),基于线性代数还有数组操作
Geometry #include <Eigen/Geometry> 变换,平移,缩放,2D 旋转和 3D 旋转 (包括四元数和角轴)
LU #include <Eigen/LU> 使用求解器进行求逆,行列式,LU 分解操作
Cholesky #include <Eigen/Cholesky> 使用求解器进行 LLT, LT, Cholesky 分解
Householder #include <Eigen/Householder> Householder 变换;被用作几个线性代数模块
SVD #include <Eigen/SVD> SVD 分解与最小二乘求解器
QR #include <Eigen/QR> QR 分解
Eigenvalues #include <EIgen/Eigenvalues> 特征值,特征向量分解
Sparse #include <Eigen/Sparse> 稀疏矩阵存储以及相关的基本线性代数
Dense #include <Eigen/Dense> 包括 Core, Geometry, LU, Cholesky, SVD, QR, Eigenvalues 的头文件
Eigen #include <Eigen/Eigen> 包括 Dense 和 Sparse 的头文件

工程集成

如果依赖库是直接安装到系统里面的

1
2
3
4
5
6
7
8
set(EIGEN_LIBRARIES "/usr/local/lib/engines-3")                 # 这里写你的Eigen3的lib路径
set(EIGEN_INCLUDES "/usr/local/include/eigen3/Eigen") # 这里写你的Eigen3的include路径


# 集成Eigen3
find_package(Eigen3 REQUIRED)
include_directories("${EIGEN_INCLUDES}")
link_directories("${EIGEN_LIBRARIES}")

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <ctime>
#include <Eigen/Dense> // 稠密矩阵的代数运算
using namespace std;

#define MATRIX_SIZE 100

int main()
{
// Eigen 以矩阵为基本数据单元,它是一个模板类,它的前三个参数为: 数据类型,行,列
Eigen::Matrix<float, 2, 3> matrix_23; // 声明一个2*3的float矩阵
// 同时Eigen通过typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix
// 例如Vector3d实质上是Eigen::Matrix<double,3,1>,即三维向量
Eigen::Vector3d v_3d;
// 这是一样的
Eigen::Matrix<float, 3, 1> vd_3d;

// Matrix3d 实质上是Eigen::Matrix<double,3,3>
Eigen::Matrix3d matrix_33 = Eigen::Matrix3d::Zero(); // 初始化为零

// 如果不确定矩阵大小,可以使用动态大小的矩阵
Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> matrix_dynamic;
// 下面是一种更简单的写法
Eigen::MatrixXd matrix_x;

// 下面是对Eigen矩阵的操作
// 输入数据(初始化)
matrix_23 << 1, 2, 3, 4, 5, 6;
// 输出
cout << matrix_23 << 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里你不能混合两种不同类型的矩阵,像这样是错的,下面是double和float
// Eigen::Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d;
// 应该显式转换
Eigen::Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d;
cout << result << endl;

// 标准的矩阵乘法
Eigen::Matrix<float, 2, 1> result2 = matrix_23 * vd_3d;
cout << result2 << endl;

// 矩阵的维数不对,会报错
// Eigen::Matrix<double, 2, 3> result_wrong_dimension = matrix_23.cast<double>() * v_3d;

// 一些矩阵运算
// 四则运算就不演示了,直接用+-*/即可。
matrix_33 = Eigen::Matrix3d::Random(); // 随机数矩阵
cout << matrix_33 << endl; // 输出矩阵
cout << "-------------------------" << endl;
cout << matrix_33.transpose() << endl; // 矩阵转置
cout << "-------------------------" << endl;
cout << matrix_33.sum() << endl; // 各元素的和
cout << "-------------------------" << endl;
cout << matrix_33.trace() << endl; // 矩阵的迹
cout << "-------------------------" << endl;
cout << 10 * matrix_33 << endl; // 数乘
cout << "-------------------------" << endl;
cout << matrix_33.inverse() << endl; // 矩阵求逆
cout << "-------------------------" << endl;
cout << matrix_33.determinant() << endl; // 行列式

// 特征值
// 实对称矩阵可以保证对角化成功
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver(matrix_33.transpose()*matrix_33);
cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl;
cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl; // 特征值对应的特征向量列排列’

Eigen::Matrix< double, MATRIX_SIZE, MATRIX_SIZE > matrix_NN; // 声明一个MATRIX_SIZE*MATRIX_SIZE矩阵

matrix_NN = Eigen::MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE); // 矩阵初始化

Eigen::Matrix< double, MATRIX_SIZE, 1> v_Nd;
v_Nd = Eigen::MatrixXd::Random(MATRIX_SIZE, 1);

clock_t time_stt = clock(); // 计时
// 直接求逆
Eigen::Matrix<double, MATRIX_SIZE, 1> x = matrix_NN.inverse()*v_Nd;
cout << "time use in normal inverse is " << 1000 * (clock() - time_stt) / (double)CLOCKS_PER_SEC << "ms" << endl;

// 通常用矩阵分解来求,例如QR分解,速度会快很多
time_stt = clock();
x = matrix_NN.colPivHouseholderQr().solve(v_Nd); // QR分解
cout << "time use in Qr decomposition is " << 1000 * (clock() - time_stt) / (double)CLOCKS_PER_SEC << "ms" << endl;
return 0;
}