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.
| joint | nq | q layout | nv | v layout |
|---|---|---|---|---|
| revolute / prismatic | 1 | [θ] / [d] | 1 | [θ̇] / [ḋ] |
| fixed | 0 | — | 0 | — |
| spherical | 4 | [w, x, y, z] | 3 | body-frame ω |
| free | 7 | [x, y, z, w, qx, qy, qz] | 6 | body-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§
- Collision
Mesh - 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/vcoordinate split for a model under this module’s layout. - Ground
Contact - A ground plane at
z = heightwith 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’sBodyMassProps::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.