Eigen是C++的线性代数库,能提供有关矩阵的线性代数运算,还包含解方程等功能。
目录
0、Eigen库结构导图
1、Eigen库安装
2、Eigen库矩阵基础(参考)
2.1 矩阵模板函数
2.2 类型
2.3 赋值与访问
2.4 调整与操作
2.4 运算
2.5 解方程
3、Eigen库的向量基础
3.1 类型与存储
3.2 访问与赋值
3.3 运算
4、Eigen/Geometry(参考文章)
4.1 旋转向量:Eigen::AngleAxis
4.2 欧拉角:Eigen::eulerAngles
4.3 欧式变换:Eigen::Isometry
4.4 四元数:Eigen::Quaternion
4.5 转化:
5、完整代码
5.1 高翔《视觉SLAM十四讲》程序1
5.2 高翔《视觉SLAM十四讲》程序1
6、参考文章
0、Eigen库结构导图
1、Eigen库安装
参考文章1.4节(链接)
2、Eigen库矩阵基础(参考)
2.1 矩阵模板函数
Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类,矩阵模板函数共包含六个参数.
template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
参数说明:
- typename _Scalar:矩阵元素的类型;
- int _Rows:矩阵的行数;
- int _Cols:矩阵的列数;
- int _Options:矩阵是行主序(RowMajor)或列主序(ColMajor),默认是列主序;
- int _MaxRows:矩阵的最大行数;
- int _MaxCols:矩阵的最大列数;
Eigen::Matrix<int, 3, 4> mat1; // 3x4 的 int 类型的矩阵 mat1
Eigen::Matrix<double, 3, Dynamic> mat2; // 3x? 的 double 类型的矩阵 mat2
Eigen::Matrix<float, Dynamic, 4> mat3; // ?x4 的 float 类型的矩阵 mat3
Eigen::Matrix<long, Dynamic, Dynamic> mat4; // ?x? 的 long 类型的矩阵 mat4
动态矩阵:在 Eigen 中可以用 Dynamic 表示行或者列数未知,所以在定义一个矩阵时并不能确定矩阵的大小,只有在运行时才可以确定大小,然后进行动态分配。
静态矩阵:在定义时便明确给定了行数以及列数,在编译时就可以分配好内存。
2.2 类型
在 EigenEigen 中 typedef 了很多矩阵的类型,通常命名为 Matrix前缀加一个长度为 1∼4 的字符串 S 的命名——MatrixS。
其中 S 可以用来判断该矩阵类型,数字 n 表示 n ∗ n,n 的范围是2∼4,字母 d、f、i、cd、f、i、c 表示 double、float、int、complexdouble、float、int、complex,另外 X 表示行或者列数未知的矩阵。
typedef Matrix<std::complex<double>, 2, 2> Eigen::Matrix2cd; // 2x2 的 cd 类型的矩阵
typedef Matrix<double, 2, 2> Eigen::Matrix2d; // 2x2 的 d 类型的矩阵
typedef Matrix<std::complex<double>, 2, Dynamic> Eigen::Matrix2Xcd; // 2x? 的 cd 类型的矩阵
typedef Matrix<std::complex<float>, Dynamic, 2> Eigen::MatrixX2cf; // ?x2 的 cf 类型的矩阵
typedef Matrix<std::complex<double>, Dynamic, Dynamic> Eigen::MatrixXcd;// ?x? 的 cd 类型的矩阵
typedef Matrix<int, Dynamic, Dynamic> Eigen::MatrixXi; // ?x? 的 i 类型的矩阵
同时,Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix,
//例如 Vector3d 实质上是 Eigen::Matrix<double, 3, 1>,即三维向量
Vector3d v_3d;
// 这是一样的
Matrix<float, 3, 1> vd_3d;
// Matrix3d 实质上是 Eigen::Matrix<double, 3, 3>
Matrix3d matrix_33 = Matrix3d::Zero(); //初始化为零
但是在Eigen里你不能混合两种不同类型的矩阵,同样你不能搞错矩阵的维度,需要注意如下的问题。
// 但是在Eigen里你不能混合两种不同类型的矩阵,像这样是错的
// Matrix<double, 2, 1> result_wrong_type = matrix_23 * v_3d;
// 应该显式转换,matrix_23.cast<double>(),然后进行计算
Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d;
cout << "[1,2,3;4,5,6]*[3,2,1]=" << result.transpose() << endl;
Matrix<float, 2, 1> result2 = matrix_23 * vd_3d;
cout << "[1,2,3;4,5,6]*[4,5,6]: " << result2.transpose() << endl;
// 同样你不能搞错矩阵的维度
// 试着取消下面的注释,看看Eigen会报什么错
// Eigen::Matrix<double, 2, 3> result_wrong_dimension = matrix_23.cast<double>() * v_3d;
2.3 赋值与访问
(1)Eigen::Matrix 使用 ()访问赋值数据,矩阵之间通过 = 来进行赋值(拷贝)。
x = mat(a, b); // 获取到矩阵 mat 的 a 行 b 列的元素并赋值给 x
mat(b, a) = x; // 将 x 赋值给矩阵 mat 的 b 行 a 列
mat1 = mat2; // 将矩阵 mat2 赋值(拷贝)给矩阵 mat1
注意: 通过 = 进行矩阵之间的拷贝时,如果左右两侧矩阵尺寸不一样并且左侧矩阵为动态矩阵,那么会将左侧矩阵的尺寸修改为与右侧一致。
(2)在 Eigen 中重载了 << 可以用来赋值矩阵,也可以用来 cout 输出矩阵。
MatrixXf m(4, 4); // 定义一个 4x4 的 float 类型的动态矩阵
m << 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16;// 赋值
std::cout << m; // 输出 m
(3)Eigen 矩阵可以进行分块操作,通过成员函数 block() 获取某一部分矩阵。
mat = mat1.block(i, j, p, q); // 从矩阵 mat1 的 i 行 j 列开始获取一个 p 行 q 列的子矩阵
mat = mat1.block<p, q>(i, j); // 从矩阵 mat1 的 i 行 j 列开始获取一个 p 行 q 列的子矩阵(动态矩阵)
(4)Eigen 矩阵还可以使用成员函数 fill() 进行统一赋值。
mat.fill(n); // 将 mat 的所有元素均赋值为 n
2.4 调整与操作
(1)调整大小
mat.resize(nb_rows, nb_cols); // 调整成具有指定行列数的矩阵
mat.resize(Eigen::NoChange, nb_cols); // 只调整列
mat.resize(nb_rows, Eigen::NoChange); // 只调整行
mat.resizeLike(other_matrix); // 按other_matrix的大小调整矩阵
mat.conservativeResize(nb_rows, nb_cols); // 矩阵重排,resize()函数会改变原有矩阵中的值。
(2)获取矩阵信息
mat.rows(); // 获取矩阵的行数
mat.cols(); // 获取矩阵的列数
mat.innerSize(); // 按列存储的矩阵返回其行数,接行存储的矩阵返回其列数
mat.outerSize(); // 按列存储的矩阵返回其列数,按行存储的矩阵返回其行数
mat.innerStride(); // 返回内部实际存储时相邻元素之间的指针增量
mat.outerStride(); // 返回按逻辑顺序存储时的指针增量
mat.data(); // 获取矩阵的数据指针
(3)Eigen 提供了从边角开始提取子矩阵的方法
描述 |
动态矩阵 | 静态矩阵 |
左上角 pxq | mat.topLeftCorner(p,q) | mat.topLeftCorner<p,q>() |
左下角 pxq | mat.bottomLeftCorner(p,q) | mat.bottomLeftCorner<p,q>() |
右上角 pxq | mat.topRightCorner(p,q) | mat.topRightCorner<p,q>() |
右下角 pxq | mat.bottomRightCorner(p,q) | mat.bottomRightCorner<p,q>() |
前 p 行 | mat.topRows(p) | mat.topRows<p>() |
后 p 行 | mat.bottomRows(p) | mat.bottomRows<p>() |
前 q 列 | mat.leftCols(q) | mat.leftCols<q>() |
后 q 列 | mat.rightCols(q) | mat.rightCols<q>() |
2.4 运算
(1)Eigen 重载了 +、−(减)、∗、/、−(负)、+=、−=、∗=、/=+、−(减)、∗、/、−(负)、+=、−=、∗=、/=。
mat = mat1 + mat2; // +
mat = mat1 - mat2; // -(减)
mat = mat1 * mat2; // *
mat = mat1 * n; // *
mat = mat1 / n; // /
mat = -mat1; // -(负)
mat += mat1; // +=
mat -= mat1; // -=
mat *= mat1; // *=
mat *= n; // *=
mat /= n; // /=
(2)对于 MatrixMatrix 的转置矩阵、共轭矩阵、伴随矩阵、对角矩阵可以通过成员函数 transpose()、conjugate()、adjoint()、diagonal()来获得。如果想要在原矩阵上进行转换,则需要通过成员函数 transposeInPlace()、conjugateInPlace()、adjointInPlace()。
mat1.transpose(); // 获取 mat1 的转置矩阵
mat1.conjugate(); // 获取 mat1 的共轭矩阵
mat1.adjoint(); // 获取 mat1 的伴随矩阵
mat1.diagonal(); // 获取 mat1 的对角矩阵
mat1.transposeInPlace();// mat1 转换为对应的转置矩阵
mat1.conjugateInPlace();// mat1 转换为对应的共轭矩阵
mat1.adjointInPlace(); // mat1 转换为对应的伴随矩阵
mat1.diagonalInPlace(); // mat1 转换为对应的对角矩阵
mat1.sum(); // mat1各元素和
mat1.trace() // mat1迹
mat1.inverse() //mat1 逆
mat1.determinant() // mat1行列式
mat1.transpose().colwise().reverse(); // mat1 Rot90
mat.minCoeff(); // 求矩阵的最小元素
mat.colwise().minCoeff(); // 求每列的最小元素,所以输出为行
mat.rowwise().minCoeff(); // 求每行的最小元素,所以输出为列
(3)特殊矩阵
mat = MatrixXd::Identity(rows, cols); // 生成 rows x cols 的单位阵
mat.setIdentity(rows, cols); // 将 mat 设置为 rows x cols 的单位阵
mat = MatrixXd::Zero(rows, cols); // 生成 rows x cols 的零矩阵
mat.setZero(rows, cols); // 将 mat 设置为 rows x cols 的零矩阵
mat = MatrixXd::Ones(rows, cols); // 生成 rows x cols 的壹矩阵
mat.setOnes(rows, cols); // 将 mat 设置为 rows x cols 的壹矩阵
mat = MatrixXd::Random(rows, cols); // 生成 rows x cols 的随机矩阵
mat.setRandom(rows, cols); // 将 mat 设置为 rows x cols 的随机矩阵
2.5 解方程
我们求解 matrix_NN * x = v_Nd 这个方程为例直接求逆自然是最直接的,但是求逆运算量大。
// 求解的方程matrix_NN * x = v_Nd
// 定义matrix_NN矩阵
Matrix<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN= MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
// 保证半正定,矩阵和矩阵的转置相乘可以保证半正定
matrix_NN = matrix_NN * matrix_NN.transpose();
// 定义v_Nd
Matrix<double, MATRIX_SIZE, 1> 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 of normal inverse is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
// 方法二:通常用矩阵分解来求,例如QR分解,速度会快很多
time_stt = clock();
x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
cout << "time of Qr decomposition is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
// 方法三:对于正定矩阵,还可以用cholesky分解来解方程
time_stt = clock();
x = matrix_NN.ldlt().solve(v_Nd);
cout << "time of ldlt decomposition is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
3、Eigen库的向量基础
3.1 类型与存储
类似于矩阵的类型,不过前缀从 Matrix 改成 Vector。
typedef Matrix<std::complex<float>, 2, 1> Eigen::Vector2cf; // cf 类型的 2 向量
typedef Matrix<int, 2, 1> Eigen::Vector2i; // i 类型的 2 向量
typedef Matrix<double, 4, 1> Eigen::Vector4d; // d 类型的 4 向量
typedef Matrix<float, Dynamic, 1> Eigen::VectorXf; // f 类型的 ? 向量
上述的 Vector 默认的都是列向量,如果是行向量,在命名上则需要加上 Row 前缀。
typedef Matrix<float, 1, 2> Eigen::RowVector2f; // f 类型的 2 向量(行向量)
typedef Matrix<std::complex<double>, 1, 4> Eigen::RowVector4cd; // cd 类型的 4 向量(行向量)
动态向量:不指定向量的尺寸,只有在运行时才会分配对应的内存;
静态向量:一开始就定义好了大小,在编译时分配好内存。
3.2 访问与赋值
(1)类似于 Matrix 的操作,不过向量也可以使用 [],另外向量对前四个元素的访问与赋值还可以通过成员函数 x()、y()、z()、w()来操作。
x = vec(n); // 获取向量 vec 的第 n 个元素并赋值给 x
x = vec[n];
x = vec.x(); // 获取向量 vec 的第一个元素并赋值给 x
y = vec.y(); // 获取向量 vec 的第二个元素并赋值给 y
z = vec.z(); // 获取向量 vec 的第三个元素并赋值给 z
w = vec.w(); // 获取向量 vec 的第四个元素并赋值给 w
vec(n) = x; // 将 x 赋值给 vec 的第 n 个元素
(2)Eigen 向量也提供了一些分块操作来获取子向量。
描述 | 静态向量 | 动态向量 |
前 n 个元素 | vec.head(n) | vec.head<n>() |
后 n 个元素 | vec.tail(n) | vec.tail<n>() |
从 i 开始取 n 个元素 | vec.segment(i,n) | vec.segment<n>(i) |
3.3 运算
(1)Eigen 向量提供了 norm()、squareNorm()、dot()、cross()等成员函数。
vec.norm() // 返回向量的二范数
vec.squaredNorm() // 返回向量所有元素的平方和
vec.dot(y) // 返回向量点积
vec.cross(y) // 返回向量叉积
(2)向量其他成员函数
vector.size(); // 获取向量的大小,即元素个数
vector.innerStride(); // 沿数据存储的方向移动一个位置,内存中需要移动的位置数
vector.data(); // 获取向量的数据
(3)调整大小
vector.resize(size); // 调整具有动态尺寸的向量大小
vector.resizeLike(other_vector); // 按other_vector的尺寸调整具有动态尺寸的向量大小
vector.conservativeResize(size);
(4)特殊向量构造
Vector3f::UnitX() // 1 0 0
Vector3f::UnitY() // 0 1 0
Vector3f::UnitZ() // 0 0 1
4、Eigen/Geometry(参考文章)
<Eigen/Geometry >模块提供了各种旋转和平移的表示
4.1 旋转向量:Eigen::AngleAxis
(1)初始化
旋转矩阵直接使用 Matrix3d 或 Matrix3f,旋转向量使用 AngleAxis。
AngleAxisd rotation_vector(alpha, Vector3d(x, y, z)); //alpha:旋转角度,(x, y, z):旋转轴
(2)转换为旋转矩阵
Matrix3d rotation_matrix;
rotation_matrix = rotation_vector.matrix();
// 或
rotation_matrix = rotation_vector.toRotationMatrix();
(3)转换为欧拉角(需要先转换为旋转矩阵)
euler_angles = rotation_vector.matrix().eulerAngles(2, 1, 0);
(4)转换为四元数
Quaternion q = Quaterniond(rotation_vector);
4.2 欧拉角:Eigen::eulerAngles
Eigen::eulerAngles(a0, a1, a2)用法:a0, a1, a2从{0, 1, 2}中取值,0代表X轴,1代表Y轴,2代表Z轴,旋转顺序为从后到前(下一次旋转使用的旋转轴是上一次旋转之后的)。得到的Euler Angle角度值范围:[0, pi] x [-pi, pi] x [-pi, pi]。
Vector3f euler_angles = rotation_matrix.eulerAngles(2, 1, 0);
// 等价于
rotation_matrix = AngleAxisd(euler_angles[0], Vector3f::UnitZ())
* AngleAxisd(euler_angles[1], Vector3f::UnitY())
* AngleAxisd(euler_angles[2], Vector3f::UnitX());
均表示先绕X轴旋转euler_angles[2] rad,再绕Y轴旋转euler_angles[1] rad,再绕Z轴旋转euler_angles[0] rad。
注意:坐标系:X轴朝前, Y轴朝左, Z轴朝上;XYZ轴并不一定总是对应roll, pitch, yaw,要根据实际情况判断。roll表示横滚,pitch表示俯仰,yaw表示偏航。
4.3 欧式变换:Eigen::Isometry
(1)定义
Isometry3d T = Isometry3d::Identity(); //T实际为4*4矩阵
T.rotate(rotation_vector); //定义欧式变换的旋转矩阵
T.pretranslate(Vector3d(x, y, z)); //定义欧式变换的平移向量
(2)转换为4*4变换矩阵
Matrix4d transform_matrix = T.matrix();
(3)用欧式变换进行坐标变换
Vector3d v = (1, 2, 3);
Vector3d v_transformed = T * v; //相当于R*v+t
4.4 四元数:Eigen::Quaternion
(1)初始化
// 直接初始化
// 注意:初始化时实部在前,虚部在后,但在内部存储顺序为[x, y, z, w]
Quaterniond q(w, x, y, z);
// 使用旋转向量和旋转矩阵初始化:
Quaterniond q1(rotation_vector);
Quaterniond q2(rotation_matrix);
(2)打印四元数
cout << q.coeffs() << endl;
(3)使用四元数旋转向量
Vector3d v_rotated = q * v;
4.5 转化:
// 旋转向量->旋转矩阵:
rotationMatrix=rotation_vector.toRotationMatrix();
// 旋转向量->四元数:
Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_vector );
// 旋转矩阵->四元数:
Eigen::Quaterniond q = Eigen::Quaterniond ( rotation_matrix );
// 四元素->旋转矩阵:
Eigen::Matrix3d Rx = q.toRotationMatrix();
// 旋转向量->欧拉角:
Eigen::Vector3d eulerAngle=rotationVector.matrix().eulerAngles(0,1,2);
// 旋转矩阵->欧拉角:
Eigen::Vector3d euler_angles = rotation_matrix.eulerAngles ( 2,1,0 ); // ZYX顺序,即roll pitch yaw顺序
5、完整代码
5.1 高翔《视觉SLAM十四讲》程序1
(1)CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(useEigen)
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_FLAGS "-O3")
# 添加Eigen头文件
include_directories("/usr/include/eigen3")
add_executable(eigenMatrix eigenMatrix.cpp)
(2)eigenMatrix.cpp
#include <iostream>
using namespace std;
#include <ctime>
// Eigen 核心部分
#include <Eigen/Core>
// 稠密矩阵的代数运算(逆,特征值等)
#include <Eigen/Dense>
using namespace Eigen;
#define MATRIX_SIZE 50
/****************************
* 本程序演示了 Eigen 基本类型的使用
****************************/
int main(int argc, char **argv) {
// Eigen 中所有向量和矩阵都是Eigen::Matrix,它是一个模板类。它的前三个参数为:数据类型,行,列
// 声明一个2*3的float矩阵
Matrix<float, 2, 3> matrix_23;
// 同时,Eigen 通过 typedef 提供了许多内置类型,不过底层仍是Eigen::Matrix
// 例如 Vector3d 实质上是 Eigen::Matrix<double, 3, 1>,即三维向量
Vector3d v_3d;
// 这是一样的
Matrix<float, 3, 1> vd_3d;
// Matrix3d 实质上是 Eigen::Matrix<double, 3, 3>
Matrix3d matrix_33 = Matrix3d::Zero(); //初始化为零
// 如果不确定矩阵大小,可以使用动态大小的矩阵
Matrix<double, Dynamic, Dynamic> matrix_dynamic;
// 更简单的
MatrixXd matrix_x;
// 这种类型还有很多,我们不一一列举
// 下面是对Eigen阵的操作
// 输入数据(初始化)
matrix_23 << 1, 2, 3, 4, 5, 6;
// 输出
cout << "matrix 2x3 from 1 to 6: \n" << matrix_23 << endl;
// 用()访问矩阵中的元素
cout << "print matrix 2x3: " << 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_wrong_type = matrix_23 * v_3d;
// 应该显式转换
Matrix<double, 2, 1> result = matrix_23.cast<double>() * v_3d;
cout << "[1,2,3;4,5,6]*[3,2,1]=" << result.transpose() << endl;
Matrix<float, 2, 1> result2 = matrix_23 * vd_3d;
cout << "[1,2,3;4,5,6]*[4,5,6]: " << result2.transpose() << endl;
// 同样你不能搞错矩阵的维度
// 试着取消下面的注释,看看Eigen会报什么错
// Eigen::Matrix<double, 2, 3> result_wrong_dimension = matrix_23.cast<double>() * v_3d;
// 一些矩阵运算
// 四则运算就不演示了,直接用+-*/即可。
matrix_33 = Matrix3d::Random(); // 随机数矩阵
cout << "random matrix: \n" << matrix_33 << endl;
cout << "transpose: \n" << matrix_33.transpose() << endl; // 转置
cout << "sum: " << matrix_33.sum() << endl; // 各元素和
cout << "trace: " << matrix_33.trace() << endl; // 迹
cout << "times 10: \n" << 10 * matrix_33 << endl; // 数乘
cout << "inverse: \n" << matrix_33.inverse() << endl; // 逆
cout << "det: " << matrix_33.determinant() << endl; // 行列式
// 特征值
// 实对称矩阵可以保证对角化成功
SelfAdjointEigenSolver<Matrix3d> eigen_solver(matrix_33.transpose() * matrix_33);
cout << "Eigen values = \n" << eigen_solver.eigenvalues() << endl;
cout << "Eigen vectors = \n" << eigen_solver.eigenvectors() << endl;
// 解方程
// 我们求解 matrix_NN * x = v_Nd 这个方程
// N的大小在前边的宏里定义,它由随机数生成
// 直接求逆自然是最直接的,但是求逆运算量大
Matrix<double, MATRIX_SIZE, MATRIX_SIZE> matrix_NN
= MatrixXd::Random(MATRIX_SIZE, MATRIX_SIZE);
matrix_NN = matrix_NN * matrix_NN.transpose(); // 保证半正定
Matrix<double, MATRIX_SIZE, 1> 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 of normal inverse is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
// 通常用矩阵分解来求,例如QR分解,速度会快很多
time_stt = clock();
x = matrix_NN.colPivHouseholderQr().solve(v_Nd);
cout << "time of Qr decomposition is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
// 对于正定矩阵,还可以用cholesky分解来解方程
time_stt = clock();
x = matrix_NN.ldlt().solve(v_Nd);
cout << "time of ldlt decomposition is "
<< 1000 * (clock() - time_stt) / (double) CLOCKS_PER_SEC << "ms" << endl;
cout << "x = " << x.transpose() << endl;
return 0;
}
5.2 高翔《视觉SLAM十四讲》程序1
(1)CMakeLists.txt
cmake_minimum_required( VERSION 2.8 )
project( geometry )
# 添加Eigen头文件
include_directories( "/usr/include/eigen3" )
add_executable(eigenGeometry eigenGeometry.cpp)
(2)eigenGeometry.cpp
#include <iostream>
#include <cmath>
#include <Eigen/Core>
#include <Eigen/Geometry>
using namespace std;
using namespace Eigen;
// 本程序演示了 Eigen 几何模块的使用方法
int main(int argc, char **argv) {
// Eigen/Geometry 模块提供了各种旋转和平移的表示
// 3D 旋转矩阵直接使用 Matrix3d 或 Matrix3f
Matrix3d rotation_matrix = Matrix3d::Identity();
// 旋转向量使用 AngleAxis, 它底层不直接是Matrix,但运算可以当作矩阵(因为重载了运算符)
AngleAxisd rotation_vector(M_PI / 4, Vector3d(0, 0, 1)); //沿 Z 轴旋转 45 度
cout.precision(3);
cout << "rotation matrix =\n" << rotation_vector.matrix() << endl; //用matrix()转换成矩阵
// 也可以直接赋值
rotation_matrix = rotation_vector.toRotationMatrix();
// 用 AngleAxis 可以进行坐标变换
Vector3d v(1, 0, 0);
Vector3d v_rotated = rotation_vector * v;
cout << "(1,0,0) after rotation (by angle axis) = " << v_rotated.transpose() << endl;
// 或者用旋转矩阵
v_rotated = rotation_matrix * v;
cout << "(1,0,0) after rotation (by matrix) = " << v_rotated.transpose() << endl;
// 欧拉角: 可以将旋转矩阵直接转换成欧拉角
Vector3d euler_angles = rotation_matrix.eulerAngles(2, 1, 0); // ZYX顺序,即yaw-pitch-roll顺序
cout << "yaw pitch roll = " << euler_angles.transpose() << endl;
// 欧氏变换矩阵使用 Eigen::Isometry
Isometry3d T = Isometry3d::Identity(); // 虽然称为3d,实质上是4*4的矩阵
T.rotate(rotation_vector); // 按照rotation_vector进行旋转
T.pretranslate(Vector3d(1, 3, 4)); // 把平移向量设成(1,3,4)
cout << "Transform matrix = \n" << T.matrix() << endl;
// 用变换矩阵进行坐标变换
Vector3d v_transformed = T * v; // 相当于R*v+t
cout << "v tranformed = " << v_transformed.transpose() << endl;
// 对于仿射和射影变换,使用 Eigen::Affine3d 和 Eigen::Projective3d 即可,略
// 四元数
// 可以直接把AngleAxis赋值给四元数,反之亦然
Quaterniond q = Quaterniond(rotation_vector);
cout << "quaternion from rotation vector = " << q.coeffs().transpose()
<< endl; // 请注意coeffs的顺序是(x,y,z,w),w为实部,前三者为虚部
// 也可以把旋转矩阵赋给它
q = Quaterniond(rotation_matrix);
cout << "quaternion from rotation matrix = " << q.coeffs().transpose() << endl;
// 使用四元数旋转一个向量,使用重载的乘法即可
v_rotated = q * v; // 注意数学上是qvq^{-1}
cout << "(1,0,0) after rotation = " << v_rotated.transpose() << endl;
// 用常规向量乘法表示,则应该如下计算
cout << "should be equal to " << (q * Quaterniond(0, 1, 0, 0) * q.inverse()).coeffs().transpose() << endl;
return 0;
}
6、参考文章
(1)高翔《视觉SLAM十四讲》
(2)官方文档:Eigen库官方文档
(3)博客:「 C++ 库 」“Eigen库”基础使用手册文章来源:https://www.toymoban.com/news/detail-586812.html
(4)文章:Eigen —— 参考指南 | 码农家园文章来源地址https://www.toymoban.com/news/detail-586812.html
到了这里,关于Eigen线性代数库学习大全的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!