Files
Presa-Mano/README.md
T

123 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# presa-mano
Microservizio Python (FastAPI) che orchestra:
1. **Calibrazione hand-eye** (eye-to-hand) tra la **Nuwa depth camera** (fissa) e la
**base del robot**, usando un **ArUco sul polso**. Supporta **G1** e **H1**.
2. **Riconoscimento oggetto** via YOLO (bbox) + **segmentazione 3D** via SAM2.1 →
nuvola di punti dell'oggetto.
3. **Calibrazione piano di lavoro** + filtro "sotto piano" applicato a ogni cloud.
4. **Pianificazione presa**: asse dell'oggetto (PCA) + posa polso per il robot attivo.
5. **Visualizzazione 3D** (tab Pose 3D): viewport Three.js con URDF del robot
specchiato sui giunti reali, point cloud live (Nuwa eye-in-hand + D435i front
camera), gizmo di editing pose, named poses salvabili.
## Microservizi usati
| Servizio | URL (default) | Scopo |
|-------------------|-----------------------|----------------------------------------------------|
| Nuwa-HP60C | `:8877` | RGB, depth, point cloud, intrinsics (eye-in-hand polso DX) |
| Front camera (D435i) | config `services.front_camera` | Point cloud frontale fissa sul torso |
| Marker-finder | `:8082` | Detection 2D ArUco (corners + pose) |
| YOLO Server | `:8879` | Bbox oggetto da prendere |
| SAM21 (NuwaSeg) | `:8002` | Segmentazione promptable: bbox → cloud 3D filtrata |
| G1 API Server | `:8765` | Controllo polso/mano G1, diagnostics giunti (dry-run se down) |
| H1 API Server | `:8767` | Controllo polso/mano H1 (network host, 4 DoF) |
Il servizio **presa-mano** espone GUI + API su **http://localhost:8890**. Il robot attivo
è scelto da `config.yaml > robot.active` (`g1` | `h1`); l'orchestrator fa routing
automatico verso il bridge corrispondente.
## Quick start
```bash
docker compose up -d presa-mano
# Apri http://localhost:8890
```
## GUI
Tab principali:
- **Calibrazione**: muovi il polso in varie pose, *Acquisisci sample*, poi *Risolvi*.
Salva `data/calibration_<side>.yaml`.
- **Calibrazione Piano**: clicca due angoli sull'RGB per definire la ROI sul tavolo
→ *Calcola piano* fa il fit SVD sui punti depth, mostra in 3D (vista camera) il
piano e l'intera scena con punti esclusi in rosso / tenuti in ciano. Il parametro
*offset (mm)* regola la soglia del filtro "punti sotto piano" (positivo = buffer
permissivo, negativo = esclude anche un buffer sopra il piano per togliere rumore
depth). *Salva piano* persiste in `data/work_plane.json`.
- **Cerca modello**: rileva oggetto via YOLO, segmenta via SAM21 (con fallback bbox
se SAM21 è down), visualizza bbox + asse, esegui (toggle dry-run).
- **Movimento EE**: lettura pose correnti, invio pose assolute/relative, apri/chiudi mano,
emergency stop.
- **Braccio DX**: detection via YOLO su front camera + camera polso, move-toward
iterativo, pose salvabili (home, custom), verifica tag ArUco.
- **Pose 3D**: viewport Three.js con URDF G1 specchiato sui giunti reali
(`/diagnostics`), point cloud Nuwa + D435i, modello 3D camera sul polso,
gizmo traslazione/rotazione, editor pose (Apply/Jog), named poses salvabili.
Overlay tag ArUco su entrambe le camere (Nuwa = rosso, D435i = ciano).
Calibrazione cross-camera: pulsante *Allinea* esegue fit Umeyama Nuwa→D435i
da un tag comune, persiste in `config.yaml > pose_3d.cross_camera`.
La tab resta utilizzabile anche senza robot (mostra il modello URDF, config,
pose salvate).
## Pipeline cloud-from-bbox
Dopo che YOLO ha trovato un bbox, gli endpoint `/api/grasp/{plan,execute}`,
`/api/perception/{scan,object_points}` estraggono la nuvola di punti
dell'oggetto con questa pipeline:
1. **SAM21** segmenta il bbox usando RGB + depth allineata IR→RGB
(`perception/depth_align.py`) → cloud nell'OpenCV camera frame, in metri.
Su fallimento (transport / `SEGMENTATION_EMPTY` / `<10` punti) → fallback
trasparente al path bbox legacy (Nuwa ROI pointcloud).
2. **Filtro piano**: se `data/work_plane.json` esiste, scarta i punti con
`signed_distance < -offset_mm/1000` (vedi tab Calibrazione Piano).
3. Le response includono `source ∈ {"sam21","bbox_roi"}`, `plane_applied`,
`n_points_before_plane` e — su fallback SAM21 — `sam21_fallback_reason`.
Disabilitabile via `grasp.use_sam21: false` in `config.yaml`.
## Calibrazione automatica via script
In `scripts/`:
| Script | Cosa fa |
|----------------------------|--------------------------------------------------------------------------|
| `explore_and_calibrate.py` | Probe assiale dell'envelope di visibilità tag → workspace JSON + PNG → N sample random nel workspace → solve. |
| `extend_calibration.py` | Estende la calibrazione esistente fino a `n_used >= TARGET_GOOD` (default 30). Riusa il workspace, NON cancella i sample. |
| `repeatability.py` | N detection consecutive a polso fermo per misurare σ del marker-finder (diagnostica bias vs noise). |
Tutti producono file in `data/`: `workspace_<side>.json`, `workspace_<side>.png`,
`samples_<side>.json`, `calibration_<side>.yaml`.
## Configurazione
`config.yaml` contiene i default; l'API espone `GET/PUT /api/config` per override
runtime (es. cambiare ID/size ArUco, robot attivo, side di default).
Default rilevanti:
- `calibration.method: DANIILIDIS` — sui dati H1+Nuwa con bias intrinsics di
marker-finder, DANIILIDIS dà ~4× meno residuo di TSAI (default OpenCV).
- `robot.active: h1` / `robot.wrist_side: right`.
## Caveat noti
- **Wrist passthrough**: i campi `speed_mm_s` e `traj_step_mm` non sono nello schema
di `/api/wrist/{absolute,relative}`. Per move "precisi" (closed-loop, slow,
waypoint fitti) bisogna chiamare direttamente il bridge robot, es.
`POST http://localhost:8767/wrist/absolute` su H1.
- **Marker-finder intrinsics**: il sibling service usa intrinsics D435i bakeate che
non corrispondono alla Nuwa → bias sistematico in `T_cam_marker`. È il floor del
residuo hand-eye (~14 mm su H1+Nuwa). Per scendere serve ricalibrare quel servizio.
- **H1 4 DoF**: nessun polso articolato. Si comanda solo posizione; l'orientamento
del polso è output dell'IK e drifta con la posa (fino a ~30° su Y ai bordi
dell'envelope). I solver hand-eye risentono della scarsa diversità rotazionale.
- **`/api/arms/go_neutral`**: il proxy nell'orchestrator non è montato (404). Per
riportare le braccia H1 a home: `POST http://localhost:8767/arms/go_neutral` diretto.
Vedi `CLAUDE.md` per i dettagli di architettura e per le frame conventions
(Nuwa native vs OpenCV vs Nuwa ROI).