如图所示,坐标系O x 1 y 1 z 1 为O x 0 y 0 z 0 旋转得到,记其三个单位基向量x 1 , y 1 , z 1 在O x 0 y 0 z 0 系下的坐标表示分别为x 1 0 , y 1 0 , z 1 0 ,则旋转矩阵为:
R 1 0 = [ x 1 0 | y 1 0 | z 1 0 ] 根据内积的定义,有
R 1 0 = [ x 1 ⋅ x 0 y 1 ⋅ x 0 z 1 ⋅ x 0 x 1 ⋅ y 0 y 1 ⋅ y 0 z 1 ⋅ y 0 x 1 ⋅ z 0 y 1 ⋅ z 0 z 1 ⋅ z 0 ] 绕x, y, z三个轴旋转θ 角的旋转矩阵为:
R x , θ = [ 1 0 0 0 cos θ − sin θ 0 sin θ cos θ ] R y , θ = [ cos θ 0 sin θ 0 1 0 − sin θ 0 cos θ ] R z , θ = [ cos θ − sin θ 0 sin θ cos θ 0 0 0 1 ] Info
下文的旋转矩阵、向量、四元数等都用右上标表明是在哪个坐标系下描述的。
R T = R − 1 R ∈ S O ( 3 ) det ( R ) = 1 R 的各行、各列相互正交,且都是单位向量p 0 = R 1 0 p 1 T 1 = ( R 1 0 ) − 1 T 0 R 1 0 Tips
这个变换在线性代数里面叫做相似变换,其几何意义就是在不同坐标系下对同一矩阵(线性变换)的不同表示。
Important
特殊地,线性变换T 可以是以一个旋转矩阵来描述的旋转运动,由此就得到了旋转运动在不同坐标系下的变换:
R 1 = ( R 1 0 ) − 1 R 0 R 1 0 证明
记绕固定坐标系的旋转为R ,则R 2 0 = R 1 0 ( ( R 1 0 ) − 1 R R 1 0 ) = R R 1 0
可以证明,三维刚体的任意旋转及其叠加,都可以用一个转轴(方向向量)和绕这个轴的旋转角度表示。记转轴k → = [ k x k y k z ] T ,角度为θ ,则
R k → , θ = [ k x 2 v θ + c θ k x k y v θ − k z s θ k x k z v θ + k y s θ k x k y v θ + k z s θ k y 2 v θ + c θ k y k z v θ − k x s θ k x k z v θ − k y s θ k y k z v θ + k x s θ k z 2 v θ + c θ ] 其中c θ = cos θ ,s θ = sin θ ,v θ = versine θ = 1 − cos θ 。
反过来,对旋转矩阵
R = [ r 11 r 12 r 13 r 21 r 22 r 23 r 31 r 32 r 33 ] 则有
θ = arccos ( tr ( R ) − 1 2 ) k = 1 2 sin θ [ r 32 − r 23 r 13 − r 31 r 21 − r 12 ] 转轴和角度不是唯一的,绕一个方向旋转θ 等同于绕相反方向旋转− θ :
R k → , θ = R − k → , − θ 定义:𝒾 𝒿 𝓀 i , j , k 是正交的三个虚数单位,𝒾 𝒿 𝓀 𝒾 𝒿 𝓀 i 2 = j 2 = k 2 = i j k = − 1 ,a , b , c , d ∈ R ,则
𝒾 𝒿 𝓀 q = a + b i + c j + d k 也可以写成向量形式q = [ a b c d ] 或q = [ a v → ]
加减法:实部和三个虚部分别运算
乘法:三个虚数单位的乘法关系为:
𝒾 𝒿 𝓀 𝒿 𝓀 𝒾 𝓀 𝒾 𝒿 𝓍 𝓎 𝓎 𝓍 𝓍 𝓎 𝒾 𝒿 𝓀 i j = k j k = i k i = j x y = − y x , x , y ∈ { i , j , k } 根据分配律和结合律可以写出乘法公式,但是那样太复杂了,比较简单的形式被称为Graßmann积:
[ s , u → ] [ t , v → ] = [ s t − u → ⋅ v → , s v → + t u → + u → × v → ] 纯四元数:实部为0的四元数称为纯四元数。性质:
[ 0 , u → ] [ 0 , v → ] = [ − u → ⋅ v → , u → × v → ] 模:| | [ a b c d ] | | = a 2 + b 2 + c 2 + d 2
共轭:q ∗ = [ s , − v → ] ,q q ∗ = q ∗ q = | | q | | 2 ,q 1 ∗ q 2 ∗ = ( q 2 q 1 ) ∗
逆:q − 1 = q ∗ / | | q | | ,特殊地对于描述旋转的单位四元数,逆和共轭相等
3D旋转公式 :向量v → 绕旋转轴u → 旋转θ 角之后的向量v → ′ 可以使用四元数乘法计算。令v = [ 0 , v → ] ,q = [ cos ( θ / 2 ) , sin ( θ / 2 ) u → ] ,则
v ′ = q v q ∗ = q v q − 1 q v q ∗ 这个变换对v 平行于旋转轴的分量实施的变换是q q ∗ (无旋转),而正交于旋转轴的分量实施的变换是q 2 (旋转θ / 2 + θ / 2 )。
对于一个单位四元数q = [ a , v → ] ,可以提取它对应的旋转角度和旋转轴:
θ = 2 arccos ( a ) k → = v → sin ( θ / 2 ) 再根据前面旋转的转轴-角度表示一节的公式可以得到:
R = [ 1 − 2 c 2 − 2 d 2 2 b c − 2 a d 2 a c + 2 b d 2 b c + 2 a d 1 − 2 b 2 − 2 d 2 2 c d − 2 a b 2 b d − 2 a c 2 a b + 2 c d 1 − 2 b 2 − 2 c 2 ] a = 1 2 tr ( R ) + 1 b = r 32 − r 23 4 a c = r 13 − r 31 4 a d = r 21 − r 12 4 a 但是当a ≃ 0 时容易出现数值不稳定(除零)
在算法一基础上分类以避免数值不稳定,核心就是让作为分母的那一项比较大
当tr ( R ) > 0
同算法一
当r 11 > r 22 + r 33
a = r 32 − r 23 4 b b = 1 2 1 + r 11 − r 22 − r 33 c = r 12 + r 21 4 b d = r 13 + r 31 4 b 当r 22 > r 11 + r 33
a = r 13 − r 31 4 c b = r 12 + r 21 4 c c = 1 2 1 + r 22 − r 11 − r 33 d = r 23 + r 32 4 c 当r 33 > r 11 + r 22
a = r 21 − r 12 4 d b = r 13 + r 31 4 d c = r 23 + r 32 4 d d = 1 2 1 + r 33 − r 11 − r 22 来自,首先将旋转矩阵转化成K 3 矩阵:
K 3 = 1 3 [ r 11 − r 22 − r 33 r 21 + r 12 r 31 + r 13 r 23 − r 32 r 21 + r 12 r 22 − r 11 − r 33 r 32 + r 23 r 31 − r 13 r 31 + r 13 r 32 + r 23 r 33 − r 11 − r 22 r 12 − r 21 r 23 − r 32 r 31 − r 13 r 12 − r 21 r 11 + r 22 + r 33 ] 这个矩阵只有一个特征向量q → = [ b , c , d , a ] T ,特征值为1.
和旋转矩阵其实一样,当绕固定坐标系旋转时(或者理解为旋转刚体,坐标系不动),复合旋转的四元数是每个旋转的依次左乘 :
v ″ = q 2 v ′ q 2 ∗ = ( q 2 q 1 ) v ( q 2 q 1 ) ∗ 而当绕随动坐标系旋转时(或者理解为旋转坐标系,刚体不动),复合旋转的四元数是每个旋转依次右乘 。
若坐标系B相对于坐标系A的旋转为q B A ,平移为T B A (三维向量都用前面所述的方式转化为纯四元数表示),则点P 在两个坐标系下的坐标关系为:
p A = q B A p B ( q B A ) ∗ + T B A 旋转的部分可以这样理解:点不动旋转坐标系,坐标数值上其实相当于坐标系不动反向旋转点,也就是说
p B = ( q B A ) ∗ p A q B A (rotation only) 这个结果其实和旋转矩阵很类似。
对于用四元数表示的一个旋转r ,在不同坐标系下表示它的四元数关系为:
q r B = ( ± ) ( q B A ) ∗ q r A q B A 这和旋转矩阵在形式上一致。
类似欧拉公式,对于单位向量u → ,有
e u → θ = cos θ + u → sin θ 由此可以得到四元数的幂运算:
q t = e u → ( t θ ) 即相当于将其旋转角度乘t ,旋转轴不变。
四元数相比其他旋转表示方法的优点之一就是其插值较为容易。
要计算两个旋转之间的变化量很简单:
Δ q = q 1 q 0 ∗ 如果将四元数看作四维空间的向量,那么q 0 ⋅ q 1 就是这个四维空间中两个向量夹角的余弦值,这个夹角正是三维空间中该旋转变化量角度的一半。
即线性插值(Linear Interpolation),将两个四元数看作四维空间的两个向量,Lerp就是通过线性方式插值两个向量:
q t = Lerp ( q 0 , q 1 , t ) ≜ ( 1 − t ) q 0 + t q 1 显然,这样插值出的向量终点在弦上,因此并不是单位四元数。
对Lerp的结果归一化为单位四元数就得到了正规化线性插值(Normalized Linear Interpolation):
q t = Nlerp ( q 0 , q 1 , t ) ≜ ( 1 − t ) q 0 + t q 1 | | ( 1 − t ) q 0 + t q 1 | | Nlerp在需要插值的角度较大时,角速度会显著变化,在t = 0.5 附近角速度最大,而在两端角速度较小。
球面线性插值(Spherical Linear Interpolation)不是对向量插值而是对角度插值,因此它对角度是均匀的。根据四元数的幂定义可以很容易写出一个版本:
q t = Slerp ( q 0 , q 1 , t ) = ( q 1 q 0 ∗ ) t q 0 可见当t = 0 时为q 0 ,当t = 1 时为q 1 。但是这个公式中含有多次四元数乘法和幂运算,实际计算的效率很低。更高效的方法是:
θ = arccos ( q 0 ⋅ q 1 ) q t = Slerp ( q 0 , q 1 , t ) = sin ( ( 1 − t ) θ ) sin θ q 0 + sin ( t θ ) sin θ q 1 实际应用中,当夹角很小时sin θ 接近于0,就必须改用Nlerp来插值。
由于q 和− q 表示了相同的旋转,因此四元数空间其实是双倍覆盖了S O ( 3 ) 空间。而插值时对q 还是− q 进行插值,旋转的变化量是不同的。因此需要判断q 0 ⋅ q 1 是否为负数,如果是负数说明两个四元数在四维空间中的夹角是钝角,需要将其中一个四元数取反来得到最短的插值路径。
为了解决Slerp插值只实现C 0 连续,在轨迹点存在角速度突变的问题,以牺牲固定角速度为条件实现轨迹的C 1 连续,即角速度连续。Squad指Spherical and quadrangle,球面四边形插值,核心思想是利用Slerp来构建三次贝塞尔曲线。构造贝塞尔曲线有一个著名的递归算法de Casteljau算法,但是它的计算太复杂,对于三次贝塞尔曲线,它是三层一次插值的嵌套:
Bezier ( v → 0 , v → 1 , v → 2 , v → 3 ; t ) = L ( L ( L ( v → 0 ) , v → 1 ; t ) , L ( v → 1 , v → 2 ; t ) ; t ) , L ( L ( v → 1 , v → 2 ; t ) , L ( v → 2 , v → 3 ; t ) ; t ) ; t ) 其中L 是Lerp线性插值;对于四元数来说要换成Slerp插值。
而Quad使用一个二次插值和一个一次插值的嵌套来近似它:
Bezier ( v → 0 , v → 1 , v → 2 , v → 3 ; t ) = L ( L ( v → 0 , v → 3 ; t ) , L ( v → 1 , v → 2 ; t ) ; 2 t ( 1 − t ) ) 将Lerp换成Slerp就得到Squad插值:
S q u a d ( q 0 , q 1 , q 2 , q 3 ; t ) = S ( S ( q 0 , q 3 ; t ) , S ( q 1 , q 2 ; t ) ; 2 t ( 1 − t ) ) 对于四元数序列q 0 , q 1 , … , q n ,对每一对四元数q i , q i + 1 都进行插值S q u a d ( q i , s i , s i + 1 , q i + 1 ; t ) ,其中控制点s i , s i + 1 的选择使得切换点处可导,这里给出结果:
s i = q i exp ( − log ( q i ∗ q i − 1 ) + log ( q i ∗ q i + 1 ) 4 )