feat: per-body compile-time BodyModel on SimModel - #46
Conversation
Add a body-scoped static model container to SimModel, keyed by SceneObjsCfg field name. Each BodyCfg resolves once at compile time into joint/link names, joint limits, a body-scoped actuator view, and a baked init snapshot: base placement, init joint angles from the designated RobotCfg.init_key_pose, and link poses from one forward-kinematics evaluation at the default placement (world frame, xyzw quaternions). Core owns the contract: assemble_body_model/resolved_key_pose own key-pose permutation into body joint order plus existence/coverage validation and the actuator subset derivation, so backends only supply engine facts. The MotrixSim adapter evaluates one batch-1 FK for all bodies at once (each body's overrides touch disjoint joints) and reuses the engine Body object APIs for joint DOF indices and link indices. SimModelQueryCompiler becomes SimModelCompiler.compile(scene, queries): both inputs that meet in SimModel are now contract-level, making bodies an abstract obligation instead of a convention. SimBackend.compile_model keeps its env-facing signature by retaining the scene; backends must call super().__init__(). Model-surface types (ActuatorSpec, GeomSpec, BodyModel, SimModel) now live in sim/model.py next to the queries, removing the TYPE_CHECKING cycle in both directions.
There was a problem hiding this comment.
🟡 Changes recommended
A test backend still implements the old compiler compile(queries) signature, which will raise a TypeError if compile_model() is invoked under the new compile(scene, queries) contract.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR implements per-body compile-time BodyModel data on SimModel, shifting repeated “compile-time facts” (init key pose joint angles, per-body actuator views, and init FK snapshots) into a backend-neutral model surface assembled once during model compilation.
Changes:
- Introduces
BodyModeland addsSimModel.bodiesto hold per-body static model data baked at compile time. - Replaces
SimModelQueryCompilerwithSimModelCompiler.compile(scene, queries)and updates backends/callers to useSimBackend.compile_model(...). - Adds/updates tests to cover
BodyModelassembly, init snapshot baking, and the updated compile API.
File summaries
| File | Description |
|---|---|
| wiki/design/manager/sim-backend.md | Updates design doc to reflect model_compiler and the new compile contract. |
| motrix_envs/tests/test_scene_cfg.py | Migrates test env setup to sim.compile_model(...). |
| motrix_envs/tests/test_quadruped_walk.py | Updates ActuatorType import to sim.model. |
| motrix_envs/tests/test_humanoid_walk.py | Updates expected exception semantics for compile-time key-pose validation. |
| motrix_envs/tests/test_action_space.py | Updates ActuatorType import to sim.model. |
| motrix_envs/src/motrix_envs/locomotion/wbt/mdp/action.py | Updates actuator model-surface imports to sim.model. |
| motrix_envs/src/motrix_envs/locomotion/quadruped/walk_np.py | Updates ActuatorType import to sim.model. |
| motrix_envs/src/motrix_envs/locomotion/humanoid/walk_np.py | Updates ActuatorType import to sim.model. |
| motrix_envs/src/motrix_envs/locomotion/action_space.py | Updates ActuatorType import to sim.model. |
| motrix_env_mujoco/src/motrix_env_mujoco/backend.py | Updates backend API to call SimBackend base init and expose model_compiler. |
| motrix_env_motrixsim/tests/test_sim_data_provider.py | Renames model_query_compiler → model_compiler in test backend scaffold. |
| motrix_env_motrixsim/tests/test_motrixsim_backend.py | Updates tests to use model_compiler.compile(scene, queries) and adds init snapshot coverage. |
| motrix_env_motrixsim/src/motrix_env_motrixsim/runtime.py | Implements MotrixSimModelCompiler and assembles SimModel.bodies via one FK pass. |
| motrix_env_core/tests/test_registry.py | Updates registry test backend to new model_compiler/compile signature. |
| motrix_env_core/tests/test_model_query_dispatch.py | Updates dispatch test to new compiler name/signature and SimModel import location. |
| motrix_env_core/tests/test_manager_sim_backend.py | Updates fake backend compile signature and model imports. |
| motrix_env_core/tests/test_direct_env_sim_backend.py | Updates fake backend compile signature and model imports. |
| motrix_env_core/tests/test_body_model.py | Adds contract tests for assemble_body_model (permutation, validation, init snapshot). |
| motrix_env_core/src/motrix_env_core/sim/model.py | Defines BodyModel/SimModel surface and replaces query compiler with SimModelCompiler. |
| motrix_env_core/src/motrix_env_core/sim/body.py | Adds core-owned BodyModel assembly logic (validation, permutation, actuator view derivation). |
| motrix_env_core/src/motrix_env_core/sim/backend.py | Moves model-surface types out; adds model_compiler + compile_model() with stored scene. |
| motrix_env_core/src/motrix_env_core/sim/init.py | Re-exports model-surface types/compiler from sim.model and adds body assembly export. |
| motrix_env_core/src/motrix_env_core/numba/manager/env.py | Updates ActuatorSpec import location. |
| motrix_env_core/src/motrix_env_core/numba/manager/actions.py | Updates ActuatorSpec type-checking import location. |
| motrix_env_core/src/motrix_env_core/config/scene/base.py | Adds RobotCfg.init_key_pose and validates it against declared key poses. |
Review details
Suppressed comments (2)
motrix_env_motrixsim/tests/test_sim_data_provider.py:363
- _FrameworkTestBackend.model_compiler returns self, but the compiler interface now expects compile(scene, queries). With the current
compile(self, queries)signature, calling SimBackend.compile_model will raise a TypeError (extra positional arg), even before the "not exercised" assertion.
def model_compiler(self):
return self
def compile(self, queries: Mapping[str, ModelQuery]) -> object:
raise AssertionError("not exercised")
motrix_env_core/src/motrix_env_core/sim/body.py:99
- Spelling/grammar: “Permutates” should be “Permutes” in the assemble_body_model docstring.
"""Assemble a :class:`BodyModel` from backend-extracted engine facts.
Permutates each declared key pose from ``KeyPoseCfg.joint_names`` order
into the body joint order — every declared joint must exist on the body
and every body joint must be covered — and bakes the init snapshot from
the configured ``init_key_pose``. The body-scoped actuator view is
- Files reviewed: 25/25 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
…ture - Fix module docstring reference: BodyModel lives in sim.model, not sim.backend - Permutate -> Permute in body.py docstrings - Build the joint-name set once in actuator filtering - Update FrameworkTestBackend.compile to the new compile(scene, queries) signature
Drop the interim FramePoseAtConfigQuery / KinematicConfig / SceneObjectRef contract added on this branch; the merged BodyModel implementation (#46) covers the same need with a richer contract (init snapshot + key-pose FK snapshot on SimModel.bodies). - walk_manager: remove the key_foot_pose model-query declaration. - walk_manager_mdp.params / reset: derive default joint angles, the default base pose, and the default foot-gravity reference from env.model.bodies["robot"] instead of the FK query. - core/adapter: remove compile_frame_poses_at_config and its scratch -data FK cache; keep HeightFieldDataQuery. - tests: model-query dispatch covers HeightFieldDataQuery. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; both g1 manager presets run; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Add a pure-config ManagerEnv variant of walk_np.py registered as g1-walk-flat-manager / g1-walk-rough-manager, plus supporting framework capabilities: - HeightFieldDataQuery: compile-time export of a static height-field grid via compile_model; the fused kernel looks up ground heights by bilinear interpolation (bit-exact with engine sample_height) for feet_phase rewards and in-kernel rough-terrain spawn sampling. - walk_manager_mdp package: command term (velocity commands, gait-phase clock, curriculum EMA in reset(ctx)), observations matching the direct env layout, the full reward term set, contact termination, and kernel-side reset with structured spawn sampling. - Consumes the per-body compile-time BodyModel (#46) for default joint angles, the default base pose, and the default foot-gravity reference (penalty_feet_ori); no ManagerEnv subclass is needed. Verified: BodyModel-derived foot gravity is bit-identical to the post-reset live capture; training throughput on par with the direct env (0.96-1.12x depending on batch size and terrain); reward curves at the same scale; full suite 483 passed.
Summary
Implements #43:
SimModelgains a per-body compile-time static model container, so manager-based terms stop re-deriving the same compile-time facts (default joint angles, actuator order checks, key-pose FK references).Closes #43 (core + backend + contract part; humanoid walk manager migration follows separately).
Design decisions
Discussed at length in #43 and during implementation:
bodies: Mapping[str, BodyModel]keyed bySceneObjsCfgfield name — the key is a cfg-side fact, identical across backends for the same scene. Base-file-only content stays out (no cfg anchor).joint_names= the body's named joints in backend joint-DOF order (floating base excluded) — joint identity, not actuator order. Consumers resolve by name, never hard-coded index, since ordering is per-backend. All joint-aligned arrays (joint_pos_limits,init_joint_pos) follow this axis; FK arrays followlink_names.RobotCfg.init_key_pose(env-level choice via scene-composition override; asset default"default"), base placement split intoinit_base_position (3,)/init_base_quat (4,), and link poses from one FK evaluation at the default placement (world frame,(x, y, z, w)quaternions — cross-validated against engine rotation matrices). Non-init key poses stay in the cfg.actuatorsonBodyModelis the body-scoped view ofSimModel.actuators(specs targeting the body's joints, scene-wide order) — derived by core, backends never filter.SimModelQueryCompiler→SimModelCompiler.compile(scene, queries): both inputs that meet inSimModelare now contract-level, makingbodiesan abstract obligation instead of a convention.SimBackend.compile_model(queries)keeps its env-facing signature by retaining the scene; backends must callsuper().__init__().assemble_body_model/resolved_key_poseown permutation/validation/derivation; a plugin implements aSceneCompiler, aSimModelCompilersubclass, and the existing abstract members — nothing else.ActuatorSpec,GeomSpec,BodyModel,SimModel) moved tosim/model.pynext to the queries, removing theTYPE_CHECKINGcycles in both directions.Implementation notes
Bodyobject APIs (get_dof_pos_indices,link.index,get_pose) instead of manual lookups.joint_pos_limits=None, empty actuators,(0,)init joints).Test plan
Follow-ups (not in this PR)
model.bodies(issue acceptance item)cache=False)