我们有一个代表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’计算这些。这似乎是一个简单的从头开始编写代码的过程,但是我怀疑这样的事情需要线性代数自行解决。非常感谢您朝着正确的方向前进。
从代数的角度来看,使用四元数来表示旋转并不困难。就个人而言,我发现很难从 视觉上 对四元数进行推理,但是使用四元数进行旋转所涉及的公式非常简单。我将在此处提供一组基本的参考功能。
您可以将四元数(出于我们的目的)视为一个标量加上一个3-d向量-抽象地w + xi + yj + zk表示为此处的一个简单元组(w, x, y, z)。3-d旋转空间完全由四元数的子空间( 单位 四元数的空间)表示,因此您需要确保四元数被标准化。您可以按照将任何4个向量归一化的方式进行操作(即,幅度应接近1;否则,将值按幅度缩减):
w + xi + yj + zk
(w, x, y, z)
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和离开x,y以及z相同的),然后乘以q * v * q_conjugate(q):
w = 0
x
y
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。
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 = k,jk = i,ki = j; 最后是ji = -k,kj = -i,ik = -j。然后将两个四元数相乘,分配这些项并根据16个乘法中每一个的结果重新排列它们。这也有助于说明 为什么 可以使用四元数来表示旋转。最后六个恒等式遵循右手定则,在 从 i到的j旋转与 围绕的 旋转之间创建双射k,依此类推。
ii = jj = kk = -1
ij = k
jk = i
ki = j
ji = -k
kj = -i
ik = -j
i
j
k