虽然有点不务正业,但是这个被这个问题卡住的我,真的是寝食难安,毕竟我刚开始以为这个只是一个两小时的问题。
话不多说,先整文档吧,就当一个学习笔记。
一般来说大家都会提到哈密顿大佬的故事和复数的概念,大家看看上面的教程就好了。以及四元数的优势和缺点,简单说说,
首先最大的坑就是,四元数不好理解,所以我想写这个笔记,也许会给大家一些帮助。
其次四元数无法表示转180°和超过180°的旋转。尤其是180°,sin(180°/2)=0,这时候的p1,p2,p3都不存在。
但是四元数的优点也比较明显(虽然我没看出来,要不是别人用的是这个,我没办法改,我早放弃了!):简洁、不混淆,好像会避免万向锁的问题,以及用四个数就可以表示一个旋转,而不像旋转矩阵需要九个数。
这里假定大家都能理解这些基本知识。
不能理解应该也无所谓,毕竟我们作为一个嗑盐搬砖狗,利用这些数学知识,解决实际问题就好。
所以,捋清我们的问题:
现在想用四元数来表示坐标系或者向量的旋转,我们已知一个在坐标系O下的向量P(x0,y0,z0),然后所在坐标系发生了旋转,旋转量为Quat[q0, q1, q2, q3],变成了新的坐标系A,那么向量P在新的坐标系A中的三维坐标应该是多少?和Quat的关系如何?如果再转一次呢?
四元数的定义如下:
quat = [q0, q1, q2, q3]
待旋转的向量为:
P = [p0, p1, p2]
其中quat中q0代表实数,物理含义为旋转角度一半的余弦,即cos(theta/2)=q0
,其中theta为旋转角度。
q1,q2,q3为ijk三个基的值,也就是一个旋转轴向量,分别可以对应XYZ轴的分量值,具体对应关系如下:
q1=x*sin(theta/2)
q2=y*sin(theta/2)
q3=z*sin(theta/2)
然后旋转的效果是,原始的坐标轴(不是向量!),绕着(x,y,z)这个旋转向量的指向方向,逆时针,也就是右手坐标系(注意,这里默认都是右手法则)旋转theta角度,得到新的坐标系, 然后原始向量P在新的坐标系下的坐标自然就会变化成为P’=[p0’, p1’, p2’].
具体对应关系如下。
P_new = Quat*P*Quat_inverse
动态图可以看上面的b站视频。
然后具体的值和公式如下:
对于原始坐标系下的一个向量P(0, 2, 0),这时候会有一个quat的旋转,其实这个quat表示的是它的坐标系旋转了!
然后这个quat一般会是单位四元数,为啥是单位四元数呢?因为我们的计算公式是:p_new = quatpquat_inverse。
而quat_inverse = quat*/|quat|,
(quat就当成共轭了)也就是quat的共轭除以它的模,共轭特别好求,但是模需要计算,所以直接将模变成1,quat_inverse=quat
好了,我们现在知道了单位quat,那么也就知道了quat_inverse
下面进行计算,直接调用四元数乘法公式,这个上面的参考资料都有,我就不放出来了,直接上代码:
P = [0, 2, 0]
# 将只有三个值的纯四元数转成可以计算的真实四元数
P = [0, P[0], P[1], P[2]]
# 已知单位四元数的数值!
quat = [0.7071, 0.7071, 0, 0]
quat_inverse = [quat[0], -quat[1], -quat[1], -quat[2]]
# 四元数乘法函数!每次只算两个相乘就好了,只需要分开左右就行了!
Def mul(left, right):
# 尽量用一个copy()进行深复制,万一原始数据变了就乱了
left = left.copy()
right = right.copy()
s = left[0]*right[0] - left[1]*right[1] - left[2]*right[2] - left[3] * right[3]
x = left[0]*right[1] + left[1]*right[0] + left[3]*right[2] - left[2] * right[3]
y = left[0]*right[2] + left[2]*right[0] + left[1]*right[3] - left[3] * right[1]
z = left[0]*right[3] + left[3]*right[0] + left[2]*right[1] - left[1] * right[2]
return [s, x, y, z]
quat_P = mul(quat, P)
P_new = mul(quat_P, quat_inverse)
print("P_new:", P_new)
# P_new(0, 0, 0, -2)
上面是公式计算出来的坐标系旋转后,新向量值。下面用图来表示,空间中到底是咋转的:
如果是坐标系的话,那么就将坐标系每个轴当成被旋转的向量,都旋转了之后,就会称为新的坐标系.
举个例子:右手法则的世界坐标系(X, Y, Z)如下图:
然后表示旋转的单位四元数是这样:quat = [0.7071, 0.7071, 0 , 0]
那么它的旋转角是:
theta = np.arccos(0.7071)*2*np.pi/180
# theta = 90°
XYZ轴的分量是:
R_x = 0.7071/np.sin(theta/2)
# R_x = 1.0
其它两个轴的计算都是0,所以旋转向量是:(1.0, 0, 0),下图的黑色向量。
旋转角和旋转轴都拿到了,局势就很明朗了,转就完事儿了。
下面该怎么转?
沿着旋转轴的正方向,逆时针转theta角度,就OK了。
首先原Y轴要绕(1.0, 0, 0)正方向逆时针旋转90°,变成新的X轴.但是朝向变成了原坐标系Z轴的正方向.
Z轴也是,变成新的Z轴,朝向为原Y轴负方向.
X轴就不会变化,因为涉及到旋转的垂直分量和平行分量,只有和旋转轴垂直的分量才会有转动效果;
而X轴和旋转轴水平,自然不会有旋转效果.
这里就会形成一个新的坐标系
在新的坐标系中,向量P的坐标变成了(0, 0, -2)!!!
是不是和上面公式计算出来的一样?!!
OK了,基本上四元数表示旋转的概念和实现都完成了,其它复杂的计算,直接用公式就好了,
具体理解的话,可以用参考资料12中的可视化图,看看旋转轴到底在哪儿,但是无法看出转之后的变化。
差不多就OK了~