Skip to content

WIP: feat: add manipulation planning groups#2489

Draft
TomCC7 wants to merge 46 commits into
mainfrom
cc/spec/movegroup
Draft

WIP: feat: add manipulation planning groups#2489
TomCC7 wants to merge 46 commits into
mainfrom
cc/spec/movegroup

Conversation

@TomCC7

@TomCC7 TomCC7 commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

  • add first-class manipulation planning group models, SRDF/fallback discovery, resolved joint naming, and world group resolution
  • add group-scoped kinematics/planner APIs plus GeneratedPlan preview/execution projection through coordinator trajectory tasks
  • add OpenArm mock planner/coordinator large E2E coverage for single-arm and dual-arm planning flows
  • add OpenSpec artifacts and user/contributor docs for planning groups
image

Verification

  • uv run pytest dimos/manipulation -q
  • uv run pytest dimos/e2e_tests/test_manipulation_planning_groups.py -m self_hosted_large -q
  • uv run pytest --collect-only dimos/e2e_tests/test_manipulation_planning_groups.py -q
  • openspec validate add-planning-groups
  • pre-commit hooks via git commit

Notes

  • PR remains draft.
  • OpenSpec manual QA tasks 8.12 and 8.13 remain unchecked because no SRDF-backed chain fixture or auxiliary pose-planning fixture was available in this environment.

@TomCC7 TomCC7 changed the title WIP spec: movegroup concept and bimanual/multi-target motion planning WIP spec: planning group and bimanual/multi-target motion planning Jun 13, 2026
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

❌ 2 Tests Failed:

