博主最近在学习高博的《视觉SLAM十四讲》,将对其中一些常用的库文件的使用方式进行记录。本篇博文将简单记录如何使用Eigen库进行基础的矩阵运算。
博主的开发环境为Ubuntu20.04+Clion。
一般而言,在Ubuntu安装完成时,将自动安装Eigen3库文件,但是为了防止存在误删或其他原因导致不存在库文件,此处建议大家运行如下指令进行安装库文件
sudo apt-get install -y libeigen3-dev
安装的默认位置一般为:/usr/include/eigen3,可以使用如下指令查看库文件的具体安装位置。
sudo update
locate eigen3
打开Clion,新建工程study_eigen。在该工程下编辑新建的CMakeLists.txt文件,添加如下语句:
include_directories("/usr/include/eigen3")
打开main.cpp文件,输入如下内容:
#include
using namespace std;
// 导入Eigen库的核心和用于稠密矩阵运算的Dense
#include
#inclued
using namespace Eigen;
int main(int argc, chr **argv){
return 0;
}
此部分内容引用了Eigen库的核心Core和用于稠密矩阵线性计算的Dense。
在Eigen库中定义矩阵主要采用如下语句:
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
其中三个参数分别为数据类型、行数、列数。例如定义一个三行两列的double类型矩阵的语句如下:
Matrix<double, 3, 1> matrix_32d
其中matrix_32d为定义的矩阵的名称,博主习惯用matrix表示该变量为一个矩阵,而32d代表该矩阵为三行两列的double类型矩阵。
列向量即为N行1列的矩阵,同样行向量为1行N列的矩阵,在Eigen库中的定义方式如下:
// 行向量
Matrix<typename Scalar, int RowsAtCompileTime, 1>
Matrix<typename Scalar, 1, int ColsAtCompileTime>
为了使得向量的定义更加直观,Eigen库使用typedef
关键字设置了Vector和 RowVector用于定义行向量和列向量。
// 定义一个3维float类型列向量
Vector3f vector_31f
// 定义一个2维int类型行向量
RowVector2i vector_12i
同定义矩阵一样,博主习惯将向量定义为vector,其后加上向量的维度和数据类型。
除了上述使用的定义方式外,Eigen库自定义了大量typedef
用于快捷定义矩阵。
// 定义一个3X3的double类型矩阵
Matrix3d matrix_33d
// 定义一个3X3的float类型矩阵
Matrix3f matrix_33f
如果在开发过程中需要定义一个动态的矩阵或则向量可以使用如下方式进行定义
// 定义一个动态大小的矩阵
Matrix<double, Dynamic, Dynamic> matrix_Nd
// 等同于如下定义
MatrixXd matrix_NNd
// 定义一个动态大小的列向量
VectorXi vector_X1i
其中常量Dynamic为Eigen内置的值,为 -1。
一个非动态大小的矩阵有自己固有的大小和行列,可以通过如下语句进行调用:
// 矩阵的行数
cout << matrix_33d.rows() << endl;
// 矩阵的列数
cout << matrix_33d.cols() << endl;
// 矩阵的元素个数
cout << matrix_33d.size() << endl;
对于动态大小的矩阵,可以使用resize()
对其进行修改个数
// 定义一个初始大小为2*2的动态矩阵
MatrixXd matrix(2, 2);
// 修改其大小为3*3
matrix.resize(3, 3);
矩阵的输入格式如下:
matrix_33d << 1,2,3,
4,5,6,
7,8,9;
也可以通过索引的方式进行逐个赋值:
// 给第一行第一列的元素赋值
matrix_33d(0,0) = 1
输出矩阵的元素可以使用迭代的形式进行:
for(int i=0, i<matrix_33d.rows(), i++){
for(int j=0, j<matrix_33d.cols(), j++){
cout << matrix_33d(i, j)<< "\n";
}
cout << endl;
}
此外也可以直接输出整个举矩阵:
cout << matrix_33d <<endl;
在Eigen中声明矩阵后,可以将其初始化为零一矩阵、单位矩阵等。
# include
#include
#include
using namespace std;
using namespace Eigen;
int main(int argc, char **argv)
{
// 初始化零矩阵
Matrix3d matrix_33d_0 = Matrix3d::Zero();
// 初始化一矩阵
Matrix3d matrix_33d_1 = Matrix3d::Ones();
// 单位矩阵
Matrix3d matrix_33d_I = Matrix3d::Identity();
// 随机矩阵,可填入参数:矩阵行数,矩阵列数
// 生成-1~1的矩阵
Matrix3d matrix_33d_random = Matrix3d::Random();
return 0;
}
在Eigen库中,通过对四则算术运算符(+、-、*、/)重载实现了矩阵间的四则运算。由于向量即在某一维度上为1的矩阵,所以除了特殊的运算外不展开说明其他与矩阵相同的运算。
#include
using namespace std;
#include
#include
using namespace Eigen;
int main(int argc, char **argv){
// 定义矩阵
Matrix3d matrix_33d = Matrix3d:Zero();
a << 1, 2,
3, 4;
MatrixXd b(2,2);
b << 2, 3,
1, 4;
// 矩阵间加减
cout << "a + b =\n" << a + b << endl;
cout << "a - b =\n" << a - b << endl;
// 加减复合运算符
cout << "Doing a += b;" << endl;
a += b;
cout << "Now a =\n" << a << endl;
// 取反运算符
cout << -a << endl;
// 矩阵数乘\数除运算
cout << "0.25 * a =\n" << 0.25 * a << endl;
cout << "a / 4 =\n" << a / 4 << endl;
// 矩阵间相乘
cout << "a * a =\n" << a * a << endl;
return 0;
}
需要注意的是Eigen不支持自动进行类型升级,若需要在不同数据类型的矩阵间进行运算,需要进行显式的数据转换。
// float和double类型间的运算需要显式的数据转换
Matrix<double, 2 ,3> result = matrix_23f. cast<double>() * matrix_33d
对于矩阵具备的转置、逆矩阵、共轭矩阵、迹等通过调用对应API即可。
cout << matrix_33d.transpose() << endl; // 矩阵求转置
cout << matrix_33d.inverse() << endl; // 矩阵求逆
cout << matrix_33d.conjugate() << endl; // 矩阵求共轭
cout << mtrix_33d.adjoint() << endl; // 矩阵求伴随矩阵
cout << matrix_33d.trace() << endl; // 矩阵求迹
cout << matrix_33d.determinant() << endl; // 矩阵行列式
在Eigen库中使用dot() 和 cross() 方法来实现向量的叉乘 和点乘。
#include
#include
using namespace Eigen;
using namespace std;
int main(){
Vector3d v(1,2,3);
Vector3d w(0,1,2);
cout << "向量点乘 " << v.dot(w) << endl;
// 等同于将其中一个向量转换为其伴随矩阵,然后两个矩阵相乘
double dp = v.adjoint()*w;
cout << dp << endl;
cout << "向量叉乘\n" << v.cross(w) << endl;
Eigen库自带了用于统计运算的函数,如求和、求最大值、最小值等。
#include
#include
using namespace std;
using namespace Eigen;
int main(){
Matrix2d mat;
mat << 1, 2,
3, 4;
cout << "矩阵元素求和:" << mat.sum() << endl;
cout << "矩阵元素求积:" << mat.prod() << endl;
cout << "矩阵元素取中位数:" << mat.mean() << endl;
cout << "矩阵元素取最小值:" << mat.minCoeff() << endl;
cout << "矩阵元素取最大值:" << mat.maxCoeff() << endl;
}
在矩阵运算中,计算矩阵的特征值和特征向量十分重要,Eigen库中提供了如下方法,计算矩阵的特征值。
SelfAdjointEigenSolver<Matrix3d> eigensolver(matrix_33d.transpose() * matrix_33d)
cout << eigensolver.eigenvalues() << endl; // 求矩阵特征值
cout << eigensolver.eigenvectors() << endl; // 求矩阵特征向量
广播构造了一个表达式,其中向量(列或行)通过在一个方向上复制从而形成矩阵。实现广播需要使用方法**.colwise()** 和 .rowwise()。
#include
#include
using namespace std;
using namespace Eigen;
int main()
{
MatrixXf mat(2,4);
VectorXf v(2);
mat << 1, 2, 6, 9,
3, 1, 7, 2;
v_1 << 0,
1;
v_2 << 0,1,2,3;
// 列向量广播
mat.colwise() += v_1;
cout << "Broadcasting result: " << endl;
cout << mat << endl;
/*
* 输出结果如下:
* Broadcasting result:
* 1 2 6 9
* 4 2 8 3
*/
// 行向量广播
mat.rowwise() += v_2
cout << "Broadcasting result: " << endl;
cout << mat << endl;
/*
* 输出结果如下:
* Broadcasting result:
* 1 3 8 12
* 4 3 10 6
*/
}