Skip to content

feat: default control IK to Pink#2992

Open
TomCC7 wants to merge 26 commits into
mainfrom
cc/feat/ik-task-self-collision
Open

feat: default control IK to Pink#2992
TomCC7 wants to merge 26 commits into
mainfrom
cc/feat/ik-task-self-collision

Conversation

@TomCC7

@TomCC7 TomCC7 commented Jul 16, 2026

Copy link
Copy Markdown
Member

Contribution path

  • Small, safe change that does not need a tracking issue
  • Linked issue or discussion: DIM-1272

Problem

Cartesian and keyboard EEF-twist control used a custom Pinocchio IK implementation. Not quite extensible.

Solution

Pink is now the sole Cartesian and EEF-twist control IK backend. Both task types share the Cartesian pipeline, re-anchor every solve to measured joint state, bound control dt once per task tick, enforce model and joint mappings, and hold measured position for expected solve failures.

All shipped manipulator teleop blueprints, including Piper, now use direct URDF/Xacro models with named end-effector frames. Pink remains optional: task modules import without it, while constructing a Pink task without the manipulation extra gives an actionable install instruction.

The legacy Pinocchio control IK fallback and all self-collision logic were removed from this control path.

How to Test

Run Piper keyboard teleop with the manipulation extra and robot connection configured:

uv run --extra manipulation dimos run keyboard-teleop-piper

AI assistance

OpenCode with GPT-5.6-terra was substantially involved in implementation, testing, review-response triage, and PR maintenance.

Checklist

  • This PR is scoped to one clearly stated problem.
  • I ran the relevant focused pytest, Ruff, mypy, blueprint-registry, and diff checks.
  • I have reviewed and understood every line in this PR.
  • I disclosed AI assistance above.
  • I have read and approved the CLA.

@mintlify

mintlify Bot commented Jul 16, 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 Jul 16, 2026, 1:34 AM

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

@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

❌ 17 Tests Failed:

Tests completed Failed Passed Skipped
2776 17 2759 221
View the top 3 failed test(s) by shortest run time
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_applies_position_velocity_limits_and_finite_output
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_applies_position_vel0')

    def test_pink_applies_position_velocity_limits_and_finite_output(tmp_path: Path) -> None:
        model_path = _write_urdf(tmp_path)
        robot = _robot(model_path).model_copy(
            update={"joint_limits_lower": [-0.5, -0.25], "joint_limits_upper": [0.5, 0.25]}
        )
>       backend = PinkControlIK(
            PinkControlIKConfig(robot_model=robot, max_velocity=0.2),
        )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_applies_position_vel0/tiny.urdf')
