Skip to content

refactor(manipulation): introduce static single-robot models - #3420

Draft
TomCC7 wants to merge 28 commits into
mainfrom
cc/feat/cc-refactor-1
Draft

refactor(manipulation): introduce static single-robot models#3420
TomCC7 wants to merge 28 commits into
mainfrom
cc/feat/cc-refactor-1

Conversation

@TomCC7

@TomCC7 TomCC7 commented Aug 10, 2026

Copy link
Copy Markdown
Member

Summary

  • accept canonical hierarchical joint/link names such as left/joint1 without backend-specific encoding
  • validate prepared one-model URDF/Xacro/SRDF configurations before Drake or RoboPlan world mutation
  • remove the model-wide end-effector compatibility property and resolve tips through planning groups
  • add a static dual-xArm6 model configuration and SRDF with left_arm, right_arm, and both_arms groups
  • cover the common parser, Viser/yourdfpy, Pink/Pinocchio, Drake, and RoboPlan loading paths

Stack

This is PR1, Static Single-Robot Model, from refactor-control-coordinator-connections.

Depends on #3381. That prerequisite is still open, so this PR remains a draft until the branch is rebased onto its merge commit.

The actual dual-arm blueprint cutover is intentionally deferred to PR2 task 2.10.

Validation

  • ./bin/pytest-fast — 3966 passed, 34 skipped
  • focused manipulation/backend suite — 133 passed, 13 skipped
  • regression suite — 103 passed
  • ruff check and ruff format --check
  • mypy on changed production modules
  • pre-commit run
  • openspec validate refactor-control-coordinator-connections --strict

Drake-only tests are present but skipped locally because the manipulation extra is not installed; CI will exercise them in its configured environment.

TomCC7 and others added 28 commits June 4, 2026 13:41
@mintlify

mintlify Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
dimensional 🟢 Ready View Preview Aug 10, 2026, 5:04 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@codecov

codecov Bot commented Aug 10, 2026

Copy link
Copy Markdown

❌ 1 Tests Failed:

Tests completed Failed Passed Skipped
3742 1 3741 176
View the top 1 failed test(s) by shortest run time
dimos.robot.manipulators.xarm.test_model_config::test_dual_xarm6_is_one_prepared_model_with_canonical_groups
Stack Traces | 3.11s run time
def test_dual_xarm6_is_one_prepared_model_with_canonical_groups() -> None:
        config = make_dual_xarm6_model_config()
    
>       model = validate_robot_model_config(config)

config     = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp...m_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xff3c4aa45ef0>

.../manipulators/xarm/test_model_config.py:24: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../planning/spec/validation.py:47: in validate_robot_model_config
    model = parse_model(
        config     = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp...m_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xff3c4aa45ef0>
dimos/robot/model_parser.py:71: in parse_model
    path = Path(path)
        package_paths = {'xarm_description': <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.....lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0981e50>}
        path       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp.../.lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>
        xacro_args = {'dof_1': '6', 'dof_2': '6', 'limited': 'true', 'prefix_1': 'left/', ...}
....../usr/lib/python3.12/pathlib.py:1164: in __init__
    super().__init__(*args)
        __class__  = <class 'pathlib.Path'>
        args       = (<[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attem...lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>,)
        kwargs     = {}
        self       = <[AttributeError("'PosixPath' object has no attribute '_raw_paths'") raised in repr()] PosixPath object at 0xff3c4aa58430>
....../usr/lib/python3.12/pathlib.py:362: in __init__
    if arg._flavour is ntpath and self._flavour is posixpath:
        arg        = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp.../.lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>
        args       = (<[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attem...lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>,)
        ntpath     = <module 'ntpath' (frozen)>
        paths      = []
        self       = <[AttributeError("'PosixPath' object has no attribute '_raw_paths'") raised in repr()] PosixPath object at 0xff3c4aa58430>
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = '_flavour'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp.../.lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'xarm_description/urdf/dual_xarm_device.urdf.xacro'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attemp.../.lfs/xarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff3cc0983950>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'xarm_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/xarm_description/urdf/dual_xarm_device.urdf.xacro')
        name       = 'xarm_description/urdf/dual_xarm_device.urdf.xacro'
        nested_path = PosixPath('urdf/dual_xarm_device.urdf.xacro')
        path_parts = ('xarm_description', 'urdf', 'dual_xarm_device.urdf.xacro')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/xarm_description.tar.gz')
        filename   = 'xarm_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/xarm_description.tar.gz')
repo_root = PosixPath('.../work/dimos/dimos')

    def _lfs_pull(file_path: Path, repo_root: Path, *, retries: int = 2) -> None:
        relative_path = file_path.relative_to(repo_root)
    
        env = os.environ.copy()
        env["GIT_LFS_FORCE_PROGRESS"] = "1"
    
        last_err: subprocess.CalledProcessError | None = None
        for attempt in range(1, retries + 2):  # retries + 1 total attempts
            try:
                subprocess.run(
                    ["git", "lfs", "pull", "--include", str(relative_path)],
                    cwd=repo_root,
                    check=True,
                    env=env,
                )
                return
            except subprocess.CalledProcessError as e:
                last_err = e
                if attempt <= retries:
                    time.sleep(attempt)  # 1s, 2s backoff
    
>       raise RuntimeError(
            f"Failed to pull LFS file {file_path} after {retries + 1} attempts: {last_err}"
        )
E       RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/xarm_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/xarm_description.tar.gz']' returned non-zero exit status 1.

attempt    = 3
env        = {'ACCEPT_EULA': 'Y', 'ACTIONS_ID_TOKEN_REQUEST_TOKEN': 'eyJhbGciOiJSUzI1NiIsImtpZCI6IjM4ODI2YjE3LTZhMzAtNWY5Yi1iMTY5LT...-version=2.0', 'ACTIONS_ORCHESTRATION_ID': 'c1420bb4-fedd-4d22-9ab3-0db97bf447a4.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/xarm_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/xarm_description.tar.gz'])
relative_path = PosixPath('data/.lfs/xarm_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

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.

1 participant