Eigen(一) 安装配置 和 基础

Eigen是一个基于C++模板的线性代数库,安装后只需要包含头文件即可,它没有库文件,所以不需要链接,一般是INCLUDE_DIRECTORIES(/usr/include/eigen3)

安装:sudo apt-get install libeigen3-dev,这是最新版本

查看Eigen的版本:不能用命令查看,版本在Macros.h,在/usr/local/include/eigen3/Eigen/src/Core/util,可以执行命令: grep 'define EIGEN_....._VERSION\>' $(locate Macros.h | grep Core/util),结果是三个版本部分的宏定义。目前我所用为3.2.92

卸载Eigen:删除所有Eigen的文件即可

1
2
3
sudo rm -rf /usr/include/eigen3 /usr/lib/cmake/eigen3 /usr/share/doc/libeigen3-dev /usr/share/pkgconfig/eigen3.pc /var/lib/dpkg/info/libeigen3-dev.list /var/lib/dpkg/info/libeigen3-dev.md5sums
sudo updatedb
locate eigen

如果locate之后没有结果,那就是卸载成功了

要安装其他版本,就需要下载对应得deb安装,比如libeigen3-dev_3.2.0-8_all.deb,我使用gdebi安装时失败,报警 A later version is already installed ,最终发现还是要用dpkg安装,这一点以前没有发现,其他软件包应该也会有这样问题

矩阵Eigen::Matrix基本使用方法

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
#include <Eigen/Core>  // 核心文件
#include <Eigen/Dense> // 包含了常用函数,稠密矩阵的运算
#include <Eigen/Eigen>  // 稀疏部分

// 矩阵基本使用
Eigen::Matrix<float, 2, 3> m23;
m23.setZero(2,3); // 矩阵置为0,参数分别为行数和列数
cout << "After m23 initilized!" << endl;
cout<<m23<<endl; // 可以将矩阵放入标准输出流,输出结果就是矩阵形式,源码应当做了空格和换行处理
m23<<1,2,3,4,5,6;  // 矩阵赋值的常用方式
cout << "After m23 set value!" << endl;
m23(1,2) = 54;   // 单独给某元素赋值
// 输出元素的值
 cout<<m23(1,2)<<endl;


Eigen::Matrix<double,2,2> information;
information.fill(0.0); // 矩阵元素全赋值为 0.0

// 常用的类型Matrix3Xd和Matrix3Xf其实是动态矩阵,定义如下
// typedef Matrix< double, 3, Dynamic > Eigen::Matrix3Xd
// typedef Matrix< float, 3, Dynamic > Eigen::Matrix3Xf
// Eigen::Matrix3d 三阶方阵

Eigen::Matrix<float, 2, 3> m;
m.setRandom(2,3); // 矩阵用随机值初始化,但每次随机值似乎都是一样的
m = m23; // 将另一矩阵的值赋给m
m<<3,4,7,12,-2,10;
cout<<m23+m<<endl; // 矩阵加法
cout<<m23 * m.transpose()<<endl; // 矩阵乘法

Eigen::Matrix3d m33
m33<<1,6,3,4,12,5,7,8,9;
cout<<m33<<endl;
cout<<m33.rows()<<endl; // 行数
cout<<m33.cols()<<endl; // 列数
cout << "Column major input:" << endl << m33 << "\n";
// 获取第一列的元素
cout << "The first column is:" << endl << m33.col(0) << "\n";
cout << "The last column is: " << endl << m33.rightCols(1) << "\n";
cout << "The first row is: " << endl << m33.topRows<1>() << endl;
cout << "The last row is: " << endl << m33.bottomRows<1>() << endl;
// rowwise和colwise两个函数很奇怪,不要使用
cout<<m33.transpose()<<endl; // 转置矩阵
cout<<m33.sum()<<endl; // 所有元素的和
cout<<m33.trace()<<endl; // 矩阵的迹
cout<<10*m33<<endl; // 矩阵数乘
cout<<m33.isUnitary()<<endl; // 判断该矩阵是否是酉矩阵(矩阵各列是否为标准正交基)
cout<<m33.conjugate()<<endl; // 共轭矩阵
cout<<m33.adjoint()<<endl; // 伴随矩阵
cout<<m33.inverse()<<endl; // 逆矩阵,若不存在会报错
cout<<m33.determinant()<<endl; // 行列式结果,若不存在会报错

// 矩阵乘法, m23模板类型是float,需要转换,注意cast不能自动提示
Eigen::Matrix<double, 2, 1> m21 = m23.cast<double>() * v3d;

由于元素级运算在矩阵中很常见,所以Eigen对于Matrix其实内置了一些函数,均以cwise开头。mat.cwiseAbs()元素取绝对值、mat.cwiseSqrt()逐元素开根号、mat.cwiseMin()将两个矩阵中相应位置的最小值组成一个新矩阵

Eigen提供了一些基础的算法,如sum(),求所有元素和;prod(),求所有元素之积;mean(),求元素平均值;minCoeff(),最小元素;maxCoeff(),最大元素;trace(),求迹

Eigen::Isometry3d

Eigen::Isometry3d相当于一个四维矩阵

1
2
3
4
5
6
7
8
9
Eigen::AngleAxisd  v(M_PI / 3, Eigen::Vector3d(1,0,0).normalized() );
Eigen::Quaterniond q(1,0,0,0);
Eigen::Vector3d translate(1,2,3);
// 初始化为单位矩阵
Eigen::Isometry3d T = Eigen::Isometry3d::Identity();
T.translate(translate);
// T.rotate(q.matrix() ) 或者 T.rotate(v)
T.rotate(q);
cout << endl << T.matrix() << endl;

