data_classes — core sim records & dataclass philosophy
Purpose
The per-step record types the control loop passes around:
State(live q, v + circumcentroidal
views),Desired(reference targets), the actual-kinematicsKinematicsCache, theForceCache
control effort,Gains, and the groupedStepLog. Each carries its own field-normalization so
call-sites stay thin.
Role in the system
- Consumed across the control chain: com_controller / breve_controller build a
Desiredand
readStateeach tick, then emit aForceCacheof control effort. - The guidance chain (com_guidance → base_guidance → ee_guidance, guidance_rollout) builds
theDesiredtargets. State.update_viewscalls into robot (GiordanoRobot.Gamma) to refresh the cached reduced views.- All records subclass
Packagefrom infra; field defaults come from data_class_helpers
(default_flat_zero,default_z,default_eye). StepLogis the per-step serialization that feeds the logger → data_analyzer pipeline.enum_from_valuecoerces YAML config strings into enums for orchestrator /analysis/statistics.py.
Inputs / Outputs
- In: raw
q, varrays, motion records from robot, YAML gain specs, reference targets. - Out: normalized column/unit/matrix fields; stacked views (
v_breve,K_breve,D_breve);
grouped log packages.
Key classes & methods
State— live (q, v) + cachedv_c / omega_b / nu_e_oplus—utils/data_classes.py:103update_views— refresh the circumcentroidal views viaΓ(q)·v—:124v_breve— reduced velocity (base rate over reduced EE twist) —:134
Desired— reference targets, normalized to col3 / unit / mat3 in__post_init__—:144KinematicsCache— cached actual kinematics for the (q, v) it was built from;matches/update—:180ForceCache— control-effort record (f_c, τ_b⊕, ω_e⊕, f_b, τ_b, τ) —:66Gains— gain specs → 3x3 matrices, with stackedK_e/K_breve(etc.) views —:315StepLog— one step’s grouped record (state, tracking pairs, effort, diagnostics) —:246enum_from_value— coerce a raw value/list into an enum (lowercased) —:54EEKinematicsCache = KinematicsCache— back-compat alias still imported by ee_guidance —:311
Footguns
Prefer MUTABLE (non-frozen) dataclasses
Frozen dataclasses force
object.__setattr__in every__post_init__; reserve frozen for true
value objects. Freeze decisions are a Codex-era sensitivity — always explain the choice and expect
questions. (utils/INSIGHTS.md[philosophy])
One owner per field — never hand-repeat field-name strings
If a record gains or loses a field, only its owner or its named normalizer changes. A long,
dict-like constructor is a smell: make a named normalizer owned by the relevant module, and iterate
__dataclass_fields__rather than listing names by hand. (utils/INSIGHTS.md[convention])
Normalization belongs in
__post_init__, not at the call siteRecords carry their own field-shaping (flat→col3, unit vectors, 3x3 matrices) so consumers stay
thin.DesiredandKinematicsCachereshape every field by name prefix; don’t re-normalize
upstream. (utils/INSIGHTS.md[philosophy])
Pseudocode (a control step’s record flow)
des = Desired(p_c, z_b, p_e, z_e, ...) # __post_init__ → col3 / unit / mat3
st = State(q, v) # flattened; views None until refreshed
st.update_views(robot) # Γ(q)·v → v_c, omega_b, nu_e_oplus
... controller produces ForceCache ...
log = StepLog(desired=des, actual=cache) # groups (actual, desired) pairs + effort by subsystem
Related
infra · data_class_helpers · robot · breve_controller · com_controller · ee_guidance · data_analyzer · terminology