612 lines
27 KiB
HTML
612 lines
27 KiB
HTML
<!doctype html>
|
||
<html lang="it">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<title>presa-mano</title>
|
||
<link rel="stylesheet" href="/static/style.css" />
|
||
<link rel="stylesheet" href="/static/pose_3d.css">
|
||
</head>
|
||
<body>
|
||
<header>
|
||
<h1>presa-mano</h1>
|
||
<nav class="tabs">
|
||
<button class="tab active" data-tab="home">Home</button>
|
||
<button class="tab" data-tab="workplane">Calibrazione Piano</button>
|
||
<button class="tab" data-tab="grasp">Cerca modello</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>
|
||
</nav>
|
||
<div class="status-strip" id="statusStrip"></div>
|
||
<span class="active-robot-pill" id="activeRobotPill" title="Robot attualmente collegato">robot: —</span>
|
||
</header>
|
||
|
||
<main>
|
||
|
||
<!-- ============================== HOME ============================== -->
|
||
<section class="panel active" id="tab-home">
|
||
<div class="posture-toolbar">
|
||
<label class="inline">Robot attivo
|
||
<select id="homeRobotSelect">
|
||
<option value="g1">G1</option>
|
||
<option value="h1">H1</option>
|
||
</select>
|
||
</label>
|
||
<button id="homeApplyRobotBtn" class="primary">Applica</button>
|
||
<label class="inline"><input type="checkbox" id="homeAutoPoll" checked /> Auto-poll</label>
|
||
<label class="inline">ogni
|
||
<input type="number" id="homePollMs" value="2000" min="500" step="500" style="width:80px"> ms
|
||
</label>
|
||
<button id="homeRefreshBtn">Aggiorna ora</button>
|
||
</div>
|
||
|
||
<div class="info-box">
|
||
Stato di comunicazione con i microservizi upstream. Il selettore sopra cambia il robot attivo (G1 o H1) e aggiorna <code>config.yaml</code> a runtime; le chiamate <code>/api/wrist</code>, <code>/api/posture</code>, <code>/api/robot</code> useranno il robot selezionato.
|
||
</div>
|
||
|
||
<h2>Stato dispositivi</h2>
|
||
<table class="device-status-table" id="homeDeviceTable">
|
||
<thead>
|
||
<tr>
|
||
<th>Servizio</th>
|
||
<th>URL</th>
|
||
<th>Stato</th>
|
||
<th>Latenza</th>
|
||
<th>Dettagli</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td colspan="5" class="dim">caricamento…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<h3>Ultimo esito</h3>
|
||
<pre id="homeLog" class="log small">—</pre>
|
||
</section>
|
||
|
||
<!-- ============================== CALIBRAZIONE PIANO ============================== -->
|
||
<section class="panel" id="tab-workplane">
|
||
<div class="diag3d-toolbar">
|
||
<span class="post-health" id="wpStatusChip">—</span>
|
||
</div>
|
||
<div class="grid-2 diag3d-layout">
|
||
<div>
|
||
<div id="wpViewport" style="position:relative;width:min(100%,720px);height:440px;overflow:hidden;background:#000;border:1px solid var(--border);border-radius:4px;cursor:grab;user-select:none">
|
||
<div id="wpPanZoom" style="position:absolute;left:0;top:0;transform-origin:0 0;will-change:transform">
|
||
<img id="wpImg" alt="Nuwa RGB" style="display:block;max-width:none;pointer-events:none" draggable="false" />
|
||
<canvas id="wpOverlay" style="position:absolute;left:0;top:0;pointer-events:none"></canvas>
|
||
</div>
|
||
</div>
|
||
<div class="marker-info">RGB live · 2 click = angoli del rettangolo ROI · 3° click ricomincia</div>
|
||
<h3 style="margin-top:14px">Vista 3D (frame camera)</h3>
|
||
<div class="canvas-wrap" style="width:min(100%,720px)">
|
||
<canvas id="wp3dCanvas" width="720" height="360" style="display:block;background:#0a0d11;border:1px solid var(--border);border-radius:4px"></canvas>
|
||
</div>
|
||
<div class="marker-info">vista camera · blu = punti usati per il fit · ciano = scena tenuta · rosso = scena esclusa (sotto piano−offset) · drag = ruota · wheel = zoom</div>
|
||
</div>
|
||
<div>
|
||
<h2>Calibrazione</h2>
|
||
<div class="ctrl-block">
|
||
<div class="info-box">
|
||
Clicca due angoli opposti sul tavolo (senza oggetti) per definire il
|
||
rettangolo ROI, poi clicca <strong>Calcola piano</strong>. Regola
|
||
l'offset finché i punti gialli rappresentano l'oggetto e i rossi
|
||
spariscono. Poi <strong>Salva piano</strong>.
|
||
</div>
|
||
<div class="kv compact">
|
||
<label>ROI <span id="wpRoiInfo" class="dim">— · clicca 2 angoli sull'RGB</span></label>
|
||
<label>offset (mm) <input type="number" id="wpOffset" value="5.0" step="0.5" min="-200" max="200" /></label>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="wpFitBtn" class="primary" disabled>Calcola piano (preview)</button>
|
||
<button id="wpSaveBtn" disabled>Salva piano</button>
|
||
<button id="wpClearBtn" class="danger" disabled>Rimuovi piano salvato</button>
|
||
</div>
|
||
</div>
|
||
|
||
<h2>Stato</h2>
|
||
<div id="wpStateBlock" class="ctrl-block">
|
||
<div class="dim">nessun piano</div>
|
||
</div>
|
||
|
||
<h2>Dettagli piano</h2>
|
||
<pre id="wpDetails" class="log small">—</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== TROVA UTENSILE ============================== -->
|
||
<!-- ============================== CERCA MODELLO ============================== -->
|
||
<section class="panel" id="tab-grasp">
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>RGB + detection — click per misurare</h2>
|
||
<div class="canvas-wrap">
|
||
<canvas id="graspCanvas" width="640" height="480"></canvas>
|
||
</div>
|
||
<div class="measure-strip" id="measureInfo">click sull'immagine per la distanza</div>
|
||
<div class="kv">
|
||
<label>Nuvola di <select id="graspObjectPick"><option value="">(top overall)</option></select></label>
|
||
<label>Confidenza <input type="number" id="graspConf" value="0.5" step="0.05" min="0" max="1" /></label>
|
||
</div>
|
||
<div id="graspLegend" class="grasp-legend"></div>
|
||
<div class="actions">
|
||
<button id="detectBtn">Rileva oggetto</button>
|
||
<button id="loopBtn">▶ Loop riconoscimento</button>
|
||
<label class="inline" style="margin-left:8px"><input type="number" id="loopRateMs" value="500" min="100" step="50" style="width:80px"> ms</label>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<h2>Fit modello 3D</h2>
|
||
<div class="kv">
|
||
<label>Modello
|
||
<select id="findModelPick"><option value="">— carico ricette… —</option></select>
|
||
</label>
|
||
<button id="findRecipesRefreshBtn" class="ghost" title="Ricarica ricette">↻</button>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="findBtn" class="primary" disabled>Trova</button>
|
||
</div>
|
||
|
||
<div id="findStatus" class="status-line">—</div>
|
||
|
||
<div class="canvas-wrap">
|
||
<canvas id="findResultCanvas" width="640" height="480"></canvas>
|
||
</div>
|
||
|
||
<div class="kv">
|
||
<label class="inline"><input type="checkbox" id="findShowModel" checked> Punti modello (blu)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowAccepted"> Punti accettati (verdi)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowExcluded"> Punti esclusi (rossi)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowSam21"> Punti SAM21 (gialli)</label>
|
||
<label class="inline"><input type="checkbox" id="findShowPlane"> Piano (viola)</label>
|
||
</div>
|
||
|
||
<pre id="findDetails" class="log small">—</pre>
|
||
|
||
<!-- DOM hooks dormienti consumati da app.js residuo. Tenuti per evitare null deref. -->
|
||
<div style="display:none">
|
||
<span id="statsWindow">5</span>
|
||
<table id="statsTable"><tbody>
|
||
<tr><td>x</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
<tr><td>y</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
<tr><td>z</td><td>—</td><td>—</td><td>—</td><td>—</td><td>—</td></tr>
|
||
</tbody></table>
|
||
<div id="statsMeta"></div>
|
||
<pre id="graspOut"></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== BRACCIO DX ============================== -->
|
||
<section class="panel" id="tab-right-arm">
|
||
<div class="ra-grid">
|
||
<!-- FRONTE -->
|
||
<div class="ra-card">
|
||
<h3>Fronte</h3>
|
||
<div class="ra-imgwrap">
|
||
<img id="raFrontImg" alt="front camera" />
|
||
<canvas id="raFrontCanvas"></canvas>
|
||
</div>
|
||
<div class="ra-row">
|
||
<label>Classe <select id="raFrontClass"></select></label>
|
||
<label>Conf <input id="raFrontConf" type="number" step="0.05" min="0" max="1" value="0.4" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raFrontDetectBtn" type="button">Cerca su fronte</button>
|
||
<label><input id="raFrontAutoRefresh" type="checkbox" checked /> auto-refresh 1s</label>
|
||
</div>
|
||
<div id="raFrontResult" class="ra-result dim">— nessuna detection</div>
|
||
</div>
|
||
|
||
<!-- POLSO -->
|
||
<div class="ra-card">
|
||
<h3>Polso (Nuwa)</h3>
|
||
<div class="ra-imgwrap">
|
||
<img id="raWristImg" alt="wrist camera" />
|
||
<canvas id="raWristCanvas"></canvas>
|
||
</div>
|
||
<div class="ra-row">
|
||
<label>Classe <select id="raWristClass"></select></label>
|
||
<label>Conf <input id="raWristConf" type="number" step="0.05" min="0" max="1" value="0.4" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raWristDetectBtn" type="button">Cerca su polso</button>
|
||
<label><input id="raWristAutoRefresh" type="checkbox" checked /> auto-refresh 1s</label>
|
||
</div>
|
||
<div id="raWristResult" class="ra-result dim">— nessuna detection</div>
|
||
</div>
|
||
|
||
<!-- POSA POLSO -->
|
||
<div class="ra-card">
|
||
<h3>Posa polso DX</h3>
|
||
<table class="ra-pose-table" id="raPoseTable">
|
||
<tr><th>X</th><td id="raPoseX">—</td><td>m</td></tr>
|
||
<tr><th>Y</th><td id="raPoseY">—</td><td>m</td></tr>
|
||
<tr><th>Z</th><td id="raPoseZ">—</td><td>m</td></tr>
|
||
<tr><th>Roll</th><td id="raPoseR">—</td><td>°</td></tr>
|
||
<tr><th>Pitch</th><td id="raPoseP">—</td><td>°</td></tr>
|
||
<tr><th>Yaw</th><td id="raPoseW">—</td><td>°</td></tr>
|
||
</table>
|
||
<button id="raPoseRefreshBtn" type="button">Refresh</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- OFFSET + MOVE -->
|
||
<div class="ra-card ra-wide">
|
||
<h3>Offset presa rispetto a camera (mm)</h3>
|
||
<div class="ra-row">
|
||
<label>X <input id="raOffX" type="number" step="1" value="0" /></label>
|
||
<label>Y <input id="raOffY" type="number" step="1" value="0" /></label>
|
||
<label>Z <input id="raOffZ" type="number" step="1" value="100" /></label>
|
||
<span class="ra-sep">|</span>
|
||
<label>Sign X <input id="raSgnX" type="number" step="2" min="-1" max="1" value="1" /></label>
|
||
<label>Sign Y <input id="raSgnY" type="number" step="2" min="-1" max="1" value="-1" /></label>
|
||
<label>Sign Z <input id="raSgnZ" type="number" step="2" min="-1" max="1" value="-1" /></label>
|
||
</div>
|
||
<div class="ra-row" title="Traslazione costante dall'origine polso all'origine camera, nel frame polso">
|
||
<span class="ra-sep">Camera rispetto a polso (mm):</span>
|
||
<label>X <input id="raCamOffX" type="number" step="1" value="0" /></label>
|
||
<label>Y <input id="raCamOffY" type="number" step="1" value="0" /></label>
|
||
<label>Z <input id="raCamOffZ" type="number" step="1" value="0" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raSaveOffsetBtn" type="button">Salva offset</button>
|
||
<label><input id="raMoveDryRun" type="checkbox" checked /> dry-run</label>
|
||
<label title="Bypassa il limite max_step_m"><input id="raMoveForce" type="checkbox" /> force</label>
|
||
<button id="raMoveBtn" type="button" class="ra-danger">Muovi verso pezzo</button>
|
||
</div>
|
||
<div id="raMoveResult" class="ra-result dim">— nessun move</div>
|
||
</div>
|
||
|
||
<!-- VERIFICA TAG (relativa) -->
|
||
<div class="ra-card ra-wide">
|
||
<h3>Verifica tag id=1 (relativa, no calibrazione)</h3>
|
||
<p class="ra-help">
|
||
Premi <em>Snapshot</em> con il polso fermo, muovi il polso, premi ancora.
|
||
Per ogni coppia consecutiva confronto ‖Δpolso‖ (FK) con ‖Δtag‖ (front camera).
|
||
Se combaciano (errore < ~10%) FK e detection sono coerenti.
|
||
</p>
|
||
<div class="ra-row">
|
||
<button id="raVerifySnapBtn" type="button">Snapshot</button>
|
||
<button id="raVerifyResetBtn" type="button">Reset</button>
|
||
<span id="raVerifyCount" class="dim">0 snapshot</span>
|
||
</div>
|
||
<div id="raVerifyResult" class="ra-result dim">— nessuno snapshot</div>
|
||
</div>
|
||
|
||
<!-- HOME + CUSTOM -->
|
||
<div class="ra-row ra-wide-grid">
|
||
<div class="ra-card">
|
||
<h3>Posa Home (preset)</h3>
|
||
<div class="ra-pose-form">
|
||
<label>X <input id="raHomeX" type="number" step="0.01" /></label>
|
||
<label>Y <input id="raHomeY" type="number" step="0.01" /></label>
|
||
<label>Z <input id="raHomeZ" type="number" step="0.01" /></label>
|
||
<label>Roll <input id="raHomeR" type="number" step="1" /></label>
|
||
<label>Pitch <input id="raHomeP" type="number" step="1" /></label>
|
||
<label>Yaw <input id="raHomeW" type="number" step="1" /></label>
|
||
</div>
|
||
<button id="raHomeBtn" type="button">Vai a Home</button>
|
||
</div>
|
||
|
||
<div class="ra-card">
|
||
<h3>Posa Custom (editabile)</h3>
|
||
<div class="ra-pose-form">
|
||
<label>X <input id="raCustX" type="number" step="0.01" /></label>
|
||
<label>Y <input id="raCustY" type="number" step="0.01" /></label>
|
||
<label>Z <input id="raCustZ" type="number" step="0.01" /></label>
|
||
<label>Roll <input id="raCustR" type="number" step="1" /></label>
|
||
<label>Pitch <input id="raCustP" type="number" step="1" /></label>
|
||
<label>Yaw <input id="raCustW" type="number" step="1" /></label>
|
||
</div>
|
||
<div class="ra-row">
|
||
<button id="raCustomGoBtn" type="button">Vai a Custom</button>
|
||
<button id="raCustomSaveBtn" type="button">Salva custom</button>
|
||
<button id="raCustomUseCurrentBtn" type="button">Usa posa corrente</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== POSE 3D ============================== -->
|
||
<section class="panel" id="tab-pose-3d">
|
||
<div class="p3-layout">
|
||
<div class="p3-viewport">
|
||
<canvas id="p3Canvas"></canvas>
|
||
<div class="p3-mode-badge" id="p3ModeBadge">Editor</div>
|
||
<div class="p3-help">click polso = seleziona · gizmo: T sposta / R ruota · drag = muovi · orbit = wheel/drag fondo · pan = shift+drag</div>
|
||
</div>
|
||
<aside class="p3-sidebar">
|
||
<div class="p3-group" data-group="pose">
|
||
<div class="p3-section">
|
||
<h3>Modalità</h3>
|
||
<div class="p3-row">
|
||
<label><input type="radio" name="p3Mode" value="editor" checked> Editor (simulato)</label>
|
||
<label><input type="radio" name="p3Mode" value="jog"> Jog (move reale)</label>
|
||
</div>
|
||
<label class="p3-check"><input type="checkbox" id="p3Precise" checked> Precise (controllo accurato)</label>
|
||
</div>
|
||
|
||
<div class="p3-section">
|
||
<h3>Polso attivo</h3>
|
||
<div class="p3-row">
|
||
<label><input type="radio" name="p3Side" value="right" checked> Destro</label>
|
||
<label><input type="radio" name="p3Side" value="left"> Sinistro</label>
|
||
</div>
|
||
<div class="p3-row">
|
||
<label><input type="radio" name="p3Gizmo" value="translate" checked> T sposta</label>
|
||
<label><input type="radio" name="p3Gizmo" value="rotate"> R ruota</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p3-section">
|
||
<h3>Posa target</h3>
|
||
<div class="p3-readout">
|
||
<label>X <input id="p3X" type="number" step="1" /> mm</label>
|
||
<label>Y <input id="p3Y" type="number" step="1" /> mm</label>
|
||
<label>Z <input id="p3Z" type="number" step="1" /> mm</label>
|
||
<label>R <input id="p3R" type="number" step="1" /> °</label>
|
||
<label>P <input id="p3P" type="number" step="1" /> °</label>
|
||
<label>W <input id="p3W" type="number" step="1" /> °</label>
|
||
</div>
|
||
<div id="p3Feasibility" class="p3-feasibility"></div>
|
||
<div class="p3-status" id="p3Status">— polso fermo</div>
|
||
<div class="p3-row" id="p3EditorActions">
|
||
<button id="p3ApplyBtn">Applica (Editor)</button>
|
||
<button id="p3CancelBtn">Annulla modifica</button>
|
||
</div>
|
||
<div class="p3-row">
|
||
<button id="p3SyncBtn" title="Ricopia la posa attuale del robot negli input">⟳ Sincronizza con robot</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p3-section">
|
||
<h3>Pose salvate — DX</h3>
|
||
<div class="p3-row">
|
||
<select id="p3PoseSelectR"><option value="">(seleziona)</option></select>
|
||
</div>
|
||
<div class="p3-row">
|
||
<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><!-- /p3-group pose -->
|
||
|
||
<div class="p3-group" data-group="cameras" hidden>
|
||
<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" title="Calibrazione persistente Nuwa→frontale via tag condiviso (Umeyama). Premi Allinea con il tag visto da entrambe le camere.">
|
||
<label><input type="checkbox" id="p3AlignEnabled"> Allinea Nuwa→frontale</label>
|
||
<label>tag id <input id="p3AlignTagId" type="number" min="0" max="999" value="0" style="width:55px"></label>
|
||
<button id="p3AlignBtn">Allinea</button>
|
||
</div>
|
||
<div class="marker-info" id="p3AlignStatus">—</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><!-- /p3-group cameras -->
|
||
|
||
<div class="p3-group" data-group="misc" hidden>
|
||
<div class="p3-section">
|
||
<h3>Tag locator</h3>
|
||
<div class="p3-row">
|
||
<label><input type="checkbox" id="p3TagPlaceMode"> <b>Modalità tag click</b> (click sul modello = piazza crocetta)</label>
|
||
</div>
|
||
<div class="p3-row">
|
||
<label>Tag
|
||
<select id="p3TagSelect">
|
||
<option value="10:40:wrist_sx">wrist sx (id=10, 40mm)</option>
|
||
<option value="1:20:nuwa_dx">nuwa dx (id=1, 20mm)</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
<div class="p3-row">
|
||
<label>x <input id="p3TagPosX" type="number" readonly style="width:70px"> mm</label>
|
||
<label>y <input id="p3TagPosY" type="number" readonly style="width:70px"> mm</label>
|
||
<label>z <input id="p3TagPosZ" type="number" readonly style="width:70px"> mm</label>
|
||
</div>
|
||
<div class="p3-row">
|
||
<button id="p3TagSaveBtn" disabled>Salva</button>
|
||
<button id="p3TagClearBtn" disabled>✕ rimuovi</button>
|
||
</div>
|
||
<div class="marker-info" id="p3TagStatus">attiva la modalità tag click sopra, poi clicca sul modello 3D</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>
|
||
|
||
<div class="p3-section">
|
||
<h3>Tags</h3>
|
||
<div class="p3-row">
|
||
<label><input type="checkbox" id="p3ShowFrontTags"> Mostra tag visibili (quad + dim + dist)</label>
|
||
</div>
|
||
<div class="p3-row">
|
||
<label>IDs <input id="p3FrontTagIds" type="text" value="0-49" style="width:90px"></label>
|
||
</div>
|
||
<div class="marker-info" id="p3FrontTagStatus">—</div>
|
||
</div>
|
||
|
||
<div class="p3-section">
|
||
<button id="p3UnlockBtn">Sblocca braccia</button>
|
||
</div>
|
||
|
||
<div class="p3-section" id="p3ErrorBox" style="display:none">
|
||
<div class="p3-error" id="p3ErrorText"></div>
|
||
</div>
|
||
</div><!-- /p3-group misc -->
|
||
</aside>
|
||
<nav class="p3-tabrail" aria-label="Pose 3D groups">
|
||
<button type="button" class="p3-tab active" data-target="pose">POSE</button>
|
||
<button type="button" class="p3-tab" data-target="cameras">CAMERAS</button>
|
||
<button type="button" class="p3-tab" data-target="misc">MISC</button>
|
||
</nav>
|
||
</div>
|
||
</section>
|
||
|
||
<!-- ============================== EE ============================== -->
|
||
<section class="panel" id="tab-ee">
|
||
<div class="grid-2">
|
||
<div>
|
||
<h2>Polso corrente</h2>
|
||
<pre id="wristCurrent" class="log">—</pre>
|
||
<button id="refreshWristBtn">Aggiorna pose</button>
|
||
|
||
<h3>Posa assoluta</h3>
|
||
<div class="kv">
|
||
<label>Side <select id="eeSide"><option value="right">right</option><option value="left">left</option></select></label>
|
||
<label>Px <input type="number" id="absPx" step="0.01" value="0.30"></label>
|
||
<label>Py <input type="number" id="absPy" step="0.01" value="0.10"></label>
|
||
<label>Pz <input type="number" id="absPz" step="0.01" value="0.20"></label>
|
||
<label>Qx <input type="number" id="absQx" step="0.01" value="0"></label>
|
||
<label>Qy <input type="number" id="absQy" step="0.01" value="0"></label>
|
||
<label>Qz <input type="number" id="absQz" step="0.01" value="0"></label>
|
||
<label>Qw <input type="number" id="absQw" step="0.01" value="1"></label>
|
||
<label class="inline"><input type="checkbox" id="eeDryRun" /> Dry-run</label>
|
||
</div>
|
||
<button id="absSendBtn" class="primary">Invia assoluta</button>
|
||
|
||
<h3>Delta relativo</h3>
|
||
<div class="kv">
|
||
<label>ΔX <input type="number" id="relDx" step="0.01" value="0"></label>
|
||
<label>ΔY <input type="number" id="relDy" step="0.01" value="0"></label>
|
||
<label>ΔZ <input type="number" id="relDz" step="0.01" value="0.05"></label>
|
||
</div>
|
||
<button id="relSendBtn" class="primary">Invia relativa</button>
|
||
</div>
|
||
|
||
<div>
|
||
<h2>Mano + sicurezza</h2>
|
||
<div class="actions">
|
||
<button id="handOpenBtn">RIGHT_OPEN</button>
|
||
<button id="handCloseBtn">RIGHT_CLOSE</button>
|
||
<button id="handOpenLBtn">LEFT_OPEN</button>
|
||
<button id="handCloseLBtn">LEFT_CLOSE</button>
|
||
</div>
|
||
<div class="actions">
|
||
<button id="estopBtn" class="danger">EMERGENCY STOP</button>
|
||
<button id="unlockBtn">Unlock</button>
|
||
</div>
|
||
<h3>Ultimo esito</h3>
|
||
<pre id="eeLog" class="log">—</pre>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
<div id="toastHost" class="toast-host" aria-live="polite"></div>
|
||
<script type="importmap">
|
||
{
|
||
"imports": {
|
||
"three": "/static/vendor/three/build/three.module.js",
|
||
"three/addons/": "/static/vendor/three/addons/"
|
||
}
|
||
}
|
||
</script>
|
||
<script src="/static/app.js"></script>
|
||
<script type="module" src="/static/work_plane.js"></script>
|
||
<script type="module" src="/static/right_arm.js"></script>
|
||
<script type="module" src="/static/pose_3d.js"></script>
|
||
</body>
|
||
</html>
|