robot      = RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_rpc_timeout=120.0, rpc_timeouts=... coordinator_task_name=None, gripper_hardware_id=None, tf_extra_links=[], home_joints=[0.4, 0.4], pre_grasp_offset=0.1)
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_applies_position_vel0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:320: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390125f200>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_clamps_tiny_position_limit_overshoot
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_clamps_tiny_position0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012cb110>

    def test_pink_clamps_tiny_position_limit_overshoot(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        model_path = _write_urdf(tmp_path)
        robot = _robot(model_path).model_copy(
            update={"joint_limits_lower": [-1.22, -0.25], "joint_limits_upper": [1.22, 0.25]}
        )
>       backend = PinkControlIK(PinkControlIKConfig(robot_model=robot))

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_clamps_tiny_position0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012cb110>
robot      = RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_rpc_timeout=120.0, rpc_timeouts=... coordinator_task_name=None, gripper_hardware_id=None, tf_extra_links=[], home_joints=[0.4, 0.4], pre_grasp_offset=0.1)
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_clamps_tiny_position0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:340: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff39012ca810>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_posture_task_can_be_disabled
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_posture_task_can_be_0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012cb350>

    def test_pink_posture_task_can_be_disabled(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        model_path = _write_urdf(tmp_path)
>       backend = PinkControlIK(PinkControlIKConfig(robot_model=_robot(model_path), posture_cost=0.0))

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_posture_task_can_be_0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012cb350>
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_posture_task_can_be_0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:266: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_....0001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.0, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff39012cb500>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_reanchors_measured_state_and_runs_one_frame_task_step
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_reanchors_measured_s0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390125dac0>

    def test_pink_reanchors_measured_state_and_runs_one_frame_task_step(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        model_path = _write_urdf(tmp_path)
>       backend = PinkControlIK(
            PinkControlIKConfig(robot_model=_robot(model_path), qpsolver_options={"eps": 1e-6}),
        )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_reanchors_measured_s0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390125dac0>
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_reanchors_measured_s0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:189: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...in=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={'eps': 1e-06})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390125c170>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_receives_pre_bounded_dt_unchanged
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_receives_pre_bounded0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012c9be0>

    def test_pink_receives_pre_bounded_dt_unchanged(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        model_path = _write_urdf(tmp_path)
>       backend = PinkControlIK(
            PinkControlIKConfig(robot_model=_robot(model_path)),
        )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_receives_pre_bounded0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012c9be0>
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_receives_pre_bounded0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:244: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff39012c9c10>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_rejects_material_position_limit_violation
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_material_pos0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390130eb70>

    def test_pink_rejects_material_position_limit_violation(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        model_path = _write_urdf(tmp_path)
        robot = _robot(model_path).model_copy(
            update={"joint_limits_lower": [-1.22, -0.25], "joint_limits_upper": [1.22, 0.25]}
        )
>       backend = PinkControlIK(PinkControlIKConfig(robot_model=robot))

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_material_pos0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390130eb70>
robot      = RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_rpc_timeout=120.0, rpc_timeouts=... coordinator_task_name=None, gripper_hardware_id=None, tf_extra_links=[], home_joints=[0.4, 0.4], pre_grasp_offset=0.1)
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_material_pos0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:364: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390130d220>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_solver_dependency_failure_is_translated_to_runtime_error
Stack Traces | 0.002s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_solver_dependency_fa0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012c8b90>

    def test_pink_solver_dependency_failure_is_translated_to_runtime_error(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
>       backend = PinkControlIK(PinkControlIKConfig(robot_model=_robot(_write_urdf(tmp_path))))

monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff39012c8b90>
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_solver_dependency_fa0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:225: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff39012c8aa0>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_continuous_joint_scalar_limits_fail_with_actionable_diagnostic
Stack Traces | 0.003s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_continuous_joint_scalar_l0')

    def test_continuous_joint_scalar_limits_fail_with_actionable_diagnostic(tmp_path: Path) -> None:
        model_path = _write_urdf(tmp_path, "continuous.urdf", _CONTINUOUS_URDF)
        robot = _robot(model_path, joints=["joint1"])
    
        with pytest.raises(ValueError, match="continuous joints.*tangent-space"):
>           PinkControlIK(
                PinkControlIKConfig(robot_model=robot),
            )

model_path = PosixPath('.../pytest-0/popen-gw1/test_continuous_joint_scalar_l0/continuous.urdf')
robot      = RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_rpc_timeout=120.0, rpc_timeouts=...g={}, coordinator_task_name=None, gripper_hardware_id=None, tf_extra_links=[], home_joints=[0.4], pre_grasp_offset=0.1)
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_continuous_joint_scalar_l0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:299: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390130dc70>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_prepares_xacro_with_package_paths_and_arguments
Stack Traces | 0.003s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_prepares_xacro_with_0')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390125d4f0>

    def test_pink_prepares_xacro_with_package_paths_and_arguments(
        tmp_path: Path, monkeypatch: pytest.MonkeyPatch
    ) -> None:
        model_path = _write_urdf(tmp_path)
        package_path = tmp_path / "description"
        package_path.mkdir()
        robot = _robot(model_path).model_copy(
            update={
                "model_path": tmp_path / "robot.xacro",
                "package_paths": {"description": package_path},
                "xacro_args": {"dof": "2"},
            }
        )
        prepared: dict[str, object] = {}
    
        def prepare(
            path: Path,
            package_paths: dict[str, Path],
            xacro_args: dict[str, str],
            convert_meshes: bool,
        ) -> str:
            prepared.update(
                path=path,
                package_paths=package_paths,
                xacro_args=xacro_args,
                convert_meshes=convert_meshes,
            )
            return str(model_path)
    
        monkeypatch.setattr(
            "dimos.control.tasks.cartesian_ik_task.pink_control_ik.prepare_urdf_for_drake",
            prepare,
        )
    
>       PinkControlIK(
            PinkControlIKConfig(robot_model=robot),
        )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_prepares_xacro_with_0/tiny.urdf')
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0xff390125d4f0>
package_path = PosixPath('.../pytest-0/popen-gw1/test_pink_prepares_xacro_with_0/description')
prepare    = <function test_pink_prepares_xacro_with_package_paths_and_arguments.<locals>.prepare at 0xff3918007380>
prepared   = {}
robot      = RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_rpc_timeout=120.0, rpc_timeouts=... coordinator_task_name=None, gripper_hardware_id=None, tf_extra_links=[], home_joints=[0.4, 0.4], pre_grasp_offset=0.1)
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_prepares_xacro_with_0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:156: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390125d430>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_rejects_uncontrolled_end_effector_chain_without_reference
Stack Traces | 0.003s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_uncontrolled0')

    def test_pink_rejects_uncontrolled_end_effector_chain_without_reference(
        tmp_path: Path,
    ) -> None:
        model_path = _write_urdf(tmp_path, "uncontrolled.urdf", _UNCONTROLLED_URDF)
        with pytest.raises(ValueError, match="reference_q.*uncontrolled joint"):
>           PinkControlIK(
                PinkControlIKConfig(robot_model=_robot(model_path)),
            )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_uncontrolled0/uncontrolled.urdf')
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_rejects_uncontrolled0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:289: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff390130c9e0>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.control.tasks.cartesian_ik_task.test_pink_control_ik::test_pink_validates_named_frame_and_exact_joint_mapping
Stack Traces | 0.003s run time
tmp_path = PosixPath('.../pytest-0/popen-gw1/test_pink_validates_named_fram0')

    def test_pink_validates_named_frame_and_exact_joint_mapping(tmp_path: Path) -> None:
        model_path = _write_urdf(tmp_path)
    
        with pytest.raises(ValueError, match="end-effector frame"):
>           PinkControlIK(
                PinkControlIKConfig(robot_model=_robot(model_path, frame="missing")),
            )

model_path = PosixPath('.../pytest-0/popen-gw1/test_pink_validates_named_fram0/tiny.urdf')
tmp_path   = PosixPath('.../pytest-0/popen-gw1/test_pink_validates_named_fram0')

.../tasks/cartesian_ik_task/test_pink_control_ik.py:172: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
.../tasks/cartesian_ik_task/pink_control_ik.py:111: in __init__
    pink_module = _require_pink()
        config     = PinkControlIKConfig(robot_model=RobotModelConfig(rpc_transport=<class 'dimos.protocol.rpc.pubsubrpc.LCMRPC'>, default_...001, task_gain=1.0, position_cost=1.0, orientation_cost=1.0, posture_cost=0.001, reference_q=None, qpsolver_options={})
        self       = <dimos.control.tasks.cartesian_ik_task.pink_control_ik.PinkControlIK object at 0xff3901225430>
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

    def _require_pink() -> ModuleType:
        if pink is None:
>           raise ModuleNotFoundError(_PINK_INSTALL_ERROR, name="pink") from None
E           ModuleNotFoundError: Pink control tasks require the optional 'pink' dependency. Install it with `uv sync --extra manipulation`.


.../tasks/cartesian_ik_task/pink_control_ik.py:62: ModuleNotFoundError
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[openarm]
Stack Traces | 3.1s run time
robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/openarm_description.tar.gz after 3 att...m_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335cb90>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

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

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/openarm_description.tar.gz after 3 att...fs/openarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe8640cdf50>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = '.../urdf/robot/openarm_v10_left.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/openarm_description.tar.gz after 3 att...fs/openarm_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe8640cdf50>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'openarm_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/.../urdf/robot/openarm_v10_left.urdf')
        name       = '.../urdf/robot/openarm_v10_left.urdf'
        nested_path = PosixPath('urdf/robot/openarm_v10_left.urdf')
        path_parts = ('openarm_description', 'urdf', 'robot', 'openarm_v10_left.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/openarm_description.tar.gz')
        filename   = 'openarm_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/openarm_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/openarm_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/openarm_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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/openarm_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/openarm_description.tar.gz'])
