Adriano Dal PastroandClaude Opus 4.7 1fa7d12ad7 feat(right_arm): force, camera↔polso offset, cluster filter sul centroide
Tre miglioramenti al move-toward dopo il primo uso live:

1. checkbox "force" accanto a "Muovi": bypassa max_step_m quando
   l'utente sa che il delta richiesto e' grande ma corretto (es. primo
   avvicinamento all'oggetto).

2. camera_offset_wrist_mm: il modello assumeva camera e TCP coincidenti
   (sole sign flips). La camera Nuwa e' invece montata con traslazione
   costante rispetto al polso. La formula diventa:
       delta_wrist = sign * (centroid_cam - offset_cam) + t_wrist_cam
   Esposto in config (default 0,0,0), GUI (3 input "Camera rispetto a
   polso (mm)") e in /state. Il termine compensa il fatto che muovere
   il polso porta la camera, non il TCP, sul punto di presa.

3. filter_nearest_cluster: il centroide era media di tutti i punti
   SAM21 della mask. In presenza di sfondo lontano visibile attraverso
   buchi della depth del primo piano la media veniva trascinata a
   metri di distanza. Ora si tiene il cluster piu' vicino:
       z_near = p5(Z)
       keep   = punti con Z <= z_near + cluster_depth_window_m
   Default window 0.20 m, configurabile. La risposta di /detect/wrist
   espone n_sam21_points, n_centroid_points e z_near/z_cutoff per
   diagnosi.

Aggiornata anche la persistenza: /offset accetta camera_offset_wrist_mm.
GUI: i campi Offset/Sign/CamOff rispettano "user touched" (refresh non
li cancella mentre l'utente digita).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 12:18:24 +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%