refactor(pose_3d): rimuovi agent/chat, aggiungi cloud+diagnostics+camera polso

Rimosso il modulo agent (LLM chat) e la relativa config OpenRouter.
La tab Pose 3D ora: espone i giunti reali via G1 /diagnostics per
specchiare l'URDF senza IK; acquisisce point cloud Nuwa+D435i
(/api/pose_3d/clouds); mostra il modello 3D della camera sul polso;
degrada gracefully a 200 (wrist=null) quando il robot è offline.
Il vecchio renderer canvas-2D (app.js view3d + diagnostic3d.js) è
stato consolidato nel viewport Three.js di pose_3d.js.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Adriano Dal PastroandClaude Opus 4.7 committed 2026-05-27 17:34:42 +02:00
1 parent 9d30073d57
commit 6c2452fe20
20 files changed
+152757 -2714

No files matched your search

+109 -162
View File
@@ -12,11 +12,8 @@
<h1>presa-mano</h1>
<nav class="tabs">
<button class="tab active" data-tab="home">Home</button>
<button class="tab" data-tab="agent">Assistente AI</button>
<button class="tab" data-tab="ai-settings">Impostazioni AI</button>
<button class="tab" data-tab="workplane">Calibrazione Piano</button>
<button class="tab" data-tab="grasp">Cerca modello</button>
<button class="tab" data-tab="view3d">Vista 3D</button>
<button class="tab" data-tab="right-arm">Braccio DX</button>
<button class="tab" data-tab="pose-3d">Pose 3D</button>
<button class="tab" data-tab="ee">Movimento EE</button>
@@ -68,115 +65,6 @@
<pre id="homeLog" class="log small">—</pre>
</section>
<!-- ============================== ASSISTENTE AI ============================== -->
<section class="panel" id="tab-agent">
<div class="posture-toolbar">
<span class="post-health" id="agentHealth">—</span>
<span class="active-robot-pill" id="agentActiveRobotPill" title="Robot attivo">robot: —</span>
<button id="agentClearBtn" title="Resetta la conversazione">Nuova conversazione</button>
</div>
<div class="grid-2 agent-layout">
<!-- LEFT: chat history + input -->
<div>
<h2>Chat</h2>
<div id="agentChat" class="agent-chat">
<div class="agent-msg system">Ciao. Dimmi cosa vuoi fare con il robot. Es: "muovi il braccio destro 200 mm in X+", "calibra il braccio destro", "mostra lo stato dei dispositivi".</div>
</div>
<div class="agent-input-row">
<textarea id="agentInput" rows="3" placeholder="Scrivi un comando o una richiesta in linguaggio naturale… (Ctrl+Invio per inviare)"></textarea>
<button id="agentSendBtn" class="primary">Invia ▸</button>
</div>
<div class="marker-info" id="agentStatus">—</div>
</div>
<!-- RIGHT: saved tasks + tool trace -->
<div>
<h2>Task salvati</h2>
<div class="info-box">
Salva un prompt usato spesso (es. "calibra braccio dx") per riutilizzarlo con un click. Il prompt corrente nella textarea viene salvato.
</div>
<div class="kv compact">
<label>Nome <input type="text" id="agentTaskName" placeholder="es. calibrazione_dx" maxlength="80"></label>
<label>Descrizione <input type="text" id="agentTaskDesc" placeholder="(opzionale)" maxlength="400"></label>
</div>
<div class="actions">
<button id="agentTaskSaveBtn" class="primary">💾 Salva prompt corrente</button>
<button id="agentTaskReloadBtn">⟳ Ricarica</button>
</div>
<ul id="agentTaskList" class="agent-task-list">
<li class="dim">caricamento…</li>
</ul>
<h2>Tool trace ultima risposta</h2>
<pre id="agentTrace" class="log small">(nessuna chiamata)</pre>
</div>
</div>
</section>
<!-- ============================== IMPOSTAZIONI AI ============================== -->
<section class="panel" id="tab-ai-settings">
<div class="info-box">
Parametri del client OpenRouter usato dall'Assistente AI. Le modifiche vengono persistite in <code>config.yaml</code> via <code>PUT /api/config</code> e applicate alla chat successiva (non c'è bisogno di restart). Il <strong>prompt aggiuntivo</strong> viene appeso al prompt di sistema dinamico — non sostituisce le regole base (frame, tool, sicurezza).
</div>
<div class="grid-2">
<div>
<h2>Modello e parametri</h2>
<div class="ctrl-block">
<div class="kv compact">
<label>Modello (combo)
<select id="aiModelSelect">
<option value="">caricamento…</option>
</select>
</label>
<label>Modello (custom — sovrascrive la combo)
<input type="text" id="aiModelCustom" placeholder="es. anthropic/claude-sonnet-4.5" />
</label>
<label title="0 = deterministico, 1 = creativo. Per orchestrazione robotica tieni basso (0.0–0.3).">
Temperature
<input type="number" id="aiTemperature" min="0" max="2" step="0.05" />
</label>
<label title="Massimo numero di iterazioni di tool calling per singolo messaggio">
Max tool iterations
<input type="number" id="aiMaxIterations" min="1" max="20" step="1" />
</label>
<label title="Timeout HTTP per la singola chiamata a OpenRouter (secondi)">
Timeout (s)
<input type="number" id="aiTimeout" min="5" max="300" step="5" />
</label>
</div>
<div class="actions">
<button id="aiSaveBtn" class="primary">💾 Salva impostazioni</button>
<button id="aiReloadBtn">⟳ Ricarica da config</button>
<button id="aiResetTempBtn" title="Riporta temperature a 0.1 (default safe)">Reset temp</button>
</div>
<div class="marker-info" id="aiSettingsStatus">—</div>
</div>
<h2>Prompt aggiuntivo</h2>
<div class="ctrl-block">
<div class="info-box">
Testo che viene appeso al system prompt dinamico (sezione "Istruzioni aggiuntive dell'operatore"). Usa per personalizzare lo stile, aggiungere contesto specifico al tuo setup, o regole custom. Lascia vuoto per non aggiungere nulla.
</div>
<textarea id="aiPromptAddendum" rows="10" style="width:100%;font-family:ui-monospace,monospace;font-size:12px;padding:8px;background:#0e1116;color:inherit;border:1px solid var(--border);border-radius:4px"></textarea>
</div>
</div>
<div>
<h2>Anteprima system prompt corrente</h2>
<div class="info-box">
Quello che il modello riceve davvero al prossimo messaggio (include lo stato corrente: robot attivo, marker, ecc., più l'eventuale addendum sopra dopo che lo salvi).
</div>
<div class="actions">
<button id="aiPromptPreviewBtn">⟳ Aggiorna anteprima</button>
<button id="aiPromptCopyBtn">📋 Copia</button>
</div>
<pre id="aiPromptPreview" class="log small" style="max-height:560px;overflow:auto">(premi 'Aggiorna anteprima')</pre>
</div>
</div>
</section>
<!-- ============================== CALIBRAZIONE PIANO ============================== -->
<section class="panel" id="tab-workplane">
<div class="diag3d-toolbar">
@@ -292,50 +180,6 @@
</div>
</section>
<!-- ============================== VISTA 3D ============================== -->
<section class="panel" id="tab-view3d">
<div class="grid-2">
<div>
<h2>Scena 3D — frame base robot</h2>
<div class="canvas-wrap">
<canvas id="view3dCanvas" width="720" height="540"></canvas>
</div>
<div class="marker-info" id="view3dHint">drag = ruota · wheel = zoom · doppio click = reset vista · shift+drag = pan</div>
<div class="marker-info" id="view3dStatusLine">—</div>
</div>
<div>
<h2>Dati</h2>
<div class="kv">
<label>Classe (filtro) <input type="text" id="view3dClass" placeholder="(tutte)" /></label>
<label>Confidenza <input type="number" id="view3dConf" value="0.5" step="0.05" min="0" max="1" /></label>
<label class="inline"><input type="checkbox" id="view3dShowCloud" checked /> Punti oggetto</label>
<label class="inline"><input type="checkbox" id="view3dShowGrid" checked /> Griglia tavolo</label>
<label class="inline"><input type="checkbox" id="view3dShowPlan" checked /> Pianifica grasp</label>
<label class="inline"><input type="checkbox" id="view3dAutoFit" checked /> Auto-fit vista</label>
</div>
<div class="actions">
<button id="view3dRefreshBtn" class="primary">Aggiorna scena</button>
<button id="view3dLoopBtn">▶ Loop</button>
<label class="inline" style="margin-left:8px"><input type="number" id="view3dRateMs" value="800" min="200" step="50" style="width:80px"> ms</label>
<button id="view3dResetBtn">Reset vista</button>
</div>
<div class="legend">
<div class="legend-row"><span class="dot" style="background:#e74c3c"></span> X (rosso) — base</div>
<div class="legend-row"><span class="dot" style="background:#3ecf8e"></span> Y (verde) — base</div>
<div class="legend-row"><span class="dot" style="background:#4da3ff"></span> Z (blu) — base</div>
<div class="legend-row"><span class="dot" style="background:#f0ad4e"></span> Polso destro</div>
<div class="legend-row"><span class="dot" style="background:#9b59b6"></span> Polso sinistro</div>
<div class="legend-row"><span class="dot" style="background:#aaaaaa"></span> Punti oggetto</div>
<div class="legend-row"><span class="dot" style="background:#ffd54f"></span> Centroide oggetto</div>
<div class="legend-row"><span class="dot" style="background:#ff5e7e"></span> Punto di presa</div>
<div class="legend-row"><span class="dot" style="background:#ffffff;border:1px solid #888"></span> Pre-grasp + traiettoria</div>
</div>
<h3>Stato</h3>
<pre id="view3dOut" class="log">(nessun dato — premi "Aggiorna scena")</pre>
</div>
</div>
</section>
<!-- ============================== BRACCIO DX ============================== -->
<section class="panel" id="tab-right-arm">
<div class="ra-grid">
@@ -519,15 +363,118 @@
</div>
<div class="p3-section">
<h3>Pose salvate</h3>
<h3>Pose salvate — DX</h3>
<div class="p3-row">
<select id="p3PoseSelect"><option value="">(seleziona)</option></select>
<select id="p3PoseSelectR"><option value="">(seleziona)</option></select>
</div>
<div class="p3-row">
<button id="p3PoseGoBtn">Vai</button>
<input type="text" id="p3PoseName" placeholder="nome" maxlength="40">
<button id="p3PoseSaveBtn">Salva</button>
<button id="p3PoseDelBtn">✕</button>
<button id="p3PoseGoBtnR">Vai</button>
<input type="text" id="p3PoseNameR" placeholder="nome" maxlength="40">
<button id="p3PoseSaveBtnR">Salva</button>
<button id="p3PoseDelBtnR">✕</button>
</div>
</div>
<div class="p3-section">
<h3>Pose salvate — SX</h3>
<div class="p3-row">
<select id="p3PoseSelectL"><option value="">(seleziona)</option></select>
</div>
<div class="p3-row">
<button id="p3PoseGoBtnL">Vai</button>
<input type="text" id="p3PoseNameL" placeholder="nome" maxlength="40">
<button id="p3PoseSaveBtnL">Salva</button>
<button id="p3PoseDelBtnL">✕</button>
</div>
</div>
<div class="p3-section">
<h3>Camera Nuwa (polso DX)</h3>
<div class="p3-row">
<label><input type="checkbox" id="p3NuwaEnabled"> Abilitata</label>
</div>
<div class="p3-readout">
<label>dX <input id="p3NuwaX" type="number" step="1" /> mm</label>
<label>dY <input id="p3NuwaY" type="number" step="1" /> mm</label>
<label>dZ <input id="p3NuwaZ" type="number" step="1" /> mm</label>
<label>R <input id="p3NuwaR" type="number" step="1" /> °</label>
<label>P <input id="p3NuwaP" type="number" step="1" /> °</label>
<label>W <input id="p3NuwaW" type="number" step="1" /> °</label>
</div>
<div class="p3-row">
<button id="p3NuwaApplyBtn">Applica</button>
<button id="p3NuwaResetBtn">Default</button>
</div>
</div>
<div class="p3-section">
<h3>Camera D435i (offset URDF)</h3>
<div class="p3-readout">
<label>dX <input id="p3D435X" type="number" step="1" /> mm</label>
<label>dY <input id="p3D435Y" type="number" step="1" /> mm</label>
<label>dZ <input id="p3D435Z" type="number" step="1" /> mm</label>
<label>R <input id="p3D435R" type="number" step="1" /> °</label>
<label>P <input id="p3D435P" type="number" step="1" /> °</label>
<label>W <input id="p3D435W" type="number" step="1" /> °</label>
</div>
<div class="p3-row">
<button id="p3D435ApplyBtn">Applica</button>
<button id="p3D435ResetBtn">Default</button>
</div>
</div>
<div class="p3-section">
<h3>Cloud cross-camera</h3>
<div class="p3-row">
<label><input type="checkbox" id="p3ShowNuwaCloud"> Nuwa</label>
<label><input type="checkbox" id="p3FlipNuwaX">↔X</label>
<label><input type="checkbox" id="p3FlipNuwaY">↔Y</label>
<label><input type="checkbox" id="p3FlipNuwaZ">↔Z</label>
</div>
<div class="p3-row">
<label><input type="checkbox" id="p3ShowD435Cloud"> D435i</label>
<label><input type="checkbox" id="p3FlipD435X">↔X</label>
<label><input type="checkbox" id="p3FlipD435Y">↔Y</label>
<label><input type="checkbox" id="p3FlipD435Z">↔Z</label>
</div>
<div class="p3-row">
<label><input type="checkbox" id="p3D435UseHandeye"> D435i: usa hand-eye (vs URDF)</label>
</div>
<div class="p3-row" title="Workaround per bug intrinsics-mismatch del servizio D435i (fx_ir/fx_rgb ≈ 0.65). Scala solo X e Y del cloud.">
<label>D435i scale XY
<input id="p3D435ScaleXY" type="number" min="0.1" max="3" step="0.01" value="0.65" style="width:60px">
</label>
</div>
<div class="p3-row">
<label>step <input id="p3CloudStep" type="number" min="1" max="32" value="8" style="width:50px"></label>
<label>max <input id="p3CloudMaxDepth" type="number" min="100" max="5000" step="100" value="2000" style="width:70px"> mm</label>
</div>
<div class="p3-row">
<label>size Nuwa <input id="p3NuwaCloudSize" type="number" min="0.5" max="100" step="0.5" value="5" style="width:55px"> mm</label>
<label>size D435i <input id="p3D435CloudSize" type="number" min="0.5" max="100" step="0.5" value="5" style="width:55px"> mm</label>
</div>
<div class="p3-row">
<button id="p3CloudAcquireBtn">Acquisisci</button>
<button id="p3CloudClearBtn">✕</button>
</div>
<div class="marker-info" id="p3CloudStatus">—</div>
</div>
<div class="p3-section">
<h3>Debug</h3>
<div class="p3-row">
<label><input type="checkbox" id="p3ShowJointUCS"> Mostra UCS giunti</label>
</div>
<div class="p3-row">
<label>Scala
<input id="p3JointUCSSize" type="number" min="10" max="200" step="5" value="40" style="width:60px"> mm
</label>
</div>
<div class="p3-row">
<label><input type="checkbox" id="p3ShowD435CalibUCS" checked> UCS D435i hand-eye (arancio)</label>
</div>
<div class="p3-row">
<label><input type="checkbox" id="p3ShowRobotBody" checked> Mostra robot (mesh)</label>
</div>
</div>