This document defines a phased refactor plan for separating kinematics, statics, and dynamics responsibilities while preserving the current DynamicsGraph public API.
Each phase is intended to be independently implementable by an agent.
Primary goals:
- Keep behavior stable while reducing duplicated logic.
- Make slice-level physics explicit (
Statics,Dynamics) and keep trajectory assembly inDynamicsGraph. - Make shared wrench/torque logic reusable by both statics and dynamics.
-
Kinematicsis a solver-oriented class with slice/interval/phase support.- Q/V-level graph construction and optimization helpers exist.
- See:
gtdynamics/kinematics/Kinematics.hgtdynamics/kinematics/KinematicsSlice.cppgtdynamics/kinematics/KinematicsInterval.cppgtdynamics/kinematics/KinematicsPhase.cpp
-
DynamicsGraphis a broad orchestrator/facade.- It already delegates q/v factors to an internal
Kinematicsinstance. - It still directly builds acceleration factors and dynamic wrench/contact factors.
- It also owns trajectory and multiphase collocation assembly.
- See:
gtdynamics/dynamics/DynamicsGraph.hgtdynamics/dynamics/DynamicsGraph.cpp
- It already delegates q/v factors to an internal
-
Staticsexists and is functional for slice-level static wrench solving.- Inherits
Kinematics. - Implements static wrench balance graph + solve/minimize methods.
- See:
gtdynamics/statics/Statics.hgtdynamics/statics/StaticsSlice.cppgtdynamics/statics/StaticWrenchFactor.h
- Inherits
-
Dynamicscurrently exists only as math utilities.- Contains
CoriolisandMatVecMult. - It is not yet a solver class.
- See:
gtdynamics/dynamics/Dynamics.hgtdynamics/dynamics/Dynamics.cpp
- Contains
-
Shared joint-side factor expressions already exist and are reused:
WrenchEquivalenceFactorTorqueFactorWrenchPlanarFactor- See
gtdynamics/factors/*.h.
DynamicsGraph still contains slice-level dynamics assembly that should move into a dedicated Dynamics solver layer, and there is partial overlap between statics/dynamics wrench graph building loops.
There is also a parameter hierarchy mismatch:
OptimizerSettingcurrently derives fromKinematicsParameters.Staticshas its own parameter type, but there is no shared statics/dynamics parameter base.Dynamics(as a future solver class) needs a parameter model aligned withKinematicsstyle.
-
Kinematics- Kinematic constraints and objectives (q/v level).
- Context support:
Slice,Interval,Phase,Trajectorywhere applicable.
-
Statics- Dedicated static solver for rest-state wrench equilibrium.
- Remains independent from
Dynamicsimplementation.
-
Dynamics(new solver class)- Dedicated dynamic solver for slice-level motion factors.
- Does not inherit from or compose with
Statics. - Owns:
aFactors(Slice, ...)(migrated fromDynamicsGraph)graph(Slice, ...)for dynamic-only wrench/contact factors that are not in the three statics-provided groups
-
DynamicsGraph- Backward-compatible facade and trajectory assembler.
- Delegates:
- q/v to
Kinematics - a-level slice assembly to
Dynamics - dynamics-level assembly as:
Staticsslice interface for the three shared groupsDynamics::graph(Slice, ...)for all remaining factors
- q/v to
- Owns collocation and multiphase stitching.
Parameter classes should follow the same style as KinematicsParameters: explicit typed parameter structs with inheritance for shared concerns.
Required target hierarchy:
KinematicsParameters(existing).MechanicsParameters(new): shared statics/dynamics parameters.- Typical shared fields:
gravity,planar_axis, and any shared wrench/joint/contact dynamics noise models.
- Typical shared fields:
StaticsParameters : MechanicsParameters.DynamicsParameters : MechanicsParameters.OptimizerSetting : DynamicsParameters.
Notes:
- This is a class hierarchy requirement, not a behavior rewrite.
- Existing constructor ergonomics should be preserved where possible.
DynamicsGraph::dynamicsFactors should obtain the following three factor groups through the Statics slice interface, not by direct standalone factor calls in the dynamics path.
These factor groups are:
WrenchEquivalenceFactorTorqueFactorWrenchPlanarFactor
Practical rule:
- The direct constructors above are allowed inside
Staticsimplementation. DynamicsGraph::dynamicsFactorsshould add these groups viaStaticsslice methods.Dynamics::graph(Slice, ...)must not add these three groups and should only add the remaining dynamic-specific factors.
- No redesign of factor math or noise model semantics in this refactor.
- No forced API break for existing
DynamicsGraphcallers. - No full optimizer framework rewrite.
- No broad Python API expansion until C++ layering is stable.
- No migration of linear FD/ID path in this plan (
linearDynamicsGraph,linearSolveFD,linearSolveIDremain out of scope).
- Keep each phase isolated. Do not implement future phases early.
- Preserve existing behavior and test expectations unless explicitly called out.
- Maintain compatibility with current wrappers unless phase says otherwise.
- Do not introduce inheritance/composition between
DynamicsandStatics. - In dynamics assembly paths, do not directly call
WrenchEquivalenceFactor,TorqueFactor, orWrenchPlanarFactor; obtain those groups via theStaticsslice interface. Dynamics::graph(Slice, ...)is dynamic-only delta: it must exclude those three statics-provided groups.- Enforce parameter hierarchy target:
OptimizerSettingmust derive fromDynamicsParameters.
Align statics/dynamics parameter types with Kinematics-style parameter modeling before moving factor builders.
In scope:
- Introduce a shared statics/dynamics base parameter class, e.g.
MechanicsParameters. - Define/adjust:
StaticsParameters : MechanicsParametersDynamicsParameters : MechanicsParameters
- Change
OptimizerSettinginheritance to:OptimizerSetting : DynamicsParameters
- Preserve behavior and defaults.
Out of scope:
- Factor-graph migration.
DynamicsGraphdelegation changes.
gtdynamics/statics/Statics.hgtdynamics/dynamics/Dynamics.hgtdynamics/dynamics/OptimizerSetting.h- optional
.cppfiles for constructor/default wiring.
- Parameter hierarchy matches the target model in this document.
- Existing call sites compile without behavior regressions.
- No factor construction logic is moved in this phase.
make -j6 testStatics.run(frombuild/)make -j6 testDynamicsGraph.run(frombuild/)
Create the first real slice-level API in Dynamics by migrating acceleration-related factor construction out of DynamicsGraph.
In scope:
- Introduce/expand
Dynamicsclass to exposeaFactors(const Slice&, ...). - Move implementation currently in
DynamicsGraph::aFactorsintoDynamics. - Keep behavior identical.
Out of scope:
- Changing factor math.
- Moving
dynamicsFactorslogic.
- Update:
gtdynamics/dynamics/Dynamics.hgtdynamics/dynamics/Dynamics.cppgtdynamics/dynamics/DynamicsSlice.cpp(new or equivalent)gtdynamics/dynamics/DynamicsGraph.cpp
Dynamics::aFactors(Slice, ...)exists and is used byDynamicsGraph.DynamicsGraph::aFactors(...)is removed or reduced to a thin wrapper.- Existing nonlinear behavior is unchanged.
make -j6 testDynamicsGraph.run(frombuild/)make -j6 testTrajectory.run(frombuild/, smoke for integration)
Move slice-level dynamic wrench/contact graph assembly into Dynamics.
In scope:
- Implement
Dynamics::graph(const Slice&, const Robot&, contact_points, mu). - Migrate only the non-statics-group part of current
DynamicsGraph::dynamicsFactors. - Keep these three groups out of
Dynamics::graph(...):WrenchEquivalenceFactorTorqueFactorWrenchPlanarFactor
- Keep
DynamicsGraph::dynamicsFactorsresponsible for obtaining those three groups viaStaticsslice methods and composing withDynamics::graph(...). - Keep behavior identical, including contact/friction handling.
Out of scope:
- Trajectory/collocation migration.
- Linear FD/ID path.
gtdynamics/dynamics/Dynamics.hgtdynamics/dynamics/Dynamics.cpp- New file(s), e.g.:
gtdynamics/dynamics/DynamicsSlice.cppgtdynamics/dynamics/DynamicsParameters.h(or nested inDynamics.h)
Dynamics::graph(Slice, ...)contains only factors not in the three statics-provided groups.Dynamicsremains independent ofStatics(no inheritance/composition).DynamicsGraph::dynamicsFactorscomposes:Staticsslice groups (WrenchEquivalenceFactor,TorqueFactor,WrenchPlanarFactor)Dynamics::graph(Slice, ...)delta.
Dynamics::graph(Slice, ...)does not directly add those three factor groups.- Existing callers are unaffected.
- Existing tests continue passing.
make -j6 testDynamics.runmake -j6 testDynamicsGraph.run
Turn DynamicsGraph into orchestration/facade for slice-level dynamics instead of owning the full implementation.
In scope:
DynamicsGraphcomposes/usesDynamicssimilarly to current internalKinematicsusage.dynamicsFactorsdelegates toDynamics::graph(Slice, ...)(or becomes a thin adapter).dynamicsFactorGraphusesDynamicsfor both a-level and dynamics-level slice assembly.dynamicsFactorsexplicitly combinesStaticsslice groups plusDynamicsdelta graph.- Preserve current signatures and behavior.
Out of scope:
- Removing current public methods from
DynamicsGraph. - Trajectory rewrites.
gtdynamics/dynamics/DynamicsGraph.hgtdynamics/dynamics/DynamicsGraph.cpp
DynamicsGraphstill passes all existing tests.- No behavioral diff for known tests and examples.
- TODOs about migrating to
Dynamics::graph<Slice>are resolved.
make -j6 testDynamicsGraph.runmake -j6 testTrajectory.runmake -j6 testPhase.run
Finalize delegation, remove redundant code paths, and resolve migration TODOs.
In scope:
- Remove migrated duplicate logic from
DynamicsGraph. - Resolve migration TODOs in
DynamicsGraphrelated toDynamics::graph<Slice>and statics reuse comments where no longer applicable. - Keep interfaces clean and explicit for future phases.
Out of scope:
- Major symbolic expression redesign.
- Changing numerical behavior intentionally.
- Update:
gtdynamics/dynamics/DynamicsGraph.cppgtdynamics/dynamics/Dynamics.hgtdynamics/dynamics/DynamicsSlice.cpp(or equivalent)
- No duplicated a/dynamics slice assembly remains in
DynamicsGraph. DynamicsGraphacts as facade/orchestrator for slice-level dynamics calls.- Tests pass with unchanged behavior.
make -j6 testStaticsSlice.runmake -j6 testDynamicsGraph.run
Mirror Kinematics context pattern for Dynamics and Statics where useful.
In scope:
- Add explicit support helpers for
Interval/Phasecomposition from slice-level builders where practical. - Keep trajectory assembly in
DynamicsGraph.
Out of scope:
- Removing existing trajectory API.
- Context expansion is additive and tested.
- No regression in multiphase planning paths.
make -j6 testPhase.runmake -j6 testTrajectory.runmake -j6 testDynamicsGraph.run
Expose stable new architecture pieces to wrappers only after C++ layering is stable.
In scope:
- Update wrapper interface (
gtdynamics.i) ifStaticsand newDynamicsAPIs are deemed stable. - Add/update docs and examples.
Out of scope:
- Early wrapper exposure before stability.
- Wrapper builds and tests pass (if wrapper changes are included).
- Docs reflect final class responsibilities and recommended usage.
make -j6 python-test(if wrapper touched)- Existing C++ test set for touched areas.
Use this exact prompt pattern when assigning:
- "Implement Phase X from
doc/kinodynamics-architecture-refactor-plan.md." - "Do not implement future phases."
- "Preserve behavior and public API unless the phase explicitly says otherwise."
- "Run the listed suggested tests for that phase."
- "Summarize:
- files changed
- any deviations from phase scope
- test results."
DynamicsGraphis a clean facade/orchestrator.Kinematics,Statics, andDynamicseach have clear solver responsibilities.- Shared wrench/torque infrastructure is reused, not duplicated.
- Existing behaviors remain stable per test suite.
- Documentation and wrappers (if updated) match the final architecture.