三维旋转矩阵推算(任意轴旋转矩阵推导及Julia动画演示)
三维旋转矩阵推算(任意轴旋转矩阵推导及Julia动画演示)V1旋转θ角度后的向量为V1',我们可以绕Z轴旋转的矩阵来表示。接下来计算新坐标系中互相垂直的向量V1、V2,先计算V1,V2可以通过V1和a的叉积来计算。绕y轴旋转矩阵:可以证明3D旋转都是围绕某一个轴旋转一定的角度,见下图向量V绕a轴旋转一周,向量V的末端点会形成一个圆,这个圆所在的平面与旋转轴a垂直,我们在这个垂直a轴的平面上选择两个互相垂直的向量v1、v2,使v1、v2和a组成一个新的坐标系,其中v1的末端和向量V的末端点重合,那么向量V旋转θ角度的末端点和v1旋转θ角度的末端点依然重合;而在v1、v2、a组成的新坐标系下的旋转可以由前面介绍的绕某个坐标轴旋转的矩阵来表达,最终我们再把它转换到原坐标系下就可以推导出绕任意指定轴旋转的变换矩阵了。下图中V'表示为向量V旋转θ角度后的新向量,我们的目标就是由已知的向量V和旋转角度推导出向量V'。约定a为标准化向量
摘要:可以证明3D旋转都是围绕某一个轴旋转一定的角度,绕x、y、z轴的旋转相对比较简单,本文介绍如何推导出绕任意指定轴旋转的旋转矩阵,并以Julia动画的形式进行展示旋转的效果。
绕x、y、z轴旋转矩阵要推导绕任意轴旋转的变换矩阵需要先清楚相对简单的绕x、y、z轴旋转的变换矩阵,可以参考我之前的文章https://www.toutiao.com/i7030291974259442207/,这里不做过多的讲解,仅列出对应的变换矩阵形式。
绕z轴旋转矩阵:
绕x轴旋转矩阵:
绕y轴旋转矩阵:
可以证明3D旋转都是围绕某一个轴旋转一定的角度,见下图向量V绕a轴旋转一周,向量V的末端点会形成一个圆,这个圆所在的平面与旋转轴a垂直,我们在这个垂直a轴的平面上选择两个互相垂直的向量v1、v2,使v1、v2和a组成一个新的坐标系,其中v1的末端和向量V的末端点重合,那么向量V旋转θ角度的末端点和v1旋转θ角度的末端点依然重合;而在v1、v2、a组成的新坐标系下的旋转可以由前面介绍的绕某个坐标轴旋转的矩阵来表达,最终我们再把它转换到原坐标系下就可以推导出绕任意指定轴旋转的变换矩阵了。下图中V'表示为向量V旋转θ角度后的新向量,我们的目标就是由已知的向量V和旋转角度推导出向量V'。
约定a为标准化向量(长度为1),首先计算V投射到旋转轴a的向量Vc,Vc和V之间的夹角为α,可以通过V和a的点积来计算。
接下来计算新坐标系中互相垂直的向量V1、V2,先计算V1,V2可以通过V1和a的叉积来计算。
V1旋转θ角度后的向量为V1',我们可以绕Z轴旋转的矩阵来表示。
代入并展开可以得出下面的形式
由于V1、V2互相绘制且长度相同,我们可以很容易得出V2.x=-V1.y、V2.y=V1.x,根据这个关系我们可以把V1'表示为由V1和V2组成的形式
得出了V1',由于V1'的末端点和V绕a轴旋转θ角度后的末端点始终重合,那么可以计算V旋转后的向量V'。
其中,Vc、V1、V2都是由V计算而来的,旋转轴a和旋转角度θ为已知量,V为未知量,这样我们就得出了V到V'的计算公式,接下来就需要把这个公式用矩阵的形式来表示如下,我们的任务就是计算矩阵中的未知量,得出的矩阵R就是旋转矩阵。
我们分别用V为[1 0 0 1]、[0 1 0 1]、[0 0 1 1]向量的列形式代入前面的计算V'的公式,就可以计算出矩阵的未知量,具体过程就不做展开了,最终得出绕指定轴a旋转角度θ的变换矩阵R的形式为。
下图的动画展示了红色的向量绕绿色的向量所在轴旋转的动画,不过由于观察角度和透视的关系,看起来的效果不是太明显。
下面给出了动画示例的Julia代码,已添加相应的注释:
# 引用Makie可视化库
using GLMakie
# 引用线性代数库
using LinearAlgebra
# 创建图像
fig = Figure()
# 在图像中创建子图
ax = Axis3(fig[1 1] aspect=(1 1 1) title="绕任意轴旋转示例" perspectiveness=0.4)
# 设置x、y、z轴坐标范围
limits!(ax -2 2 -2 2 -2 2)
# 定义旋转轴(旋转向量转换为标准化向量,即向量长度为1),旋转向量v(从行向量通过转置转为列向量)
a = normalize([-10 -10 10 1])
v = transpose([1 2 1 1])
# 绕a轴旋转角度
θ = Node(0.0)
# 绕旋转轴a旋转θ角矩阵 使用4x4齐次矩阵
M = @lift([
a[1]*a[1] (1-a[1]*a[1])*cos($θ) a[1]*a[2]*(1-cos($θ)) a[3]*sin($θ) a[1]*a[3]*(1-cos($θ))-a[2]*sin($θ) 0
a[1]*a[2]*(1-cos($θ))-a[3]*sin($θ) a[2]*a[2] (1-a[2]*a[2])*cos($θ) a[2]*a[3]*(1-cos($θ)) a[1]*sin($θ) 0
a[1]*a[3]*(1-cos($θ)) a[2]*sin($θ) a[2]*a[3]*(1-cos($θ))-a[1]*sin($θ) a[3]*a[3] (1-a[3]*a[3])*cos($θ) 0
0 0 0 1
])
# 计算v旋转θ角后的向量v'
newV = @lift($M*v)
newV_x = @lift($newV[1:1])
newV_y = @lift($newV[2:2])
newV_z = @lift($newV[3:3])
# 绘制旋转轴a
arrows!(ax [0] [0] [0] [a[1]] [a[2]] [a[3]] label=["旋转轴"] color=[:green])
# 绘制旋转向量newV
arrows!(ax [0] [0] [0] newV_x newV_y newV_z label=["向量v"] color=[:red])
# 帧速
framerate = 30
# 为一迭代序列,范围为(0 2π),即一周,每一帧的变化步长为1/framerate
timestamps = append!(collect(range(0 2*pi step=1/framerate)))
record(fig "3D_rotate.gif" timestamps; framerate = framerate) do t
# t就是timestamps迭代序列中的值
# 设置旋转角θ值
θ[] = t
end