$npx -y skills add graph-robots/open-robot-skills --skill grasping-direct-ikDirect IK align-then-descend grasping. The gripper pre-rotates to
| 1 | # grasping-direct-ik |
| 2 | |
| 3 | Direct-IK grasp: rotate the gripper to grasp orientation at a safe height |
| 4 | above the target, then descend straight down, then close. No trajectory |
| 5 | planner — works on platforms where CuRobo is not deployed, or in |
| 6 | uncluttered scenes where planning is overkill. |
| 7 | |
| 8 | ## When to use |
| 9 | |
| 10 | - The `curobo` tool bundle is not deployed (no collision-aware planner |
| 11 | available). |
| 12 | - The scene is uncluttered enough that a straight-line approach is safe. |
| 13 | |
| 14 | ## When NOT to use |
| 15 | |
| 16 | - Cluttered scenes where the arm must thread between obstacles. Prefer |
| 17 | `grasping-with-planner` if available. |
| 18 | |
| 19 | ## Recommended subgraph state flow |
| 20 | |
| 21 | The subgraph state machine the agent generates should look like (6 states): |
| 22 | |
| 23 | ```text |
| 24 | open → compute_grasp → compute_align → rotate_align → descend → close → grasped |
| 25 | ``` |
| 26 | |
| 27 | (`grasped` is the success-marker `noop` from `sg.add_exit("grasped")`, |
| 28 | with an edge to `END`.) |
| 29 | |
| 30 | State details: |
| 31 | |
| 32 | 1. **`open`** — `type: tool`, `tool: "robot.open_gripper"`, `inputs: { settle_steps: 40 }`. |
| 33 | 2. **`compute_grasp`** — `type: tool`, `tool: "geometry.top_down_grasp_candidates"`, |
| 34 | `inputs: { obb: Ref("in.target_obb") }`. |
| 35 | 3. **`compute_align`** — `type: script`, file |
| 36 | `scripts/<sg>/compute_align_pose.py` (from this bundle's |
| 37 | `canonical_scripts`). Inputs: |
| 38 | `grasp_pose = Ref("compute_grasp.candidates.poses.0")`, |
| 39 | `target_obb = Ref("in.target_obb")`. Returns `align_pose`. |
| 40 | 4. **`rotate_align`** — `type: tool`, `tool: "robot.go_to_pose"`, |
| 41 | `inputs: { pose: Ref("compute_align.align_pose") }`. |
| 42 | 5. **`descend`** — `type: tool`, `tool: "robot.go_to_pose"`, |
| 43 | `inputs: { pose: Ref("compute_grasp.candidates.poses.0") }`. |
| 44 | 6. **`close`** — `type: tool`, `tool: "robot.close_gripper"`, `inputs: { settle_steps: 60 }`. |
| 45 | Edge directly from `close` to the `grasped` success marker; the |
| 46 | subgraph's `on_error: "failed"` catches any raise from earlier steps. |
| 47 | Whether the gripper actually closed on the object is checked by the |
| 48 | `target_held` postcondition checkpoint (see `## Checkpoints`), NOT by |
| 49 | a re-check-and-raise node (none such exists). |
| 50 | |
| 51 | ```json |
| 52 | "edges": [ ..., ["close", "grasped"], ["grasped", "END"] ], |
| 53 | "conditional_edges": {}, |
| 54 | "exit": { "router_field": null, "success_values": ["grasped"] }, |
| 55 | "on_error": "failed" |
| 56 | ``` |
| 57 | |
| 58 | The lift onto a safe carry height is handled by the next |
| 59 | `transporting-objects` subgraph (its `waypoint_move` script lifts before |
| 60 | lateral motion); do NOT add a lift step here. |
| 61 | |
| 62 | ## Hard rules |
| 63 | |
| 64 | 1. Use `geometry.top_down_grasp_candidates` (returns |
| 65 | `candidates: {poses: list[Se3Pose]}`), not |
| 66 | `geometry.top_down_grasp_from_obb` (single bare pose). The align-pose |
| 67 | construction in step 3 assumes `compute_grasp.candidates.poses.0` exists. |
| 68 | 2. The `align_pose` descends straight down with the gripper pre-rotated. |
| 69 | Do NOT skip the `compute_align` + `rotate_align` states — a direct |
| 70 | `robot.go_to_pose` to the grasp pose blends rotation and descent and |
| 71 | twist |