小编典典

通过四元数旋转坐标系

python

我们有一个代表3d空间中原子的无数个空间坐标(x,y和z),并且我正在构造一个函数,将这些点转换为新的坐标系。将坐标移动到任意原点很简单,但是我无法绕下下一步:3d点旋转计算。换句话说,我正在尝试将点从(x,y,z)转换为(x’,y’,z’),其中x’,y’和z’分别是i’,j’和k’,我在euclid
python模块
的帮助下创建的新轴矢量。

认为 我只需要一个四元体即可做到这一点,即

>>> q * Vector3(x, y, z)
Vector3(x', y', z')

但是要做到这一点,我相信我需要一个旋转轴矢量和一个旋转角度。但是我不知道如何从i’,j’和k’计算这些。这似乎是一个简单的从头开始编写代码的过程,但是我怀疑这样的事情需要线性代数自行解决。非常感谢您朝着正确的方向前进。


阅读 221

收藏
2020-12-20

共1个答案

小编典典

从代数的角度来看,使用四元数来表示旋转并不困难。就个人而言,我发现很难从 视觉上
对四元数进行推理,但是使用四元数进行旋转所涉及的公式非常简单。我将在此处提供一组基本的参考功能。

您可以将四元数(出于我们的目的)视为一个标量加上一个3-d向量-抽象地w + xi + yj + zk表示为此处的一个简单元组(w, x, y, z)。3-d旋转空间完全由四元数的子空间( 单位
四元数的空间)表示,因此您需要确保四元数被标准化。您可以按照将任何4个向量归一化的方式进行操作(即,幅度应接近1;否则,将值按幅度缩减):

def normalize(v, tolerance=0.00001):
    mag2 = sum(n * n for n in v)
    if abs(mag2 - 1.0) > tolerance:
        mag = sqrt(mag2)
        v = tuple(n / mag for n in v)
    return v

请注意,为简单起见,以下函数假定四元数值 已标准化
。在实践中,您将需要不时地对它们进行规范化,但是最好的解决方法将取决于问题域。这些功能仅提供基本信息,仅供参考。

每次旋转都用一个单位四元数表示,旋转的级联对应于单位四元数的 乘法 。公式2如下:

def q_mult(q1, q2):
    w1, x1, y1, z1 = q1
    w2, x2, y2, z2 = q2
    w = w1 * w2 - x1 * x2 - y1 * y2 - z1 * z2
    x = w1 * x2 + x1 * w2 + y1 * z2 - z1 * y2
    y = w1 * y2 + y1 * w2 + z1 * x2 - x1 * z2
    z = w1 * z2 + z1 * w2 + x1 * y2 - y1 * x2
    return w, x, y, z

要通过四元数旋转 向量 ,还需要四元数的共轭。这很容易:

def q_conjugate(q):
    w, x, y, z = q
    return (w, -x, -y, -z)

现在四元数-向量乘法很简单,只要矢量变换成四元数(通过设置w = 0和离开xy以及z相同的),然后乘以q * v * q_conjugate(q)

def qv_mult(q1, v1):
    q2 = (0.0,) + v1
    return q_mult(q_mult(q1, q2), q_conjugate(q1))[1:]

最后,您需要知道如何将轴角旋转转换为四元数。还容易!在这里通过调用“清理”输入和输出是有意义的normalize

def axisangle_to_q(v, theta):
    v = normalize(v)
    x, y, z = v
    theta /= 2
    w = cos(theta)
    x = x * sin(theta)
    y = y * sin(theta)
    z = z * sin(theta)
    return w, x, y, z

然后回来:

def q_to_axisangle(q):
    w, v = q[0], q[1:]
    theta = acos(w) * 2.0
    return normalize(v), theta

这是一个快速使用示例。绕x,y和z轴旋转90度的序列会将y轴上的向量返回到其原始位置。此代码执行这些旋转:

x_axis_unit = (1, 0, 0)
y_axis_unit = (0, 1, 0)
z_axis_unit = (0, 0, 1)
r1 = axisangle_to_q(x_axis_unit, numpy.pi / 2)
r2 = axisangle_to_q(y_axis_unit, numpy.pi / 2)
r3 = axisangle_to_q(z_axis_unit, numpy.pi / 2)

v = qv_mult(r1, y_axis_unit)
v = qv_mult(r2, v)
v = qv_mult(r3, v)

print v
# output: (0.0, 1.0, 2.220446049250313e-16)

请记住,这种旋转顺序不会将 所有
矢量返回到相同位置。例如,对于x轴上的矢量,它将对应于围绕y轴旋转90度。(此处请牢记右手法则;围绕y轴的正向旋转会将x轴上的矢量推入z 负向 区域。)

v = qv_mult(r1, x_axis_unit)
v = qv_mult(r2, v)
v = qv_mult(r3, v)

print v
# output: (4.930380657631324e-32, 2.220446049250313e-16, -1.0)

与往常一样,如果您在此处发现任何问题,请告诉我。


1.这些改编自此处存档的OpenGL教程。

2.四元数乘法公式看起来像老鼠的巢,但是推导很简单(如果很乏味)。首先请注意ii = jj = kk = -1; 那么ij = kjk = iki = j; 最后是ji = -kkj = -iik = -j。然后将两个四元数相乘,分配这些项并根据16个乘法中每一个的结果重新排列它们。这也有助于说明 为什么
可以使用四元数来表示旋转。最后六个恒等式遵循右手定则,在 i到的j旋转与 围绕的 旋转之间创建双射k,依此类推。

2020-12-20