Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
c2b4297
Add pluggable simulation providers
Nabla7 Jul 23, 2026
410ee00
Unify G1 real and simulated perception stacks
Nabla7 Jul 24, 2026
41aaa92
Relay native PointLIO output to Zenoh
Nabla7 Jul 24, 2026
056cacc
Restore scene visual package alignment
Nabla7 Jul 24, 2026
a9b02a2
Restore provider-owned G1 localization
Nabla7 Jul 24, 2026
235846e
Share PointLIO Zenoh relay
Nabla7 Jul 24, 2026
d22ad2f
Show G1 lidar in its public frame
Nabla7 Jul 24, 2026
4f3fa15
Tune Groot simulation visualization
Nabla7 Jul 24, 2026
5c89fc8
Keep coordinator RPC on the local control plane
Nabla7 Jul 24, 2026
61c549b
Revert "Keep coordinator RPC on the local control plane"
Nabla7 Jul 24, 2026
e9774b1
Select the Go2 simulation provider from the ordinary blueprint
Nabla7 Jul 25, 2026
b51231c
Parameterize the DimSim agent tests over scene-control providers
Nabla7 Jul 25, 2026
6728252
Give the Go2 costmap the shared palette and explicit robot dimensions
Nabla7 Jul 25, 2026
377027f
Route local Zenoh sessions through a router
Nabla7 Jul 25, 2026
854ee10
Align simulator mapping and visualization boundaries
Nabla7 Jul 29, 2026
1630351
Merge remote-tracking branch 'origin/main' into feat/pimsim-simulatio…
Nabla7 Jul 29, 2026
df56d7f
Fix G1 apartment navigation clearance
Nabla7 Jul 29, 2026
623bf81
Make simulation E2E tests transport agnostic
Nabla7 Jul 31, 2026
74f7ad8
Merge origin/main into feat/pimsim-simulation-provider
Nabla7 Jul 31, 2026
1bf8cf3
Merge remote-tracking branch 'origin/main' into feat/pimsim-simulatio…
Nabla7 Jul 31, 2026
071ebb3
Fix transport-agnostic agent readiness in E2E tests
Nabla7 Jul 31, 2026
c569c63
fix macOS CLIP inference in workers
Nabla7 Aug 3, 2026
e76029d
test(simulation): unify semantic navigation acceptance
Nabla7 Aug 3, 2026
c0e26d2
Merge remote-tracking branch 'origin/main' into feat/pimsim-simulatio…
Nabla7 Aug 3, 2026
0abc1d8
feat: run xarm7 simulation through pimsim
Nabla7 Aug 3, 2026
bfa5515
feat: configure rerun for xarm simulation
Nabla7 Aug 3, 2026
8bac125
refactor: move simulator control ABI to hardware boundary
Nabla7 Aug 3, 2026
70e554c
feat(manipulation): add simulation operator controls
Nabla7 Aug 3, 2026
08e9013
fix(manipulation): restore interactive target planning
Nabla7 Aug 3, 2026
d31c2b8
fix(manipulation): align planner with simulated arm base
Nabla7 Aug 4, 2026
15d61ea
Merge remote-tracking branch 'origin/main' into feat/pimsim-simulatio…
Nabla7 Aug 4, 2026
7dcbd36
fix: preserve runtime objects in blueprint config
Nabla7 Aug 4, 2026
c4d202b
Fix simulated xArm pick-and-place execution
Nabla7 Aug 4, 2026
0c236e6
Add full-fidelity mesh scene cooking
Nabla7 Aug 4, 2026
3ad0cdc
Use world-frame mapping for simulated G1
Nabla7 Aug 4, 2026
6f2c826
feat(sim): publish updated PimSim scene packages
Nabla7 Aug 4, 2026
85acc0e
Merge remote-tracking branch 'origin/main' into feat/pimsim-simulatio…
Nabla7 Aug 5, 2026
d01b00b
refactor(sim): narrow integration to provider contract
Nabla7 Aug 5, 2026
4634598
fix(sim): restore PimSim Go2 startup
Nabla7 Aug 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion dimos/core/coordination/blueprint_config/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
plain,
plain_mapping,
snapshot_mapping,
validated_model_values,
)
from dimos.core.coordination.blueprints import (
Blueprint,
Expand Down Expand Up @@ -421,7 +422,7 @@ def _validate_modules(
raise BlueprintConfigError(
format_validation_error(module.atom.name, error)
) from error
dumped = model.model_dump(mode="python", exclude_unset=True)
dumped = validated_model_values(model)
dumped.pop("g", None)
dumped.pop("instance_name", None)
parsed[module.atom.name] = dumped
Expand Down
22 changes: 22 additions & 0 deletions dimos/core/coordination/blueprint_config/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
# limitations under the License.

from collections.abc import Callable
from dataclasses import dataclass
from pathlib import Path
import pickle
from typing import Annotated, Any, Literal

from pydantic import BaseModel, Field
Expand Down Expand Up @@ -452,6 +454,14 @@ def __str__(self) -> str:
return "Anchor:\n multi\n line"


@dataclass(frozen=True)
class CallableAnchor:
prefix: str

def __call__(self, value: Any) -> str:
return f"{self.prefix}:{value}"


class ArbitraryConfig(ModuleConfig):
scaling: Anchor = Field(default_factory=Anchor)
hybrid: Anchor | str = "fallback"
Expand Down Expand Up @@ -490,6 +500,18 @@ def test_blueprint_pinned_arbitrary_value_survives_filtering() -> None:
assert isinstance(parsed.module_kwargs("arbitrarymodule")["scaling"], Anchor)


def test_blueprint_pinned_callable_dataclass_survives_worker_serialization() -> None:
blueprint = ArbitraryModule.blueprint(handlers={"scene": CallableAnchor("render")})

parsed = BlueprintConfigParser(blueprint).parse(environ={})
worker_kwargs = pickle.loads(pickle.dumps(parsed.module_kwargs("arbitrarymodule")))
worker_config = ArbitraryConfig.model_validate(worker_kwargs)
handler = worker_config.handlers["scene"]

assert isinstance(handler, CallableAnchor)
assert handler("apartment") == "render:apartment"


def test_format_help_uses_nested_parent_default_instance() -> None:
class NestedRequiredConfig(BaseModel):
value: int
Expand Down
25 changes: 25 additions & 0 deletions dimos/core/coordination/blueprint_config/values.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,31 @@ def plain(value: Any) -> Any:
return _copy_opaque(value)


def validated_model_values(model: BaseModel) -> dict[str, Any]:
"""Copy explicitly set validated fields without serializing runtime objects."""
return {
name: _validated_value(getattr(model, name))
for name in type(model).model_fields
if name in model.model_fields_set
}


def _validated_value(value: Any) -> Any:
if isinstance(value, BaseModel):
return validated_model_values(value)
if isinstance(value, Mapping):
return {_copy_opaque(key): _validated_value(item) for key, item in value.items()}
if isinstance(value, list):
return [_validated_value(item) for item in value]
if isinstance(value, tuple):
return tuple(_validated_value(item) for item in value)
if isinstance(value, set):
return {_validated_value(item) for item in value}
if isinstance(value, frozenset):
return frozenset(_validated_value(item) for item in value)
return _copy_opaque(value)


def deep_merge(destination: dict[str, Any], incoming: Mapping[str, Any]) -> None:
for key, value in incoming.items():
if key in destination and isinstance(destination[key], dict) and isinstance(value, Mapping):
Expand Down
9 changes: 8 additions & 1 deletion dimos/core/coordination/coordinator_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

from dimos.core.global_config import global_config
from dimos.core.transport_factory import rpc_backend
from dimos.protocol.rpc.zenohrpc import ZenohRPC
from dimos.protocol.service.zenohservice import ZENOH_LOCAL_ROUTER_ENDPOINT
from dimos.utils.logging_config import setup_logger

if TYPE_CHECKING:
Expand Down Expand Up @@ -51,7 +53,12 @@ def serve(cls, coordinator: RPCInspectable) -> CoordinatorRPC:
@classmethod
def connect(cls, *, timeout: float) -> CoordinatorRPC:
"""Attach to a running Coordinator, raising `TimeoutError` if none answers."""
rpc = rpc_backend()()
backend = rpc_backend()
rpc = (
ZenohRPC(mode="client", connect=[ZENOH_LOCAL_ROUTER_ENDPOINT])
if backend is ZenohRPC
else backend()
)
rpc.start()
client = cls(rpc)
deadline = time.monotonic() + timeout
Expand Down
35 changes: 32 additions & 3 deletions dimos/core/coordination/module_coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import dataclasses
import importlib
import inspect
import os
import shutil
import sys
import threading
Expand All @@ -41,6 +42,11 @@
pZenohTransport,
)
from dimos.core.transport_factory import make_transport
from dimos.protocol.service.zenohservice import (
ZENOH_LOCAL_ROUTER_ENDPOINT,
ZENOH_ROUTER_ENDPOINT_ENV,
ZenohRouter,
)
from dimos.spec.utils import is_spec, spec_annotation_compliance, spec_structural_compliance
from dimos.utils.generic import short_id
from dimos.utils.logging_config import setup_logger
Expand Down Expand Up @@ -89,13 +95,24 @@ def __init__(
self._modules_lock = threading.RLock()
self._rpc_lock = threading.RLock()
self._coordinator_rpc: CoordinatorRPC | None = None
self._zenoh_router: ZenohRouter | None = None
self._previous_zenoh_router_endpoint: str | None = None

def start(self) -> None:
from dimos.core.o3dpickle import register_picklers

register_picklers()
for m in self._managers.values():
m.start()
if self._global_config.transport == "zenoh":
self._zenoh_router = ZenohRouter()
self._zenoh_router.start()
self._previous_zenoh_router_endpoint = os.environ.get(ZENOH_ROUTER_ENDPOINT_ENV)
os.environ[ZENOH_ROUTER_ENDPOINT_ENV] = ZENOH_LOCAL_ROUTER_ENDPOINT
try:
for m in self._managers.values():
m.start()
except BaseException:
self._stop_zenoh_router()
raise
self._started = True

def stop(self) -> None:
Expand All @@ -119,9 +136,21 @@ def _stop_manager(m: WorkerManager) -> None:
logger.error("Error stopping manager", manager=type(m).__name__, exc_info=True)

safe_thread_map(tuple(self._managers.values()), _stop_manager)
self._stop_zenoh_router()

def _stop_zenoh_router(self) -> None:
if self._zenoh_router is not None:
self._zenoh_router.stop()
self._zenoh_router = None
if self._global_config.transport != "zenoh":
return
if self._previous_zenoh_router_endpoint is None:
os.environ.pop(ZENOH_ROUTER_ENDPOINT_ENV, None)
else:
os.environ[ZENOH_ROUTER_ENDPOINT_ENV] = self._previous_zenoh_router_endpoint

def start_rpc_service(self) -> None:
"""Expose the coordinator's API as @rpc methods over LCM."""
"""Expose the coordinator's API over the configured RPC transport."""
with self._rpc_lock:
if self._coordinator_rpc is not None:
return
Expand Down
1 change: 1 addition & 0 deletions dimos/core/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ class GlobalConfig(BaseSettings):
can_port: str | None = None
device_path: str | None = None # device path for real robot (e.g. /dev/ttyUSB0)
simulation: str = ""
simulation_provider: str = ""
replay: bool = False
replay_db: str = "go2_short"
new_memory: bool = False
Expand Down
6 changes: 6 additions & 0 deletions dimos/core/native_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ class MyCppModule(NativeModule):
from dimos.core.core import rpc
from dimos.core.global_config import global_config
from dimos.core.module import Module, ModuleConfig
from dimos.protocol.service.zenohservice import (
ZENOH_LOCAL_ROUTER_ENDPOINT,
ZENOH_ROUTER_ENDPOINT_ENV,
)
from dimos.utils.logging_config import setup_logger

if sys.platform.startswith("linux"):
Expand Down Expand Up @@ -254,6 +258,8 @@ def start(self) -> None:

# set transport so native modules know which one to spawn
env["DIMOS_TRANSPORT"] = global_config.transport
if global_config.transport == "zenoh":
env[ZENOH_ROUTER_ENDPOINT_ENV] = ZENOH_LOCAL_ROUTER_ENDPOINT

# set Rust logging to match Python level
env["RUST_LOG"] = _PYTHON_TO_RUST_LEVELS.get(
Expand Down
2 changes: 1 addition & 1 deletion dimos/hardware/manipulators/sim/adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
JointLimits,
ManipulatorInfo,
)
from dimos.simulation.engines.mujoco_shm import (
from dimos.hardware.simulation.shared_memory import (
ManipShmReader,
shm_key_from_path,
)
Expand Down
2 changes: 1 addition & 1 deletion dimos/hardware/manipulators/sim/test_shm_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import dimos.hardware.manipulators.sim.adapter as adapter_mod
from dimos.hardware.manipulators.sim.adapter import ShmMujocoAdapter
from dimos.hardware.manipulators.spec import ControlMode, ManipulatorAdapter
from dimos.simulation.engines.mujoco_shm import ManipShmWriter
from dimos.hardware.simulation.shared_memory import ManipShmWriter

ARM_DOF = 7

Expand Down
Loading
Loading