| name | teleop-bridge |
| description | Wire a `geniesim_teleop` VR/Pico session into a running Genie Sim RT Engine scene — pick the right scene yaml, launch `wbc.launch.py` with `use_ros2_control:=false` so move_group serves `/compute_ik` while the teleop publisher owns `/joint_command`, and confirm no topic fighting. Trigger: When the user asks to "connect teleop to the sim", "桥接 teleop", "drive the engine with VR", "use teleop instead of ros2_control", "stop the controllers from fighting teleop", or has a teleop loop ready and a scene up but the robot jitters / flicks.
|
| license | MPL-2.0 |
| metadata | {"author":"genie-sim","version":"1.0"} |
| prerequisites | ["geniesim_ros:launch-scene","geniesim_teleop:run-teleop"] |
| inputs | [{"name":"scene_gripper","desc":"Gripper variant of the running scene; the MoveIt `gripper:=` must match (e.g. `omnipicker` for `scene_pnp_g2_op`)","required":true}] |
| outputs | [{"desc":"Exactly one publisher on `/joint_command` (the teleop node); no topic fighting from the controller manager"}] |
When to Use
- VR teleop loop (
geniesim_teleop skill run-teleop) and an RT
Engine scene (launch-scene skill) need to share /joint_command
without the ros2_control hardware interface rebroadcasting it at
the controller-manager update rate.
- User sees "the arm jitters / payloads get flung" — that's the
classic two-publisher symptom.
- External motion script (e.g.
wok_flip_cmds.py) and MoveIt are
both interested in the same topic.
Do not use for:
- Starting the teleop loop itself →
run-teleop skill in
geniesim_teleop.
- MoveIt planning with the simulator's controllers driving →
moveit-wbc skill (default mode).
- Recording the teleop session →
record-episode skill.
Critical Patterns
use_ros2_control:=false is the bridge switch. With it,
wbc.launch.py starts move_group only (/compute_ik,
/compute_fk) and skips the ros2_control node + controller
spawners. /joint_command then has exactly one source: teleop.
- Match scene gripper to teleop config.
run-teleop defaults
to G2_omnipicker.json. Use scene_pnp_g2_op (or any
scene_*_g2_op) so the engine is built with the same gripper
the teleop loop expects. Mismatched grippers produce a robot
MoveIt can plan against but the engine refuses to drive.
- Three processes, three shells. Scene + MoveIt + teleop each
run as a long-lived ROS node. Don't try to background them in one
shell — the engine and teleop both want stdin / SIGINT semantics.
use_sim_time:=true is engine-side, but the teleop loop
publishes wall-clock by default. The bridge uses sim time inside
the scene; the publisher rate adapts. Don't override unless you
know what you're doing.
Workflow
Step 1 — Confirm the prerequisites
- Scene is up via
launch-scene skill (e.g.
scene_pnp_g2_op × launcher_ovrtx_isaac_physx).
- Teleop config matches the scene's gripper.
- VR device reachable, default port
8080.
Step 2 — Launch MoveIt without controllers
In a fresh shell:
source devel/setup.bash
ros2 launch genie_sim_moveit wbc.launch.py \
arm:=crsB \
gripper:=omnipicker \
use_ros2_control:=false
Console banner should print:
[wbc.launch.py] use_ros2_control=false → move_group only (no ros2_control
node / controllers); drive the arm via /joint_command.
Step 3 — Start teleop
In another shell (geniesim docker into):
geniesim teleop run \
--device_type=pico \
--port=8080 \
--robot_config=G2_omnipicker.json
Step 4 — Verify single-publisher on /joint_command
ros2 topic info /joint_command -v
If you see two publishers, you forgot use_ros2_control:=false —
stop MoveIt and relaunch with the flag.
Step 5 — Plan-then-execute combo (optional)
Even without ros2_control, /compute_ik is still available. So you
can:
- Use MoveIt's RViz markers to plan and call
/compute_ik for
manual goals.
- Hand the resulting joint target to your teleop publisher (or skip
MoveIt entirely for direct VR drive).
Commands (copy-paste summary for the user)
source devel/setup.bash
ros2 launch genie_sim_bringup app.launch.py \
scene:=scene_pnp_g2_op \
launcher_config:=launcher_ovrtx_isaac_physx \
headless:=false
source devel/setup.bash
ros2 launch genie_sim_moveit wbc.launch.py \
arm:=crsB gripper:=omnipicker use_ros2_control:=false
geniesim teleop run --device_type=pico --port=8080 \
--robot_config=G2_omnipicker.json
ros2 topic info /joint_command -v
Notes
simple_body_controller is configured but not auto-spawned even in
the controller mode — it conflicts with
simple_waist_controller + simple_torso_controller on the same
body-joint resources. The teleop bridge avoids that ambiguity by
not spawning any of them.
- For wok-flip / scripted motion (
wok_flip_cmds.py,
benchmark replay scripts), the same use_ros2_control:=false
flag applies — anything that owns /joint_command directly needs
the controllers out of the way.
- The teleop loop has its own per-episode recording hooks
(
--record-dir). For raw-topic recording on top, see the
record-episode skill.
Resources