Skip to content

feat: in-house robot asset manager#2505

Open
TomCC7 wants to merge 21 commits into
mainfrom
cc/chore/robot-description
Open

feat: in-house robot asset manager#2505
TomCC7 wants to merge 21 commits into
mainfrom
cc/chore/robot-description

Conversation

@TomCC7

@TomCC7 TomCC7 commented Jun 16, 2026

Copy link
Copy Markdown
Member

Problem

DimOS currently keeps several manipulation robot model bundles in Git LFS. That copies upstream robot description assets into this repo, makes updates manual, and makes CI brittle on runners that do not have those LFS objects available.

robot_descriptions.py solves a related problem, but it is not the right main asset layer for DimOS:

  • it is a curated registry of pre-registered description modules, not a direct way to point DimOS at any upstream robot description repository;
  • Piper and A-750 are not covered, and OpenArm still has DimOS-local model changes;
  • DimOS catalogs need to choose exact repo refs, package roots, Xacro args, and repo-relative files that match the existing planning/control stack;
  • relying on an external registry would make routine robot source changes depend on upstream registry changes.

This PR keeps one Git-backed robot asset supply path for built-in and user-supplied robots. pip install dimos installs code only; repos are resolved lazily when a concrete model path is used.

Solution

Add a small in-house robot description source layer:

  • add GitAssetCache for fresh-when-safe Git checkouts under the platform user cache directory, in dimos/robot_assets;
  • preserve local edits and local commits in cached checkouts by warning and skipping upstream updates;
  • add RobotDescriptionSource, a lazy path-like source handle for an upstream repo/ref;
  • let catalogs form paths directly from the source root, for example xarm_repo / "xarm_description" / "urdf" / "xarm_device.urdf.xacro";
  • remove the earlier central registry/artifact-role design so custom robot adapters can create their own source handles without registration;
  • migrate xArm, Piper, and A-750 runtime model paths and package roots to Git-backed source handles where upstream sources are suitable;
  • keep LFS for assets that still need DimOS-local/generated variants, such as FK-only/no-gripper models, sim assets, and OpenArm's locally modified models;
  • keep universal URDF/Xacro rendering and package URI handling in dimos/robot/assets, while Drake-specific cleanup stays in Drake utilities.

The asset code is self-contained under dimos/robot/assets and avoids an __init__.py to match DimOS import policy.

How to Test

Manual check for a human reviewer:

uv run --extra manipulation dimos run xarm7-planner-coordinator

Open the Drake Meshcat URL printed in the logs. The xArm7 model should load and visualize correctly. The first run may clone xarm_ros2 into the platform user cache under dimos/robot_assets; derived URDFs should be written under dimos/robot_assets/derived.

For daemon mode:

uv run --extra manipulation dimos run xarm7-planner-coordinator --daemon
uv run --extra manipulation dimos log -f

Contributor License Agreement

  • I have read and approved the CLA.

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
dimos/robot/assets/git_cache.py 80.43% 15 Missing and 3 partials ⚠️
dimos/robot/assets/source.py 80.76% 15 Missing ⚠️
dimos/robot/assets/processing.py 86.51% 8 Missing and 4 partials ⚠️
dimos/control/examples/cartesian_ik_jogger.py 0.00% 2 Missing ⚠️
dimos/robot/model_parser.py 50.00% 1 Missing ⚠️
dimos/utils/ament_prefix.py 50.00% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main    #2505      +/-   ##
==========================================
+ Coverage   70.81%   70.89%   +0.08%     
==========================================
  Files         862      868       +6     
  Lines       77475    77839     +364     
  Branches     6882     6958      +76     
==========================================
+ Hits        54862    55187     +325     
- Misses      20818    20853      +35     
- Partials     1795     1799       +4     
Flag Coverage Δ
OS-ubuntu-24.04-arm 63.07% <86.30%> (+0.14%) ⬆️
OS-ubuntu-latest 65.88% <86.30%> (+0.13%) ⬆️
Py-3.10 65.87% <86.30%> (+0.12%) ⬆️
Py-3.11 65.88% <86.30%> (+0.12%) ⬆️
Py-3.12 65.88% <86.30%> (+0.13%) ⬆️
Py-3.13 65.88% <86.30%> (+0.13%) ⬆️
Py-3.14 65.89% <86.30%> (+0.13%) ⬆️
Py-3.14t 65.88% <86.30%> (+0.13%) ⬆️
SelfHosted-Large 30.29% <42.25%> (+0.11%) ⬆️
SelfHosted-Linux 37.97% <59.11%> (+0.09%) ⬆️
SelfHosted-macOS 36.76% <54.18%> (+0.14%) ⬆️

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