relative_path = PosixPath('data/.lfs/openarm_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[xarm7]
Stack Traces | 3.1s run time
robot_model = <[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 0xffe86335c780>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

robot_model = <[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 0xffe86335c780>

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        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 0xffe86416e950>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'xarm_description/urdf/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 0xffe86416e950>
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/xarm_device.urdf.xacro')
        name       = 'xarm_description/urdf/xarm_device.urdf.xacro'
        nested_path = PosixPath('urdf/xarm_device.urdf.xacro')
        path_parts = ('xarm_description', 'urdf', '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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.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
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[piper]
Stack Traces | 3.11s run time
robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/piper_description.tar.gz after 3 attem...r_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335c960>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/piper_description.tar.gz after 3 attem...r_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335c960>

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/piper_description.tar.gz after 3 attem....lfs/piper_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe863b84fd0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'piper_description/urdf/piper_description.xacro'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/piper_description.tar.gz after 3 attem....lfs/piper_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe863b84fd0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'piper_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/piper_description/urdf/piper_description.xacro')
        name       = 'piper_description/urdf/piper_description.xacro'
        nested_path = PosixPath('urdf/piper_description.xacro')
        path_parts = ('piper_description', 'urdf', 'piper_description.xacro')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/piper_description.tar.gz')
        filename   = 'piper_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/piper_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/piper_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/piper_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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/piper_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/piper_description.tar.gz'])
relative_path = PosixPath('data/.lfs/piper_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[a750]
Stack Traces | 3.12s run time
robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a750_description.tar.gz after 3 attemp...0_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335cd70>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

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

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a750_description.tar.gz after 3 attemp.../.lfs/a750_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe8641a2a50>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'a750_description/urdf/a750_rev1.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a750_description.tar.gz after 3 attemp.../.lfs/a750_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe8641a2a50>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'a750_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/a750_description/urdf/a750_rev1.urdf')
        name       = 'a750_description/urdf/a750_rev1.urdf'
        nested_path = PosixPath('urdf/a750_rev1.urdf')
        path_parts = ('a750_description', 'urdf', 'a750_rev1.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/a750_description.tar.gz')
        filename   = 'a750_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/a750_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/a750_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a750_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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/a750_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/a750_description.tar.gz'])
