关节:为什么 ragdoll 会抖成面条
全课骨架:物理模拟每一帧只做三件事 —— ① 对每个物体积分;② 找出接触(谁压着谁、压多深);③ 解约束(按质量分配地纠回来)。 第 3 课的接触、第 4 课的绳,都是"两个物体各自有位置、被一条规则拉住"。关节是同一类东西的第三种形态: 它不需要 ② —— 约束方程直接就写在两个刚体之间("这两个锚点必须重合"),而它的新东西是 物体开始会转:位置对了不够,还得让转动也听话。ragdoll 抖,抖的就是这一层。 保真度:真机可证(画面与读数都由本页自己的刚体求解器给出;对照物是复摆的闭式周期与关节误差,读者能拿笔复核)。
本页的原理与数值表照常可读(画面只是把同一个数画出来)。
(脚本启动中…)
定点观察:先看「5.1 点约束(含转动项)」,棒会像一个复摆那样摆;再点「5.1 变体:不含转动项」→ 看关节处的白点与棒的距离 → 不含转动项时锚点会分离、周期也乱(差多少见表①)。
本帧的读数
(脚本启动中…)
5.1 段 · 把两个锚点钉在一起
本段只增加一样东西:关节约束(一个点约束把一个刚体的局部锚点钉到世界锚点上;其余控件与本课各段一致)。 保真度:真机可证。接触要"先检测到才算数"(第 3 课),绳是"相邻粒子之间"(第 4 课); 而 ragdoll 的肩关节既没有接触、也不是粒子对 —— 它是两个刚体各自的局部锚点必须落在世界上的同一个点。 新的麻烦是:物体现在会转,所以约束的有效质量里除了 1/m,还要多一项 (r×n)²/I。
| 棒长 L (m) | 闭式复摆 2π√(2L/3g) (s) | 实测(含转动项)(s) | 相对误差 | 实测(不含转动项)(s) | 不含转动项时棒转过的角度 |
|---|
算式:匀质棒绕质心 I = mL²/12;锚点在端点 ⇒ 由平行轴定理绕锚点 I_锚 = I + m(L/2)² = mL²/3。
复摆的周期 T = 2π√(I_锚 / (m·g·d)),d = L/2 ⇒ T = 2π√(2L/(3g))。拿 L = 0.6 验算:
2π√(1.2/29.43) = 2π×0.20195 = 1.2689 s —— 表① 那一行(含转动项)实测 1.271,差 0.21%。
这条式子只在小角度成立(大角度周期会变长,修正是 O(θ²));本表用 0.15 rad(8.6°),修正量约 0.14%,可忽略。
不含转动项那一列是本段的正题,而且结论比预想的更硬:它不是"周期偏一点",是"根本没有周期" ——
实测棒转过的角度是 0.0000°。原因:位置级投影照样能把锚点拉到重合(所以看不到分离),
但没有 (r×n)²/I 这一项,冲量就无法产生角冲量 ⇒ 这条约束只能改平移、传不了力矩,
于是棒被钉在一个固定姿态上、只有质心被锁住 —— 一个关节如果传不了力矩,绑出来的就是木头人,不是骨架。
5.2 段 · 只能转到这儿:关节限位
本段只增加一样东西:角度限位(在同一个点约束之外,再加一条"相对角不许超出 [−θmax, +θmax]";其余控件一致)。 保真度:真机可证。5.1 把位置钉住了,可棒还能自由转一整圈 —— 真实的肘关节不是这样。 限位是同一种约束的一个更简单的版本:它落在角度上,不是位置上,所以有效质量只剩 1/I_A + 1/I_B(没有 (r×n)² 那类项)。
| θmax (°) | 实测最大偏离角 (°) | 差 (°) | 帧末越界占比 |
|---|
算式:限位这一条没有位置项,它的有效质量就是 1/I_A + 1/I_B(锚在世界时只剩 1/I_B = 12/(mL²))。
拿 L = 0.6、m = 1 验算:I = 1×0.36/12 = 0.03 kg·m² ⇒ 一次冲量要改变 1 rad/s 需要 0.03 N·m·s —— 这就是"让这根棒转起来多难"的度量。
表② 的读法:前两档的「差」是 1e−15 量级(也就是逐位咬住)—— 限位是硬约束,它把角度恰好压在 θmax 上;
第 4 行是特意留的对照:θmax = 45° 时实测只有 27.6°,因为棒在这个初速下的自然摆幅就是这么大、根本没撞到限位 ⇒ 限位不生效。
限位只在"本来会超"的时候才有作用,它不改变本来就合规的运动 —— 这一句是这一段最该记住的。
最后一列「帧末越界占比」四行都是 0.00%:这是位置级投影的功劳(越界当帧就被纠回来)。
本页如实标出限制:真正的引擎里硬限位配合速度级的恢复系数才不抖,本页没有做那一层,所以只报告"帧末"的越界占比。
5.3 段 · 让它自己转:motor
本段只增加一样东西:motor(驱动)(把限位换成"把相对角速度打到目标值";其余控件一致)。 保真度:真机可证。限位只能挡,挡不出动作。想让它自己转(风车、机器人关节、车轮)就得给一个目标: motor 就是"把相对角速度驱到 ω_target"的那条约束 —— 形式和限位一模一样,只把目标从"角度边界"换成"角速度值"。
| 目标 ω_target (rad/s) | 实测稳态 ω (rad/s) | 差 | 末态能量 (J) |
|---|
算式:一次冲量把相对角速度从 relW 打到 target,需要 λ = (target − relW) / (1/I_A + 1/I_B), 所以稳态时 relW 恒等于 target(残差只剩数值误差)。拿 target = 1.2 验算:稳态相对角速度就是 1.2 rad/s —— 表③ 那一行应当逐位对上。 第 4 列要如实看:能量在涨,因为电机在做功(W = τ·Δθ)—— 这是本课唯一一个"能量不守恒是应该的"段, 它的账不能拿守恒判据去卡(本页在门禁里把这一段标成"不查能量",改由"稳态值 = 目标值"这条断言来管)。
5.4 段 · 为什么 ragdoll 会抖
本段只增加一样东西:把关节串成链(节数 ≥ 2,其余控件一致)。 保真度:真机可证。5.1~5.3 都只有一条约束,一遍就解完了。真实角色是一串关节: 肩带着肘、肘带着腕 —— 而且求解器是按顺序一条一条解的,解完肩再解肘时,肩那一条又被弄歪了。 一遍扫描只把信息传一节,剩下的就是"抖"。
| 迭代次数 | 关节最大误差 (mm) | 链末端最低点 (m) | 相对上一档 |
|---|
算式:一遍自根向梢的扫描,只把"根的要求"传到第一节;要让信息走完 nb 节,至少要 nb 遍。
表④ 实测:迭代 1 → 2 → 4 → 8 时,稳态关节误差是 348.6 / 194.2 / 77.3 / 44.5 mm,
「相对上一档」是 0.557 / 0.398 / 0.576 —— 一路在缩,是持续收敛(不是"过某个次数就没事了")。
拿"每节 600 mm 长"当尺子验算量级:iters = 1 时误差 349 mm ≈ 半节棒长,肉眼就是"被拉断了";
iters = 8 时 44 mm ≈ 7% 节长 —— 仍然是能看见的面条感,这正是真引擎还要叠上子步与 warm starting 的原因(附录)。
另外两条如实标注:这是准静态加载(竖直起步、重力斜约 20°、加了与上一课同源的一点速度阻尼),
所以量到的是干净的稳态误差;「链末端最低点」用来交叉验证 —— 关节被拉开时链会"变长"、末端荡得更低(2523 → 2028 mm)。
课末:这五课其实是一件事
把五课的名字排在一起看:积分器(怎么把时间推下去)、接触(怎么发现两个物体碰上了)、
约束求解(怎么把它们纠回来)、软体(怎么把"必须"放松成"尽量")、关节(怎么把两个会转的刚体绑在一起)。
五课的求解器其实是同一条式子在不同约束上的重复:
算出这条约束的"有效质量"(1/m 加上转动项、再按质量倒数分配),乘上"违反了多少",一正一负地打回去。
接触、绳、关节、限位、motor 的差别只有两处:约束是关于什么量的(距离?角度?角速度?),
以及有效质量里有没有转动项。
如果只记一句话:物理引擎里没有"碰撞""绳子""关节"这些不同的东西,只有一堆约束与一个求解器;
那些名字是给美术与策划用的。本课题到此收口;附录里留了三样下一层的东西:子步与 warm starting、摩擦锥、以及关节的软约束与电机模型。