Tests completed Failed Passed Skipped
1952 2 1950 159
View the top 2 failed test(s) by shortest run time
::dimos.control.blueprints.test_dual
Stack Traces | 0s run time
.../control/blueprints/test_dual.py:19: in <module>
    from dimos.manipulation.blueprints import dual_xarm6_planner_coordinator
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        __builtins__ = <builtins>
        __cached__ = '.../blueprints/__pycache__/test_dual.cpython-312.pyc'
        __doc__    = 'Tests for dual-arm control blueprints.'
        __file__   = '.../work/dimos/dimos/.../control/blueprints/test_dual.py'
        __loader__ = <_pytest.assertion.rewrite.AssertionRewritingHook object at 0xff4fdd69a720>
        __name__   = 'dimos.control.blueprints.test_dual'
        __package__ = 'dimos.control.blueprints'
        __spec__   = ModuleSpec(name='dimos.control.blueprints.test_dual', loader=<_pytest.assertion.rewrite.AssertionRewritingHook object at 0xff4fdd69a720>, origin='.../work/dimos/dimos/.../control/blueprints/test_dual.py')
        coordinator_dual_xarm = Blueprint(blueprints=(BlueprintAtom(kwargs={'hardware': [HardwareComponent(hardware_id='left_arm', hardware_type=<Hard...lobal_config_overrides=mappingproxy({}), remapping_map=mappingproxy({}), requirement_checks=(), configurator_checks=())
dimos/manipulation/blueprints.py:435: in <module>
    address=str(XARM7_SIM_PATH),
        Any        = typing.Any
        ControlCoordinator = <class 'dimos.control.coordinator.ControlCoordinator'>
        JointState = <class 'dimos.msgs.sensor_msgs.JointState.JointState'>
        LCMTransport = <class 'dimos.core.transport.LCMTransport'>
        LfsPath    = <class 'dimos.utils.data.LfsPath'>
        ManipulationModule = <class 'dimos.manipulation.manipulation_module.ManipulationModule'>
        McpClient  = <class 'dimos.agents.mcp.mcp_client.McpClient'>
        McpServer  = <class 'dimos.agents.mcp.mcp_server.McpServer'>
        MujocoSimModule = <class 'dimos.simulation.engines.mujoco_sim_module.MujocoSimModule'>
        ObjectSceneRegistrationModule = <class 'dimos.perception.object_scene_registration.ObjectSceneRegistrationModule'>
        Path       = <class 'pathlib.Path'>
        PickAndPlaceModule = <class 'dimos.manipulation.pick_and_place_module.PickAndPlaceModule'>
        PoseStamped = <class 'dimos.msgs.geometry_msgs.PoseStamped.PoseStamped'>
        Quaternion = <class 'dimos.msgs.geometry_msgs.Quaternion.Quaternion'>
        RealSenseCamera = <class 'dimos.hardware.sensors.camera.realsense.camera.RealSenseCamera'>
        RerunBridgeModule = <class 'dimos.visualization.rerun.bridge.RerunBridgeModule'>
        RobotModelConfig = <class 'dimos.manipulation.planning.spec.config.RobotModelConfig'>
        TaskConfig = <class 'dimos.control.coordinator.TaskConfig'>
        Transform  = <class 'dimos.msgs.geometry_msgs.Transform.Transform'>
        Vector3    = <class 'dimos.msgs.geometry_msgs.Vector3.Vector3'>
        XARM7_SIM_PATH = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm7.tar.gz after 3 attempts: Command...ude', 'data/.lfs/xarm7.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff4f3c4747d0>
        XARM_GRIPPER_COLLISION_EXCLUSIONS = [('right_inner_knuckle', 'right_outer_knuckle'), ('left_inner_knuckle', 'left_outer_knuckle'), ('right_inner_knuckle',...ft_inner_knuckle', 'left_finger'), ('left_finger', 'right_finger'), ('left_outer_knuckle', 'right_outer_knuckle'), ...]
        _BASE_MANIPULATION_AGENT_SYSTEM_PROMPT = 'You are a robotic manipulation assistant controlling an xArm7 robot arm.\n\nAvailable skills:\n- get_robot_state: Get...e robot to near-origin.\n\nERROR RECOVERY: If a motion fails or the state becomes FAULT, call reset before retrying.\n'
        _MANIPULATION_AGENT_SYSTEM_PROMPT = 'You are a robotic manipulation assistant controlling an xArm7 robot arm with an eye-in-hand RealSense camera and a gr...ecovery\nIf planning fails with COLLISION_AT_START: call **clear_perception_obstacles**, then **reset**, then retry.\n'
        _XARM_MODEL_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 0xff4f12abe150>
        _XARM_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 0xff4f12abe0d0>}
        _XARM_PERCEPTION_CAMERA_TRANSFORM = Transform(translation=Vector([   0.066937   -0.030956   0.0069148]), rotation=Quaternion(0.705134, 0.005357, 0.708976, -0.010522))
        __annotations__ = {'XARM_GRIPPER_COLLISION_EXCLUSIONS': list[tuple[str, str]], '_XARM_PACKAGE_PATHS': dict[str, pathlib.Path]}
        __builtins__ = <builtins>
        __cached__ = '.../manipulation/__pycache__/blueprints.cpython-312.pyc'
        __doc__    = '\nManipulation blueprints.\n\nQuick start:\n    # 1. Verify manipulation deps load correctly (standalone, no hardware... dimos run dual-xarm7-planner-coordinator\n    python -i -m dimos.manipulation.planning.examples.manipulation_client\n'
        __file__   = '.../dimos/manipulation/blueprints.py'
        __loader__ = <_frozen_importlib_external.SourceFileLoader object at 0xff4f3daae1e0>
        __name__   = 'dimos.manipulation.blueprints'
        __package__ = 'dimos.manipulation'
        __spec__   = ModuleSpec(name='dimos.manipulation.blueprints', loader=<_frozen_importlib_external.SourceFileLoader object at 0xff4f3daae1e0>, origin='.../dimos/manipulation/blueprints.py')
        _base_pose = <function _base_pose at 0xff4f3c4d5440>
        _left_arm_hw = HardwareComponent(hardware_id='left_arm', hardware_type=<HardwareType.MANIPULATOR: 'manipulator'>, joints=['left_arm/j...adapter_type='mock', address=None, auto_enable=True, gripper_joints=[], domain_id=0, adapter_kwargs={}, wb_config=None)
        _left_xarm7_hw = HardwareComponent(hardware_id='left_arm', hardware_type=<HardwareType.MANIPULATOR: 'manipulator'>, joints=['left_arm/j...adapter_type='mock', address=None, auto_enable=True, gripper_joints=[], domain_id=0, adapter_kwargs={}, wb_config=None)
        _make_xarm6_model_config = <function _make_xarm6_model_config at 0xff4f12ad0680>
        _make_xarm7_model_config = <function _make_xarm7_model_config at 0xff4f12ad0720>
        _make_xarm_model_config = <function _make_xarm_model_config at 0xff4f12b487c0>
        _right_arm_hw = HardwareComponent(hardware_id='right_arm', hardware_type=<HardwareType.MANIPULATOR: 'manipulator'>, joints=['right_arm...adapter_type='mock', address=None, auto_enable=True, gripper_joints=[], domain_id=0, adapter_kwargs={}, wb_config=None)
        _right_xarm7_hw = HardwareComponent(hardware_id='right_arm', hardware_type=<HardwareType.MANIPULATOR: 'manipulator'>, joints=['right_arm...adapter_type='mock', address=None, auto_enable=True, gripper_joints=[], domain_id=0, adapter_kwargs={}, wb_config=None)
        _trajectory_task = <function _trajectory_task at 0xff4f12ad07c0>
        _xarm7_hw  = HardwareComponent(hardware_id='arm', hardware_type=<HardwareType.MANIPULATOR: 'manipulator'>, joints=['arm/joint1', 'a...'mock', address=None, auto_enable=True, gripper_joints=['arm/gripper'], domain_id=0, adapter_kwargs={}, wb_config=None)
        _xarm7_sim_home = [0.0, 0.0, 0.0, 0.0, 0.0, -0.7, ...]
        autoconnect = <function autoconnect at 0xff4fab92cc20>
        dual_xarm6_planner_coordinator = <[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()] Blueprint object at 0xff4f129bd370>
        dual_xarm7_planner_coordinator = <[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()] Blueprint object at 0xff4f12fe2810>
        global_config = GlobalConfig(robot_ip=None, robot_ips=None, unitree_aes_128_key=None, xarm7_ip=None, xarm6_ip=None, can_port=None, dev...e, obstacle_avoidance=True, detection_model='moondream', listen_host='127.0.0.1', dimsim_scene='apt', dimsim_port=8090)
        manipulator = <function manipulator at 0xff4f3c464900>
        math       = <module 'math' (built-in)>
        xarm6_planner_only = <[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()] Blueprint object at 0xff4f12fd5e50>
        xarm7_planner_coordinator = <[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()] Blueprint object at 0xff4f129bdf70>
        xarm7_planner_coordinator_agent = <[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()] Blueprint object at 0xff4f12fe3050>
        xarm_perception = <[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()] Blueprint object at 0xff4f12fe3440>
        xarm_perception_agent = <[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()] Blueprint object at 0xff4f12a7c800>
dimos/utils/data.py:369: in __str__
    return str(self._ensure_downloaded())
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm7.tar.gz after 3 attempts: Command...ude', 'data/.lfs/xarm7.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff4f3c4747d0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'xarm7/scene.xml'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/xarm7.tar.gz after 3 attempts: Command...ude', 'data/.lfs/xarm7.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xff4f3c4747d0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'xarm7'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/xarm7/scene.xml')
        name       = 'xarm7/scene.xml'
        nested_path = PosixPath('scene.xml')
        path_parts = ('xarm7', 'scene.xml')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/xarm7.tar.gz')
        filename   = 'xarm7'
        repo_root  = PosixPath('.../work/dimos/dimos')
dimos/utils/data.py:216: in _lfs_pull
    raise RuntimeError(
E   RuntimeError: Failed to pull LFS file .../dimos/data/.lfs/xarm7.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/xarm7.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': 'dc4b386c-5f6a-4d20-9a62-996820b35697.tests.ubuntu-24_04-arm_3_14_fal', ...}
        file_path  = PosixPath('.../dimos/data/.lfs/xarm7.tar.gz')
        last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/xarm7.tar.gz'])
        relative_path = PosixPath('data/.lfs/xarm7.tar.gz')
        repo_root  = PosixPath('.../work/dimos/dimos')
        retries    = 2
dimos.codebase_checks.test_no_all::test_no_all
Stack Traces | 4.85s run time
def test_no_all():
        """Fail if any file defines `__all__`."""
        dimos_dir = DIMOS_PROJECT_ROOT / "dimos"
        hits = find_all_definitions()
        if hits:
            listing = "\n".join(f"  - {p.relative_to(dimos_dir)}:{lineno}" for p, lineno in hits)
>           raise AssertionError(
                f"Found __all__ definition(s) in dimos/:\n{listing}\n\n"
                "__all__ is not allowed. We don't use `from x import *`, so __all__ "
                "lists serve no purpose and are tedious to maintain. Remove them. For "
                "an import that exists purely to be re-exported, use `# noqa: F401`."
            )
E           AssertionError: Found __all__ definition(s) in dimos/:
E             - control/blueprints/dual.py:103
E             - manipulation/blueprints.py:481
E             - .../planning/groups/identifiers.py:115
E             - .../planning/kinematics/pink_ik.py:822
E             - manipulation/planning/planning_identifiers.py:33
E             - robot/config.py:290
E             - .../manipulators/xarm/blueprints.py:138
E           
E           __all__ is not allowed. We don't use `from x import *`, so __all__ lists serve no purpose and are tedious to maintain. Remove them. For an import that exists purely to be re-exported, use `# noqa: F401`.

dimos_dir  = PosixPath('.../dimos/dimos/dimos')
hits       = [(PosixPath('.../dimos/dimos/dimos/control/blueprints/dual.py'), 103), (PosixPath('.../runner/work/dim.../planning/planning_identifiers.py'), 33), (PosixPath('.../dimos/dimos/dimos/robot/config.py'), 290), ...]
listing    = '  - control/blueprints/dual.py:103\n  - manipulation/blueprints.py:481\n  - .../planning/groups/identifiers....nipulation/planning/planning_identifiers.py:33\n  - robot/config.py:290\n  - .../manipulators/xarm/blueprints.py:138'

dimos/codebase_checks/test_no_all.py:51: AssertionError

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

@TomCC7 TomCC7 changed the title WIP spec: planning group and bimanual/multi-target motion planning feat: add manipulation planning groups Jun 17, 2026
@TomCC7 TomCC7 changed the title feat: add manipulation planning groups WIP: feat: add manipulation planning groups Jun 17, 2026
Comment thread dimos/e2e_tests/test_manipulation_planning_groups.py Outdated
Comment thread dimos/manipulation/planning/kinematics/jacobian_ik.py Outdated
Comment thread dimos/manipulation/planning/kinematics/jacobian_ik.py
Comment thread dimos/manipulation/planning/kinematics/jacobian_ik.py Outdated
Comment thread dimos/manipulation/manipulation_module.py
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py
@TomCC7 TomCC7 force-pushed the cc/spec/movegroup branch from f157926 to 31731b1 Compare June 19, 2026 02:31
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py
Comment thread dimos/manipulation/manipulation_module.py
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/planning/world/drake_world.py Outdated
Comment thread dimos/manipulation/planning/planning_group_utils.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/planning/examples/manipulation_client.py Outdated
Comment thread dimos/control/blueprints/dual.py
Comment thread dimos/manipulation/planning/groups/__init__.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py
Comment thread dimos/manipulation/visualization/types.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py
Comment thread dimos/manipulation/planning/planning_identifiers.py
Comment thread dimos/manipulation/visualization/viser/adapter.py Outdated
Comment thread dimos/manipulation/visualization/viser/adapter.py Outdated
Comment thread dimos/manipulation/visualization/viser/adapter.py Outdated
Comment thread dimos/manipulation/manipulation_module.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py Outdated
Comment thread dimos/robot/catalog/openarm.py Outdated
Comment thread dimos/manipulation/visualization/viser/module_access.py Outdated
Comment thread dimos/manipulation/visualization/types.py Outdated
Comment thread docs/capabilities/manipulation/readme.md Outdated
Comment thread docs/capabilities/manipulation/readme.md Outdated
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