eigen
Eigen简介
安装
linux
sudo apt-get install libeigen3-dev
|
- 查找安装包,一般在
/usr/include/eigen3
sudo updatedb locate eigen3
|
sudo ln -sf eigen3/Eigen Eigen sudo ln -sf eigen3/unsupported unsupported
|
windows
使用
库与头文件
- Eigen库被分为了一个Core模块和几个其他额外的模块,每个模块都有对应的头文件,使用时需要进行包含。其中通过Dense和Eigen头文件可以很方便的使用其他的模块,因此一般情况下使用这俩就够了。

Matrix类
- 在Eigen中,所有的矩阵和向量都是Matrix模板类的对象,向量只是特殊的矩阵而已,无论是行向量还是列向量。
Matrix模板类
Matrix <typename Scalar,int RowsAtCompileTime,int ColsAtCompileTime,int Options=0,int MaxRowsAtCompileTime=RowsAtCompileTime,MaxColsAtCompileTime=ColsAtCompileTime>
- Matrix类共有6个模板参数,主要使用的是前三个参数,剩余的都有默认值。前三个参数分别是<变量类型 ,行 , 列>
| 参数 |
说明 |
| typename |
变量类型 |
| RowsAtCompileTime |
行 |
| ColsAtCompileTime |
列 |
| Options |
是一个标志位,表明矩阵的存储方式,RowMajor表示按行存储。默认是按列存储的。例如:Matrix<float, 3, 3, RowMajor> |
| MaxRowsAtCompileTime |
表示编译时矩阵尺寸的上限,主要是为了避免动态内存分配。 |
| MaxColsAtCompileTime |
矩阵尺寸列的上限 |
Matrix派生类对象
| 对象 |
定义 |
| Vector3f |
typedef Matrix<float,3,1> Vector3f; |
| RowVector2i |
typedef Matrix<int,1,2> RowVector2i; |
| Matrix2d |
Eigen::Matrix3d R = Eigen::AngleAxisd(M_PI/2, Eigen::Vector3d(0,0,1)).toRotationMatrix();//由轴角构造旋转向量,用于后面的由旋转向量构造李群 |
| AngleAxisd |
旋转向量:AngleAxisd ortation_vector(M_PI/4,Vector3d(0,0,1));沿z轴转45° |
| Quaterniond |
四元数:Quaterniond q=Quaterniond(rotaion_vector)、Eigen::Quaterniond q(R); |
参数Dynamic
- RowsAtCompileTIme和ColsAtCompileTIme参数可以取特殊值Dynamic,这代表编译时不能确定矩阵的尺寸,必须在运行时确定。Eigen中称为动态尺寸,编译时确定尺寸的矩阵称为固定尺寸的矩阵
| 对象 |
定义 |
| MatrixXd |
typedef Matrix <double,Dynamic,Dynamic> MatrixXd ; |
| VectorXi |
typedef Matrix <int,Dynamic,1> VectorXi ; |
构造函数
- 默认的构造函数不执行动态内存分配,也不初始化矩阵的元素。
- 对于构造函数指定尺寸也是可以的,对于矩阵首先传递的是行参数,对于向量则只传递尺寸就好,构造函数会分配相应的空间但是不进行初始化工作。
MatrixXf a(10,15); VectorXf b(30);
|
Vector2d a(5.0,6.0); Vector3d b(5.0,6.0,7.0); Vector4d c(5.0,6.0,7.0,8.0);
|
元素获取
MatrixXd m(2,2); m(0,0)=3;m(1,0)=4;m(0,1)=5;m(1,1)=-3; cout<<m<<endl; MatrixXcf a = MatrixXcf::Random(2,2); cout<<a<<endl;
|
逗号初始化
Matrix3f m; m<< 1,2,3, 4,5,6, 7,8,9; cout<<m<<endl;
|
调整尺寸
- 矩阵的大小可以通过rows(), cols(), size()方法获取。对动态尺寸的矩阵调整尺寸时使用resize()方法
Matrix3d m(2,5); m.resize(4,5); cout<<m.rows()<<m.cols()<<m.size()<<endl;
|
Assignment和resizing
- Assignment是将一个矩阵赋值给另一个,如果尺寸不同时Eigen会将左边的矩阵的尺寸自动resize成和右边的矩阵相同的尺寸
MatrixXf m(2,2); cout<<m.rows()<<m.cols()<<m.size()<<endl; MatrixXf n(3,3); m = n; cout<<n.rows()<<n.cols()<<n.size()<<endl;
|
固定尺寸VS动态尺寸
-
对于小的矩阵,尤其是小于16个元素的,使用固定尺寸有利于代码性能,因为会避免动态内存的分配,固定尺寸的矩阵实际上就是个数组,
-
比如Matrix4f mymatrix等价于float mymatrix[16]。
-
而动态尺寸的矩阵总是在堆上申请内存,因此MatrixXf mymatrix[rows,cols]相当于float *mymatrix = new float[rows*cols],并且MatrixXf对象还会存储它的行和列作为成员变量。
-
使用固定尺寸的局限在于仅仅当你能提前确定矩阵的尺寸。对于较大的矩阵,如大于32,使用固定尺寸带来的性能效果就变得可以忽略了,更糟糕的是,当试图在函数内部创建一个很大的固定尺寸的矩阵时可能会导致栈溢出
矩阵与向量的运算
- Eigen提供了一些矩阵和向量的数值运算,其中一些是通过通用的C++运算符重载实现,如
+,-,*等,另一些通过特殊的方法实现,如dot(),cross()等方法。对于Matrix类,这些操作只支持线性代数运算,比如matrix1*matrix2代表矩阵的乘积,而向量+标量则是不允许的。
加减
- 运算符两侧必须有相同的行和列。而且还要有相同的数据类型,因为Eigen并不做自动类型转换
| 可用的操作符 |
示例 |
| 二元运算+ |
a+b |
| 二元运算- |
a-b |
| 一元运算- |
-a |
| 复合运算+= |
a+=b |
| 复合运算-= |
a-=b |
标量的乘除
| 可用的操作符 |
示例 |
| 二元运算符* |
matrix*scalar,scalar*matrix |
| 二元运算符/ |
matrix/scalar |
| 复合运算符*= |
matrix*=scalar |
| 复合运算符/= |
matrix/=scalar |
表达式模板
- 不需要担心在Eigen中使用大的算法表达式。它仅仅是给了Eigen更多的优化机会。
- Eigen中数值运算符如+并不执行任何计算,而是返回一个表达式对象描述需要执行的计算。一般是当整个表达式被评估完之后(典型的比如遇到符号=)实际的计算才进行。这样做主要是为了优化提高效率,比如当你写
VectorXf a(50), b(50), c(50), d(50);...a = 3*b + 4*c + 5*d;
|
- Eigen会把他们编译成一个循环以便数组们只需要访问一次,比如会编译成如下形式
for(int i = 0; i < 50; ++i) a[i] = 3*b[i] + 4*c[i] + 5*d[i];
|
转置和共轭
- 对矩阵的转置、共轭和共轭转置由成员函数transpose(), conjugate(), adjoint()实现
| 函数 |
说明 |
| transpose() |
转置a.transpose() |
| conjugate() |
共轭a.conjugate() |
| adjoint() |
共轭转置a.adjoint() |
- 对于实数矩阵,conjugate()不做任何操作,所以adjoint()等同于transpose()。
- 基本的数值运算符、transpose()和adjoint()会简单的返回一个中间对象而不是对原对象做处理。
Matrix2i a;a<<1,2,3,4; cout<<a<<endl; a = a.transpose(); cout<<a<<endl;
|
- 这称为别名问题,在debug模式下当assertions没有禁止时,这种问题会被自动检测到。要避免错误,可以使用in-place转置。类似的还有adjointInPlace()。
矩阵相乘,矩阵乘向量
- 矩阵相乘使用运算符*实现,由于向量也是一种特殊的矩阵,因此矩阵乘以向量和向量间的外积是一样的方法。所有这些情况都由两个运算符处理
| 可用的操作符 |
示例 |
| 二元运算符* |
a*b |
| 复合运算符*= |
a*=b,等于a = a*b。 |
Eigen对矩阵相乘这种情况进行特殊处理,不用担心别名问题,比如m = m*m会被编译成tmp = m*m; m = tmp
点积和叉积
| 函数 |
说明 |
| 点积 |
v.dot(w) |
| 叉积 |
v.cross(w) |
基础的算术规约操作
| 函数 |
说明 |
| 和 |
sum() |
| 各元素相乘 |
prod() |
| 矩阵行的最大值和索引 |
maxCoeff() |
| 矩阵行的最小值和索引 |
minCoeff() |
| 迹 |
trace为矩阵的迹,也可以由a.diagonal().sum()得到。 |
minCoeff和maxCoeff函数也可以返回相应的元素的位置信息
操作的有效性
- Eigen会检查你的操作的有效性,如果可能的话会在编译阶段产生,输出信息。这些信息可能比较长并且难看,不过重要的信息都用了大写的语句标出了,可以很容易找到。如
Matrix3f m; Vector4f v; v = m*v;
|
- 但是另一些情况下,比如对于动态尺寸的检查不能在编译阶段完成,那么Eigen会在运行期间检查,如果出错,那么程序会崩溃并输出一些信息。
MatrixXf m(3,3); VectorXf v(4); v = m * v;
|
实例
求解矩阵方程耗时比较(直接求逆,Qr分解,LU分解)
#include <iostream> #include <ctime> #include <Eigen/Core> #include <Eigen/Dense> #include <Eigen/LU> #include <Eigen/Cholesky> using namespace std; using namespace Eigen;
const int MATRIX_SIZE = 100;
int main() { Matrix< double, MATRIX_SIZE, MATRIX_SIZE > matrix_NN; matrix_NN = MatrixXd::Random( MATRIX_SIZE, MATRIX_SIZE ); Matrix<double, MATRIX_SIZE, 1> v_Nd; v_Nd = MatrixXd::Random( MATRIX_SIZE,1); clock_t time_stt = clock(); Matrix< double, MATRIX_SIZE, 1> x = matrix_NN.inverse()*v_Nd; cout << "time use in normal inverse is " << 1000.0 * (clock() - time_stt) / (double)CLOCKS_PER_SEC << "ms" << endl; time_stt = clock(); x = matrix_NN.colPivHouseholderQr().solve(v_Nd); cout << "time use in Qr composition is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; time_stt = clock(); matrix_NN = matrix_NN.transpose() * matrix_NN; x = matrix_NN.partialPivLu().solve(v_Nd); cout << "time use in cholesky composition is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; time_stt = clock(); x = matrix_NN.partialPivLu().solve(v_Nd); cout << "time use in LU composition is " << 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl; return 0; }
|
矩阵使用的案例
#include <iostream> using namespace std; #include <ctime>
#include <Eigen/Core>
#include <Eigen/Dense> #define MATRIX_SIZE 50 int main( int argc, char** argv ) { Eigen::Matrix<float, 2, 3> matrix_23; Eigen::Vector3d v_3d; Eigen::Matrix<float,3,1> vd_3d; Eigen::Matrix3d matrix_33 = Eigen::Matrix3d::Zero(); Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic > matrix_dynamic; Eigen::MatrixXd matrix_x; 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::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; matrix_33 = Eigen::Matrix3d::Random(); cout << matrix_33 << endl << endl; cout << matrix_33.transpose() << endl; cout << matrix_33.sum() << endl; cout << matrix_33.trace() << endl; cout << 10*matrix_33 << endl; cout << matrix_33.inverse() << 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_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_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; return 0; }
|
几何模块
#include <iostream> #include <cmath> using namespace std; #include <Eigen/Core>
#include <Eigen/Geometry> int main ( int argc, char** argv ) { Eigen::Matrix3d rotation_matrix = Eigen::Matrix3d::Identity(); Eigen::AngleAxisd rotation_vector ( M_PI/4, Eigen::Vector3d ( 0,0,1 ) ); cout .precision(3); cout<<"rotation matrix =\n"<<rotation_vector.matrix() <<endl; rotation_matrix = rotation_vector.toRotationMatrix(); Eigen::Vector3d v ( 1,0,0 ); Eigen::Vector3d v_rotated = rotation_vector * v; cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl; v_rotated = rotation_matrix * v; cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl; Eigen::Vector3d euler_angles = rotation_matrix.eulerAngles ( 2,1,0 ); cout<<"yaw pitch roll = "<<euler_angles.transpose()<<endl; Eigen::Isometry3d T=Eigen::Isometry3d::Identity(); T.rotate ( rotation_vector ); T.pretranslate ( Eigen::Vector3d ( 1,3,4 ) ); cout << "Transform matrix = \n" << T.matrix() <<endl; Eigen::Vector3d v_transformed = T*v; cout<<"v tranformed = "<<v_transformed.transpose()<<endl; Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_vector ); cout<<"quaternion = \n"<<q.coeffs() <<endl; q = Eigen::Quaterniond ( rotation_matrix ); cout<<"quaternion = \n"<<q.coeffs() <<endl; v_rotated = q*v; cout<<"(1,0,0) after rotation = "<<v_rotated.transpose()<<endl; return 0; }
|