PCL点云库入门——PCL库中Eigen数学工具库的基本使用(持续更新)
0、前言
PCL点云库中的算法都基于Eigen数学工具库来实现的,因此,掌握Eigen库对于深入理解和应用PCL点云库至关重要。Eigen库不仅提供了高效的矩阵和向量运算,还支持复杂的线性代数、几何变换等操作,为PCL点云处理提供了强大的数学支持。PCL库中已经包含了Eigen库不需要单独的安装,直接包含头文件就可以使用,因此安装不在讲述。
1、Eigen库的简介
Eigen库是一个高性能的C++模板库,专注于线性代数、矩阵和向量运算,以及相关的数值分析和算法。
1.1、Eigen库的特性
Eigen库有如下一些特点:
1)、易用性
Eigen库是一个头文件库,这意味着它不需要编译成库文件,也不需要动态链接。开发者只需将Eigen的头文件包含在项目中即可使用,这极大地简化了集成过程。同时,Eigen支持多种数据类型,包括整数、浮点数和复数等,通过模板编程,Eigen能够为这些数据类型提供高效的矩阵和向量运算。
2)、高性能与优化
Eigen经过高度优化,能够充分利用现代CPU的性能。它支持SIMD(单指令多数据)指令集,如SSE和AVX,以及多线程计算,从而实现了高效的并行运算。此外,Eigen对稀疏矩阵和密集矩阵都提供了良好的支持,并且针对各种矩阵运算进行了优化,如矩阵乘法、矩阵分解(如Cholesky分解、QR分解、SVD分解)等。
3)、灵活性与可扩展性
Eigen库提供了多种矩阵大小选项,包括动态大小和固定大小,以满足不同的应用需求。用户还可以自定义扩展Eigen以适应特殊的数据结构或运算。这种灵活性和可扩展性使得Eigen能够广泛应用于各种领域。
4)、应用领域广
Eigen库被广泛应用于科学计算、机器学习、计算机图形学、计算机视觉、信号处理、金融等领域。在科学计算中,Eigen可以用于求解线性方程组、矩阵分解和特征值问题等。在机器学习和计算机视觉中,Eigen常用于数据降维、模型参数估计、图像特征提取等任务。此外,Eigen还支持常用的几何运算,如旋转矩阵、四元数、矩阵变换等,这使得它在3D计算和机器人学中也具有广泛的应用。
1.2、Eigen库中的主要模块
Eigen库中的主要模块如下表所示:
功能模块 | 简要功能描述 |
Core | 包含Matrix和Array类,支持基础的线性代数运算和数组操作。 |
Geometry | 提供旋转、平移、缩放以及2维和3维的各种变换功能。 |
LU | 用于求逆、行列式以及LU分解。 |
Cholesky | 支持LLT和LDLT Cholesky分解。 |
Householder | 提供Householder变换。 |
SVD | 实现奇异值分解(SVD),并配备最小二乘解算器。 |
QR | 实现QR分解,并提供HouseholderQR、ColPivHouseholderQR、FullPivHouseholderQR三种方法。 |
Eigenvalues | 用于特征值和特征向量的分解,提供EigenSolver、SelfAdjointEigenSolver、ComplexEigenSolver等方法 |
Sparse | 支持稀疏矩阵的存储和运算。 |
Dense | 包含Core、Geometry、LU、Cholesky、SVD、QR、Eigenvalues等模块,是Eigen库的核心部???? |
2、Eigen库中向量和矩阵的定义
在Eigen库中,所有的矩阵和向量均被视为矩阵模板类的实例。向量可以被看作是特殊形式的矩阵,具体表现为只包含1行或1列。
2.1、 Eigen库中的矩阵类
Eigen库中Matrix类包含六个模板参数,目前仅需掌握其前三个参数。余下的三个参数均设有默认值,目前我们不予以深入探讨,后续章节将对此进行详细讨论。
Matrix的三个必要模板参数为:
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
其中三个参数的含义如下:
Scalar:代表标量类型,即数据类型。换言之,若您需要一个浮点数矩阵,请在此选择float。
常用的还有double和int类型。
RowsAtCompileTime和ColsAtCompileTime:指的是在编译阶段已确定的矩阵行数和列数(若编译时无法确定,可以动态的指定后面进行讨论)。
Eigen库中提供了许多常用的矩阵类型的定义。Eigen常用的矩阵定义有如下:
//2维方矩阵Eigen::Matrix2i;Eigen::Matrix2f;Eigen::Matrix2d;Eigen::Matrix2cf;Eigen::Matrix2cd;//3维方矩阵Eigen::Matrix3i;Eigen::Matrix3f;Eigen::Matrix3d;Eigen::Matrix3cf;Eigen::Matrix3cd;//4维方矩阵Eigen::Matrix4i;Eigen::Matrix4f;Eigen::Matrix4d;Eigen::Matrix4cf;Eigen::Matrix4cd;
Eigen库定义的都是比较常用的定义,矩阵维度小于4的,但维度超过4时,我们可以根据自己的数据进行定义,方式如下:
typedef Eigen::Matrix<float, 10, 10> Matrix10f;//10维的方正,其他类型的可以参照此定义方式进行。typedef Eigen::Matrix<double, 10, 10> Matrix10d;//10维的方正,其他类型的可以参照此定义方式进typedef Eigen::Matrix<double, 10, 10> Matrix10d;typedef Eigen::Matrix<float, 10, 15> Matrix10_15f;
2.2 、Eigen库中的向量
正如先前所述,在Eigen库中,向量实际上是矩阵的一个特例,它们要么仅包含1行,要么仅包含1列。最常见的情形是仅含1列的向量,这种向量被称为列向量,通常简称为向量。相对地,仅含1行的向量则被称为行向量。
//2维向量Eigen::Vector2i;Eigen::Vector2f;Eigen::Vector2d;Eigen::Vector2cf;Eigen::Vector2cd;//3维向量Eigen::Vector3i;Eigen::Vector3f;Eigen::Vector3d;Eigen::Vector3cf;Eigen::Vector3cd;//4维向量Eigen::Vector4i;Eigen::Vector4f;Eigen::Vector4d;Eigen::Vector4cf;Eigen::Vector4cd;
注意:Eigen库中上述常用定义的向量是列向量,定义方式是:
typedef Matrix<float, 3, 1> Vector3f;
另外,行向量的定义为:
typedef Matrix<int, 1, 2> RowVector2i;
按照自己的数据大小定义为:
typedef Eigen::Matrix<float, 10, 1> Vector10f;//10维的向量,其他类型的可以参照此定义方式进行。
2.3 、Eigen库中矩阵的动态维度(行、列)
Eigen库的功能不仅限于处理编译时已知维度的矩阵。通过使用模板参数RowsAtCompileTime和ColsAtCompileTime,我们可以指定一个特殊值Dynamic,这表明矩阵的维度在编译时是未知的,因此必须在运行时作为变量处理。在Eigen库中,这样的维度被称为动态大小;相对地,编译时已知的维度则被称为固定大小。例如,定义的 typedef MatrixXd 类型,它表示一个具有动态大小的双精度矩阵,其定义如下:
typedef Matrix<double, Dynamic, Dynamic> MatrixXd;
同理,可以定义了动态的向量类型VectorXi,定义如下:
typedef Matrix<int, Dynamic, 1> VectorXi;
3、Eigen库中矩阵和向量的初始化
下面对常用矩阵和向量的初始化进行应用实例说明。
3.1、矩阵的初始化
方式1 :‘<<’法:
Eigen:: Matrix3f m;m << 1, 2, 3,4, 5, 6,7, 8, 9;
方式2:‘(i,j)’法:
Eigen::Matrix3f m2;m2(0, 0) = 1, m2(0, 1) = 2, m2(0, 2) = 3;m2(1, 0) = 3, m2(1, 1) = 5, m2(1, 2) = 6;m2(2, 0) = 7, m2(2, 1) = 8, m2(2, 2) = 8;
方式3 :常数矩阵法:
//零矩阵Eigen::Matrix4f rt1 = Eigen::Matrix4f::Zero();//单位矩阵Eigen::Matrix4f rt2 = Eigen::Matrix4f::Identity();//随机矩阵Eigen::Matrix4f rt3 = Eigen::Matrix4f::Random();
3.2、向量的初始化
方式1: ‘<<’法:
Eigen::Vector3f v1;v1 << 1, 2, 3;
方式2:‘(i)’法:
Eigen::Vector3f v1;v1(0) = 1;v1(1) = 2;v1(2) = 3;
方式3: 常数矩阵法:
//全部值是0的向量Eigen::Vector3f v2 = Eigen::Vector3f::Zero();//全部值是1的向量Eigen::Vector3f v3 = Eigen::Vector3f::Identity();//随机向量Eigen::Vector3f v4 = Eigen::Vector3f::Random();
方式4 :构造法:
Eigen::Vector3f v5(1, 2, 3);
4、Eigen库中矩阵和向量的算术操作
Eigen库重载了常见的C++算术运算符(例如 +、-、*),以支持矩阵和向量的算术运算,如 dot() 和 cross() 等函数。对于Matrix类(包括矩阵和向量),运算符重载仅限于支持线性代数操作。例如,Matrix1 * Matrix2 表示两个矩阵的乘积,而向量与标量的加法是不被允许的。若需执行非线性代数的各种数组运算,则必须依赖于数组运算,这部分内容将在后续讨论。
4.1 、矩阵/向量加减
void MatrixVector_AddAndSubtraction()
{Eigen::Matrix2d a;a << 1, 2,3, 4;Eigen::MatrixXd b(2, 2);b << 2, 3,1, 4;std::cout << "a + b =\n" << a + b << std::endl;std::cout << "a - b =\n" << a - b << std::endl;a += b;std::cout << "Now a =\n" << a << std::endl;Eigen::RowVector3d v(1, 2, 3);Eigen::RowVector3d w(1, 0, 0);std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
}
结果:
4.2、标量与矩阵/向量的乘除
示例代码:
void MatrixVectore_ScalarMD()
{Eigen::Matrix2d a;a << 1, 1,1, 1;Eigen::Vector3d v(1, 2, 3);std::cout << "a * 3 =\n" << a * 3 << std::endl;std::cout << "3 * v =\n" << 3 * v << std::endl;v *= 2;std::cout << "Now v =\n" << v << std::endl;Eigen::Matrix2d b;b << 9, 9,9, 9;Eigen::Vector3d v2(2, 4, 6);std::cout << "a / 3 =\n" << (b / 3 )<< std::endl;std::cout << " v/2 =\n" << (v2/2) << std::endl;
}
结果:
4.3、矩阵与矩阵、矩阵与向量的相乘
代码示例:
void MatrixVectoreMulti()
{Eigen::Matrix2d a;a << 1, 1,1, 1;Eigen::Matrix2d b;b << 9, 9,9, 9;std::cout << "a *b =\n" << a*b << std::endl;Eigen::Vector3d v(2, 4, 6);Eigen::Matrix3d m = Eigen::Matrix3d::Random();std::cout << "m =\n" <<m << std::endl;std::cout << "v*m=\n" <<m*v << std::endl;}
结果:
4.4、向量的点乘和叉乘
代码示例:
void VectorDotAndCross()
{Eigen::Vector3d v(1, 2, 3);Eigen::Vector3d w(1, 1, 1);cout << "Dot product: " << v.dot(w) << endl;double dp = v.adjoint() * w; cout << "Dot product via a matrix product: " << dp << endl;cout << "Cross product:\n" << v.cross(w) << endl;
}
结果:
4.5、Eigen库中归约操作
Eigen还提供了一系列归约操作,用于将给定的矩阵或向量简化为单一数值。这些操作包括计算总和(通过sum()函数)、乘积(通过prod()函数)、以及所有系数的最大值(通过maxCoeff()函数)和最小值(通过minCoeff()函数)。
代码示例:
void ReductionOperations()
{Eigen::Matrix2d mat;mat << 4, 5,6, 7;cout << "Matrix is mat.sum(): " << mat.sum() << endl; cout << "Matrix is mat.prod(): " << mat.prod() << endl; cout << "Matrix is mat.mean(): " << mat.mean() << endl; cout << "Matrix is mat.minCoeff(): " << mat.minCoeff() << endl; cout << "Matrix is mat.maxCoeff(): " << mat.maxCoeff() << endl; cout << "Matrix is mat.trace(): " << mat.trace() << endl;
}
结果:
5、矩阵的块操作
本小节主要进行矩阵中块操作的要点说明。块是矩阵或数组的矩形部分。块表达式既可以用作右值,也可以用作左值。与通常的Eigen表达式一样,只要让编译器进行优化,块操作的运行时成本为零。Eigen中有两个版本的块操作,语法如下:
块操作 | 动态尺寸块 | 固定尺寸块 |
Block of size (p,q), starting at (i,j) | matrix.block(i,j,p,q); | matrix.block<p,q>(i,j); |
在Eigen库中,索引从0开始的。这两个版本均适用于处理固定尺寸和动态尺寸的矩阵与数组。从语义上讲,这两种表达方式是等效的。唯一的差异在于,当处理的块维度较小时,固定稳定版本往往能够生成更快速的代码,前提是编译时已知该尺寸。
5.1、块的基本操作
代码示例:
void BasicOperationBlock()
{Eigen::MatrixXf m(4, 4);m << 1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12,13, 14, 15, 16;cout << m.block<3, 3>(1, 1) << endl << endl;for (int i = 1; i <= 3; ++i){cout << "Block of size " << i << "x" << i << endl;cout << m.block(0, 0, i, i) << endl << endl;}
}
结果:
5.2、块左值操作
代码示例:
void OperationBlockLetfValue()
{Eigen::Array22f m;m << 1, 2,3, 4;Eigen::Array44f a = Eigen::Array44f::Constant(0.6);cout << "Array a:" << endl << a << endl << endl;a.block<2, 2>(1, 1) = m;cout << "Here is now a with m copied into its central 2x2 block:" << endl << a << endl << endl;a.block(0, 0, 2, 3) = a.block(2, 1, 2, 3);cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x3 block:" << endl << a << endl << endl;
}
结果:
5.3、块的行和列操作
代码示例:
void OperationBlockRowAndCol()
{Eigen::MatrixXf m(3, 3);m << 1, 2, 3,4, 5, 6,7, 8, 9;cout << "M:" << endl << m << endl;cout << "2nd Row: " << m.row(1) << endl;m.col(2) += 3 * m.col(0);cout << "After adding 3 times the first column into the third column, the matrix m is:\n";cout << m << endl;
}
结果:
5.4、块的角点操作
Eigen库提供了专门的方法来处理矩阵或数组角部或边缘的块操作。例如,.topleftcorner()方法可用于引用矩阵左上角的特定块。下表总结了不同的角点操作:
块操作 | 动态尺寸块 | 固定尺寸块 |
Top-left p by q block * | matrix.topLeftCorner(p,q); | matrix.topLeftCorner<p,q>(); |
Bottom-left p by q block * | matrix.bottomLeftCorner(p,q); | matrix.bottomLeftCorner<p,q>(); |
Top-right p by q block * | matrix.topRightCorner(p,q); | matrix.topRightCorner<p,q>(); |
Bottom-right p by q block * | matrix.bottomRightCorner(p,q); | matrix.bottomRightCorner<p,q>(); |
Block containing the first q rows * | matrix.topRows(q); | matrix.topRows<q>(); |
Block containing the last q rows * | matrix.bottomRows(q); | matrix.bottomRows<q>(); |
Block containing the first p columns * | matrix.leftCols(p); | matrix.leftCols<p>(); |
Block containing the last q columns * | matrix.rightCols(q); | matrix.rightCols<q>(); |
Block containing the q columns starting from i * | matrix.middleCols(i,q); | matrix.middleCols<q>(i); |
Block containing the q rows starting from i * | matrix.middleRows(i,q); | matrix.middleRows<q>(i); |
示例代码:
void CornerOperationBlock()
{Eigen::Matrix4f m=Eigen::Matrix4f::Random();cout << "m = " << endl << m << endl;cout << "m.leftCols(2) =" << endl << m.leftCols(2) << endl << endl;cout << "m.bottomRows<2>() =" << endl << m.bottomRows<2>() << endl << endl;m.topLeftCorner(1, 3) = m.bottomRightCorner(3, 1).transpose();cout << "After assignment, m = " << endl << m << endl;
}
结果:
6、向量的块操作
同样Eigen库也为向量和数组提供了块操作,格式如下表。
块操作 | 动态尺寸块 | 固定尺寸块 |
Block containing the first n elements * | vector.head(n); | vector.head<n>(); |
Block containing the last n elements * | vector.tail(n); | vector.tail<n>(); |
Block containing n elements, starting at position i * | vector.segment(i,n); | vector.segment<n>(i); |
代码示例:
void VectroeOperationBlock()
{Eigen::ArrayXf a(6);a << 1, 2, 3, 4, 5, 6;cout << "a.head(3) =" << endl << a.head(3).transpose() << endl << endl;cout << "a.tail<3>() = " << endl << a.tail<3>().transpose() << endl << endl;a.segment(1, 4) *= 2;cout << "after 'a.segment(1,4) *= 2', a =" << endl << a.transpose() << endl;Eigen::VectorXf v(6);v << 11, 22, 33, 44, 55, 66;cout << "v.head(2) =" << endl << v.head(2).transpose() << endl << endl;cout << "v.tail<2>() = " << endl << v.tail<3>().transpose() << endl << endl;v.segment(1, 4) *= 2;cout << "after 'v.segment(1,4) *= 2', v =" << endl << v.transpose() << endl;}
结果:
7、所有示例代码
新建文件名PCLEigenmain.cpp,内容如下:
/*****************************************************************//**
* \file PCLEigenmain.cpp
* \brief
*
* \author YZS
* \date December 2024
*********************************************************************/
#include <iostream>
#include <string>
#include <Eigen/Dense> //Eigen 头文件
using namespace std;void MatrixVector_AddAndSubtraction()
{Eigen::Matrix2d a;a << 1, 2,3, 4;Eigen::MatrixXd b(2, 2);b << 2, 3,1, 4;std::cout << "a + b =\n" << a + b << std::endl;std::cout << "a - b =\n" << a - b << std::endl;a += b;std::cout << "Now a =\n" << a << std::endl;Eigen::RowVector3d v(1, 2, 3);Eigen::RowVector3d w(1, 0, 0);std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
}void MatrixVectore_ScalarMD()
{Eigen::Matrix2d a;a << 1, 1,1, 1;Eigen::Vector3d v(1, 2, 3);std::cout << "a * 3 =\n" << a * 3 << std::endl;std::cout << "3 * v =\n" << 3 * v << std::endl;v *= 2;std::cout << "Now v =\n" << v << std::endl;Eigen::Matrix2d b;b << 9, 9,9, 9;Eigen::Vector3d v2(2, 4, 6);std::cout << "a / 3 =\n" << (b / 3 )<< std::endl;std::cout << " v/2 =\n" << (v2/2) << std::endl;
}void MatrixVectoreMulti()
{Eigen::Matrix2d a;a << 1, 1,1, 1;Eigen::Matrix2d b;b << 9, 9,9, 9;std::cout << "a *b =\n" << a*b << std::endl;Eigen::Vector3d v(2, 4, 6);Eigen::Matrix3d m = Eigen::Matrix3d::Random();std::cout << "m =\n" <<m << std::endl;std::cout << "v*m=\n" <<m*v << std::endl;}void VectorDotAndCross()
{Eigen::Vector3d v(1, 2, 3);Eigen::Vector3d w(1, 1, 1);cout << "Dot product: " << v.dot(w) << endl;double dp = v.adjoint() * w; cout << "Dot product via a matrix product: " << dp << endl;cout << "Cross product:\n" << v.cross(w) << endl;
}void ReductionOperations()
{Eigen::Matrix2d mat;mat << 4, 5,6, 7;cout << "Matrix is mat.sum(): " << mat.sum() << endl; cout << "Matrix is mat.prod(): " << mat.prod() << endl; cout << "Matrix is mat.mean(): " << mat.mean() << endl; cout << "Matrix is mat.minCoeff(): " << mat.minCoeff() << endl; cout << "Matrix is mat.maxCoeff(): " << mat.maxCoeff() << endl; cout << "Matrix is mat.trace(): " << mat.trace() << endl;
}void BasicOperationBlock()
{Eigen::MatrixXf m(4, 4);m << 1, 2, 3, 4,5, 6, 7, 8,9, 10, 11, 12,13, 14, 15, 16;cout << m.block<3, 3>(1, 1) << endl << endl;for (int i = 1; i <= 3; ++i){cout << "Block of size " << i << "x" << i << endl;cout << m.block(0, 0, i, i) << endl << endl;}
}void OperationBlockLetfValue()
{Eigen::Array22f m;m << 1, 2,3, 4;Eigen::Array44f a = Eigen::Array44f::Constant(0.6);cout << "Array a:" << endl << a << endl << endl;a.block<2, 2>(1, 1) = m;cout << "Here is now a with m copied into its central 2x2 block:" << endl << a << endl << endl;a.block(0, 0, 2, 3) = a.block(2, 1, 2, 3);cout << "Here is now a with bottom-right 2x3 block copied into top-left 2x3 block:" << endl << a << endl << endl;
}void OperationBlockRowAndCol()
{Eigen::MatrixXf m(3, 3);m << 1, 2, 3,4, 5, 6,7, 8, 9;cout << "M:" << endl << m << endl;cout << "2nd Row: " << m.row(1) << endl;m.col(2) += 3 * m.col(0);cout << "After adding 3 times the first column into the third column, the matrix m is:\n";cout << m << endl;
}void CornerOperationBlock()
{Eigen::Matrix4f m=Eigen::Matrix4f::Random();cout << "m = " << endl << m << endl;cout << "m.leftCols(2) =" << endl << m.leftCols(2) << endl << endl;cout << "m.bottomRows<2>() =" << endl << m.bottomRows<2>() << endl << endl;m.topLeftCorner(1, 3) = m.bottomRightCorner(3, 1).transpose();cout << "After assignment, m = " << endl << m << endl;
}void VectroeOperationBlock()
{Eigen::ArrayXf a(6);a << 1, 2, 3, 4, 5, 6;cout << "a.head(3) =" << endl << a.head(3).transpose() << endl << endl;cout << "a.tail<3>() = " << endl << a.tail<3>().transpose() << endl << endl;a.segment(1, 4) *= 2;cout << "after 'a.segment(1,4) *= 2', a =" << endl << a.transpose() << endl;Eigen::VectorXf v(6);v << 11, 22, 33, 44, 55, 66;cout << "v.head(2) =" << endl << v.head(2).transpose() << endl << endl;cout << "v.tail<2>() = " << endl << v.tail<3>().transpose() << endl << endl;v.segment(1, 4) *= 2;cout << "after 'v.segment(1,4) *= 2', v =" << endl << v.transpose() << endl;}int main(int argc, char* argv[])
{//MatrixVector_AddAndSubtraction();//MatrixVectore_ScalarMD();//MatrixVectoreMulti();//VectorDotAndCross();//ReductionOperations();//BasicOperationBlock();//OperationBlockLetfValue();//OperationBlockRowAndCol();//CornerOperationBlock();VectroeOperationBlock();std::cout << "Hello PCL!" << std::endl;std::system("pause");return 0;
}
至此完成第六节PCL库中Eigen数学工具库简单学习,由于Eigen库涉及的应用较多,后序还会持续更新该库的更多应用。下一节我们将进入《PCL库中点云数据的拓扑关系构建》的学习。