参考
Eigen3 主页,Eigen3 官网教程
矩阵的本质,通过多种矩阵的应用去感受矩阵本质
3Blue1Brown 的线性代数,用可视化方法来表现线性代数的特性,强推
如何理解复数和虚数,有动画方便理解复数的意义
相关文章
Eigen3 基础数据类型和操作
矩阵的核心是向量和线性代数,而线性代数的核心是向量加法和标量乘法。
Vector 向量类
向量类是列或行只有 1 的特殊矩阵,Vector 类是列向量,RowVector 类是行向量。
预定义好的向量模板
和矩阵类似,向量也有预定义好的模板:
VectorNt 格式:N 大小的列向量,例如 Vector2f
是 Matrix<float, 2, 1>
;
RowVectorNt 格式:N 大小的行向量,例如 RowVector3d
是 Matrix<double, 1, 3>
;
维数大小 N ∈ {2,3,4,X}。
数据类型 t ∈ {i,f,d,cf,cd}。分别是 int ,float,double,complex<float>(复数),complex<double>(复数)。
向量初始化和系数读写
void TestVector()
{
setlocale(LC_ALL, "zh_CN.UTF-8"); // 支持 ros 打印中文
Eigen::Vector3i v(3, 6, 9);
ROS_INFO_STREAM("列向量3i v 构造初始化指定了系数和行数:" << std::endl << v);
//! 固定向量类不支持 << 赋值,会导致段错误,但是动态向量类支持。
Eigen::RowVectorXf u(5);
u << 1.2, 2.3, 3.4, 4.5, 5.6;
ROS_INFO_STREAM("动态行向量 u 默认初始化:" << std::endl << u);
Eigen::VectorXd w(5);
w.Random(5);
ROS_INFO_STREAM("列向量 w 随机赋值:" << std::endl << w);
ROS_INFO_STREAM("列向量[]访问 w[2]:" << std::endl << w[2]);
w[2] = 12.34;
ROS_INFO_STREAM("列向量[]修改 w[2]:" << std::endl << w[2]);
}
向量类的默认初始化并不会默认把系数设置为 0,支持 [ ] 访问修改系数。固定的向量类不支持 << 赋值,但是动态向量类支持。
向量运算
-
向量加减运算和标量乘法运算
向量加法:
几何上看向量加法:u+v 表示把向量 v 起点移动到向量 u 终点,再从 u 起点指向 v 终点的向量 w。
e.g. 向量 u [2, 1],v [1, 3],u + v = w [3, 4]。
向量减法:
几何上看向量的标量乘法:u*k 表示把向量 u 的模缩放 k 倍,k < 0 表示向量方向取反。
e.g. 向量 u [2, 2],v = 1.8 * u,w = -0.8 * u。
void TestVectorOperate()
{
Eigen::Vector3i u(3, 6, 9);
Eigen::Vector3i v(2, 3, 3);
ROS_INFO_STREAM("列向量3i u:" << std::endl << u);
ROS_INFO_STREAM("列向量3i v:" << std::endl << v);
ROS_INFO_STREAM("u+v=" << std::endl << u+v);
Eigen::Vector3f fu(2.5, 0, -1.5);
Eigen::Vector3f fv(5.1, 3.3, 1.3);
ROS_INFO_STREAM("列向量3f fu:" << std::endl << u);
ROS_INFO_STREAM("列向量3f fv:" << std::endl << v);
ROS_INFO_STREAM("fu-fv=" << std::endl << fu-fv);
Eigen::Vector3d du(0.7, -1.2, -3.5);
ROS_INFO_STREAM("列向量3d du:" << std::endl << du);
ROS_INFO_STREAM("列向量3d -3.3 * du=" << std::endl << -3.3*du);
}
-
向量点乘(内积)运算
向量点乘的几何意义:v 在 u 方向上投影与 u 的模的乘积,反映了两个向量的相似度,结果越大越相似。
u ⋅ v > 0,两向量方向基本同向,夹角在 0°~90° 之间;
u ⋅ v = 0,向量正交,互相垂直;
u ⋅ v < 0,两向量方向相反,夹角在 90°~180° 之间;
向量点乘(内积)有下面应用:
-
寻找一个向量投影到另一个向量的样子;
-
测量两个方向的接近程度;
-
确定另一个方向是向前还是向后;
void TestVectorDot()
{
Eigen::Vector3f u0(0.0, 0.3, 0.5);
Eigen::Vector3f v0(0.5, 0.0, 0.3);
ROS_INFO_STREAM("u0:" << std::endl << u0);
ROS_INFO_STREAM("v0:" << std::endl << v0);
ROS_INFO_STREAM("u0 · v0 = " << u0.dot(v0));
ROS_INFO_STREAM("v0 · u0 = " << v0.dot(u0));
Eigen::Vector3f u1(1.2, 2.3, 3.4);
Eigen::Vector3f v1(5.4, 6.5, 7.6);
ROS_INFO_STREAM("u1:" << std::endl << u1);
ROS_INFO_STREAM("v1:" << std::endl << v1);
ROS_INFO_STREAM("u1 · v1 = " << u1.dot(v1));
}
-
向量叉乘(外积)运算
几何意义:外积得到的向量由“右手法则”确定,模长为向量 u 和 v 构成的四边形面积。
向量叉乘(外积)有下面应用:
-
判断左右;
-
判断内外;
void TestVectorCross()
{
Eigen::Vector3f u0(0.0, 0.3, 0.5);
Eigen::Vector3f v0(0.5, 0.0, 0.3);
ROS_INFO_STREAM("u0:" << std::endl << u0);
ROS_INFO_STREAM("v0:" << std::endl << v0);
ROS_INFO_STREAM("u0 x v0 = " << u0.cross(v0));
ROS_INFO_STREAM("v0 x u1 = " << v0.cross(u0));
Eigen::Vector3f u1(1.2, 2.3, 3.4);
Eigen::Vector3f v1(5.4, 6.5, 7.6);
ROS_INFO_STREAM("u1:" << std::endl << u1);
ROS_INFO_STREAM("v1:" << std::endl << v1);
ROS_INFO_STREAM("u1 x v1 = " << u1.cross(v1));
}
Array 数组类
数组类模板 Array,与 Matrix 类有一样的模板参数。
预定义好的模板
ArrayNt:N 大小的数组,例如 ArrayXf
是 Array<float, Dynamic, 1>
;
ArrayNNd:N*N大小的数组,例如 Array33d
是 Array<double, 3, 3>
;
数组初始化和系数读写
数组类的 () 运算符被重载作读写,[ ] 只能读写一维数组,运算符<< 运算符也可以给数组赋值。
void TestArray()
{
Eigen::ArrayXf a0(3);
a0.Random(3);
ROS_INFO_STREAM("a0:" << a0);
Eigen::Array33d a1;
a1(2, 2) = 23.33;
ROS_INFO_STREAM("a1(2, 2):" << a1(2, 2));
a1 << 1.2, 2.3, 3.4,
4.5, 5.6, 6.7,
7.8, 8.9, 9.1;
ROS_INFO_STREAM("a1:" << std::endl << a1);
Eigen::Array4i a2;
a2[2] = 123;
ROS_INFO_STREAM("a2[2]=" << a2[2]);
}
数组运算
-
加减法
如果两个数组大小相同,那么加减法运算是对应数组系数一一加减。
同时还支持数组加减一个标量,这样会让整个数组系数全部加减标量值。
-
乘法
如果两个数组大小相同,数组的乘法运算是对应数组系数一一相乘。
数组类乘以标量是将数组的系数全部乘以标量值。
除法类似。
-
其他系数运算
abs() 运算可以得到系数绝对值的数组,sqrt() 运算可以得到系数平方根,min() 能够得到两个大小相同数组中,相同位置最小系数组成的数组。
void TestArrayOperate()
{
Eigen::Array22f a0;
a0 << 1.2, -2.3,
-3.4, 4.5;
Eigen::Array22f a1;
a1 << -6.5, 7.6,
8.7, -9.8;
ROS_INFO_STREAM("a0= " << std::endl << a0);
ROS_INFO_STREAM("a1= " << std::endl << a1);
ROS_INFO_STREAM("a0 + a1 = " << std::endl << a0+a1);
ROS_INFO_STREAM("a1 + 5.5 = " << std::endl << a1+5.5);
ROS_INFO_STREAM("a0 * a1 = " << std::endl << a0*a1);
ROS_INFO_STREAM("a0 * 2.33 = " << std::endl << a0*2.33);
ROS_INFO_STREAM("a0 / a1 = " << std::endl << a0/a1);
ROS_INFO_STREAM("a0.abs() = " << std::endl << a0.abs());
ROS_INFO_STREAM("a1.sqrt() = " << std::endl << a1.sqrt());
ROS_INFO_STREAM("a0.min(a1) = " << std::endl << a0.min(a1));
}
数组类和矩阵类的转换
Matrix.array()
可以转换为数组类,Array.matrix()
可以转换为矩阵类。这两个方法都能做左值和右值!
大小相同的 Matrix 和 Array 可以相互赋值。
void TestArrayTransform()
{
Eigen::Array22f a;
a << 1.2, -2.3,
-3.4, 4.5;
Eigen::Matrix2f t;
t << 0, -1,
1, 0;
Eigen::Matrix2f m;
m = t*a.matrix();
ROS_INFO_STREAM("数组 a = " << std::endl << a);
ROS_INFO_STREAM("旋转矩阵 t = " << std::endl << t);
ROS_INFO_STREAM("旋转后的矩阵 m = t * a.matrix() = " << std::endl << m);
ROS_INFO_STREAM("m.array() 和 a 中最小系数组成的数组 = " << std::endl << m.array().min(a));
}
数组 a 对应的向量组是 u 和 w,旋转后的矩阵 m 对应的向量组是 v 和 s。可以结合上面矩阵乘法运算的几何意义想象下旋转过程。