Files with missing lines Coverage Δ
dimos/manipulation/planning/utils/mesh_utils.py 58.09% <100.00%> (-0.50%) ⬇️
...mos/manipulation/planning/utils/test_mesh_utils.py 100.00% <100.00%> (ø)
dimos/manipulation/planning/world/drake_world.py 50.27% <100.00%> (ø)
dimos/manipulation/test_manipulation_module.py 97.20% <100.00%> (-1.42%) ⬇️
dimos/robot/assets/test_processing.py 100.00% <100.00%> (ø)
dimos/robot/assets/test_source.py 100.00% <100.00%> (ø)
dimos/robot/catalog/a750.py 100.00% <100.00%> (ø)
dimos/robot/catalog/piper.py 100.00% <100.00%> (ø)
dimos/robot/catalog/ufactory.py 100.00% <100.00%> (ø)
dimos/robot/config.py 74.24% <ø> (ø)
... and 7 more

... and 3 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.

@TomCC7 TomCC7 changed the title WIP: in-house robot description loader feat: in-house robot description loader Jun 16, 2026
@TomCC7 TomCC7 marked this pull request as ready for review June 16, 2026 20:08
@greptile-apps

greptile-apps Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces Git LFS robot model bundles with a lightweight in-house asset manager (dimos/robot/assets) that clones and caches upstream robot description repositories on demand. pip install dimos no longer requires LFS objects; model repos are fetched lazily the first time a concrete path is resolved.

  • GitAssetCache clones (repo_url, ref) pairs under the platform user cache dir, uses a FileLock for concurrent safety, and skips upstream updates when the working tree is dirty.
  • RobotDescriptionPath is a lazy Path subclass whose __fspath__ defers the Git checkout until the path is actually observed as a filesystem path, letting catalog constants be declared at import time without triggering network I/O.
  • xArm, Piper, and A-750 runtime model paths and package roots are migrated to Git-backed source handles; FK-only/no-gripper models and OpenArm's locally modified assets remain on LFS.

Confidence Score: 4/5

Safe to merge after addressing the _directory_fingerprint cost-before-cache-check ordering and the unguarded local-commit case in _checkout_ref.

Two issues in the new asset layer affect every robot startup: _directory_fingerprint reads the full byte content of every mesh file in the package root on each render_urdf call — including calls that hit the rendered-URDF cache — because the key is computed before the existence check, making each session startup pay O(total package bytes) in disk I/O regardless of whether anything has changed. Additionally, _checkout_ref forcibly repoints a local branch to origin/ref even when the working tree is clean but has local commits ahead of origin, silently making those commits unreachable.

dimos/robot/assets/processing.py (_directory_fingerprint / _generate_render_key ordering) and dimos/robot/assets/git_cache.py (_checkout_ref / _is_dirty for local commits)

Important Files Changed

