Skip to content

feat: add unified dual-arm VR teleoperation - #3392

Open
TomCC7 wants to merge 24 commits into
krishna/feat/openarm-damiaofrom
cc/feat/dual-arm-teleop
Open

feat: add unified dual-arm VR teleoperation#3392
TomCC7 wants to merge 24 commits into
krishna/feat/openarm-damiaofrom
cc/feat/dual-arm-teleop

Conversation

@TomCC7

@TomCC7 TomCC7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Contribution path

  • Linked issue or discussion: closes DIM-1395

Problem

Quest arm teleoperation assumed one controller, one end-effector, and one IK task. OpenArm needs both controllers to drive one bimanual model in a single solve. The existing Cartesian, twist, and teleop paths also duplicated Pink setup and lacked a common streaming safety envelope for noisy, delayed hardware feedback.

Solution

  • Replace the legacy teleop task and duplicated Cartesian Pink controller with a shared PoseTargetIKTask core. CartesianIKTask, EEFTwistTask, and TeleopIKTask remain thin input-specific leaves.
  • Support one or two hand-to-frame bindings in TeleopIKTask, including optional gripper joints. A two-hand task captures both references together and requires both primary buttons; timeout, release, preemption, or E-stop clears the full session.
  • Keep a persistent command trajectory separate from measured feedback. Each Pink step is bounded by configured and URDF velocity limits, measured tracking error, joint limits, and a command margin, while feedback receives a small limit tolerance for sensor error.
  • Keep planning and control ownership separate: PinkIK exposes the planning API, PinkPoseTargetSolver owns streaming control state, and both use the private shared Pink solver core.
  • Allow each robot to inject a PinkPoseTargetSolver subclass. OpenArm tunes frame costs, posture weights, and per-arm manipulability tasks without replacing common solving or safety logic.
  • Add TeleopControlCoordinator for teleop-only pose and button ports while leaving the generic coordinator free of Quest streams. Existing A1Z, mixed-arm, OpenYam, Piper, and XArm Quest blueprints use the unified task configuration.
  • Add teleop-quest-openarm with one bimanual IK task and one OpenArm URDF. It uses fake hardware by default; specifying both CAN interfaces selects real hardware. The blueprint also includes the manipulation planner, Viser visualization, and a higher-priority dual-arm trajectory task.

How to Test

Run the complete feature with fake hardware:

uv run dimos run teleop-quest-openarm

To use real OpenArm hardware, specify both CAN interfaces:

uv run dimos run teleop-quest-openarm --left-can-port can0 --right-can-port can1

Run the focused control, Pink, routing, Quest, and OpenArm tests:

uv run pytest dimos/control/tasks/test_pose_target_ik.py dimos/control/tasks/cartesian_ik_task/test_cartesian_ik_task.py dimos/control/tasks/eef_twist_task/test_eef_twist_task.py dimos/control/tasks/teleop_ik_task/test_teleop_ik_task.py dimos/control/test_coordinator_routing.py dimos/manipulation/planning/kinematics/test_pink_ik.py dimos/robot/manipulators/openarm/test_openarm_teleop.py dimos/teleop/quest/test_blueprints.py dimos/teleop/quest/test_quest_teleop_module.py

Current result: 156 passed.

AI assistance

Codex with GPT-5 assisted substantially with architecture exploration, implementation, tests, documentation, and verification. The author developed and reviewed the changes interactively.

Checklist

  • I have read and approved the CLA.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

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

