Eigen常见用法总结

总结工程实践常用的Eigen库应用,持续更新…

1. Combining multiple transformations in Eigen into one transformation matrix

Q: I have several transformations in Eigen, in the form of translations (Eigen::Vector3f) and rotations (Eigen::Quaternionf). I would like to combine all these transformations, in an order of my choosing, into a 4x4 transformation matrix Eigen::Matrix4f.

For example, I would like to apply the following transformations in the order A, B, C, D, E:

1
2
3
4
5
Eigen::Vector3f translation_A;
Eigen::Quaternionf rotation_B;
Eigen::Quaternionf rotation_C;
Eigen::Quaternionf rotation_D;
Eigen::Vector3f translation_E;

A:

1
2
3
4
5
6
7
8
9
10
11
Eigen::Vector3f trans_vec_A;
//note that you have to create a Translation because multiplying a
//Transform with a vector will _apply_ the transform to the vector
Eigen::Translation<float,3> translation_A(trans_vec_A);
Eigen::Quaternionf rotation_B;
Eigen::Quaternionf rotation_C;
Eigen::Quaternionf rotation_D;
Eigen::Vector3f trans_vec_E;
Eigen::Translation<float,3> translation_E(trans_vec_E);
Eigen::Transform<float,3,Affine> combined =
translation_A * rotation_B * rotation_C * rotation_D * translation_E;

Note that

1
combined = A*B*C*D*E

so combined applied to a vector v is

1
combined*v = A*B*C*D*E*v = A*(B*(C*(D*(E*v))))

that is, E is applied first, then D, and so on. In my mind, this is the correct order, but that may be a matter of taste.

then apply pcl::transformPointCloud(*src_pointcloud, *dst_pointcloud, combined);

2. Cast Eigen::MatrixXd to Eigen::MatrixXf

*Q: *I am using Eigen on a C++ program.

I wonder if there is a way to cast from Eigen::MatrixXd to Eigen::MatrixXf.
static_cast <Eigen::MatrixXf> doesn’t seem to work and neither A.cast<MatrixXf> (this is the cast method from Eigen).

Any solution for this type of cast?

*A: *Try this:

1
2
Eigen::MatrixXd d;                       // Matrix of doubles.
Eigen::MatrixXf f = d.cast <float> (); // Matrix of floats.

3. template function casting

*Q: *I’m having trouble multiplying two matrices using the Eigen library. I have the following function. Here is a small example of what I would like to do :

Program name: testMatOp.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
#include <Eigen/Dense>
using namespace std;

template <typename DerivedA, typename DerivedB>
void multiply(const Eigen::MatrixBase<DerivedA> &A,
const Eigen::ArrayBase<DerivedB> &B){
Eigen::MatrixXf C(2,4);
C.array() = A.array().rowwise() * B.cast<float>();
}

int main()
{
Eigen::MatrixXf A(2,4);
Eigen::MatrixXf C(2,4);
//igen::VectorXf v(4);
Eigen::Array<int,1,Eigen::Dynamic>B;
B.resize(4);

A << 1, 2, 6, 9,
3, 1, 7, 2;

B << 0,
1,
0,
0;

multiply(A,B);
}

I would like to pass Matrix A and Vector B to multiply. I understand that Eigen does not do automatic promotion and that B needs to be casted as a float vector in order for the multiplication to happen. When I compile, I get the following compilation error among others

1
2
3
4
5
6
7
8
9
testMatOp.cpp:34:44: error: expected primary-expression before 'float'
testMatOp.cpp:34:44: error: expected ';' before 'float'
testMatOp.cpp: In instantiation of 'void multiply(const Eigen::MatrixBase<Derived>&, const Eigen::ArrayBase<DerivedB>&) [with DerivedA = Eigen::Matrix<float, -1, -1>; DerivedB = Eigen::Array<int, 1, -1>]':
testMatOp.cpp:54:15: required from here
testMatOp.cpp:34:3: error: no match for 'operator*' in '((const Eigen::DenseBase<Eigen::ArrayWrapper<const Eigen::Matrix<float, -1, -1> > >*)(&(& A)->Eigen::MatrixBase<Derived>::array<Eigen::Matrix<float, -1, -1> >()))->Eigen::DenseBase<Derived>::rowwise<Eigen::ArrayWrapper<const Eigen::Matrix<float, -1, -1> > >() * B.Eigen::ArrayBase<Derived>::cast<NewType>'
testMatOp.cpp:34:3: note: candidates are:
In file included from ../3rdparty/Eigen/Core:336:0,
from ../3rdparty/Eigen/Dense:1,
from testMatOp.cpp:26:

What might I be doing wrong. I did take a look at this post : Cast Eigen::MatrixXd to Eigen::MatrixXfwhich correctly describes how to cast, but I’m unable to make it work for this example.

*A: *B is a dependent name. To access its template member cast, you have to write

1
B.template cast

C++ is context-sensitive. When encountering a <, it tries to figure out if it is operator< or angle bracket.

1
2
3
4
5
// std::vector is a template, so < is an angle bracket
std::vector < float >

// 3 is not a template, so < is operator<
3 < 5

However, the type of B is const Eigen::ArrayBase<DerivedB>&, which depends on the template parameter DerivedB. C++ cannot decide if B.cast is a template. When this happens, C++ always guess that it is not a template and interprets the following < as operator<.

Why C++ is so stupid that it cannot recognize previously declared template ArrayBase::cast? Well, someone may specialize ArrayBase<int>.

1
2
3
4
5
6
template<>
class ArrayBase<int>
{
public:
int cast = 3;
};

Therefore one cannot deduce that B.cast is a template.

4. Using an aligned allocator

STL containers take an optional template parameter, the allocator type. When using STL containers on fixed-size vectorizable Eigen types, you need tell the container to use an allocator that will always allocate memory at 16-byte-aligned locations. Fortunately, Eigen does provide such an allocator: Eigen::aligned_allocator.

For example, instead of

1
std::map<int, Eigen::Vector4f>

you need to use

1
std::map<int, Eigen::Vector4f, std::less<int>, Eigen::aligned_allocator > >

Note that the third parameter “std::less“ is just the default value, but we have to include it because we want to specify the fourth parameter, which is the allocator type.