Body Stage 1 collapse root-caused + fixed: NaN in torso servo math (GetAngle = 2*acos(W) at W=1.0000001), not solver stacking

- BodyMath.RotationError: atan2-based shortest-arc error (no NaN, no GetAxis gain collapse
  inside 0.16 deg); used by PhysicsTorso orientation servo and PhysicsHands rotation lock
- BodyMath.ResetIfNonFinite escape hatch on torso + hands
- PhysicsHands.Drive extracted (static) so spikes run the real hand chase
- CollapseSpike: deterministic repro (slow yaw drift across a parked chest, 7/40 NaN on old
  code) + solver-stacking scenarios (seated chest in table, two-hand-welded 50 kg anvil,
  real PropShop) — 49/49 clean after fix, x3; all other spikes PASS x3
- Failure brief RESOLUTION section; HANDOFF/AGENTS updated

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
FoxyzandClaude Opus 5.5 committed 2026-10-01 23:55:46 +03:00
1 parent ce1e0e3b0c
commit cb518999e5
9 files changed
+466 -47

No files matched your search

+6 -7
View File
@@ -92,6 +92,9 @@ public partial class PhysicsTorso : Node
_mesh.Visible = true;
}
if (BodyMath.ResetIfNonFinite(_body, new Transform3D(_torsoBasis, target), "PhysicsTorso"))
return;
if (_body.GlobalPosition.DistanceTo(target) > SnapDistance)
{
_body.GlobalTransform = new Transform3D(_torsoBasis, target);
@@ -109,13 +112,9 @@ public partial class PhysicsTorso : Node
force = force.Normalized() * MaxForce;
_body.LinearVelocity += force / TorsoMass * dt;
Quaternion qErr = _torsoBasis.GetRotationQuaternion()
* _body.GlobalTransform.Basis.GetRotationQuaternion().Inverse();
if (qErr.W < 0f)
qErr = new Quaternion(-qErr.X, -qErr.Y, -qErr.Z, -qErr.W);
float angle = qErr.GetAngle();
Vector3 spinAxis = angle > 1e-5f ? qErr.GetAxis() : Vector3.Up;
Vector3 spinDesired = spinAxis * Mathf.Min(angle * OrientKp, OrientMaxSpin);
Vector3 rotErr = BodyMath.RotationError(_torsoBasis.GetRotationQuaternion(),
_body.GlobalTransform.Basis.GetRotationQuaternion());
Vector3 spinDesired = (rotErr * OrientKp).LimitLength(OrientMaxSpin);
Vector3 wErr = spinDesired - _body.AngularVelocity;
float maxDeltaW = OrientMaxAlpha * dt;