4371 字
22 分钟
飞行器动力学-P2_Eigen库入门

封面图片:2025年7月15日,“天舟九号”成功发射.

Eigen是C++下一个广泛应用的线性代数库,本文对其主要用法进行记录,以备使用.

运行环境#

本文使用CMake自行搭建编译环境,使用VSCode进行代码编辑.

软件版本号
CMake4.0.0
Eigen3.4.0

基本数据类型#

  • Eigen中的基本数据类型为Matrix,Vector与Array;
  • 所有Matrix/Vector本质上都是Matrix类,Vector是其中单行/单列的特殊情况,支持部分额外操作(如[]运算符);Array类型在部分操作略有不同(如乘法).
// 1.标准定义
// 矩阵:三个参数分别为值类型(如double),行数与列数.后两者必须在编译期给定.
// 向量:同理,只是行数/列数至少有一个为1.
Matrix<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
Array<typename Scalar, int RowsAtCompileTime, int ColsAtCompileTime>
// 扩展定义:Options == 0:列优先(默认), == RowMajor:行优先
// MaxRowsAtCompileTime:指定动态数组最大行数; MaxColsAtCompileTime列数同理.
Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
Array<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
// 2.Dynamic参数
// 可用于替代行数/列数/同时替代两者,表示该值在运行时给定.
// 以下定义均合法.
// 注意:对元素数量<16的情况,动态分配(堆分配)会显著降低性能.
Matrix<double, Dynamic, Dynamic> m1;
Matrix<double, Dynamic, 1> m2;
Matrix<double, 3, Dynamic> m3;
Array<double, Dynamic, Dynamic> a1;
Array<double, Dynamic, 1> a2;
Array<double, 3, Dynamic> a3;
// 3.矩阵基本成员函数
// 行数,列数与元素总个数.
Matrix<double, 3, 3> m;
Array<double, 3, 3> m;
m.rows();
m.cols();
m.size();
a.rows();
a.cols();
a.size();
// 4.初始化
// 默认初始化:不会将元素置0(置0初始化参见zero()函数).
Matrix<int, 3, 3> m;
Array<double, Dynamic, Dynamic> a;
// 动态数组的reserve,首参数为行
Matrix<double, Dynamic, Dynamic> m(10,15); // 10*15矩阵
Matrix<double, Dynamic, 1> v(30); // 30*1向量
Array<double, Dynamic, Dynamic> a(10,15);
// (C++11)使用初始化列表
Matrix<int, 3, 1> v{1, 2, 3};
Array<int, 3, 1> a{1, 2, 3};
// 使用<<运算符
Matrix<int, 4, 4> m;
Matrix<int, 2, 2> m2;
Matrix<int, 3, 1> v1, v2;
Matrix<int, 6, 1> v3;
Array<int, 3, 3> a;
m2 << 1,2,3,4; // 等价于{{1,2}, {3,4}};
m << m2, m2, m2, m2; // 合法,次序同上一行(行优先填充)
a << 1,2,3,4,5,6,7,8,9;
v3 << v1, v2; // 合法
// 相同大小的Array和Matrix在赋值时可以直接互换.
Matrix<int, 3, 3> m;
Array<int, 3, 3> a;
a = m; // 合法
// 5.下标访问
// 从0开始,使用(), 第一个参数是行.
// 对Matrix,Vector和Array都支持.
m(0, 0) = 3;
v(0) = 3;
a(0, 0) = 3;
v[2] = 3; // []运算符只允许用在vector上
// 6.Resize
// 仅适用于Dynamic类.
// 6.1 .resize() 不保证操作后矩阵元素仍有效.
m.resize(3, 4);
v.resize(5);
// 6.2 .conservativeResize() 保证操作后矩阵元素仍有效
// 缩小:保留原矩阵左上角的元素.
// 扩大:多出来的元素未初始化.
m.conservativeResize(3, 4);
// 6.3 Assign and resizing
Matrix<double, Dynamic, Dynamic> m2(2, 2), m3(3, 3);
m2 = m3;
assert(m2.rows == 3 && m2.cols == 3);
// 7.类型别名
// 以下定义中, N == 2, 3, 4, X(Dynamic);
// t == i(int), f(float), d(double), cf(std::complex<float>), cd(std::complex<double>)
using MatrixNt = Matrix<type, N, N>; // 如Matrix3d = Matrix<double, 3, 3>;
using MatrixN1N2t = Matrix<type, N1, N2>; // 如MatrixX3d = Matrix<double, 3, dynamic>;
using VectorNt = Matrix<type, N, 1>;
using RowVectorNt = Matrix<type, 1, N>;
using ArrayNt = Array<type, N, N>;
using ArrayN1N2t = Array<type, N1, N2>;

