Skip to main content

Module step

Module step 

Source
Expand description

Scalar-polymorphic dynamics step: ABA + forward kinematics + per-vertex ground contact + semi-implicit Euler, generic over T: tang::Scalar.

Instantiated at f64 this is the plain forward rollout; instantiated at tang::Dual<f64> with one seeded input it yields an exact partial derivative of the step through every branch the f64 computation takes (dual comparisons act on the primal). The adjoint driver in super::adjoint uses those dual lanes as its per-step Jacobian oracle.

§Scope

Every joint type the model can express: revolute/hinge, prismatic/slide, fixed, spherical/ball (3 DOF) and free (6 DOF). Multi-DOF joints go through a generic ndof×ndof articulated-inertia solve and carry quaternion configuration, so nq ≠ nv in general — see DofLayout.

§State layout (nq ≠ nv)

Velocity coordinates are the joint’s motion subspace, one per DOF. Position coordinates are not: rotational sub-blocks of multi-DOF joints are stored as unit quaternions rather than exponential coordinates, so the configuration update is a Lie-group step rather than q += dt·v.

jointnqq layoutnvv layout
revolute / prismatic1[θ] / [d]1[θ̇] / [ḋ]
fixed0—0—
spherical4[w, x, y, z]3body-frame ω
free7[x, y, z, w, qx, qy, qz]6body-frame [ω; v]

[x, y, z] of a free joint is the successor origin in predecessor coordinates (the pos half of the Plücker transform), matching tang::SpatialTransform; its rate is therefore Eᵀ·v_lin, not v_lin. The quaternion p is the one with E = R(p), i.e. the coordinate transform predecessor→successor, matching the sign convention phyz_model::Joint::joint_transform_slice uses for its exponential coordinates. Since Ė = −ω× E, the update is p' = normalize(exp(−dt·ω) ⊗ p).

This differs from phyz_model’s own nq == nv exponential-coordinate packing (and from phyz::sim::SemiImplicitEulerSolver’s flat q += dt·v, which for a free joint would add angular rates to positional coordinates — not a doc link, since phyz depends on this crate rather than the other way round). Build the initial configuration with DofLayout::neutral_q and index it with DofLayout::q_offsets.

§Contact model

Contact here is a differentiable forward model of its own, not a wrapper around the GJK/EPA narrow phase: every collision-mesh vertex below the ground plane contributes an independent penalty force

f_z = max(0, k·depth − c·v_z),   depth = ground_height − x_z  (> 0 inside)

applied at the vertex (wrench about the body origin). This is the standard differentiable-simulation choice: the force is a smooth function of the vertex position wherever the contact is active, and the “contact set” is implicit in the smooth max rather than a combinatorial narrow-phase output. Friction is deliberately absent from v1 (its ‖v_t‖ kink sits at exactly the sticking state a resting gate converges to).

Structs§

CollisionMesh
A body’s collision skin: vertices in the body frame (m). Only the vertices matter to the per-vertex penalty model — triangles are not used.
DofLayout
The q/v coordinate split for a model under this module’s layout.
GroundContact
A ground plane at z = height with a linear penalty contact law.

Constants§

N_INERTIA_PARAMS
Number of inertia parameters per body: [m, cx, cy, cz, Ixx, Iyy, Izz, Ixy, Ixz, Iyz] — mass, COM (body frame), inertia about the COM. The packing (and the symmetric off-diagonal convention) matches vcad’s BodyMassProps::scalars() so a phyz gradient drops into the vcad seam unchanged.

Functions§

inertia_from_params
Rebuild a spatial inertia from the canonical 10-vector. Each off-diagonal scalar populates both symmetric entries, so seeding one parameter differentiates with respect to the symmetric pair — the same convention a central difference on the packed scalars probes.
inertia_params
Pack a body’s spatial inertia into the canonical 10-vector.
joint_nq
Number of position coordinates a joint type carries in this module’s layout: quaternions for the rotational sub-blocks of multi-DOF joints, so this is not ndof() in general. See the module docs for the packing.