$npx -y skills add graph-robots/open-robot-skills --skill geometryPure-math 3D geometry toolbox — back-project masks and depth to
| 1 | # geometry |
| 2 | |
| 3 | Pure-math perception/planning geometry as in-process typed tools, from |
| 4 | mask back-projection through OBB fitting to grasp-candidate generation, |
| 5 | plus the two scalar helpers (`geometry.iou`, `geometry.pose_distance`). |
| 6 | Fully CPU — no model weights, no GPU. |
| 7 | |
| 8 | ## When to use |
| 9 | |
| 10 | - Turning a segmentation mask + depth + camera calibration into world-frame |
| 11 | points (`mask_to_world_points`) and an object OBB |
| 12 | (`filter_and_compute_obb`). |
| 13 | - Deriving grasp poses from an OBB: `top_down_grasp_candidates` for tabletop |
| 14 | pick (feed the full list to `curobo.plan_to_grasp_poses` as a goalset), |
| 15 | `front_grasp_from_obb` for horizontal interactions (drawer/door handles). |
| 16 | - Building the collision world for the planner: `build_world_config` with the |
| 17 | target's mask in `object_masks` so the planner can `ignore_obstacle_names` |
| 18 | it. |
| 19 | |
| 20 | ## Install |
| 21 | |
| 22 | ```bash |
| 23 | uv sync --extra geometry # open3d + scikit-learn (cv2/scipy come with gap core) |
| 24 | # (pip: pip install -e ".[geometry]") |
| 25 | ``` |
| 26 | |
| 27 | The module imports lazily — the bundle loads (and the light tools work) |
| 28 | without the extra; only OBB fitting, DBSCAN filtering and world |
| 29 | reconstruction need open3d/sklearn/cv2. |
| 30 | |
| 31 | ## Gotchas (carried over from the service) |
| 32 | |
| 33 | - **OBB `extent` is HALF-extents** (gap.types convention, same as the proto). |
| 34 | `compute_obb` is upright-only: rotation is around world Z (no 3D tilt), and |
| 35 | extents use the 2nd/98th percentile of points, not strict min/max. |
| 36 | - Single-camera clouds are 2.5D: only camera-facing surfaces are observed, so |
| 37 | OBB centers carry a few cm of depth bias on opaque objects. (The service's |
| 38 | rehearsal-sandbox ground-truth snap that compensated for this in-container |
| 39 | was deliberately NOT ported — it depended on a `/app` sandbox file.) |
| 40 | - `top_down_grasp_candidates` default `z_offset=-0.04`: fingertip 4 cm below |
| 41 | the OBB top. With `z_offset=0.0` the fingers close above the object |
| 42 | (silent empty grip). Grasp Z is clamped to -0.05 m (table-clearance floor; |
| 43 | LIBERO table top is at world z=0). |
| 44 | - `mask_to_world_points` keeps only depths in [0.015, 20.0] m (HyRL bounds); |
| 45 | invalid/zero-depth pixels are dropped. |
| 46 | - `filter_noise` returns the ORIGINAL cloud unchanged when DBSCAN labels |
| 47 | everything noise (defensive fallback, mirrors HyRL). |
| 48 | - `build_world_config`: table removal only runs when `table_z_threshold != 0` |
| 49 | (typical -0.01); robot-point exclusion is Franka-only (simplified DH FK) |
| 50 | and skips non-7-DOF joint states; prefer explicit `object_masks` over the |
| 51 | `target_obb` projection fallback — masks are pixel-accurate, the OBB |
| 52 | projection is a corner-AABB approximation inflated by 2 cm. |
| 53 | - `top_down_grasp_from_obb` yaw is NOT derived from the OBB — fingers may |
| 54 | close across the wide axis; use the candidate fan when orientation matters. |