基本操作#

介绍Matrix/Array的基本运算操作.

NOTE

行优先&列优先

计算机内的内存空间是一维的,要将二维矩阵存储其中有两种方式.

行优先:顾名思义,优先存储同一行内的元素.第二行的第一个元素存储在第一行的最后一个元素之后;

列优先:优先存储同一列内的元素.第二列的第一个元素存储在第一列的最后一个元素之后;

Eigen默认使用列优先模式.将科学计算中最常用的列向量进行连续存储,方便提升缓存命中率/进行SIMD优化,加速计算.

Matrix运算#

/* Ext. Eigen中的运算时机
为提高效率,Eigen采用"懒求值"模式,也即使用各类运算符时,运算不会立即发生;
相反,Eigen会返回一个表达式对象,实际计算在值实际被用到时才发生.
"用到"值的最典型场景是将运算结果赋值给一个对象,也即使用operator=.
有时会导致数据竞争,具体运算符条目中有说明.
*/
MatrixXi mat(3, 3);
mat << 1, 2, 3, 4, 5, 6, 7, 8, 9;
// 危险!对矩阵(1,1)点同时存在读取与写入操作,存在数据竞争.
mat.bottomRightCorner(2, 2) = mat.topLeftCorner(2, 2);
// 使用eval()函数显式生成中间变量,消除竞争.
mat.bottomRightCorner(2, 2) = mat.topLeftCorner(2, 2).eval();
// 1. 加法与减法
// 相关运算符: 单目运算符-; 双目运算符+ -; 复合运算符+= -=.
// 前提1:两对象的行数与列数对应
// 前提2:两对象的元素类型相同(自动类型提升不可用)
// 前提3:两侧运算对象均为array或均为matrix
Matrix2d m1;
Matrix3d m2;
Matrix2f m3;
Array22d a1, a2;
auto res = m1 + m2; // 不合法,违反原则1
auto res = m1 + m3; // 不合法,违反原则2
auto res = m1 + a1; // 不合法,违反原则3
// 以下运算正确.
Matrix2d m4, m5;
m4 + m5;
m4 += m5;
m4 - m5;
m4 -= m5;
-m4;
m4 + a1.matrix();
// 2. 数乘与数除
// 以下运算均合法.
Matrix m1;
double d;
m1 * d;
d * m1;
m1 *= d;
m1 / d;
m1 /= d;
// 3. 转置与共轭
// 注意懒求值:a=a.transpose()会带来数据竞争,计算结果可能与预期不符.
Matrix3d m;
m.transpose(); // 转置(有数据竞争)
m.transposeInPlace(); // 原位转置(无数据竞争)
m.conjugate(); // 共轭
m.adjoint(); // 共轭转置(有数据竞争)
m.adjointInPlace(); // 原位共轭转置(无数据竞争)
// 4. 矩阵乘法
// 这是Eigen中唯一一种默认数据竞争会发生的情况.
// 如果实际上不会,可以在左侧添加.noalias()以提高性能.
Matrix3d m1, m2, m3;
m1 * m2;
m1 *= m2; // == (m1 = m1 * m2)
m3.noalias() += m1 * m2; // 如果确保不会发生数据竞争
// 5. 点乘与叉乘
Eigen::Vector3d v(1, 2, 3);
Eigen::Vector3d w(0, 1, 2);
v.dot(w);
v.cross(w); // 对2维向量也可用,用以计算行列式
// 6. 基本算数操作
Eigen::Matrix3d m, m2;
Eigen::Vector3d v;
std::ptrdiff_t i, j;
m.sum(); // 所有元素的和
m.prod(); // 所有元素的积
m.mean(); // 所有元素的均值
m.minCoeff(&i, &j); // 所有元素中的最小值(i,j是可选入参,表示位置)
v.minCoeff(&i); // i是可选入参,表示位置
m.maxCoeff(); // 所有元素中的最大值
m.trace(); // 矩阵的迹
m.cwiseProduct(m2); // m与m2的逐元素乘法,等效于(m.array() * m2.array()).matrix()
m.squaredNorm(); // 模的平方
m.norm(); // 模
m.normalize(); // m原位归一化
m.normalized(); // m非原位归一化
m.lpNorm<n>(); // n范数,(((m所有元素的绝对值)的n次方之和)的(1/n)次方.比如Norm是2范数.
m.lpNorm<Eigen::infinity>(); // +inf范数,m绝对值最大的元素.
m.sin(); // 逐元素sin(弧度制输入)
m.cos();
m.all(); // 返回true如果m元素全为真.
(a > 0).all(); // 合法.
m.any(); // 返回true如果m元素存在真.
m.count(); // 返回真元素数量.
// 7.向array转换,无运行时开销.
auto a = m.array();

