转动的物理计算#
从力矩说起#
“力矩”的定义是相对于“点”的,而不是“轴”。这暗示了力矩的方向并不总是沿着转轴。比如 (1,0,1) 处有一个质量为1的质点,绕 z轴 逆时针旋转,则其相对于原点的力矩为 α(−1,0,1),可以看到除了轴向,还有一个 −x 方向的分量。反作用力说明原点在此刻有一个向右的力矩,如果转轴和地面(不会动,可以提供任意力)在原点处通过一个铰链连接,启动时(从0加速)会看到轴杆向 −y 偏转。但如果轴杆在 (0,0,1) 处通过铰链和地面连接,铰链点算出来就只有轴向力矩,所以不会偏转。虽然也会受到向 +x 的力,但这个力过轴,无力矩。
这个不沿着轴向的力矩的朝向和物体的位置有关,而轴向的力矩始终沿着转轴——这就是电机要提供的力矩。所以计算电机的理论输出只需要考虑轴向的力矩,即投影到轴向。可以先算出总的力矩,再投影到轴向(更通用的方法);也可以算的时候就只在投影平面上算(高中物竞的做法)。
转动惯性张量推导#
做物理仿真需要计算力矩,但是算力矩需要的转动惯量和质量分布、参考点的位置都有关。一方面质量分布没法存,另一方面每次根据质量分布算转动惯量也很麻烦。有没有什么解决方法?那就是转动惯性张量 I。它是一个3x3的矩阵,描述了物体在各个方向上的转动惯量。以前学物理时算转动惯量只算了沿着某个轴的,这里拓展到相对于某个点、任意方向。具体推导如下:
-
角加速度和线加速度的关系:a=α×r
-
力矩 τ=r×F=r×ma=m⋅(r×(α×r))=m⋅(r×α×r) (叉乘点乘都不满足结合律,但是这里恰好相等)
如果先投影后算,这里的 r 就是转轴到质点的位移;否则就是参考点到质点的位移
-
带入计算:r×α×r=ry2+rz2−rxry−rxrz−rxryrx2+rz2−ryrz−rxrz−ryrzrx2+ry2αxαyαz=(rTrE−rrT)α
-
记上面这个矩阵为 M,对于微元(单质点) dm 的惯性张量就是 dI=Mdm。积分就能得到整个物体的惯性张量 I=∫dI=∫Mdm
-
力矩 τ=I⋅α,好比 F=(m⋅E)⋅a
可以看到 I 的取值和坐标系有关。已知一种坐标系下的 I,如何推理出任意坐标系下的 I 呢?坐标系变换可以视为先旋转再平移(不考虑空间变形):
- 旋转:即坐标乘以一个 旋转矩阵 R,则 r′=Rr,所以
I′=∫(Rr)T(Rr)E−(Rr)(Rr)Tdm=R∫rTrE−rrTdmRT=RIRT
- 平移:即坐标加上一个平移向量 d,则 r′=r+d,所以
I′=∫(r+d)T(r+d)E−(r+d)(r+d)Tdm=I+m(dTdE−ddT)
已知某坐标系下物体的 I0,要求绕某个点的力矩,更实际的做法为:
- 在 I0 的定义坐标系下,直接平移算转动惯量,然后算出力矩(向量)
- 将力矩进行旋转对齐到观察坐标系(不要理解为 I 的旋转;仅仅是力矩的坐标变换)
mujoco 中就是用 <inertial> 定义了 I。值得注意的是 diaginertia 可以只写对角线;这意味着其他位置都是0。由于 I 是对称矩阵,进行特征分解后一定长这样:I=RTdiag(λ1,λ2,λ3)R,其中 R 是旋转矩阵。根据上面的旋转变换公式,相当于只要进行旋转就可以得到一个只有对角线的 I′。此时的方向(特征向量)叫做惯性主轴,三个值叫做惯性主轴转动惯量。仅当 α 的方向为某个惯性主轴时,力矩才和角加速度同向(特征值的含义)。
惯性张量的其他用途:角动量和角速度#
用来实现角动量 (J) 和角速度 (ω) 的转换。下面考虑微元 dm 的角动量:
dJ=r×vdm=r×(ω×r)dm=(r×(ω×r))dm
这里又出现了 r×(?×r) 的形式,由上面的推导可知,可以变为 (M⋅?);再从微元积分得到整个物体的角动量:
J=∫dJ=∫Mdm⋅ω=I⋅ω
基础的物理运动计算#
复习一下物理。无阻力情况下,给某物体某个位置施加一个冲量 p,如何求解之后的运动状态?
首先看成质点,用动量定理求平动速度:v=mp。然后算绕质心的转动。先计算转动张量 I,然后算角速度 ω=I−1⋅J,其中 J=r×p 是角动量。最后根据速度和角速度更新位置和姿态。
为什么计算转动时算的是绕质点的?最重要的原因是省去了惯性力矩。
如果有约束,比如绕某个轴,直接以该轴算转动即可,不用管质心。注意,此时杆子会给予动量,因此不能用质点的动量定理算瞬时速度、再算角速度。
四元数和旋转#
这里讲的很清楚,下面是精练版。
三维空间旋转推导#
先沿着转轴 n(限定为单位长度)将待旋转分量 x 分解为平行分量 x∥ 和正交分量 x⊥:
x∥x⊥=(x⋅n)n=x−x∥
只要表示出垂直分量旋转后的新垂直分量 x⊥′,就能得到旋转后的向量 x′=x∥+x⊥′。垂直分量的旋转在 n 的垂直平面中进行,要表示这个平面内的向量,需要两个正交基,那就选择 x⊥ 和 (n×x) (利用了叉乘的性质)。假设 x 和 n 的夹角为 θ,则旋转 α 后 x⊥′ 的长度保持为 ∥x⊥∥=∥x∥sinθ,将两个正交基归一化并投影后可以得到公式:
x⊥′=∥x⊥∥⋅(cosα⋅∥x⊥∥x⊥+sinα⋅∥n×x∥n×x)=cosα⋅x⊥+sinα⋅∥n×x∥∥x⊥∥⋅(n×x)=cosα⋅x⊥+sinα⋅(n×x)
这个过程中把 sinθ 消去了。最终可以改写为矩阵的形式(略)
用四元数表示#
四元数最少的定义:
- 形式:q=w+xi+yj+zk
- 虚数单位:i2=j2=k2=ijk=−1
表示三维空间的点时,让实部为0,虚部系数为三维坐标。
绕轴 n(单位向量)旋转 α 的四元数为:
q=cos(2α)+nsin2α
此处 n 是 n 的四元数表示。将此旋转作用到向量 x 的结果为:
x′=qxq−1
这个“夹心”的形式很眼熟:用矩阵描述空间变换也有类似的形式:R=MR轴M−1。不过矩阵作用的是变换本身,改变的是观察的坐标系,而四元数直接作用在向量上。
为什么用四元数?可以解决欧拉角的万向锁问题和插值问题、参数比矩阵少。虽然自由度都是3,但四元数比欧拉角多一个维度,避免了奇点,天然平滑。在实际引擎中三者都有使用:
- 美术/策划用欧拉角调参数(因为看得懂)。
- 底层运算(骨骼插值、姿态计算)用四元数(因为平滑高效)。
- 最终送入GPU渲染前,把四元数转成旋转矩阵(因为显卡只认矩阵)。
约束求解#
Mujoco Computation 文档 的开篇就提到了“LCP”——线性互补问题(Linear Complementarity Problem)。这里主要讲怎么将约束问题转变为LCP问题。
LCP 问题的定义#
一维线性互补问题:
y=ax+by≥0,x≥0xy=0
显然解为直线在两个坐标轴上的交点。
高维线性互补问题:
y=Ax+by≥0,x≥0xTy=0
怎么高效求解就不学了。
约束问题#
已知当前时刻的速度 vt,根据牛二,下一时刻的速度为:
vA,t+1=vA,t+Δt⋅a=vA,t+Δt⋅mAfext,A−fcontact
vB,t+1=vB,t+Δt⋅a=vB,t+Δt⋅mBfext,B+fcontact
这里只考虑一维情况下的接触问题,不考虑旋转。
接触力 fcontact 必须遵循两个物理规则:
- 每个物体上的法向力 fn 必须指向物体内部,定义这个方向为正,则 fn≥0,也就是只能推,不能拉。
下面定义接触法向 n 为 A 指向 B,则 A 受到的法向力为 −fnn,B 受到的法向力为 fnn。
- 切向力 ft 必须满足摩擦定律:∥ft∥≤μ∥fn∥。这是非线性的,这里先不考虑。
物体(只考虑刚体)之间的接触间隙 ϕ 必须满足 ϕ≥0,也就是不能穿透。
ϕ 和 fn 的关系是互补的:如果 ϕ>0,则 fn=0;如果 fn>0,则 ϕ=0。也就是:
ϕ≥0,fn≥0,ϕ⋅fn=0
接触间隙的变化公式如下:
ϕt+1=ϕt+Δt⋅(vB,t+1−vA,t+1)Tn=ϕt+Δt⋅(vB,t−vA,t)Tn+Δt2⋅(mBfext,B+fcontact−mAfext,A−fcontact)Tn=ϕt+Δt⋅(vB,t−vA,t)Tn+Δt2⋅(mBfext,B−mAfext,A)Tn+Δt2⋅(mA1+mB1)fn=b+afn
此时可以看到有线性关系。对于系数 a,当一方无法移动时,只要定义质量无穷大,此时 m1=0。
这样就凑成了一个 LCP 问题。用求解器可以算出 fn,再代入上面的公式就能算出下一时刻的速度。
加上了转动也就是改变一下接触点的速度公式。
多物体、多接触点,系数 a 变成了矩阵 A,非对角元素就体现了“别的接触点对本接触的外力影响”。这个矩阵好复杂,不过实际求解也不会直接构造这个矩阵,而是通过物理规则绕过。