relative_path = PosixPath('data/.lfs/a750_description.tar.gz')
repo_root  = PosixPath('.../work/dimos/dimos')
retries    = 2

dimos/utils/data.py:216: RuntimeError
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[xarm6]
Stack Traces | 3.13s run time
robot_model = <[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 0xffe86335c5a0>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

robot_model = <[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 0xffe86335c5a0>

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        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 0xffe86416e950>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = 'xarm_description/urdf/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 0xffe86416e950>
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/xarm_device.urdf.xacro')
        name       = 'xarm_description/urdf/xarm_device.urdf.xacro'
        nested_path = PosixPath('urdf/xarm_device.urdf.xacro')
        path_parts = ('xarm_description', 'urdf', '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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.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
dimos.robot.manipulators.test_blueprints::test_shipped_model_family_has_named_eef_frame[a1z]
Stack Traces | 3.13s run time
robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335cf50>

    @pytest.mark.parametrize(
        "robot_model",
        [
            pytest.param(make_xarm6_model_config(add_gripper=False), id="xarm6"),
            pytest.param(make_xarm7_model_config(add_gripper=False), id="xarm7"),
            pytest.param(make_piper_model_config(), id="piper"),
            pytest.param(openarm_model_config("left"), id="openarm"),
            pytest.param(make_a750_model_config(), id="a750"),
            pytest.param(make_a1z_model_config(has_gripper=True), id="a1z"),
        ],
    )
    def test_shipped_model_family_has_named_eef_frame(robot_model: RobotModelConfig) -> None:
>       assert robot_model.model_path.is_file()

robot_model = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] RobotModelConfig object at 0xffe86335cf50>

.../robot/manipulators/test_blueprints.py:205: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
dimos/utils/data.py:364: in __getattribute__
    resolved = object.__getattribute__(self, "_ensure_downloaded")()
        name       = 'is_file'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe863b84ed0>
dimos/utils/data.py:347: in _ensure_downloaded
    cache = get_data(filename)
        cache      = None
        filename   = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        self       = <[RuntimeError("Failed to pull LFS file .../dimos/data/.lfs/a1z_description.tar.gz after 3 attempt...a/.lfs/a1z_description.tar.gz']' returned non-zero exit status 1.") raised in repr()] LfsPath object at 0xffe863b84ed0>
dimos/utils/data.py:304: in get_data
    archive_path = _decompress_archive(_pull_lfs_archive(archive_name))
        archive_name = 'a1z_description'
        data_dir   = PosixPath('.../dimos/dimos/data')
        file_path  = PosixPath('.../dimos/dimos/data/.../A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        name       = '.../A1Z_G1Z/urdf/A1Z_G1Z.urdf'
        nested_path = PosixPath('A1Z_G1Z/urdf/A1Z_G1Z.urdf')
        path_parts = ('a1z_description', 'A1Z_G1Z', 'urdf', 'A1Z_G1Z.urdf')
dimos/utils/data.py:248: in _pull_lfs_archive
    _lfs_pull(file_path, repo_root)
        file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
        filename   = 'a1z_description'
        repo_root  = PosixPath('.../work/dimos/dimos')
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

file_path = PosixPath('.../dimos/data/.lfs/a1z_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/a1z_description.tar.gz after 3 attempts: Command '['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_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': '22a1eb60-f986-4dc4-80a7-f15ff4d46ed3.tests.ubuntu-24_04-arm_3_14_fal', ...}
file_path  = PosixPath('.../dimos/data/.lfs/a1z_description.tar.gz')
last_err   = CalledProcessError(1, ['git', 'lfs', 'pull', '--include', 'data/.lfs/a1z_description.tar.gz'])
relative_path = PosixPath('data/.lfs/a1z_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.

Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py
Comment thread dimos/control/tasks/cartesian_ik_task/cartesian_ik_task.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
@TomCC7 TomCC7 added the backport:skip Skip creating a backport to any release branches label Jul 18, 2026
@TomCC7
TomCC7 marked this pull request as ready for review July 18, 2026 22:55
@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR moves Cartesian and end-effector twist control to Pink-based IK. The main changes are:

  • Shared Cartesian control pipeline for pose and twist tasks.
  • Pink model, joint, frame, timing, and limit validation.
  • Updated manipulator teleop blueprints and manipulation documentation.
  • Deferred optional Pink dependency handling with an installation message.

Confidence Score: 5/5

This looks safe to merge.

  • The optional Pink import no longer blocks task module loading.
  • Cartesian and twist task construction returns a clear installation message when Pink is absent.
  • The changed dependency behavior is covered for both task types.

Important Files Changed

Filename Overview
dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Adds the Pink control backend and defers the optional Pink import until task construction.
dimos/control/tasks/cartesian_ik_task/cartesian_ik_task.py Routes Cartesian control through the shared Pink backend.
dimos/control/tasks/eef_twist_task/eef_twist_task.py Reuses the Cartesian control path for measured-state twist control.
dimos/control/tasks/cartesian_ik_task/test_cartesian_ik_task.py Covers imports without Pink and the actionable missing-dependency message.

Reviews (4): Last reviewed commit: "docs: simplify control IK guidance" | Re-trigger Greptile

Comment thread dimos/control/tasks/cartesian_ik_task/pink_control_ik.py Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport:skip Skip creating a backport to any release branches

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants