小言_互联网的博客

视觉SLAM笔记(9) Eigen

459人阅读  评论(0)


1. 开源线性代数库

Eigen是一个 C++ 开源线性代数库
它提供了快速的有关矩阵的线性代数运算,还包括解方程等功能
许多上层的软件库也使用 Eigen 进行矩阵运算,包括 g2o、 Sophus 等

照应之前的理论部分,来利用 Eigen 编程
如果没有安装 Eigen,请输入以下命令来安装它:

$ sudo apt-get install libeigen3-dev

相比于其他库, Eigen 特殊之处在于
它是一个纯用头文件搭建起来的库(这非常神奇!)
这意味着只能找到它的头文件,而没有.so.a 那样的二进制文件

在使用时,只需引入 Eigen 的头文件即可,不需要链接它的库文件


2. 基本操作与运算

下面写一段代码 VSLAM_note/009/eigenMatrix.cpp ,实际练习一下 Eigen 的使用:


2.1. 向量和矩阵

Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类
它的前三个参数为:数据类型,行,列
声明一个2*3的float矩阵

Eigen::Matrix<float, 2, 3> matrix_23;

2.2. 内置类型

同时,Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix
例如 Vector3d 实质上是 Eigen::Matrix<double, 3, 1>,即三维向量

Eigen::Vector3d v_3d;

这是一样的

Eigen::Matrix<float,3,1> vd_3d;

2.3. 初始化

Matrix3d 实质上是 Eigen::Matrix<double, 3, 3>
初始化为零

Eigen::Matrix3d matrix_33 = Eigen::Matrix3d::Zero(); 

2.4. 动态大小

如果不确定矩阵大小,可以使用动态大小的矩阵

Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > matrix_dynamic;

更简单的

Eigen::MatrixXd matrix_x;

2.5. 输入数据

输入数据(初始化)

matrix_23 << 1, 2, 3, 4, 5, 6;

2.6. 输出数据

cout << matrix_23 << endl;

// 1 2 3
// 4 5 6

2.7. 访问矩阵中的元素

用()访问矩阵中的元素

for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++)
        cout << matrix_23(i, j) << "\t";
    cout << endl;
}

// 1	2	3	
// 4	5	6

2.8. 矩阵和向量相乘

矩阵和向量相乘,实际上仍是矩阵和矩阵

v_3d << 3, 2, 1;
vd_3d << 4, 5, 6;

但是在Eigen里不能混合两种不同类型的矩阵,像这样是错的

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;

// 10
// 28
Eigen::Matrix<float, 2, 1> result2 = matrix_23 * vd_3d;
cout << result2 << endl;

// 32
// 77

同样不能搞错矩阵的维度
如果这样就会报错

Eigen::Matrix<double, 2, 3> result_wrong_dimension = matrix_23.cast<double>() * v_3d;

2.9. 四则运算

直接用 + - * / 即可


2.10. 随机数矩阵

通过 Random() 产生随机数矩阵

matrix_33 = Eigen::Matrix3d::Random();
cout << matrix_33 << endl << endl;

//  0.680375   0.59688 -0.329554
// -0.211234  0.823295  0.536459
//  0.566198 -0.604897 -0.444451

2.11. 转置

通过transpose() 进行转置

cout << matrix_33.transpose() << endl;

//  0.680375 -0.211234  0.566198
//   0.59688  0.823295 -0.604897
// -0.329554  0.536459 -0.444451

2.12. 求和

通过sum() 进行转置

cout << matrix_33.sum() << endl;

// 1.61307

2.13. 求迹

一个n×n矩阵A的主对角线(从左上方至右下方的对角线)上各个元素的总和被称为矩阵A的迹

通过trace() 进行求迹

cout << matrix_33.trace() << endl;

// 1.05922

2.14. 数乘

cout << 10 * matrix_33 << endl;

//  6.80375   5.9688 -3.29554
// -2.11234  8.23295  5.36459
//  5.66198 -6.04897 -4.44451

2.15. 逆矩阵

设A是数域上的一个n阶矩阵,若在相同数域上存在另一个n阶矩阵B,使得: AB=BA=E
则称B是A的逆矩阵

通过inverse() 进行求逆矩阵

cout << matrix_33.inverse() << endl; 

// -0.198521   2.22739    2.8357
//   1.00605 -0.555135  -1.41603
//  -1.62213   3.59308   3.28973

2.16. 行列式

一个n×n的方阵A的行列式记为 det(A) 或者 |A|
一个2×2矩阵的行列式可表示如下:

通过determinant() 进行行列式

cout << matrix_33.determinant() << endl;

// 0.208598

2.17. 特征值

设 A 是n阶方阵,如果存在数 λ 和非零n维列向量 x,使得 Ax=λx 成立
则称 λ 是矩阵A的一个特征值

实对称矩阵可以保证对角化成功

Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver(matrix_33.transpose()*matrix_33);
cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl;

// Eigen values = 
// 0.0242899
//  0.992154
//   1.80558

2.18. 特征向量

设 A 是n阶方阵,如果存在数 λ 和非零n维列向量 x,使得 Ax=λx 成立
则称 λ 是矩阵A 特征值 λ 的一个特征向量

cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl;

// Eigen vectors = 
// -0.549013 -0.735943  0.396198
//  0.253452 -0.598296 -0.760134
// -0.796459  0.316906 -0.514998

2.19. 解方程

求解 matrix_NN * x = v_Nd 这个方程
N的大小在前边的宏里定义,它由随机数生成

#define MATRIX_SIZE 50

直接求逆自然是最直接的,但是求逆运算量大

Eigen::Matrix< double, MATRIX_SIZE, MATRIX_SIZE > matrix_NN;
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;

// time use in normal inverse is 3.092ms

2.20. 矩阵分解

通常用矩阵分解来求
例如:QR分解,速度会快很多

time_stt = clock();
x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
cout << "time use in Qr decomposition is " << 1000 * (clock() - time_stt) / (double)CLOCKS_PER_SEC << "ms" << endl;

// time use in Qr decomposition is 0.06ms

3. 编译

要编译它,需要在 CMakeLists.txt 里指定 Eigen 的头文件目录:

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

因为 Eigen 库只有头文件,不需要再用 tartget_link_libraries 语句将程序链接到库上
不过,对于其他大部分库,多数时候需要用到链接命令
这里的做法并不见得是最好的
因为有可能把 Eigen 安装在了不同位置,就必须手动修改这里的头文件目录

在之后的工作中,会使用 find_package 命令去搜索库

使用 cmake 编译整个工程

$ mkdir build
$ cd build
$ cmake ..
$ make

4. 输出结果

编译好这个程序后,运行它,看到各矩阵的输出结果

$ ./eigenMatrix


参考:

《视觉SLAM十四讲》


相关推荐:

视觉SLAM笔记(8) 齐次坐标
视觉SLAM笔记(7) 欧氏变换
视觉SLAM笔记(6) 坐标系
视觉SLAM笔记(5) 编程基础
视觉SLAM笔记(4) SLAM的数学表述


谢谢!


转载:https://blog.csdn.net/qq_32618327/article/details/101470881
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场