rotate函数比较强大,可接受的参数有四元数、旋转矩阵、旋转向量

必须要调用 Isometry3d::Identity()对它进行初始化,后面的translate()rotate()函数才有用。

稀疏矩阵

使用方式和普通矩阵不同

1
2
3
4
Eigen::SparseMatrix<double> hessian(2, 2);
// 注意稀疏矩阵的初始化方式,无法使用<<初始化
hessian.insert(0, 0) = 2.0;
hessian.insert(1, 1) = 2.0;

从子矩阵和向量拼接成新矩阵

比如现在有一个3X3的矩阵,一个行向量和一个列向量,二者的最后一个元素相同,要拼接成4X4的矩阵

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

1
2
3
4
5
6
7
8
9
Eigen::Matrix4d m;
Eigen::Matrix3d rotation;
// 给 rotation赋值
m.block(0,0,3,3) = rotation;

Eigen::Vector4d Vcol, Vrow;
// 给 Vcol和Vrow 赋值
m.col(3) = Vcol;
m.row(3) = Vrow.transpose();

向量Eigen::Vector

1
2
3
4
5
6
7
8
9
10
11
12
// 常用这种方式,Xd 代表double型的任意行数向量,但后面的参数必须指定行数
Eigen::VectorXd v(3);
v<< 1,2,5; // 必须不多不少传入所有的元素值,否则报错
v(2) = 9; // 给第三个元素赋值


Eigen::VectorXd vv(12);
vv<< 1,2,3,4,5,6,7,8,9,10,11,12;
// 前三行
cout<< vv.block(0,0, 3,1).transpose() <<endl<<endl;
cout<< vv.block(3,0, 3,1) <<endl<<endl;
cout<< vv.block(6,0, 3,1) <<endl<<endl;

指定了3行,必须传入3个值,否则运行报错。

我们可以用Vector3f::UnitX(), Vector3f::UnitYVector3f::UnitZ()这三个函数表示三个方向的列向量,也就是输出结果为:

1
2
3
4
5
X   Y   Z

1 0 0
0 1 0
0 0 1

其实向量只是一个特殊的矩阵,但是Eigen也为它单独提供了一些简化的块操作,如下三种形式:

head tail segment

  • 获取向量的前n个元素:vector.head(n);

  • 获取向量尾部的n个元素:vector.tail(n);

  • 获取从向量的第i个元素开始的n个元素:vector.segment(i,n);

1
2
3
4
5
6
Eigen::Vector4d v(4); 
v<< 1,2,3,4;

cout << "head: "<<v.head(1) <<endl; // 1
cout << v.segment(1,2) <<endl; // 2 3
cout << "tail: "<<v.tail(1) <<endl; // 4

Vector 转 Matrix

1
2
3
4
Eigen::Vector3d v;
v << 1,2,3;
// 这个转换十分罕见
Eigen::Matrix<double, 3, 1> m = v.template cast<double>();

从Eigen::Vector构建对角矩阵,其中Vector的元素占据矩阵的主对角线

1
2
Eigen::Vector3d v(1,2,3);
Eigen::Matrix3d m = v.asDiagonal();

从多个Eigen::Vector拼接成Eigen::Matrix

1
2
3
4
5
6
7
8
Eigen::Vector3d v(3);
v<< 2.1, 4.9, 0.4;
std::vector<Eigen::Vector3d> vv;
for(int i=0;i<4;i++)
vv.push_back( v );
// 拼接成3X4的矩阵
Eigen::Map<Eigen::Matrix3Xd> matrix(&vv[0].x(), 3, 4); //首元素地址,行,列
cout << matrix <<endl;

Map类用于通过C++中普通的连续指针或者数组 (指针地址连续)来构造Eigen里的Matrix类,这就好比Eigen里的Matrix类的数据和raw C++array 共享了一片地址,也就是引用。

有个庞大的Matrix类,在一个大循环中要不断读取Matrix中的一段连续数据,如果每次都用block operation 去引用数据会太繁琐。于是就事先将这些数据构造成若干Map,那么以后循环中直接操作Map就行了。

Eigen::Map

Map类用于通过C++中普通的连续指针或者数组来构造Eigen里的Matrix类,这就好比Eigen里的Matrix类的数据和raw C++array 共享了一片地址,也就是引用。 比如有个API只接受普通的C++数组,但又要对普通数组进行线性代数操作,那么用它构造为Map类。 实际上Map类并没有自己申请一片空内存,只是一个引用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using DynamicArray = Eigen::Array<int, Eigen::Dynamic, Eigen::Dynamic>;

std::vector<int> cells{1,2,3,4,5,6,7,8,9};
for(auto& v: cells)
cout << v << " ";
cout << endl << endl;
// 维度不要超过 3x3,否则出现invalid数据
Eigen::Map<const DynamicArray> full_correspondence_costs(cells.data(), 3, 3);
cout << full_correspondence_costs <<endl;


int data[] = {1,2,3,4,5,6,7,8,9};
Eigen::Map<Eigen::RowVectorXi> m(data, 4);
cout << "The mapped vector v is: " << m << "\n";

运行结果:
1
2
3
4
1 4 7
2 5 8
3 6 9
The mapped vector v is: 1 2 3 4

参考:
Eigen矩阵库使用说明