$npx -y skills add graph-robots/open-robot-skills --skill grasping-short-axis--- name: grasping-short-axis description: Deterministic short-axis-aligned grasp with CuRobo. The grasp pose is computed geometrically (NOT sampled/scored) — the gripper approaches straight down with its finger-opening axis locked to the OBB's SHORTER horizontal axis, so t
| 1 | --- |
| 2 | name: grasping-short-axis |
| 3 | description: Deterministic short-axis-aligned grasp with CuRobo. The grasp |
| 4 | pose is computed geometrically (NOT sampled/scored) — the gripper |
| 5 | approaches straight down with its finger-opening axis locked to the OBB's |
| 6 | SHORTER horizontal axis, so the jaws close ACROSS the narrow dimension of |
| 7 | an elongated target. Use for pan/pot handles, bottles, tools, utensils — |
| 8 | anywhere the OBB centroid is graspable but orientation is the thing that |
| 9 | matters. An optional offset_from_base node slides the grasp outward along |
| 10 | the handle, clear of the heavier object body, when a second OBB of that |
| 11 | body is wired in. A scored/sampled pose often holds at close but shears |
| 12 | out under the lift; the short-axis pose grips the bar squarely so it |
| 13 | survives lift + transport. |
| 14 | compatibility: requires gap>=0.1 |
| 15 | metadata: {category: grasping, tags: [grasping, manipulation, collision-aware, curobo, short-axis, handle]} |
| 16 | gap: |
| 17 | allowed_tools: |
| 18 | - geometry.build_world_config |
| 19 | - robot.get_observation |
| 20 | - curobo.plan_to_grasp_poses |
| 21 | - curobo.plan_grasp_motion |
| 22 | - curobo.plan_directed_linear |
| 23 | - robot.execute_trajectory |
| 24 | - robot.move_to_joints |
| 25 | - robot.get_ee_pose |
| 26 | - robot.go_to_pose |
| 27 | - robot.go_to_pose_cartesian |
| 28 | - robot.open_gripper |
| 29 | - robot.close_gripper |
| 30 | exit_conditions: |
| 31 | grasped: Object held in the gripper after `close`. |
| 32 | failed: Any failure during the grasp attempt — collision-aware planning failure or trajectory execution error (a raise propagating to `on_error`). Coordinator routes to abort. |
| 33 | required_inputs: |
| 34 | # MANDATORY — the coordinator MUST declare both in `grasp_sg.inputs` |
| 35 | # and wire them from the upstream handle-perception subgraph. |
| 36 | # OPTIONAL extra input (NOT listed here so graph-validation does not |
| 37 | # force it): `base_obb: OrientedBoundingBox`. See the `offset_from_base` |
| 38 | # hard_rule — declare + wire it ONLY when you also authored a second |
| 39 | # perception subgraph for the heavier object body (pan bowl / pot |
| 40 | # belly). Omitting it is valid; the grasp degrades to a plain |
| 41 | # short-axis grasp (still correct, just not slid off the body). |
| 42 | target_obb: OrientedBoundingBox |
| 43 | target_mask: Mask |
| 44 | produces_outputs: |
| 45 | ee_pose_at_grasp: Se3Pose |
| 46 | grasp_pose: Se3Pose |
| 47 | hard_rules: |
| 48 | - > |
| 49 | ALWAYS begin with `robot.open_gripper` (settle_steps=40). Skipping |
| 50 | this is the #1 silent grasp-failure mode when the gripper starts |
| 51 | closed from a previous task step. |
| 52 | - > |
| 53 | Use the canonical `compute_grasp` (`scripts/<sg>/short_axis_grasp_pose.py`) |
| 54 | — it returns ONE deterministic Se3Pose aligned to the OBB's shorter |
| 55 | horizontal axis. Do NOT swap in `geometry.top_down_grasp_candidates` |
| 56 | or any sampled/scored generator: the whole point of this skill is |
| 57 | that the orientation is geometry-locked, not discriminator-ranked. |
| 58 | `plan_grasp` auto-wraps the single pose into a one-element goalset. |
| 59 | - > |
| 60 | `offset_from_base` (`scripts/<sg>/offset_grasp_from_base.py`) is the |
| 61 | robustness lever for a handle attached to a heavier body (frying pan, |
| 62 | pot). It slides the grasp outward along the handle, clear of the |
| 63 | body, so the jaws grip the bar — not the rim — and the grip survives |
| 64 | the lift. To enable it: (1) author a SECOND perception subgraph that |
| 65 | segments the object BODY (e.g. perceiving-object-parts / |
| 66 | perceiving-objects with object/subpart name like "the round base of |
| 67 | the frying pan"), |
| 68 | (2) add `base_obb: OrientedBoundingBox` to THIS subgraph's `inputs` |
| 69 | and wire it from that body perception, (3) bind |
| 70 | `base_obb = Ref("in.base_obb")` on the `offset_from_base` node. |
| 71 | If you do NOT author a body perception, KEEP the `offset_from_base` |
| 72 | state but bind only `handle_obb` + `grasp_pose` (omit `base_obb`); |
| 73 | the script no-ops and returns the grasp unchanged. NEVER write |
| 74 | `Ref("in.base_obb")` unless `base_obb` is a declared subgraph input — |
| 75 | that is a graph-validation error. |
| 76 | - > |
| 77 | When building the collision world, ALWAYS pass |
| 78 | `target_mask = Ref("in.target_mask")` AND |
| 79 | `target_obb = Ref("in.target_obb")` AND a `target_name`. The mask is |
| 80 | the authoritative target silhouette (pixel-accurate); the OBB is a |
| 81 | safety fallback. The same `target_name` MUST be passed to `plan` |
| 82 | (`plan_grasp.py`) so the planner excludes that mesh from collision. |
| 83 | - > |
| 84 | ALWAYS include the `approach` state between `offset_from_base` and |
| 85 | `observe`. `plan_grasp` disables CuRobo's `use_grasp_approach` flag on |
| 86 | the assumption the gripper is already oriented over the target — |
| 87 | skipping `approach` makes the descent start from a wrong wrist |
| 88 | rotation and slip. `approach_above.py` lifts to a safe height, |
| 89 | translates above the target, then rotates in place to the grasp |
| 90 | orientation — all three legs route through |
| 91 | `robot.go_to_pose_cartesian` (gap.connector.ik.CuRoboBackend), which |
| 92 | uses cuRobo's |