C++/Eigen学习

子矩阵操作简介

子矩阵操作又称块操作,在矩阵运算中,子矩阵的提取和操作应用也十分广泛。因此Eigen中也提供了相关操作的方法。提取的子矩阵在操作过程中既可以用作左值也可以用作右值。

块操作的一般使用方法

在Eigen中最基本的快操作运算是用 .block() 完成的。提取的子矩阵同样分为动态大小和固定大小。

块操作 构建动态大小子矩阵
提取块大小为(p,q),起始于(i,j)   matrix.block(i,j,p,q)

同样需要注意的是在Eigen中,索引是从0开始。所有的操作方法都可以适用于Array.同样使用固定大小的操作方式在小型矩阵运算时更加的快,但要求在编译时就要知道矩阵的大小。下面是一个使用示例:

#include <iostream>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main()
{
MatrixXf m(4,4);
m<< 1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16;
cout<<"Block in the middle"<<endl;
cout<<m.block<2,2>(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;
}
}

执行结果如下:

执行结果如下:

Block in the middle
6 7
10 11

Block of size 1x1
1

Block of size 2x2
1 2
5 6

Block of size 3x3
1 2 3
5 6 7
9 10 11

上面的示例中,.block() 被应用为左值操作,即从中读取数据。事实上,它也可以被用作为右值操作,即也可往里面写入数据。下面是一个右值应用实例。


#include <iostream>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main()
{
Array22f m;
m<< 1,2,
3,4;
Array44f a = Array44f::Constant(0.6);
cout<<"Here is the 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 2x2 block:"<<endl<<a<<endl<<endl;
}

执行结果如下:

Here is the array a:
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6
0.6 0.6 0.6 0.6

Here is now a with m copied into its central 2x2 block:
0.6 0.6 0.6 0.6
0.6 1 2 0.6
0.6 3 4 0.6
0.6 0.6 0.6 0.6

Here is now a with bottom-right 2x3 block copied into top-left 2x2 block:
3 4 0.6 0.6
0.6 0.6 0.6 0.6
0.6 3 4 0.6
0.6 0.6 0.6 0.6

.block()方法是一种非常通用的块操作方法。除了这个通用的方法以外,Eigen中还为一些常用的特殊操作提供了特殊的函数。从运行速度的方面来看,你应该在编译阶段尽可能的提供更多的信息。比如,如果你需要操作的块是一个列,那么你可以使用.col()函数。这样Eigen可以得知这个信息以便进行更多的优化。这些特殊操作方法总结如下。

行子式和列子式

我们可以使用.col().row()方法来操作或者提取一个列或者行。

块操作   方法
第i行   matrix.row(i)
第j列   matrix.col(j)

下面是一个使用示例:

#include <iostream>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main()
{
MatrixXf m(3,3);
m<< 1,2,3,
4,5,6,
7,8,9;
cout<<"Here is the matrix 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 third column,the matrix m is:\n";
cout<<m<<endl;
}

执行结果如下:

Here is the matrix m:
1 2 3
4 5 6
7 8 9
2nd Row:4 5 6
After adding 3 times the first column into third column,the matrix m is:
1 2 6
4 5 18
7 8 30

Eigen提供了从边角开始提取子矩阵的方法,比如.topLeftCorner()表示从左上角开始提取子矩阵。这些操作总结如下:

块操作   动态矩阵版本   固定矩阵版本
左上角pxq   matrix.topLeftCorner(p,q); matrix.topLeftCorner<p,q>();
左下角pxq   matrix.bottomLeftCorner(p,q); matrix.bbottomLeftCorner<p,q>();
右上角pxq   matrix.topRightCorner(p,q); matrix.topRightCorner<p,q>();
右下角pxq   matrix.bottomRightCorner(p,q); matrix.bottomRightCorner<p,q>();
前p行   matrix.topRows(p); matrix.topRows<p>();
后p行   matrix.bottomRows(p); matrix.bottomRows<p>();
前q列   matrix.leftCols(q); matrix.leftCols<q>();
后q列   matrix.rightCols(q); matrix.rightCols<q>();

下面是一个使用示例:

#include <iostream>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main()
{
Matrix4f m;
m<< 1,2,3,4,
5,6,7,8,
9,10,11,12,
13,14,15,16;
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;
}

执行结果如下:

m.leftCols(2)=
1 2
5 6
9 10
13 14

m.bottomRows<2>()=
9 10 11 12
13 14 15 16

After assignment,m=
8 12 16 4
5 6 7 8
9 10 11 12
13 14 15 16

向量的子向量操作

Eigen中同样也为向量提供了一些子式的操作方法,总结如下:

块操作   固定向量版本   动态向量版本
前n个元素   vector.head(n);   vector.head();
后n个元素   vector.tail(n);   vector.tail();
从i开始取n个元素   vector.segment(i,n)   vector.segment(i);

再次说明一下,所有对矩阵的操作同样适用于Array,所有对列向量的操作同样适用于行向量。下面是一个使用示例:

#include <iostream>
#include "Eigen/Dense"

using namespace std;
using namespace Eigen;

int main()
{
ArrayXf v(6);
v<<1,2,3,4,5,6;
cout<<"v.head(3)="<<endl<<v.head(3)<<endl<<endl;
cout<<"v.tail<3>()="<<endl<<v.tail<3>()<<endl<<endl;
v.segment(1,4) *= 2;
cout<<"after 'v.segment(1,4) *= 2',v="<<endl<<v<<endl;
}

执行结果如下:

v.head(3)=
1
2
3

v.tail<3>()=
4
5
6

after 'v.segment(1,4) *= 2',v=
1
4
6
8
10
6

后记

1, 时隔两年回头看看自己的这个笔记, 感觉就写了分块矩阵的提取这么点内容。。。。 2023.8.5

参考

[1] Eigen子矩阵操作(block)

CMakeLists Eigen FCPX GNU Gazebo Git Interest KDL Life Linux Matrix ODE ROS Ros UML Ubuntu VcXsrv algorithm algorithms axis-angle bode calibration chrome control cpp data_struct dots figure gdb latex launch life linux mac math matlab memory motor moveit operator optimal algorithm python robot robotics ros ros2 rtb simulation stl thread tools twist urdf velocity vim web work wsl
知识共享许可协议