Files with missing lines Patch % Lines
...os/manipulation/planning/kinematics/pink_solver.py 77.73% 75 Missing and 45 partials ⚠️
dimos/control/tasks/pose_target_ik.py 87.64% 12 Missing and 10 partials ⚠️
...mos/control/tasks/teleop_ik_task/teleop_ik_task.py 91.21% 10 Missing and 8 partials ⚠️
...mos/control/tasks/eef_twist_task/eef_twist_task.py 81.91% 13 Missing and 4 partials ⚠️
...ntrol/tasks/cartesian_ik_task/cartesian_ik_task.py 85.10% 5 Missing and 2 partials ⚠️
dimos/robot/manipulators/common/blueprints.py 33.33% 2 Missing and 2 partials ⚠️
dimos/control/tasks/test_pose_target_ik.py 98.48% 2 Missing ⚠️
...os/robot/manipulators/openarm/blueprints/teleop.py 93.54% 2 Missing ⚠️
dimos/robot/manipulators/openarm/teleop_ik.py 91.30% 1 Missing and 1 partial ⚠️
@@                       Coverage Diff                       @@
##           krishna/feat/openarm-damiao    #3392      +/-   ##
===============================================================
+ Coverage                        75.92%   76.46%   +0.53%     
===============================================================
  Files                             1205     1201       -4     
  Lines                           117065   115802    -1263     
  Branches                         10425    10334      -91     
===============================================================
- Hits                             88887    88553     -334     
+ Misses                           25417    24254    -1163     
- Partials                          2761     2995     +234     
Flag Coverage Δ
OS-ubuntu-24.04-arm 70.49% <86.73%> (+0.09%) ⬆️
OS-ubuntu-latest 72.48% <86.73%> (+0.08%) ⬆️
Py-3.10 72.47% <86.73%> (+0.08%) ⬆️
Py-3.11 72.47% <86.73%> (+0.08%) ⬆️
Py-3.12 72.48% <86.73%> (+0.08%) ⬆️
Py-3.13 72.47% <86.73%> (+0.08%) ⬆️
Py-3.14 72.48% <86.73%> (+0.08%) ⬆️
Py-3.14t 72.48% <86.73%> (+0.08%) ⬆️
SelfHosted-Large 29.74% <28.17%> (+0.13%) ⬆️
SelfHosted-Linux 36.15% <39.66%> (+0.25%) ⬆️

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

Files with missing lines Coverage Δ
dimos/control/_control_test_helpers.py 91.89% <100.00%> (+2.23%) ⬆️
dimos/control/coordinator.py 83.10% <ø> (+1.08%) ⬆️
.../tasks/cartesian_ik_task/test_cartesian_ik_task.py 100.00% <100.00%> (ø)
...ontrol/tasks/eef_twist_task/test_eef_twist_task.py 100.00% <100.00%> (ø)
dimos/control/tasks/teleop_ik_task/_registry.py 100.00% <ø> (ø)
...ontrol/tasks/teleop_ik_task/test_teleop_ik_task.py 100.00% <100.00%> (ø)
dimos/control/tasks/test_registry.py 87.87% <ø> (ø)
dimos/control/teleop_coordinator.py 100.00% <100.00%> (ø)
dimos/control/test_coordinator_routing.py 99.45% <100.00%> (+0.03%) ⬆️
dimos/core/coordination/module_coordinator.py 86.06% <100.00%> (ø)
... and 28 more

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

Comment thread dimos/manipulation/planning/kinematics/pink_ik.py
Comment thread dimos/control/coordinator.py Outdated
Comment thread dimos/control/tasks/pose_target_ik.py
TomCC7 added 6 commits August 7, 2026 14:14
# Conflicts:
#	CONTEXT.md
#	dimos/cli/dimos.py
#	dimos/control/tasks/cartesian_ik_task/cartesian_ik_task.py
#	dimos/control/tasks/cartesian_ik_task/test_cartesian_ik_task.py
#	dimos/control/tasks/quest_teleop_ik_task/test_teleop_task.py
#	dimos/control/tasks/teleop_task/teleop_task.py
#	dimos/hardware/test_adapter_registries.py
#	dimos/manipulation/planning/world/roboplan_world.py
#	dimos/manipulation/test_roboplan.py
#	dimos/robot/all_blueprints.py
#	dimos/robot/manipulators/common/blueprints.py
#	dimos/robot/manipulators/common/mixed.py
#	dimos/robot/manipulators/openarm/blueprints/teleop.py
#	dimos/robot/manipulators/openyam/blueprints/teleop.py
#	dimos/robot/manipulators/piper/blueprints/teleop.py
#	dimos/robot/manipulators/xarm/blueprints/teleop.py
#	dimos/teleop/quest/README.md
#	dimos/teleop/quest/test_quest_teleop_module.py
#	docs/capabilities/manipulation/index.md
…o cc/feat/dual-arm-teleop

