Adriano Dal PastroandClaude Opus 4.7 ae510e4387 feat(work_plane gui): overlay inliers used for fit as blue dots on RGB
The fit endpoint now returns fit_points_px (the post-outlier-filter
inliers projected to RGB pixels via Nuwa's RGB intrinsics). The GUI
draws them on the same overlay as the ROI rectangle so the user can
visually verify which depth pixels were fed to fit_plane_svd.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 11:10:25 +02:00

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.

Microservizi usati

Servizio URL (default) Scopo
Nuwa-HP60C http://localhost:8877 RGB, depth, point cloud, intrinsics
Marker-finder http://localhost:8082 Detection 2D ArUco (corners + pose)
YOLO Server http://localhost:8879 Bbox oggetto da prendere
SAM21 (NuwaSeg) http://localhost:8002 Segmentazione promptable: bbox → cloud 3D filtrata
G1 API Server http://localhost:8765 Controllo polso/mano G1 (dry-run se down)
H1 API Server http://localhost: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

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.

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).

S
Description
No description provided
Readme
24 MiB
0 Stars 1 Watchers 0 Forks
Languages
Python 62.6%
JavaScript 27%
HTML 5.1%
CSS 4.8%
Shell 0.3%
Other 0.2%