Skip to content

Add the scene-entity contract and MJCF entity composition#2479

Draft
Nabla7 wants to merge 1 commit into
mainfrom
pim/feat/entity-state
Draft

Add the scene-entity contract and MJCF entity composition#2479
Nabla7 wants to merge 1 commit into
mainfrom
pim/feat/entity-state

Conversation

@Nabla7

@Nabla7 Nabla7 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Problem

There is no shared contract for scene objects. Three consumers need the same
two facts — what objects exist (shape, mesh, mass) and where they are
right now — and each is on track to invent its own answer: the manipulation
planning world needs live obstacle poses (a moved chair is invisible to the
planner today), the upcoming viser manipulation visualizer needs renderable
objects (PlanningSceneInfo carries robots only), and MuJoCo-based sims need
to compose objects as collision bodies. Without one wire format, these grow
into parallel, incompatible sync paths.

Closes DIM-XXX

Solution

Two types and one composition function, deliberately small so dependent
branches (pim/feat/g1-reachability, cc/feat/viser-vis-rework) can build
against main instead of against each other:

  • EntityDescriptor — what an entity is: stable id, kind
    (static/kinematic/dynamic), mesh_ref (GLB), shape hint + extents for
    primitives, mass, optional rgba. Everything a collision backend or a
    renderer needs to instantiate it.
  • EntityStateBatch — where everything is: a timestamped snapshot of
    (descriptor, pose) entries, streamed by whatever owns physics truth — a
    simulator today, perception later. Producers and consumers never see each
    other; the batch is their only coupling.
  • entity_scene.add_entities_to_spec — composes descriptors into an
    MjSpec as collision bodies: cooked CoACD hulls when package metadata
    provides them, AABB boxes otherwise.

Wire format: versioned, length-prefixed JSON over LCM (the existing
visualization_msgs.EntityMarkers pattern) — hand-decodable from Rust and
browser consumers, no generated lcm_msgs type, unknown keys ignored for
forward compatibility. The schema is pinned by tests. msg_name is
simulation_msgs.EntityStateBatch (LCM channel keys derive from it, so the
namespace is chosen for the type's eventual home, not its current
experimental/ path).

Proven, not speculative: this exact stream already drives a browser viewer
and a Rust lidar consumer on the pimsim dev branch, and the planning-world
consumer (port → sync_entity_poses, with a moved-crate-flips-plan-validity
test) follows in the reachability PR stacked on this one.

Also included: the mujoco lint stub gains the MjSpec/kinematics surface
that entity_scene and downstream consumers use.

How to Test

uv run pytest dimos/experimental/pimsim dimos/simulation/mujoco/test_entity_scene.py \
  -m "mujoco or not mujoco"    # 8 tests; wire schema, roundtrip, MJCF composition

Roundtrip in three lines:

uv run python -c "
from dimos.experimental.pimsim.entity import EntityDescriptor, EntityStateBatch
from dimos.msgs.geometry_msgs.Pose import Pose
b = EntityStateBatch(entries=[(EntityDescriptor(entity_id='crate', shape_hint='box', extents=(0.1, 0.4, 0.4)), Pose(0.4, 0.0, 0.1))])
print(EntityStateBatch.decode(b.encode()).entries[0][0])"

Contributor License Agreement

- [x] I have read and approved the CLA (https://github.com/dimensionalOS/dimos/blob/main/CLA.md).

EntityDescriptor (what an entity is) + EntityStateBatch (where every
entity is): an authority-agnostic wire contract between whatever owns
physics truth (sim today, perception later) and whatever mirrors the
scene (planning worlds, visualizers, recorders). Versioned,
length-prefixed JSON over LCM — the EntityMarkers pattern — so
Rust/browser consumers hand-decode it without generated lcm_msgs types;
wire tests pin the schema. msg_name is namespaced simulation_msgs.*
(LCM channel keys derive from it) ahead of the type's eventual move out
of experimental.

entity_scene composes scene entities into an MjSpec — cooked collision
hulls when the package provides them, AABB boxes otherwise. The mujoco
lint stub gains the spec/kinematics surface this and downstream
consumers use.
@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.84615% with 72 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/simulation/mujoco/entity_scene.py 55.84% 54 Missing and 14 partials ⚠️
dimos/experimental/pimsim/entity.py 95.06% 2 Missing and 2 partials ⚠️
Flag Coverage Δ
OS-ubuntu-24.04-arm 63.91% <77.84%> (+0.06%) ⬆️
OS-ubuntu-latest 64.76% <77.84%> (+0.06%) ⬆️
Py-3.10 64.76% <77.84%> (+0.06%) ⬆️
Py-3.11 64.75% <77.84%> (+0.06%) ⬆️
Py-3.12 64.75% <77.84%> (+0.06%) ⬆️
Py-3.13 64.76% <77.84%> (+0.06%) ⬆️
Py-3.14 64.76% <77.84%> (+0.06%) ⬆️
Py-3.14t 64.75% <77.84%> (+0.05%) ⬆️
SelfHosted-Large 30.33% <25.23%> (?)
SelfHosted-Linux 38.22% <25.23%> (-0.07%) ⬇️
SelfHosted-macOS 36.94% <25.23%> (-0.06%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/experimental/pimsim/test_entity.py 100.00% <100.00%> (ø)
dimos/simulation/mujoco/test_entity_scene.py 100.00% <100.00%> (ø)
dimos/experimental/pimsim/entity.py 95.06% <95.06%> (ø)
dimos/simulation/mujoco/entity_scene.py 55.84% <55.84%> (ø)

... and 17 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@spomichter

Copy link
Copy Markdown
Contributor

@Nabla7 - @mustafab0 said this isnt blocking and no rush to merge and proposes we refactor to add clean integration



def test_mesh_entity_loads_cooked_hulls(tmp_path: Path) -> None:
mujoco = pytest.importorskip("mujoco")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please no importorskip. We want tests to fail if imports are missing. Otherwise how can we ensure that things don't break.

@mustafab0 mustafab0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nabla7 I am conflicted approving this PR.

I understand what you are trying to build here with the entities as these are the building blocks for your sidecar dynamic loading of objects. However I do not believe this should be a blocker for the reachability analysis.

Happy to approve this for it being part of the experimental branch, but I want to wait until we have a complete spec for PimSim, to see how this fits in the larger system.

We already have "entities" called Objects as dimos standard messages and could just be reused here. That is not as sophisticated as your EntityDescriptor but should essentially do the same thing.

Have you taken a look at it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants