feat(lab_sim): add Quest controller teleoperation objective - #618
feat(lab_sim): add Quest controller teleoperation objective#618nbbrooks wants to merge 1 commit into
Conversation
Adds a single new "Quest Teleop" objective to lab_sim, plus the host-side infrastructure required to pair with the [meta_quest_teleoperation](https://github.com/PickNikRobotics/meta_quest_teleoperation) Unity app. Operators wearing a Quest can teleoperate the simulated UR through clutch-based pose tracking. How the disjoint TF trees are handled The Quest publishes controller pose in a `quest` frame anchored at the headset's spawn location. The robot's `world` frame is anchored at its base. We don't know the geometric relationship between them — it depends on where the operator is physically standing — and we don't try to measure it. The two TF trees are disjoint roots, and the design works by never crossing between them: * On grip press, snapshot both anchors: - controller pose at clutch time, expressed in `quest` - grasp_link pose at clutch time, expressed in `world` * While grip is held, each tick: - read the controller's current pose in `quest` - compute its pose relative to its clutch-time pose — a relative rigid-body transform with no inherent frame attachment - apply that same relative transform to the grasp_link's clutch-time pose; the result is a target pose in `world` - drive VFC at the target A basis change re-expresses the relative transform from Quest FLU (X=forward, Y=left, Z=up) into the IMarker EE convention used by `grasp_link` (X=left, Y=up, Z=forward) so the components line up when applied. The basis change is the conjugation R · X · R⁻¹, expressed with two existing core primitives — `TransformPoseWithPose` (pre-mul by R) followed by `TransformPose` (post-mul by R⁻¹). Specific to this EE — re-derive R for other conventions. Why no v1..v10 history? This is a clean cut. Earlier iterations explored in-app clutch logic, separate-subtree slot architectures, and various basis-change strategies. Only the final design ships: * App publishes raw pose + button state; host BT owns clutch + kinematics. * Disjoint quest/world TF roots are handled by snapshot + relative- transform composition, never by a frame crossing. * Basis-change is expressed via the two-call conjugation using only existing core primitives; no new behavior added. * Per-button slot actions are inline AlwaysSuccess in a single flat tree, so the objective is self-contained — no SubTree-files-to-copy. Files - src/lab_sim/objectives/quest_teleop.xml: the objective. Single flat BehaviorTree, runnable from the UI under the "User Input" subcategory. - src/lab_sim/launch/sim/robot_drivers_to_persist_sim.launch.py: drivers- to-persist launch override. Starts ros_tcp_endpoint (Unity bridge) and a debug-only world->quest static TF so RViz can render the in-quest-frame VisualizePose markers. The static TF is not used by any control path. Persists across agent_bridge restarts so the Quest's TCP socket stays up. - src/lab_sim/config/config.yaml: * simulated_robot_driver_persist_launch_file points at the new launch file (without this, the inherited empty launch wins and the Quest app never sees a peer). * experimental_behaviors loader added to behavior_loader_plugins, which provides the pose / vector / Odometry conversion, snapshot subscriber, and Bool publisher behaviors the objective uses. - .gitmodules: two new submodules under src/external_dependencies/: * moveit_pro_experimental_behaviors, pinned to feat/pose-vector-basis-snapshot-behaviors pending the PR landing on main; switch to `branch = main` once that merges. * ROS-TCP-Endpoint (Unity-Technologies), pinned to main-ros2. For end-to-end setup with the Quest app, see: https://docs.picknik.ai/hardware_guides/input_devices/setting_up_the_meta_quest_for_teleop/ Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2c0e116 to
082dc8d
Compare
|
This objective is what was used during the May Qualcomm visit for the Kinova teleop demo https://github.com/PickNikRobotics/moveit_pro_kinova_ws/tree/picknik_006_gen3-quest-teleop |
|
The docs update/rewrite to just install the apk (no installing Unity, no building the apk yourself) is https://github.com/PickNikRobotics/moveit_pro/pull/18976 |
|
The slack discussion around these PRs is here https://picknik.slack.com/archives/C0B4KSQB490/p1779126664079659 |
|
[written by AI] Stale PR sweep. Opened 2026-05-11, last commit 2026-05-11. We are cleaning up open PRs older than three months. State: the most clearly valuable of the Quest PRs and the one with the most context. You noted on 2026-07-20 that this objective is what ran the Kinova teleop demo at the May Qualcomm visit. But it has not been touched since May, four of its six paths are absent from Recommendation: schedule it or close it, but do not leave it drifting. Three PRs across two repos (this, moveit_pro #18975, moveit_pro #18976) are all waiting on the same decision, and adding two Git submodules to example_ws is a change that should not go stale before review.
|
Adds a single new "Quest Teleop" objective to lab_sim, plus the host-side infrastructure required to pair with the
meta_quest_teleoperation Unity app.
How the disjoint TF trees are handled
The Quest publishes controller pose in a
questframe anchored at the headset's spawn location. The robot'sworldframe is anchored at its base. We don't know the geometric relationship between them — it depends on where the operator is physically standing — and we don't try to measure it. The two TF trees are disjoint roots, and the design works by never crossing between them:quest- grasp_link pose at clutch time, expressed inworldquestworld- drive VFC at the targetFiles
branch = mainonce that merges.For end-to-end setup with the Quest app, see:
https://docs.picknik.ai/hardware_guides/input_devices/setting_up_the_meta_quest_for_teleop/