3D 变换的导数与对数:理解 `log(T)` 的意义
导数、对数和变换
日期:2023年2月7日 标签:Programming
给定一个变换 \(T\) 和一个点 x,我们可以使用 \(T * x\) 找到变换后的点。但是,如果我们想平滑地插值 \(T\),使其将 \(x\) 沿着从其初始位置到被 \(T\) 变换后的位置的路径移动,该怎么办?
我们想要找到的是时间 \(t\) 时的点 \(x\): \(x(t) = T(t) * x(0)\)
其中 \(x(0)\) 是点的初始位置,\(T(t)\) 是时间 \(t\) 时的变换。因为我们只有一个变换 \(T\),所以我们需要找到一种方法来随着时间的推移对其进行插值。
一种实现方法是将 \(T\) 提升到 \(t\) 的幂,这可以通过使用变换的指数和对数来完成。有趣的是,变换的对数也可以用来轻松找到空间中点 \(x\) 的速度:速度向量(也称为切线向量)只是 \(log(T) * x\)。这篇博文展示了对数和速度之间的关系。
示例
查看这个交互式示例,了解当您操纵 gizmo 来平移和旋转变换时,向量场如何变化。向量场表示变换过程中空间中每个点的速度向量。
当您移动 gizmo 时,您会注意到一条白色曲线,它描绘了从原点到 gizmo 变换的路径。沿着这条曲线,您会看到插值的变换,因为它从原点移动到 gizmo。正如您所看到的,插值沿着速度向量场的流向进行。该小程序的代码使用变换的指数和对数来计算曲线、插值变换和向量场。
该小程序的源代码可以在这里找到,其中包括刚体变换的闭式 log() 和 exp() 的实现。
接下来,我将描述如何计算您在此示例中看到的插值变换和速度向量场。
什么是 \(T(t)\)?
我们有 \(T\),但没有随时间变化的 \(T(t)\)。假设将两个变换相乘表示这些变换的组合,我们可以通过以下方式找到 \(T(t)\):
\(T(0) = I\) (单位变换) \(T(1) = T\) \(T(2) = T * T\) \(T(3) = T * T * T\)
更一般地,我们可以通过以下方式找到任何时间的 \(T\):
\(T(t) = T^t\)。
上面的技巧来自 Fabian Giesen 的一篇博文 这里,但适用于任何使用乘法进行组合的变换。
现在我们知道了 \(T(t) = T^t\),原始方程可以重新排列为
\(x(t) = T^t * x(0)\)。
什么是 \(T^t\)?
要计算 \(T^t\),我们需要使用矩阵指数和矩阵对数。
让我们从关于矩阵 X 的两个事实开始:
\(e^{log(X)} = X\) 和 \(log(X^y) = log(X) * y\)。
放在一起,我们可以说
\(T^t = e^{log(T^t)} = e^{log(T)*t}\)
我们可以将其插入到先前的等式中,得出
\(x(t) = e^{log(T) * t} * x(0)\)。
这说明,要找到时间 t 时的点 x,使用 \(e^{log(T) * t}\) 找到时间 t 时的变换,并使用它来变换其初始位置(时间 0)的点。
什么是导数?
在微积分中,我们了解到
\(\dfrac{d}{dt}e^{a t} = a e^{a t}\)
这对于矩阵也成立:
\(\dfrac{d}{dt}e^{A t} = A e^{A t}\)
这种关系在矩阵指数的导数部分有更详细的解释。
我们可以使用此属性来找到我们之前方程 \(x(t) = e^{log(T)t} x(0)\) 关于 t 的导数:
\(\dfrac{d}{dt}x(t) = log(T) e^{log(T) t} x(0)\)。
该方程指出,要找到时间 t 时点的导数(速度向量,也称为切线向量),首先使用插值变换 \(e^{log(T)t}\) 变换点的初始位置 \(x(0)\),然后将其乘以变换的对数 \(log(T)\)。此表达式遵循列向量的从右到左约定,因此您将从初始位置 \(x(0)\) 开始,然后应用插值变换 \(e^{log(T)t}\),最后乘以对数 \(log(T)\)。
\(e^{log(T) t}\) 充当一个算子,将点从其初始位置映射到时间 t 的新位置。矩阵指数可以被认为类似于积分。在时间 0,\(e^{log(T) t}\) 是单位矩阵 (\(e^0=I\) 对于矩阵指数),在时间 1.0,\(e^{log(T) t}\) 等于原始变换矩阵 T (\(e^{log(T)}=T\))。
这都意味着什么?
如果我们采用“什么是 \(T^t\)?”末尾的等式
\(x(t) = e^{log(T) t} x(0)\)
并将其代入“什么是导数?”末尾的等式
\(\dfrac{d}{dt}x(t) = log(T) e^{log(T) t} x(0)\),
那么我们有:
\(\dfrac{d}{dt}x(t) = log(T) x(t)\)。
这与移动点的导数相关联,该移动点由变换的对数移动。
一种思考 \(log(T)\) 的方式是将其视为变换的切线向量的向量场。换句话说,它是第一个导数的场。该向量场独立于时间,并显示空间中每个点的速度。
该等式说明,如果您通过变换的对数来变换空间中的任何点,您将获得该点的导数。导数是速度,因此 \(log(T)\) 定义了速度场(空间中每个点的切线向量的场)。
当一个点通过变换在空间中移动时,它会形成一条曲线。时间 t 的切线向量与时间 t 时点在曲线上的位置相切。
您可以将矩阵的对数视为由该矩阵执行的动作的速度场。上面交互式示例中可视化的速度场就是这个场。
更不正式地看待这一点的方式是说
\(velocity = log(transform) * position\)
意思是,要了解一个点将如何随时间移动,请将变换的对数的向量场视为速度场。当该点沿该速度场流动时,它会及时移动。
什么是微分方程?
我们也可以将所有这些重新表述为微分方程。早些时候,我们有
\(\dfrac{d}{dt} x(t) = log(T) x(t)\)
这是一个微分方程。因为 \(log(T)\) 是一个矩阵,所以它更具体地说是矩阵微分方程。
以下形式的标量常微分方程
\(y'(t)=ay(t)\)
具有一般解
\(y(t)=e^{at}y(0)\)。
类似地,以下形式的矩阵微分方程
\(x'(t)=Ax(t)\)
具有一般解
\(x(t)=e^{At}x(0)\)。
因此,给定我们之前的方程
\(\dfrac{d}{dt} x(t) = log(T) x(t)\)
我们有解决方案
\(x(t) = e^{log(T) t} x(0)\)。
这与我们原来的等式相同,但是我们从一个微分方程开始并找到了一个解。为了证明这个解是正确的,只需取它的导数,这正是我们之前在什么是导数?部分所做的。
指数映射和对数映射
指数映射定义为无穷级数
$$e^{A t} = I + A t + \frac{1}{2}(A t)^2 + \frac{1}{3!}(A t)^3 + ... = \sum_{i=0}^{\infty} \frac{(At)^i}{i!}$$
可用于查找实数、复数、四元数、矩阵等的指数。例如,当将方阵插入到该级数中时,结果称为矩阵指数。
类似地,对数定义为无穷级数
$$log(A) = \sum_{i=1}^{\infty} (-1)^{i+1} \frac{(A - I)^i}{i}$$
如果您想了解更多信息,请搜索指数映射和对数映射。您会发现这些是李群理论中的重要概念。指数映射和对数映射是彼此的逆运算。在李理论中,指数映射将点 p 处的切向量映射到流形上的一个点。对数映射则相反,将流形上的一个点映射回 p 处的切向量。
在阅读李群时,您会遇到许多不同类型的群。但是,只有少数群与变换相关。 SO(3) 是一个 3D 旋转矩阵,SU(2) 是一个四元数,SE(3) 是一个 3D 刚体变换(旋转和平移),SIM(3) 是旋转、平移和(正)均匀缩放,而 GL(n) 是一个 nxn 矩阵。
对于如何实际计算矩阵或其他对象的指数映射和对数映射,有几种选择:
- 使用像 Eigen 或 Armadillo 这样的数学库。这些库具有计算矩阵指数和矩阵对数的函数。
- 库 Sophus 具有用于群 SO(3)、SE(3) 和 SIM(3) 的闭式 exp/log 代码。但要注意,它将其四元数限制为 -\(\pi\) … +\(\pi\) 中的 3D 旋转角度。
- 在 Ethan Eade 的网站上有一个优秀的 PDF 这里,其中包含群 SO(3)、SE(3) 和 SIM(3) 的闭式方程。
- 通过使用上面的无穷级数定义计算矩阵指数和对数,并在一些项之后截断。根据我的经验,当使用浮点数时,这并不稳定,因为您会很快开始处理非常小和非常大的数字,具体取决于您的输入矩阵。
- 通过数值积分计算指数。给定一个起点 \(x\),将其积分一段时间 t 与指数相同。从 Euler 到 Runge-Kutta 再到自适应方法,有很多方法可以计算数值积分。
陷阱
您应该注意以下几个问题。
陷阱 #1
旋转矩阵的对数将返回 -\(\pi\) … +\(\pi\) 中的 3D 旋转角度。更技术地说,矩阵有无限多个对数,每个对数对应于比前一个对数大 2\(\pi\) 的旋转角度。通常,矩阵对数代码将返回主对数,即 -\(\pi\) … +\(\pi\) 中的对数。这可能会导致插值包含旋转的变换时出现不连续性,例如来自人体关节的旋转(您可以将头从左肩上方看向右肩上方,并且旋转稍微超过 180 度)。
另一方面,四元数的对数返回 -2\(\pi\) … +2\(\pi\) 的较大范围内的 3D 旋转角度,这使得四元数更易于使用。
陷阱 #2
在使用对数时,请注意以下属性
\(log(AB) = log(A) + log(B)\)
仅 当 A 和 B 可交换时才成立,对于大多数变换来说情况并非如此。但是,实数总是可交换的,因此该属性适用于它们。将该属性应用于变换很诱人,但重要的是要记住它仅在 A 和 B 可交换时才适用。
陷阱 #3
与陷阱 #2 相关,您可能想要使用以下方式插值两个变换 A 和 B
\(interpolate(A, B, t) = e^{(1-t)log(A) + tlog(B)}\)
但要小心:这仅在 A 和 B 可交换时才有效,对于变换来说通常不是这样。否则,此插值既不是最短路径也不是恒定速度。
相反,插值从 A 到 B 的相对(也称为增量)变换,如下所示:
\(interpolate(A, B, t) = e^{log(B A^{-1}) t} A\)
但是,此方法仅适用于在两个变换之间进行插值,而不适用于混合两个以上的变换。
将矩阵可视化为向量场
如果您想知道如何将矩阵可视化为向量场,3Blue1Brown 关于矩阵指数的视频中有一个雄辩的解释。关于作为向量场的矩阵的部分很好地解释了这一点:
矩阵指数的导数
早些时候我们使用了属性 \(\dfrac{d}{dt}e^{A t} = A e^{A t}\)。为什么此属性为真并不明显,但它是解锁所有这些的重要组成部分。
对此推导的一个很好的参考是在教科书《现代机器人学》中。可以在此处找到该书的免费副本。请参阅该书中的等式 (3.43)。
矩阵指数定义为
\(e^{A t} = I + A t + \frac{1}{2}(A t)^2 + \frac{1}{3!}(A t)^3 + ...\)
那么 \(\dfrac{d}{dt}e^{A t}\) 是什么?如果我们取矩阵指数的展开定义的每一项的导数,我们有
\(\dfrac{d}{dt}e^{A t} = 0 + A + A^2 t + \frac{1}{2} A^3 t^2 + ...\)
拉出 A,然后我们有
\(\dfrac{d}{dt}e^{A t} = A*(I + A t + \frac{1}{2} (A t)^2 + ...) = A e^{A t}\)。
值得注意的是,矩阵 \(A\) 可以放在左边或右边,并且对于任何方阵,以下情况始终成立
\(Ae^{A t} = e^{A t}A\)
如《现代机器人学》中的等式 (3.44) 中所述。
评论
在 Github Issues 上留下对此帖子的评论 这里。