How do you find the 3 euler angles between 2 3D vectors?
When I have one Vector and I want to get its rotation, this link can be usually used: Calculate rotations to look at a 3D point?
But how do I do it when calculating them according to one another?
-
3Your question is not correct. You need three angles if you want to align two **reference frames**. If you need only to align two vectors then only two angles are needed. – 6502 Feb 27 '13 at 10:04
-
How do you calculate the two angles? And how come the BVH file format (http://davedub.co.uk/bvhacker/) has 3 angles for moving the bones? – tomyake May 03 '13 at 18:23
4 Answers
As others have already pointed out, your question should be revised. Let's call your vectors a
and b
. I assume that length(a)==length(b) > 0
otherwise I cannot answer the question.
Calculate the cross product of your vectors v = a x b
; v
gives the axis of rotation. By computing the dot product, you can get the cosine of the angle you should rotate with cos(angle)=dot(a,b)/(length(a)length(b))
, and with acos
you can uniquely determine the angle (@Archie thanks for pointing out my earlier mistake). At this point you have the axis angle representation of your rotation.
The remaining work is to convert this representation to the representation you are looking for: Euler angles. Conversion Axis-Angle to Euler is a way to do it, as you have found it. You have to handle the degenerate case when v = [ 0, 0, 0]
, that is, when the angle is either 0 or 180 degrees.
I personally don't like Euler angles, they screw up the stability of your app and they are not appropriate for interpolation, see also
-
Cross production is not enough - it won't distinguish 0 and 180 degrees angles. You should compute both: cross to get a sine and scalar to get a cosine, and then use both of them to compute the angle (for instance via atan2() function in C math library). – Archie Feb 27 '13 at 10:00
-
What do you think of this method:http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToEuler/index.htm – tomyake Feb 27 '13 at 10:09
-
@Archie Yes, correct, fixed. Actually the dot product and `acos` is enough. – Ali Feb 27 '13 at 10:17
-
At first you would have to subtract vector one from vector two in order to get vector two relative to vector one. With these values you can calculate Euler angles.
To understand the calculation from vector to Euler intuitively, lets imagine a sphere with the radius of 1 and the origin at its center. A vector represents a point on its surface in 3D coordinates. This point can also be defined by spherical 2D coordinates: latitude and longitude, pitch and yaw respectively.
In order "roll <- pitch <- yaw" calculation can be done as follows:
To calculate the yaw you calculate the tangent of the two planar axes (x and z) considering the quadrant.
yaw = atan2(x, z) *180.0/PI;
Pitch is quite the same but as its plane is rotated along with yaw the 'adjacent' is on two axis. In order to find its length we will have to use the Pythagorean theorem.
float padj = sqrt(pow(x, 2) + pow(z, 2));
pitch = atan2(padj, y) *180.0/PI;
Notes:
- Roll can not be calculated as a vector has no rotation around its own axis. I usually set it to 0.
- The length of your vector is lost and can not be converted back.
- In Euler the order of your axes matters, mix them up and you will get different results.
It took me a lot of time to find this answer so I would like to share it with you now.
first, you need to find the rotation matrix, and then with scipy
you can easily find the angles you want.
There is no short way to do this. so let's first declare some functions...
import numpy as np
from scipy.spatial.transform import Rotation
def normalize(v):
return v / np.linalg.norm(v)
def find_additional_vertical_vector(vector):
ez = np.array([0, 0, 1])
look_at_vector = normalize(vector)
up_vector = normalize(ez - np.dot(look_at_vector, ez) * look_at_vector)
return up_vector
def calc_rotation_matrix(v1_start, v2_start, v1_target, v2_target):
"""
calculating M the rotation matrix from base U to base V
M @ U = V
M = V @ U^-1
"""
def get_base_matrices():
u1_start = normalize(v1_start)
u2_start = normalize(v2_start)
u3_start = normalize(np.cross(u1_start, u2_start))
u1_target = normalize(v1_target)
u2_target = normalize(v2_target)
u3_target = normalize(np.cross(u1_target, u2_target))
U = np.hstack([u1_start.reshape(3, 1), u2_start.reshape(3, 1), u3_start.reshape(3, 1)])
V = np.hstack([u1_target.reshape(3, 1), u2_target.reshape(3, 1), u3_target.reshape(3, 1)])
return U, V
def calc_base_transition_matrix():
return np.dot(V, np.linalg.inv(U))
if not np.isclose(np.dot(v1_target, v2_target), 0, atol=1e-03):
raise ValueError("v1_target and v2_target must be vertical")
U, V = get_base_matrices()
return calc_base_transition_matrix()
def get_euler_rotation_angles(start_look_at_vector, target_look_at_vector, start_up_vector=None, target_up_vector=None):
if start_up_vector is None:
start_up_vector = find_additional_vertical_vector(start_look_at_vector)
if target_up_vector is None:
target_up_vector = find_additional_vertical_vector(target_look_at_vector)
rot_mat = calc_rotation_matrix(start_look_at_vector, start_up_vector, target_look_at_vector, target_up_vector)
is_equal = np.allclose(rot_mat @ start_look_at_vector, target_look_at_vector, atol=1e-03)
print(f"rot_mat @ start_look_at_vector1 == target_look_at_vector1 is {is_equal}")
rotation = Rotation.from_matrix(rot_mat)
return rotation.as_euler(seq="xyz", degrees=True)
Finding the XYZ Euler rotation angles from 1 vector to another might give you more than one answer.
Assuming what you are rotation is the look_at_vector
of some kind of shape and you want this shape to stay not upside down and still look at the target_look_at_vector
if __name__ == "__main__":
# Example 1
start_look_at_vector = normalize(np.random.random(3))
target_look_at_vector = normalize(np.array([-0.70710688829422, 0.4156269133090973, -0.5720613598823547]))
phi, theta, psi = get_euler_rotation_angles(start_look_at_vector, target_look_at_vector)
print(f"phi_x_rotation={phi}, theta_y_rotation={theta}, psi_z_rotation={psi}")
Now if you want to have a specific role rotation to your shape, my code also supports that!
you just need to give the target_up_vector
as a parameter as well.
just make sure it is vertical to the target_look_at_vector that you are giving.
if __name__ == "__main__":
# Example 2
# look and up must be vertical
start_look_at_vector = normalize(np.array([1, 2, 3]))
start_up_vector = normalize(np.array([1, -3, 2]))
target_look_at_vector = np.array([0.19283590755300162, 0.6597510192626469, -0.7263217228739983])
target_up_vector = np.array([-0.13225754322703182, 0.7509361508721898, 0.6469955018014842])
phi, theta, psi = get_euler_rotation_angles(
start_look_at_vector, target_look_at_vector, start_up_vector, target_up_vector
)
print(f"phi_x_rotation={phi}, theta_y_rotation={theta}, psi_z_rotation={psi}")

- 491
- 4
- 13
Getting Rotation Matrix in MATLAB is very easy e.g.
A = [1.353553385, 0.200000003, 0.35]
B = [1 2 3]
[q] = vrrotvec(A,B)
Rot_mat = vrrotvec2mat(q)

- 6,100
- 8
- 32
- 54