20. Ld5 — free 3D body (stunt: jump & vertical loop)¶
Learning objectives¶
After this chapter you can:
- State why L1–L3 (planar pose + quasi-static attitude) cannot represent a ramp jump or a vertical loop, and what extra degrees of freedom Ld5 adds.
- Write the Newton–Euler equations of motion for a 6-DOF rigid body with a unit quaternion attitude, in the body frame VDSim integrates.
- Describe the penalty contact model — penetration spring–damper normal force, the airborne condition, and how tire forces are built in the wheel contact frame so they remain valid when the car is inverted.
- Derive the friction-free vertical-loop entry speed \(v_{\min} \approx \sqrt{5 g R}\) and explain why the shipped loop is an emergent (not enforced) centripetal balance.
- List the shipped tuning/limitations that make the loop a robust demo rather than a quantitatively validated maneuver.
Prerequisites¶
- Chapter 02 — rigid-body dynamics, inertia tensor, angular momentum.
- Chapter 06 — Ld3 fourteen-DOF: the planar + quasi-static vertical model Ld5 replaces with a true 3D body.
- Chapter 11 — RK4 substep integration; quaternion renormalization.
- Chapter 19 — LuGre tire (the default force law used at each contact).
- Design note —
design/V0.4_SLOPE_JUMP_DYNAMICS.md(contact architecture) andVALIDATION.md"Stunt" limits.
Source: core/src/free_3d_dynamics.cpp (Free3DDynamics, level() == L5_Stunt),
contact in core/src/contact_providers.cpp (RampGround, LoopGround).
20.1 Motivation — when planar attitude is not enough¶
L1–L3 carry pose as \((x, y, \psi)\) on a ground plane; body roll/pitch in L3 are quasi-static perturbations about a static equilibrium, and the vertical state is a small heave around ride height. That is correct for normal road driving but breaks for stunts:
- A ramp jump needs a true world-\(z\) with full gravity and an airborne phase (all tire forces vanish, the body follows a ballistic arc).
- A vertical loop puts the body at arbitrary attitude — inverted at the apex — so a small-angle attitude and a "down is \(-z\)" tire model are both invalid.
Ld5 (Free3DDynamics) is a free 6-DOF rigid body: world position, a unit
quaternion attitude, body-frame linear and angular velocity, plus the four wheel
spin states. Flat L2/L3 road behaviour is unchanged — Ld5 is opt-in via
level = L5 and a stunt scenario.
20.2 State and frames¶
| Symbol | Code | Frame | Meaning |
|---|---|---|---|
| \(\mathbf{p}\) | state.position |
world | CG position |
| \(\mathbf{q}\) | state.orientation |
world←body | unit quaternion attitude |
| \(\mathbf{v}\) | state.velocity |
body | linear velocity |
| \(\boldsymbol{\omega}\) | state.angular_velocity |
body | angular velocity |
| \(\omega_i\) | state.wheel_spin[i] |
— | wheel spin, \(i \in \{\)FL,FR,RL,RR\(\}\) |
Let \(\mathbf{R} = \mathbf{R}(\mathbf{q})\) be the body→world rotation. Wheel \(i\) has a fixed body-frame mount \(\mathbf{r}_i\) (track/wheelbase offsets, with vertical \(-(h_{cg}-R_w)\) so the hub sits a wheel radius below the CG). ISO 8855 RH applies; wheel order FL=0, FR=1, RL=2, RR=3 (Chapter 01).
20.3 Equations of motion (Newton–Euler, body frame)¶
Collect the world-frame resultant force on the CG,
where \(\mathbf{F}_i^{w}\) is the contact force of wheel \(i\) (§20.4–20.5) and rolling
resistance \(F_{rr}\) acts along the body longitudinal axis. The aero force
\(\mathbf{F}_a^{b}\) is a body-frame sum over aero pieces \(j\) (Chapter 26), with
\(q = \tfrac12 \rho\, v_x |v_x|\) and \(\rho\) = air_density (default 1.225 kg/m³):
each acting at \((x_j, y_j, z_j)\) in the vehicle-file axes (origin on the ground below the front axle). Since 0.8.0 the pieces are:
- Elements path (
aero.elements): one piece per element at its own \((x_j, y_j, z_j)\). - Four-key path: downforce $\text{ClA} = $
aero_lift_front·\(A\) at the front axle (\(x = 0\)) andaero_lift_rear·\(A\) at the rear axle (\(x = -L\)), both at ground height (\(z = 0\)); drag $\text{CdA} = $aero_drag_coeff·\(A\) at the CG, or ataero_drag_heightwhen that key is set. Before 0.8.0 L5 ignored the two lift keys.
The body-frame linear acceleration follows from differentiating \(\mathbf{v}^{w} = \mathbf{R}\mathbf{v}\) in a rotating frame:
The attitude obeys Euler's equation with a diagonal inertia \(\mathbf{I} = \mathrm{diag}(I_x, I_y, I_z)\) and the gyroscopic coupling:
with \(\mathbf{F}_i^{b} = \mathbf{R}^{\!\top}\mathbf{F}_i^{w}\) the body-frame wheel force, \(M_{z,i}\) the tire aligning moment about the body yaw axis, and \(\mathbf{r}_{a,j} = (x_j + a,\; y_j,\; z_j - h_{cg})\) the arm from the CG (\(a\) = CG behind the front axle) to aero piece \(j\). A piece at the CG has zero arm. See Chapter 26 for the inputs and §26.5 for the aero contract.
Drive and brake torque reaction (0.8.1)¶
The wheel torque has an equal-and-opposite reaction about the steered wheel-spin axis:
| Parameter | Default | Accepted values | Effective target in 0.8.1 |
|---|---|---|---|
drive_reaction_front, drive_reaction_rear |
sprung |
sprung, unsprung |
chassis |
brake_reaction_front, brake_reaction_rear |
unsprung |
sprung, unsprung |
chassis; explicit unsprung warns once |
The unsprung request is reported as equivalent until the linkage route is validated.
Penalty and spatial-strut paths therefore return the reaction through the chassis couple.
Hard-joint DAE suspension also falls back to the same sprung result and warns once per
process when unsprung is explicit. LuGre wheel-spin dynamics use the clamped tire
longitudinal force for the tire reaction torque.
Position and attitude integrate as
where \(\delta\mathbf{q}\) is the axis–angle increment over step \(h\) and the quaternion
is renormalized every step. Each wheel spin integrates \(\dot\omega_i = T_{\text{net},i}/I_i\)
(drive + brake − tire reaction; open-diff carrier coupling per drivetrain_inertia.hpp,
Chapter 06).
20.4 Contact: penalty normal force and the airborne condition¶
A contact provider returns, per wheel, a surface point, outward unit normal
\(\hat{\mathbf{n}}\), penetration \(\delta\), and an is_valid flag. The hub world
position is \(\mathbf{p} + \mathbf{R}\mathbf{r}_i\) and the hub velocity
\(\mathbf{v}^{w}_{\text{hub}} = \mathbf{R}(\mathbf{v} + \boldsymbol{\omega}\times\mathbf{r}_i)\).
The normal force is a clamped spring–damper,
i.e. near-critical vertical damping. The wheel is airborne when
!is_valid || penetration <= 0; then \(F_{z,i}=0\) and §20.5 contributes no shear
force, so the body is a projectile under gravity (and aero). The clamp at
\(F_z^{\text{cap}}\) and the floor at \(0\) are what let the same code carry both a
landing impact and a free-flight phase without branching the EOM.
20.5 Tire forces at arbitrary attitude¶
Because the body may be inverted, the wheel longitudinal/lateral axes are built in the contact plane, not from a global "forward/right":
- Project the body forward axis onto the contact plane: \(\mathbf{t}_{\text{long}} = \widehat{\mathbf{f}_b - (\mathbf{f}_b\!\cdot\hat{\mathbf{n}})\hat{\mathbf{n}}}\), then rotate it by the road-wheel steer angle \(\delta_i\) about the contact normal (Ackermann split on the front axle).
- Lateral axis \(\mathbf{t}_{\text{lat}} = \hat{\mathbf{n}}\times\mathbf{t}_{\text{long}}\).
- Slip ratio \(\kappa_i\) and slip angle \(\alpha_i\) use the hub velocity projected onto \((\mathbf{t}_{\text{long}}, \mathbf{t}_{\text{lat}})\) and the spun wheel speed \(R_w\omega_i\).
- \(F_x, F_y\) from the tire law (LuGre default, Chapter 19; MF96 + low-speed blend when LuGre is off), clipped to the friction ellipse \(\sqrt{F_x^2+F_y^2}\le \mu F_z\).
- World force $\mathbf{F}i^{w} = F_x\,\mathbf{t}}} + F_y\,\mathbf{t}_{\text{lat}
- F_{z,i}\,\hat{\mathbf{n}}$.
Building the frame from \(\hat{\mathbf{n}}\) is the key step that makes traction at the loop apex behave the same as on flat ground.
20.6 Vertical loop: entry speed and emergent centripetal balance¶
For a loop of radius \(R\) (hub circle), the classic friction-free minimum speed comes from two conditions. At the apex, gravity must supply at least the centripetal acceleration without the track pulling outward:
Energy conservation from the bottom (height gain \(2R\)) gives \(v_{\text{bot}}^2 = v_{\text{top}}^2 + 2g(2R)\), so
which is exactly LoopGround::v_min(). This is a lower bound: real entry needs
more for tire/rolling losses. VDSim does not impose the centripetal condition —
it falls out of the penalty normal force keeping the wheels on the circular
surface. Below \(v_{\min}\) the car decelerates up the back side and falls away; it
is never teleported.
To keep the contact numerically robust on the closed loop, the shipped path
(surface_id == 2) applies: a higher \(F_z\) cap (\(18\,mg/4\) vs \(6\,mg/4\) on flat),
a capped penetration (\(\le 0.016\) m) with softened \(k_t\), a track-tangent
longitudinal-slip estimate near the apex (where the projected forward speed is
small), and — with the guide rail off (default) — a radial penalty force that
pulls each hub toward the loop surface radius \(R + R_w\). These keep the loop
stable but make it a plausible demo, not a validated load case (§20.8).
20.7 Numerical integration¶
step() lowers the control input to L4, then runs \(N\) substeps of size
\(h = \Delta t / N\) with \(N = \lceil \Delta t / \texttt{max\_substep\_dt}\rceil\)
(capped by max_substeps). Each substep is RK4 over \((\mathbf{p}, \mathbf{v},
\boldsymbol{\omega}, \omega_i)\); the quaternion is advanced from the averaged
\(\boldsymbol{\omega}\) and renormalized in apply(). Stunt scenarios use a small
substep (high contact stiffness is stiff) — see realtime_server flags in the
v0.5 terrain note.
20.8 Shipped status and limitations¶
- Default road sims are unaffected. Ld5 is a separate plant (
create_stunt_dof);level = L5+ a stunt scenario is required to reach it. - Validation tier. Jump (
Stunt.JumpAirborneInterval,JumpLandingNoSink), loop completion and contact (Stunt.FreeLoopCompletesLap,LoopMidArcKeepsContact,LoopSlipAngleFrontRearBalanced,LoopAccelWheelSpinBounded), and flat-ground L5 driving (tests/integration/ test_l5_driving.cpp) are green. There is no quantitative cross-validation of loop contact loads — the stiffened/capped contact and radial penalty are robust-tuned, not measured (seeVALIDATION.md"Stunt"). - Longitudinal reaction limits (0.8.1). Penalty and spatial-strut paths return
drive/brake torque reactions through the chassis. An explicit
unsprungtarget on a hard-joint DAE path is treated assprungwith a once-per-process warning. Spatial-strut axle load transfer remains about 1–3% above the analytical target and is tracked as an informational known failure pending the 0.8.2cg_heightcorrection and revalidation. - Not yet (→ v0.5): general curved/banked track (
CurvedGround, banked oval) and a GUI panel to author stunt scenes; today the GUI renders loaded ramp/loop scenes only. Seedesign/V0.5_TERRAIN_L5.md.
Cross-references¶
06_ld3_fourteen_dof.md— planar + quasi-static vertical model.19_lugre_dynamic_tire.md— default contact force law.design/V0.4_SLOPE_JUMP_DYNAMICS.md— contact target architecture.design/V0.5_TERRAIN_L5.md— Ld5 generalized to arbitrary terrain.