# Conflicts:
#	dimos/cli/dimos.py
#	dimos/hardware/test_adapter_registries.py
#	dimos/manipulation/test_roboplan.py
Comment thread dimos/control/tasks/teleop_ik_task/teleop_ik_task.py
Comment thread dimos/core/global_config.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_ik.py
Comment thread dimos/robot/manipulators/openarm/teleop_ik.py Outdated
Comment thread dimos/robot/manipulators/piper/cli.py Outdated
Comment thread dimos/teleop/quest/quest_extensions.py Outdated
@TomCC7
TomCC7 marked this pull request as ready for review August 8, 2026 06:34
Comment thread dimos/control/tasks/pose_target_ik.py Outdated
Comment thread dimos/control/tasks/cartesian_ik_task/cartesian_ik_task.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_solver.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_solver.py Outdated
Comment thread docs/capabilities/manipulation/openarm_integration.md
Comment thread docs/capabilities/manipulation/adding_a_custom_arm.md Outdated
Comment thread dimos/utils/data.py Outdated
Comment thread dimos/manipulation/planning/kinematics/pink_solver.py
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 8, 2026
ManipulationModule._control_coordinator: ControlCoordinator resolved by
exact class identity, so OpenArmTeleopCoordinator (a ControlCoordinator
subclass added to gate real/mock hardware on CAN flags) never matched.
The ref silently became None, and viser Plan->Execute crashed hardware
with AttributeError: 'NoneType' object has no attribute
'execute_trajectory'. Reproduced live on the OpenArm 2.0 rig.

Match module-type refs by issubclass instead of identity in both the
blueprint-time resolver and get_instance()/_instance_keys_of(). Ambiguity
handling (multiple matching instances -> "pass the instance name") is
unchanged; this only rescues subclass providers that were false
negatives before.

Added a regression test reproducing the exact failure with a Calculator1
subclass; verified it fails with the pre-fix identity check and passes
with issubclass.
teleop_ik_task's max_joint_velocity_rad_s defaulted to 5.0 rad/s
(~286 deg/s) uncapped by the OpenArm quest blueprint. Krishna reported
the real OpenArm 2.0 felt too sensitive under Quest teleop. Set to
2.0 rad/s (~114 deg/s), tuned on hardware (1.0 felt too slow).
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Aug 8, 2026
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 8, 2026
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Aug 8, 2026
@TomCC7 TomCC7 changed the title feat: add unified dual-arm Quest teleoperation feat: add unified dual-arm VR teleoperation Aug 8, 2026
Measured feedback beyond feedback_limit_tolerance raised and skipped the
control tick entirely, resetting the command trajectory. On real OpenArm
2.0 hardware a joint resting a fraction of a degree past its limit
(mechanical stop give, encoder noise) could never recover: the tick that
would compute a corrective command was the exact one being skipped.
Reproduced live, right_arm/joint6 stuck until manually flexed off its
stop.

Add feedback_clamp_margin (0.05 rad default). Deviations within it now
clamp to the limit and the tick proceeds normally; only a deviation
beyond it still raises as a genuine fault. The downstream command
envelope already bounded commands safely, so removing the premature
raise is sufficient. Threaded through all four task configs that share
this Pink core: pose_target_ik, teleop_ik_task, cartesian_ik_task,
eef_twist_task.

Verified on hardware: the same joint-limit condition that previously
deadlocked now self-corrects without manual intervention.
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Aug 9, 2026
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.

2 participants