# Moveit Wbc

> Bring up MoveIt 2 + whole-body-control RViz for the Genie G2 family, using `ros2 launch genie_sim_moveit wbc.launch.py`. Covers arm × gripper selection, the three packaged IK plugins (KDL-coupled, bio_ik-coupled, relaxed-IK), the GenieBioIK human-prior A/B switch, and the `use_ros2_control:=false` mode for direct `/joint_command` driving. Trigger: When the user asks to "start moveit", "wbc rviz", "plan with moveit", "switch IK plugin", "drive joint_command directly", "compare human priors", or pairs MoveIt with a `scene_*_g2_*` launched by the `launch-scene` skill.

- Skill: `agibottech/moveit-wbc` (Agent Skill)
- Install (CLI): `npx skillmds@latest add agibottech/moveit-wbc`
- Raw SKILL.md: https://api.skillmd.com/api/skills/agibottech/moveit-wbc/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MPL-2.0
- Author: agibottech (https://skillmd.com/u/agibottech)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/agibottech/moveit-wbc

---


## When to Use

- A `scene_*_g2_{op,sp}` is running (via the `launch-scene` skill) and
  the user wants planning + RViz on top.
- User wants to switch the active IK plugin or A/B the GenieBioIK
  "human priors" (torso-straight, chassis-pin, head LookAt).
- User wants to drive the arm directly via `/joint_command` (e.g.
  `wok_flip_cmds.py`) without ros2_control fighting the publisher.

Do **not** use for:
- Robots other than Genie G2 — the SRDF, coupled-joint constraints,
  and ros2_controllers manifest are G2-only. Other robots run on the
  engine but don't have a packaged MoveIt config.
- Launching the simulator itself → `launch-scene` skill.
- Building the workspace → `build-workspace` skill.

## Critical Patterns

1. **`arm` and `gripper` are independent.** Defaults are
   `arm:=crsB gripper:=swiftpicker`. The launch wires both into the
   URDF filename and the SRDF xacro mappings, so any
   `(arm, gripper)` combo that has a corresponding URDF under
   `genie_sim_robot_model/urdf/` works.
2. **Match MoveIt's `(arm, gripper)` to the scene's robot variant.**
   `scene_pnp_g2_op` → `gripper:=omnipicker`,
   `scene_wbc_g2_sp` → `gripper:=swiftpicker`. Mismatched values
   produce a URDF MoveIt can plan against but a robot the engine
   refuses to drive.
3. **Pick the IK plugin via `use_human_priors`, not by editing yaml.**
   `use_human_priors:=true` (default) loads
   `config/kinematics.yaml` (GenieBioIK with priors);
   `use_human_priors:=false` loads `config/kinematics_vanilla.yaml`
   (upstream BioIK, every prior weight zeroed). Both files share the
   plugin selection / IK links / timeouts so the only A/B variable is
   the goal stack.
4. **`use_ros2_control:=false` for `/joint_command` driving.** When an
   external publisher (teleop, wok-flip script) writes
   `/joint_command`, the ros2_control node would republish at the CM
   update rate and fight it — set this flag to leave `move_group`
   alone (the `/compute_ik`, `/compute_fk` services) so there's a
   single source on the topic.

## The matrix

| Scene (from `launch-scene`) | Recommended `wbc.launch.py` args |
|---|---|
| `scene_pnp_g2_op` | `arm:=crsB gripper:=omnipicker` |
| `scene_wbc_g2_sp` | `arm:=crsB gripper:=swiftpicker` (defaults) |
| `scene_flat_g2_sp` / `_chef` / `_shirt` | defaults (`crsB` + `swiftpicker`) |

| IK plugin / mode | Args |
|---|---|
| GenieBioIK with human priors (default) | `use_human_priors:=true` |
| Upstream BioIK (priors zeroed, for A/B) | `use_human_priors:=false` |
| `move_group` only — `/compute_ik` services, no controllers | `use_ros2_control:=false` |

## Workflow

### Step 1 — Confirm the scene is up

`launch-scene` skill should already be running in another shell.
Verify:

```bash
ros2 topic list | grep -E "joint_states|tf|clock"
ros2 topic hz /clock                    # sim-time pacing should be live
```

### Step 2 — Pick `arm` + `gripper`

Ask via `AskUserQuestion` if not given. Default to the scene's
gripper (see matrix above).

### Step 3 — Launch MoveIt

```bash
source devel/setup.bash

# Default (Genie G2 + crsB + swiftpicker + human priors + ros2_control):
ros2 launch genie_sim_moveit wbc.launch.py

# crs arm + omnipicker:
ros2 launch genie_sim_moveit wbc.launch.py arm:=crs gripper:=omnipicker

# A/B vanilla BioIK (priors disabled):
ros2 launch genie_sim_moveit wbc.launch.py use_human_priors:=false

# Direct /joint_command (e.g. external teleop / wok_flip_cmds.py):
ros2 launch genie_sim_moveit wbc.launch.py use_ros2_control:=false
```

### Step 4 — Plan in RViz

The launch starts `move_group`, `robot_state_publisher`, and
`rviz2` with the MoveIt-Plan-Execute panel. Drag the interactive
marker on the IK link, hit **Plan**, then **Execute**.

### Step 5 — (optional) Drive the chassis as a single chain

`simple_body_controller` claims the same body-joint position
interfaces as `simple_waist_controller` + `simple_torso_controller`,
so it's configured but not auto-spawned. To drive the body as one
chain:

```bash
ros2 control switch_controllers \
  --activate simple_body_controller \
  --deactivate simple_waist_controller simple_torso_controller
```

## Commands (copy-paste summary for the user)

```bash
# Inside the container, after sourcing the overlay:
source devel/setup.bash

# Most common: match a pnp scene
ros2 launch genie_sim_moveit wbc.launch.py arm:=crsB gripper:=omnipicker

# A/B the human priors
ros2 launch genie_sim_moveit wbc.launch.py use_human_priors:=false

# move_group only (for external /joint_command publishers)
ros2 launch genie_sim_moveit wbc.launch.py use_ros2_control:=false
```

## Notes

- The MoveIt URDF joint limits are widened by ±0.01 rad / ±0.001 m
  at launch time to absorb MuJoCo soft-limit drift —
  `CheckStartStateBounds` reads URDF limits directly, so the
  per-pipeline `start_state_max_bounds_error` doesn't help. Helper
  lives in `genie_sim_robot_model.urdf_utils.pad_urdf_joint_limits`.
- Default workspace half-extent for OMPL planar-joint bounds is set
  to 200 m via `ompl.default_workspace_bounds` — without this,
  `FixWorkspaceBounds` clamps `planar_joint/trans_{x,y}` to a narrow
  box that excludes the actual map → base_link pose.
- `fix_start_state:=true` lets jazzy's `CheckStartStateBounds` adapter
  actively renormalise joint values that drift past `<limit>` by
  sub-mrad under contact load (default upstream is false).
- The MoveIt config is **distro-agnostic** (Humble + Jazzy). Distro
  drift is absorbed by `moveit_compat.hpp` in
  `genie_sim_moveit_plugins/`, never by `#ifdef` in the config
  package.

## Resources

- **Launch file**: [source/geniesim_ros/src/ros_ws/src/genie_sim_moveit/launch/wbc.launch.py](../../src/ros_ws/src/genie_sim_moveit/launch/wbc.launch.py)
- **Per-gripper SRDF fragments**: [source/geniesim_ros/src/ros_ws/src/genie_sim_moveit/config/srdf_grippers/](../../src/ros_ws/src/genie_sim_moveit/config/srdf_grippers/)
- **IK plugins**: [source/geniesim_ros/src/ros_ws/src/genie_sim_moveit_plugins/](../../src/ros_ws/src/genie_sim_moveit_plugins/)
- **MoveIt config overview**: [source/geniesim_ros/src/ros_ws/src/genie_sim_moveit/AGENTS.md](../../src/ros_ws/src/genie_sim_moveit/AGENTS.md)