Array运算#

// Array的大部分运算符与Matrix通用
// 区别在于Array间相乘时,返回逐元素积而非矩阵积.
Eigen::ArrayXXf a(2, 2);
Eigen::ArrayXXf b(2, 2);
a << 1, 2, 3, 4;
b << 5, 6, 7, 8;
auto c = a * b; // c == {{5, 12}, {21, 32}};
// 2. Array的基本算数操作
Eigen::Array33d a;
a.abs(); // 返回|a|
a.sqrt(); // 逐元素开根号
a.min(a2);// 每个元素是a和a2中较小的一个
// 3.向Matrix转换,无运行时开销.
auto m = a.matrix();

矩阵分块&Slicing#

  • 矩阵分块零运行时开销.
// 视返回值的动/静态类型,分块方法略有不同.
// 注意:两种方法控制的是返回值类型,m矩阵本身动态/静态均可.
// 分块可以用作左值或右值.
Eigen::matrix4d m;
Eigen::vector4d v;
auto m2 = m.block(1,1,2,2); // m2是matrixXXd类型,以(1,1)为左上角取2x2的块
auto m3 = m.block<2,2>(1,1);// 分块大小编译期确定;m3是matrix22d类型,内容与m2一致.
m.block<2,2>(0,0) = m2; // 合法.
m.block(0,0,2,2) = m3; // 合法.
v.head(n); // 矢量的头n个元素.
v.head<n>();
v.tail(n); // 尾n个元素.
v.tail<n>();
v.segment(i, n); // 从i开始的n个元素
v.segment<n>(i);
// 2.特殊分块函数
// 按行/按列/按角落分,比使用block方便高效.
// 2.1. 按行列分
// 返回值动/静态类型视m矩阵的动/静态类型.
Eigen::matrix4d m;
Eigen::matrixXd md;
m.row(i); // 第i行,从0开始.静态矩阵.
m.col(j); // 第j列,从0开始.静态矩阵.
md.row(i); // 内容与上面相同.但是返回值类型为动态矩阵.
md.col(j);
m.topRows(q); // 最上面q行
m.topRows<q>();
m.bottomRows(q); // 最下面q行
m.bottomRows<q>();
m.leftCols(p); // 左侧p列
m.leftCols<p>();
m.rightCols(p); // 右侧p列
m.rightCols<p>();
m.middleCols(i, q); // 从i列开始的q列
m.middleCols<q>(i);
m.middleRows(i, p); // 从i行开始的q行
m.middleRows<p>(i);
// 2.2. 按角落分
Eigen::matrix4d m;
m.topLeftCorner(p, q);
m.topLeftCorner<p, q>();
m.bottomLeftCorner(p, q);
m.bottomLeftCorner<p, q>();
m.topRightCorner(p, q);
m.topRightCorner<p, q>();
m.bottomRightCorner(p, q);
m.bottomRightCorner<p, q>();
// 2.3. 对角
m.diagonal(); // (只适用方阵)取主对角元素,其余置为0.
// 3. Slicing, ()运算符的高级应用
/* 除了取单个元素, m(p,q)中的p,q还可以接受多种类型的参数.
* 本质要求:定义了<integral type> operator[](<integral type>) const;
<integral type> size() const; 两个函数.
* 其中<integral type>可以是整型或者Eigen::Index可接受的类型(如std::ptrdiff_t).
* 常见形式如下:
* int:最常见的形式,取1行/1列.
* Eigen::placeholders::all: 取该矩阵所有行/所有列.
* ArithmeticSequence: 由Eigen::seq, Eigen::seqN或Eigen::lastN生成的序列.
* Eigen数据结构:Eigen::Vector, Eigen::Matrix(仅限1维), Eigen::Array(仅限1维).
* 标准库/C风格数据结构: std::Array, std::vector, int[N].
*/
// 3.1. seq与seqN的定义.
seq(firstIdx, lastIdx); // seq(2, 6) = {2, 3, 4, 5, 6}
seq(firstIdx, lastIdx, incr); // seq(2, 6, 2) = {2, 4, 6}
seqN(firstIdx, size); // seqN(2, 4) = {2, 3, 4, 5}
seqN(firstIdx, size, incr); // seqN(2, 4, 2) = {2, 4, 6, 8}
// 3.2. 取末位:lastN函数
A(lastN(p), lastN(q)); // == A.bottomRightCorner(p, q);
A(all, lastN(n,3)); // 取所有行,从最后一列开始,每隔2列取1列.
// 3.3. 编译期尺寸计算: 可以结合Eigen::fix()实现,在seq, seqN, lastN函数中.
v(seq(last-fix<7>, last-fix<2>);
v(seqN(last-7, fix<6>));
A(Eigen::placeholders::all, seq(fix<0>,last,fix<2>)); // A的所有偶数列(第一列为0列)
// 3.4. 倒序: 使用.reverse()或者seq/seqN.
A(seqN(last, n, fix<-1>), all);
A(lastN(n).reverse(), all);
// 3.5. 自定义下标列表
// 只需支持以下函数
/* Index s = ind.size(); or Index s = size(ind);
* Index i;
* i = ind[i];
*/
// 示例程序:pad
using Eigen::Index;
struct pad {
Index size() const { return out_size; }
Index operator[](Index i) const { return std::max<Index>(0, i - (out_size - in_size)); }
Index in_size, out_size;
};
Matrix3i A;
A.reshaped() = VectorXi::LinSpaced(9, 1, 9);
// reshaped还没讲,此时A为{{1, 4, 7} ,{2, 5, 8}, {3, 6, 9}}.
MatrixXi B(5, 5);
B = A(pad{3, 5}, pad{3, 5});
/* B =
* 1 1 1 4 7
* 1 1 1 4 7
* 1 1 1 4 7
* 2 2 2 5 8
* 3 3 3 6 9
*/

高级操作#

高级初始化#

// 1. 预定义的高级Matrix/Array.
// 三种初始化方式.
Matrix3d::Zero(); // 0参数版本,适用于固定大小Matrix/Array.
ArrayXd::Zero(2); // 1参数版本,适用于一维动态Matrix(Vector)/一维动态Array.
ArrayXXd::Zero(row, col); // 2参数版本
// 若未专门说明,三种方式均可以用于以下方法.
// 方法列表.
ArrayXXd::Zero(row, col); // 全0矩阵
ArrayXXd::Constant(row, col, val); // 初值矩阵
ArrayXXd::Random(row, col); // [0,1)随机初值
ArrayXXd::Identity(row, col);// 单位矩阵
VectorXd::Linspaced(size, low, high); // 线性排布,仅支持一维结构(size==1则返回high)
// 原位变换版本.
.setZero();
.setIdentity();
.setLinspaced(size, low, high);
// .finished()方法:基于"<<"运算符构建运算对象.
mat = (MatrixXf(2, 2) << 0, 1, 1, 0).finished() * mat;
// 2. 预定义的别名.
Vector3d::UnitX();
Vector3d::UnitY();
Vector3d::UnitZ();

部分降阶&广播(Partial Reduction & Broadcasting)#

// 1. 部分降阶.
// 1.1. 按行/列降阶.
.colwise() // 按列降阶处理
.rowwise() // 按行降阶处理
// 1.2. 常见用法
Eigen::Matrix3d m;
m.colwise().maxCoeff(); // 返回1*3向量,表示每列最大元素.
mat.colwise().sum().maxCoeff(&maxIndex); // 返回最大和列的序号
// 2. 广播.
// 第二操作数必须是Vector(对Matrix)或一维Array(对Array).
Eigen::MatriXd m(2, 4);
Eigen::VectorXd v(2);
mat.colwise() += v; // 将v加到m的每一列.
(m.colwise() - v).colwise().squaredNorm().minCoeff(&index); // 综合运用.

变形(Reshape)#

// Ex. 行优先与列优先
// Eigen 3.4: 添加reshaped(NRowsType,NColsType)函数,允许在原矩阵上创造视图.
// 提示:使用eval()以避免数据竞争.
Matrix4i m;
m.reshaped(2, 8); // 按列取数,按列填充,(0,0)->(0,0),(1,0)->(1,0),(2,0)->(0,1), ...
m.reshaped(); // 默认形成单列向量
m.reshaped<RowMajor>(); // 按行取数,按列填充.
m.reshaped<AutoOrder>();// 根据默认行/列优先设置进行填充.
// 如果希望in-place改变存储的数据,使用resize().
Matrix4i m;
m.resize(2, 8); // 根据默认行/列优先设置进行填充.

STL迭代器与算法#

// 1D Vector/1D Array定义了随机访问迭代器.
// 节选支持的功能:
// Ranged-for
VectorXi v;
for (auto x : v) cout << x << " ";
// std::sort
std::sort(v.begin(), v.end());
// 多维数组可以使用reshaped()创建一维视图.
Matrix2i A;
for (auto x : A.reshaped()) cout << x << " ";
// 使用rowwise()/colwise()配合使用.
for (auto row : A.rowwise())
std::sort(row.begin(), row.end());

与C++数组的接口#

/* 1. Map: 在C/C++数据结构上创建的Eigen视图
* MapOptions: 内存对齐Aligned/Unaligned(默认).
* StrideType<OuterStrideAtCompileTime_ ,
* InnerStrideAtCompileTime_>
* 其中Inner表示相邻元素间的指针增量,Outer表示相邻行/列间的指针增量.
* 两者均可指定为Dynamic以在运行时赋值.
*/
// 1.1. Map的基础定义
Map<Matrix<...> >
Map<Matrix<...>, int MapOptions, typename StrideType>
Map<const Matrix<...> > // 只读Map
// 1.2. 基础用法
Map<MatrixXf> mf() = delete; // 禁止默认初始化
Map<MatrixXf> mf(pf, rows, cols); // pf:首地址float*类型指针
Map<const Vector4f> mi(pf);
// 1.3. Stride的使用示例.
// Stride<Dynamic, 2>(8, 2)表示Outer值为8,Inner值为2.
// 此处采用列优先存储,故同一列相邻行间的元素距离为Inner,
// 同一行相邻列之间的元素距离为Outer.
int array[24];
for (int i = 0; i < 24; ++i) array[i] = i;
cout << Map<MatrixXi, 0, Stride<Dynamic, 2> >(array, 3, 3, Stride<Dynamic, 2>(8, 2)) << endl;
/* res =
* 0 8 16
* 2 10 18
* 4 12 20
*/
// 1.4. 修改Map对象指向的内存地址.
// 使用C++ Placement new语法.
int data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
Map<RowVectorXi> v(NULL);
new (&v) Map<RowVectorXi>(data + 4, 5); // 指向data+4位置长度为5的行向量.

以Eigen类型为参数的函数#

  • 为了使”懒求值”可以继续,需要以特殊方式书写函数.
  • 相对而言比较复杂,在性能不敏感场景下建议使用C++风格数组传值.
// 1. 使用模板函数.
// 1.1. Eigen的所有运算对象都是少数基模板类的派生.
Eigen::MatrixBase // 所有稠密矩阵运算对象的基类,用于仅矩阵函数.
Eigen::ArrayBase // 所有稠密数组运算对象的基类,用于仅数组函数.
Eigen::DenseBase // 上述两者的共同基类,用于适用于两者的函数.
Eigen::EigenBase // 进一步包含特殊矩阵,稀疏矩阵等所有运算对象.
// 1.2. 示例函数
// 输出给定矩阵的逆条件数.
template <typename Derived>
void print_inv_cond(const MatrixBase<Derived>& a)
{
const typename JacobiSVD<typename Derived::PlainObject>::SingularValuesType&
sing_vals = a.jacobiSvd().singularValues();
std::cout << "inv cond: " << sing_vals(sing_vals.size()-1) / sing_vals(0) << std::endl;
}
// 以下函数获取Array对象的最大元素.
template <typename Derived>
void print_max_coeff(const ArrayBase<Derived> &a)
{
std::cout << "max: " << a.maxCoeff() << std::endl;
}
// 以下函数获取稠密运算对象的矩阵块.
template <typename Derived>
void print_block(const DenseBase<Derived>& b, int x, int y, int r, int c)
{
std::cout << "block: " << b.block(x,y,r,c) << std::endl;
}
// 以下函数可以获取对象b的行列与总元素数量信息.
template <typename Derived>
void print_size(const Eigen::EigenBase<Derived>& b) {
std::cout << "size (rows, cols): " << b.size() << " (" << b.rows() << ", " << b.cols() << ")" << std::endl;
}
// 多输入参数情况,需要给定不同的DeriveType.
template <typename DerivedA,typename DerivedB>
typename DerivedA::Scalar squaredist(const MatrixBase<DerivedA>& p1,const MatrixBase<DerivedB>& p2)
{
return (p1-p2).squaredNorm();
}
// 2. 基于Eigen::ref非模板函数.
// 以下函数可以接受MatrixXf或其子块为实参.
float inv_cond(const Eigen::Ref<const Eigen::MatrixXf>& a) {
const Eigen::VectorXf sing_vals = a.jacobiSvd().singularValues();
return sing_vals(sing_vals.size() - 1) / sing_vals(0);
}
// 3. 在函数中resize数据对象
// 参考https://libeigen.gitlab.io/eigen/docs-nightly/TopicFunctionTakingEigenTypes.html,使用const_cast去掉const引用,过于黑魔法了,最好别用.

空间坐标系变换#

Eigen通过Transform类提供两种变换.

  • 抽象变换(Abstract Transformations),包括旋转,translations,缩放变换.
  • 投影仿射(Projective&Affine).

其中仿射变换暂时用不到,只介绍抽象变换中的旋转变换.

// 1. 常用变换.
// 1.1. 抽象旋转定义.
Rotation2D<float> rot2(angle_in_radian); // 2D旋转
AngleAxis<float> aa(angle_in_radian, Vector3f(ax,ay,az)); // 3D旋转,转轴必须是单位向量.
Quaternion<float> q(0.1, 0.2, 0.3, 0.4); // 四元数.
// 1.2. 常用别名.
using Rotation2Df = Rotation2D<float>;
using Rotation2Dd = Rotation2D<double>;
using AngleAxisf = AngleAxis<float>;
using AngleAxisd = AngleAxis<double>;
using Quaternionf = Quaternion<float>;
using Quaterniond = Quaternion<double>;
// 1.3. 转换关系.
// 只要维度,内部存储类型(即scalar类型)对应,各种类型间可以任意互转.
Rotation2Dd angle_axis_2;
AngleAxisd angle_axis_3(angle, Vector3D::UnitZ());
Quaterniond q4;
Matrix2d m2;
Matrix3d m3;
m2 = angle_axis_2; angle_axis_2 = m2;
m3 = angle_axis_3; angle_axis_3 = m3;
m3 = q4; q4 = m3;
angle_axis_3 = q4; q4 = angle_axis_3 = q4;
// 3. 变换方法.
vector3d v3, res;
AngleAxisd a1, a2, a3; // 假设两者已经有合适定义.
a3 = a1 * a2; // 连续变换,先a2后a1.
res = a3 * v3; // 进行变换.
a2 = a1.inverse(); // 反变换.
// slerp:球面线性插值
// 找到rot1与rot2中,由[0,1]间的插值因子t定义的中间变换.
// rot1与rot2需要为Rotation2D或四元数类.
rot3 = rot1.slerp(t,rot2);
// 4. 转欧拉角.
// 参数:使用3维变换的成员函数,例如(2,1,0)表示321转换.
// 返回值:三次旋转角度的弧度制表示,第一次旋转对应res[0].
// 网上部分帖子中提到,有时转换会出现角度>PI的现象.
Matrix3d m3;
AngleAxisd a;
Eigen::Vector3d res = m3.eulerAngles(2,1,0); // 对旋转矩阵使用
Eigen::Vector3d res = a.eulerAngles(2,1,0);
飞行器动力学-P2_Eigen库入门
https://www.lithium-hydroxide.space/posts/250714_eigen_tutorial/
作者
LiH
发布于
2025-07-15
许可协议
CC BY-NC-SA 4.0