【问题标题】:Given a unit vector find the two angles of rotation that aligns that vector with an axis给定一个单位向量,找到使该向量与轴对齐的两个旋转角度
【发布时间】:2025-12-01 21:05:02
【问题描述】:

所以我不太精通线性代数,所以我在努力解决这个问题。

我有一个单位向量v。我想找到两个角度(角度 1,围绕 x 轴旋转,角度 2,围绕 z 轴旋转),这样当我通过它们旋转 v 时,它会将矢量 v 与 y 轴对齐。从this 问题我有一个函数可以找到任意向量之间的角度并返回一个旋转。但是这个函数返回 3 个角度。本质上,有无限数量的 3d 旋转将v 与 y 轴对齐,所以我想要两个独特的角度。

这是我现在的代码,它需要 numpy 和 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))

【问题讨论】:

  • 即使是那些受限的旋转也不是唯一的(甚至超出了正常的角度对称性)。你在乎你得到哪一个吗?
  • @DavisHerring 嗯,你能举一个例子,说明除了角对称之外不是唯一的两个例子吗?我真的无法想象它......
  • @DavisHerring 对于上下文:我想描述向量的方向(所以没有幅度)。其中中性方向将与 Y 轴对齐。我认为 2 次旋转可以唯一地识别这样一个方向。但从你的评论来看,你说情况并非如此。然后我想知道:什么用(最好)2个参数唯一地描述了3D方向。
  • 从 开始,您可以旋转 -90 度到 ,然后旋转 45 度到 或旋转 90 度到 然后再旋转 135。两次旋转可以识别每个可能的方向(或无特征对象的旋转,如点或对称杆通过原点),但不是单射的,因此即使是这种受限类型,这种方向也不能确定唯一的旋转对。这对您的用例可能无关紧要。
  • @DavisHerring 嗯……没错。但是,如果您只能从 -90 度旋转到 +90 度,就不会出现这个问题,对吧?我想要独特旋转的原因是我想稍后比较不同的方向并检查它们是否相同。无论如何,我会检查您的答案是否很快并接受!

标签: python numpy rotation linear-algebra


【解决方案1】:

利用固定目标对齐,这可以通过三角函数以简单的方式完成:

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)

+x或+z(右手法则)看原点时,旋转定义为逆时针;旋转方向始终是具有较小幅度的方向,但可能会在评论中找到物理上的较小旋转。请注意,输入不需要归一化,并且永远不会产生 NaN(除非它出现在输入中)。

【讨论】:

    【解决方案2】:

    嗯,非标准问题,需要思考一下。

    鉴于v1v2,您希望rotate_z(rotate_x(v1, alpha), beta)v2 处于同一方向。

    我们知道对齐向量可以通过简单的缩放缩放v2得到,这将得到x1,y3,z3 = v3 = v2 * |v1| / |v2|。由于绕 z 轴旋转不会影响 z 坐标,我们可以确定 alpha 使得rotate_x(v1, alpha) 的 z 坐标等于z3。之后我们确定角度beta 以正确对齐X和Y坐标

    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
    

    那你就打电话

    alignment_angles((1,2,3), (3,4,5))
    

    或者你也可以使用 3 行的 numpy 数组。

    最初我认为这将是球坐标的应用,如果第二次旋转的轴是根据第一次旋转而旋转的 z 轴,情况就是这样。

    编辑

    有些向量无法与 x 上的旋转和 y 上的旋转对齐。 假设您要将向量v1 = (1, 0, 0) 与向量v2 = (0, 0, 1) 对齐,x 方向的旋转不会影响 v1,它始终指向 x 方向,那么当您围绕 z 轴旋转时,它将始终在 XY 平面上.

    您给出的示例确实给出了错误的答案,因为asin 不是单射的。

    当您无法对齐给定的向量时,我更改了函数以引发值错误。

    【讨论】:

    • 感谢您的回答。它似乎在许多情况下都有效。但在许多情况下,断言也会失败:例如:alignemnt_angles((-0.017868310075997075, -0.3998056402886916, -0.008330974849768172), (0,1,0))。我打印了断言值,但 z 不匹配。 [1.249000902703301e-16, 0.39994466064372136, -0.016658333534559422], [0.0, 0.4002914334001792, 0.0]