Filename Overview
dimos/robot/assets/git_cache.py New GitAssetCache implementation; _is_dirty still does not detect local commits ahead of origin, so clean-working-tree checkouts with local commits will have those commits silently overwritten on the next resolve() call.
dimos/robot/assets/processing.py New URDF render cache; _directory_fingerprint reads every file's full byte content to build the cache key, but this computation runs before the cache-hit check on every render_urdf call, making each robot startup pay the full I/O cost even when the rendered URDF is already cached.
dimos/robot/assets/source.py New lazy RobotDescriptionPath/RobotDescriptionSource handles; well-designed with careful getattribute interception that defers checkout until filesystem access, with proper fspath and truediv support.
dimos/robot/catalog/ufactory.py Migrated xArm6/7 model paths and package roots from LFS to Git-backed RobotDescriptionSource; clean and consistent with the new asset layer.
dimos/robot/catalog/piper.py Migrated Piper model path and PIPER_FK_MODEL from LFS to upstream agx_arm_urdf repo; package path uses source.parent to correctly express that the package root is one level above the checkout directory.
dimos/manipulation/planning/utils/mesh_utils.py Refactored to delegate xacro expansion and package URI rewriting to the new render_urdf layer; Drake-specific cache now stores only Drake cleanup (transmission stripping, mesh conversion), reducing duplication.
dimos/robot/config.py Added dict() copy for package_paths before passing to RobotModelConfig so Pydantic sees a plain dict with Path-subclass values rather than a frozen mapping.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["RobotDescriptionSource\n(url, ref)"] -->|"__truediv__ (lazy)"| B["RobotDescriptionPath\n(relative_path stored,\nno checkout yet)"]
    B -->|"str() / __fspath__()\ncalled by consumer"| C["RobotDescriptionSource\n.checkout_path()"]
    C -->|"_checkout_path_cache\nnot set"| D["GitAssetCache.resolve()"]
    D -->|FileLock| E{checkout_path\nexists?}
    E -->|No| F["_clone_missing()\nRepo.clone_from + _checkout_ref"]
    E -->|Yes| G["_refresh_cached()"]
    G -->|is_dirty?| H["warn + skip update"]
    G -->|clean| I["origin.fetch\n_checkout_ref -B ref origin/ref"]
    F --> J["GitAssetCheckout.path"]
    H --> J
    I --> J
    J --> K["absolute Path cached"]
    K --> L["render_urdf()\n_generate_render_key\n→ _directory_fingerprint\n→ read_bytes all files"]
    L --> M{rendered_urdf\nexists?}
    M -->|Yes| N["return cached URDF"]
    M -->|No| O["process_xacro / read URDF\nwrite to cache"]
    O --> P["prepare_urdf_for_drake()\nDrake-specific cleanup"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["RobotDescriptionSource\n(url, ref)"] -->|"__truediv__ (lazy)"| B["RobotDescriptionPath\n(relative_path stored,\nno checkout yet)"]
    B -->|"str() / __fspath__()\ncalled by consumer"| C["RobotDescriptionSource\n.checkout_path()"]
    C -->|"_checkout_path_cache\nnot set"| D["GitAssetCache.resolve()"]
    D -->|FileLock| E{checkout_path\nexists?}
    E -->|No| F["_clone_missing()\nRepo.clone_from + _checkout_ref"]
    E -->|Yes| G["_refresh_cached()"]
    G -->|is_dirty?| H["warn + skip update"]
    G -->|clean| I["origin.fetch\n_checkout_ref -B ref origin/ref"]
    F --> J["GitAssetCheckout.path"]
    H --> J
    I --> J
    J --> K["absolute Path cached"]
    K --> L["render_urdf()\n_generate_render_key\n→ _directory_fingerprint\n→ read_bytes all files"]
    L --> M{rendered_urdf\nexists?}
    M -->|Yes| N["return cached URDF"]
    M -->|No| O["process_xacro / read URDF\nwrite to cache"]
    O --> P["prepare_urdf_for_drake()\nDrake-specific cleanup"]
Loading

Reviews (4): Last reviewed commit: "Update readme.md" | Re-trigger Greptile

Comment thread dimos/robot/assets/processing.py
Comment thread dimos/robot/assets/processing.py Outdated
Comment thread dimos/robot/assets/processing.py Outdated
TomCC7 and others added 2 commits June 16, 2026 13:18
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
@TomCC7 TomCC7 changed the title feat: in-house robot description loader feat: in-house robot asset manager Jun 16, 2026
Comment thread dimos/robot/assets/git_cache.py
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 16, 2026
@TomCC7 TomCC7 marked this pull request as draft June 16, 2026 22:10
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Jun 16, 2026
Comment thread dimos/robot/assets/declarations.py Outdated
Comment thread dimos/robot/assets/git_cache.py Outdated
Comment thread dimos/robot/assets/git_cache.py Outdated
Comment thread dimos/robot/assets/manager.py Outdated
@TomCC7 TomCC7 marked this pull request as ready for review June 20, 2026 02:41
@TomCC7 TomCC7 marked this pull request as draft June 20, 2026 02:42
@TomCC7 TomCC7 marked this pull request as ready for review June 20, 2026 03:24
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant