4
votes

I am trying to find the Euler angles that allow the transformation from point A to point B in 3D space.

Consider the normalized vectors A = [1, 0, 0] and B = [0.32 0.88 -0.34].

I understand that by computing the cross product A × B I get the rotation axis. The angle between A and B is given by tan⁻¹(||cross||, A·B), where A·B is the dot product between A and B.

This gives me the rotation vector rotvec = [0 0.36 0.93 1.24359531111], which is rotvec = [A × B; angle] (the cross product is normalized).

Now my question is: How do I move from here to get the Euler angles that correspond to the transformation from A to B?

In MATLAB the function vrrotvec2mat receives as input a rotation vector and outputs a rotation matrix. Then the function rotm2eul should return the corresponding Euler angles. I get the following result (in radians): [0.2456 0.3490 1.2216], according to the XYZ convention. Yet, this is not the expected result.

The correct answer is [0 0.3490 1.2216] that corresponds to a rotation of 20° and 70° in Y and Z, respectively.

When I use eul2rot([0 0.3490 1.2216]) (with eul2rot taken from here) to verify the resulting rotation matrix, this one is different from the one I obtain when using vrrotvec2mat(rotvec).

I also have a Python spinet that yields the exactly same results as described above.

--- Python (2.7) using transform3d ---

import numpy as np
import transforms3d

cross = np.cross(A, B)
dot = np.dot(A, B.transpose())
angle = math.atan2(np.linalg.norm(cross), dot)
rotation_axes = sklearn.preprocessing.normalize(cross)
rotation_m = transforms3d.axangles.axangle2mat(rotation_axes[0], angle, True)
rotation_angles = transforms3d.euler.mat2euler(rotation_m, 'sxyz')

What I am missing here? What should I be doing instead?

Thank you

2
Euler angles between 2 points are not unique. This is why quaterions existAnder Biguri
@AnderBiguri I don't really understand how quaternions are relevant to the question. Or are you proposing this is an XY Problem?jodag
Do I get a unique solution if I use a quaternion instead? In the end I still need to convert it to euler angles.jcampos
No. A unit-quaternion is an alternative representation of a rotation in R3. It's more compact, numerically stable, and faster to apply in a computation than a rotation matrix but doesn't have any more representational power. It's less compact than Euler angles but doesn't suffer from the "gimbal lock" problem. The fact remains there are multiple rotations which satisfy your problem so you would need to come up with a way to impose further constraints to get the solution you desire.jodag

2 Answers

4
votes

A rotation matrix has 3 degrees of freedom but the constraints of your problem only constrain 2 of those degrees.

This can be made more concrete by considering the case where we have a rotation matrix R which rotates from A to B so R*A == B. If we then construct another rotation matrix RB which rotates about vector B then applying this rotation to R*A won't have any effect, i.e. B == R*A == RB*R*A. It will, however, produce a different rotation matrix RB*R with different Euler angles.

Here's an example in MATLAB:

A = [1; 0; 0];
B = [0.32; 0.88; -0.34];

A = A / norm(A);
B = B / norm(B);

ax = cross(A, B);
ang = atan2(norm(ax), dot(A, B)); % ang = acos(dot(A, B)) works too
R = axang2rotm([ax; ang].');

ang_arbitrary = rand()*2*pi;
RB = axang2rotm([B; ang_arbitrary].');

R*A - B
RB*R*A - B

rotm2eul(R)
rotm2eul(RB*R)

Result

ans =
   1.0e-15 *

   -0.0555
    0.1110
         0

ans =
   1.0e-15 *

    0.2220
    0.7772
   -0.2776

ans =    
    1.2220    0.3483    0.2452

ans =    
    1.2220    0.3483    0.7549
1
votes

I will give you a solution based on Euler's rotation theorem.

This solution gives you only the one angle, but the other angles can be derived.

import numpy as np


a_vec = np.array([1, 0, 0])/np.linalg.norm(np.array([1, 0, 0]))
b_vec = np.array([0.32, 0.88, -0.34])/np.linalg.norm(np.array([0.32, 0.88, -0.34]))

cross = np.cross(a_vec, b_vec)
ab_angle = np.arccos(np.dot(a_vec,b_vec))


vx = np.array([[0,-cross[2],cross[1]],[cross[2],0,-cross[0]],[-cross[1],cross[0],0]])
R = np.identity(3)*np.cos(ab_angle) + (1-np.cos(ab_angle))*np.outer(cross,cross) + np.sin(ab_angle)*vx


validation=np.matmul(R,a_vec)

This uses the common axis of rotation (eigenvector in this case), as the cross product.

The matrix R is then the rotation matrix.

This is a general way of doing it, and very simple.