Eigen combine rotation and translation into one matrix
Asked Answered
O

3

11

I have a rotation matrix rot (Eigen::Matrix3d) and a translation vector transl (Eigen::Vector3d) and I want them both together in a 4x4 transformation matrix. I just for the life of me can't figure out how to do this in Eigen. I think Affine can be used somehow but I don't understand how it works.

Essentially I want a combination of How translation a matrix(4x4) in Eigen? and Multiplying Transform and Matrix types in Eigen

My code (that doesn't compile as I don't understand how Affine works) looks like this:

Eigen::Affine3d r(rot);
Eigen::Affine3d t(transl);
Eigen::Matrix4d m = t.matrix();
m *= r.matrix();
Ovipositor answered 26/8, 2014 at 11:9 Comment(4)
As the answer you linked all used m = m * t.matrix(), maybe the problem is the lack of operator *=. Does it work that way?Circumspect
I don't know Eigen but a rotation matrix is in general a 3x3 matrix which you can put directly in your 4x4 matrix (assuming you don't have any scaling going on). In a 4x4 transformation matrix the elements _00, _01, _02, _10, _11, _12, _20, _21 and _22 form the rotation (time scale if you have scaling). The elements _30, _31 and _32 are the translation vector elements.Peasecod
@Circumspect No that didn't work either, I think the problem is that I somehow need to indicate where I want the different parts "inserted" into the main matrix. But I simply don't know.Ovipositor
@rashmatash, there are two type of 4x4 matrices: column-ordered, like those used in OpenGL, and row-ordered, like those often used in Math or Physics. So, the coefficients of the translation vector can be (3,0)(3,1)(3,2), or (0,3)(1,3)(2,3), depending on the definition. See for example: bitbucket.org/Coin3D/coin/src/…Juster
F
23

Another method is to do the following:

Eigen::Matrix3d R;
// Find your Rotation Matrix
Eigen::Vector3d T;
// Find your translation Vector
Eigen::Matrix4d Trans; // Your Transformation Matrix
Trans.setIdentity();   // Set to Identity to make bottom row of Matrix 0,0,0,1
Trans.block<3,3>(0,0) = R;
Trans.block<3,1>(0,3) = T;

This method literally copies the Rotation matrix into the first 3 rows and columns and the translation vector to the 4th column. Then sets the bottom right matrix entry to 1. You final matrix will look like:

R R R T
R R R T
R R R T
0 0 0 1

where R are the corresponding values from the rotation matrix and T the values from the Translation vector.

Forsta answered 8/4, 2015 at 4:42 Comment(4)
Good simple solution. I think one could even use Trans.setIdentity(); instead of Trans.setZero(4,4); and be able to skip the last Trans(3,3) = 1; line.Ovipositor
Exactly what I needed, however I used Trans.block<3,1>(0,3) = T; instead of Trans.rightCols<1>() = T; to avoid a YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES errorWilhelminawilhelmine
Thanks, I guess they've added more strict type checking since I originally posted this answer.Forsta
you could also write the IMO more intuitive Trans.linear() = R; Trans.translation() = T;. Also, if you use Eigen::Transform<..., Eigen::AffineCompact> as your transformation type you don't need to initialize with setIdentity() because there's no lowest row. Nevertheless, you might want to keep it because a) compilers are smart (i.e. no runtime cost) and b) you might want to change the type at some point, long after forgetting about this "optimization".Castaway
C
13

You didn't post the compilation errors, nor what are rot and transl. Below is a working sample showing, how you can create a 4x4 transformation matrix.

#include <Eigen/Geometry>

Eigen::Affine3d create_rotation_matrix(double ax, double ay, double az) {
  Eigen::Affine3d rx =
      Eigen::Affine3d(Eigen::AngleAxisd(ax, Eigen::Vector3d(1, 0, 0)));
  Eigen::Affine3d ry =
      Eigen::Affine3d(Eigen::AngleAxisd(ay, Eigen::Vector3d(0, 1, 0)));
  Eigen::Affine3d rz =
      Eigen::Affine3d(Eigen::AngleAxisd(az, Eigen::Vector3d(0, 0, 1)));
  return rz * ry * rx;
}

int main() {
  Eigen::Affine3d r = create_rotation_matrix(1.0, 1.0, 1.0);
  Eigen::Affine3d t(Eigen::Translation3d(Eigen::Vector3d(1,1,2)));

  Eigen::Matrix4d m = (t * r).matrix(); // Option 1

  Eigen::Matrix4d m = t.matrix(); // Option 2
  m *= r.matrix();
  return 0;
}
Contradistinction answered 26/8, 2014 at 11:35 Comment(2)
Thanks, just what I was looking for! Only problem I had with it is that if I input my translation vector directly into Eigen::Translation3d() it doesn't work (t.matrix() doesn't compile), so I have to write the more convoluted Eigen::Translation3d(Eigen::Vector3d(transl(0), transl(1), transl(2))). I guess this is due to the dark voodo that is Eigen templates.Ovipositor
@BlazBratanic What is the Eigen::Transform class for then?Breakthrough
P
6

Another way is to use the Eigen::Transform.

Let's take a example such as to implemente this affine transform ,

#include <Eigen/Dense>
#include <Eigen/Geometry>
using namespace Eigen;

Matrix4f create_affine_matrix(float a, float b, float c, Vector3f trans)
{
    Transform<float, 3, Eigen::Affine> t;
    t = Translation<float, 3>(trans);
    t.rotate(AngleAxis<float>(a, Vector3f::UnitX()));
    t.rotate(AngleAxis<float>(b, Vector3f::UnitY()));
    t.rotate(AngleAxis<float>(c, Vector3f::UnitZ()));
    return t.matrix();
}

You can also implemented as the following

Matrix4f create_affine_matrix(float a, float b, float c, Vector3f trans)
{
    Transform<float, 3, Eigen::Affine> t;
    t = AngleAxis<float>(c, Vector3f::UnitZ());
    t.prerotate(AngleAxis<float>(b, Vector3f::UnitY()));
    t.prerotate(AngleAxis<float>(a, Vector3f::UnitX()));
    t.pretranslate(trans);
    return t.matrix();
}

The difference between the first implementation and the second is like the difference between Fix Angle and Euler Angle, you can refer to this video.

Padlock answered 31/8, 2017 at 9:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.