Reputation:
So I'm not that well versed in linear algebra so I'm struggling with this.
I have a unit vectors v
. I want to find two angles(angle 1, rotation around x-axis, and angle 2, rotation around z-axis) such that when I rotate v
by them it aligns the vector v
with the y-axis. From this question I have a function that can find the angle between arbitrary vectors and returns a rotation. But this function returns 3 angles. Essentially there is an infinite number of 3d rotation that aligns v
with the y-axis so I want the two unique angles.
This the code I have now, it requires numpy and scipy:
import numpy as np
import random
from scipy.spatial.transform import Rotation as R
def rotation_from_unit_vectors(a, b):
v = np.cross(a, b)
c = np.dot(a, b)
s = np.linalg.norm(v)
kmat = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
rotation_matrix = np.eye(3) + kmat + kmat.dot(kmat) * ((1 - c) / (s ** 2))
return R.from_matrix(rotation_matrix)
y_axis = np.asarray([0.0, 1.0, 0.0])
alpha = random.uniform(0, 10)
beta = random.uniform(0, 10)
gamma = random.uniform(0, 10)
v = np.asarray([alpha, beta, gamma])
v = v / np.linalg.norm(v)
r = rotation_from_unit_vectors(v, y_axis)
print(r.as_euler('xyz', degrees = True))
print(r.apply(v))
Upvotes: 6
Views: 1932
Reputation: 39868
Taking advantage of the fixed target alignment, this can be done in a straightforward manner with just trigonometry:
import math
def to_y(x,y,z):
rx=-math.atan2(z,y) # or +math.atan2(z,-y)
y2=y*math.cos(rx)-z*math.sin(rx) # -> (x,y2,0)
return rx,math.atan2(x,y2)
The rotations are defined as counterclockwise when looking at the origin from +x or +z (the right-hand rule); the rotation direction is always that with the smaller magnitude, but it may be possible to find a physically smaller rotation as indicated in the comment. Note that the input need not be normalized, and NaN is never produced (unless it appears in the input).
Upvotes: 2
Reputation: 14654
Hum, non-standard problem, required thinking a little.
Given v1
and v2
you want to rotate_z(rotate_x(v1, alpha), beta)
to be on the same direction as v2
.
We know that the aligned vector can be obtained by simply scaling scaling v2
, this will gives x1,y3,z3 = v3 = v2 * |v1| / |v2|
. Since rotation around z-axis, does not affect the z coordinate, we can determine alpha such that the z coordinate of rotate_x(v1, alpha)
equals z3
. After that we determine the angle beta
to align place the X and Y coordinates properly
import numpy as np
def alignment_angles(v1, v2):
x1,y1,z1 = v1 # initial vector
x2,y2,z2 = v2 # reference vector
# magnitude of the two vectors
r1 = np.sqrt(x1**2 + y1**2 + z1**2)
r2 = np.sqrt(x2**2 + y2**2 + z2**2)
# this will be the result when aligning v1 to v2
# it has the magnitude of v1 and the direction of v2
x3,y3,z3 = x2*r1/r2, y2*r1/r2, z2*r1/r2
# the rotation in x must set the z coordinate to the
# final value, since the rotation over the z axis will
# not affect the z coordinate (this have two solutions)
rho1 = np.sqrt(z1**2 + y1**2)
if(abs(z3 / rho1) > 1):
raise ValueError('Cannot align these vectors')
alpha = np.arcsin(z3 / rho1) - np.arctan2(z1, y1);
# apply the rotation to make easier to calcualte the next stage
y1, z1 = (y1 * np.cos(alpha) - z1 * np.sin(alpha),
y1 * np.sin(alpha) + z1 * np.cos(alpha))
np.allclose(rho1, np.sqrt(z1**2 + y1**2))
#assert(np.allclose(z1, z3))
# now it is just a matter of aligning (x1, y1) to (x3, y3)
beta = np.arctan2(y3, x3) - np.arctan2(y1, x1)
x1, y1 = (x1 * np.cos(beta) - y1 * np.sin(beta),
x1 * np.sin(beta) + y1 * np.cos(beta))
# ensure the fotated v1 was correctly aligned
assert(np.allclose([x1, y1, z1], [x3, y3, z3]))
return alpha, beta
Then you just call
alignment_angles((1,2,3), (3,4,5))
or you can also use numpy arrays with 3 rows.
Initially I thought it would be an application of spherical coordinates, that would be the case if the axis for the second rotation was the z-axis rotated accordingly to the first rotation.
There are some vectors that cannot be aligned with a rotation on x and a rotation on y.
Suppose you want to align the vector v1 = (1, 0, 0)
to the vector v2 = (0, 0, 1)
the rotation in x will not affect v1, it will always point in the direction x, then when you rotate around the z axis it will always be on the XY plan.
The example you gave was really giving the wrong answer because asin
is not injective.
I changed the function to raise a value error when you cannot align the given vectors.
Upvotes: 2