feat(pose_3d): workspace check client-side (ghost verde/rosso) + SHOULDER da URDF

This commit is contained in:
Adriano Dal Pastro committed 2026-05-21 17:21:09 +02:00
1 parent 8c09d79505
commit d5bb9cebd3
3 files changed
+73 -2

No files matched your search

+1
View File
@@ -507,6 +507,7 @@
<label>P <input id="p3P" 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> <label>W <input id="p3W" type="number" step="1" /> °</label>
</div> </div>
<div id="p3Feasibility" class="p3-feasibility"></div>
<div class="p3-status" id="p3Status">— polso fermo</div> <div class="p3-status" id="p3Status">— polso fermo</div>
<div class="p3-row" id="p3EditorActions"> <div class="p3-row" id="p3EditorActions">
<button id="p3ApplyBtn">Applica (Editor)</button> <button id="p3ApplyBtn">Applica (Editor)</button>
+9
View File
@@ -105,6 +105,15 @@
#tab-pose-3d .p3-status.ok { color: #3ecf8e; } #tab-pose-3d .p3-status.ok { color: #3ecf8e; }
#tab-pose-3d .p3-status.error { color: #e74c3c; } #tab-pose-3d .p3-status.error { color: #e74c3c; }
#tab-pose-3d .p3-feasibility {
font-size: 12px;
margin-bottom: 6px;
font-family: monospace;
min-height: 1em;
}
#tab-pose-3d .p3-feasibility.ok { color: #3ecf8e; }
#tab-pose-3d .p3-feasibility.error { color: #e74c3c; }
#tab-pose-3d button { #tab-pose-3d button {
background: #2a3038; background: #2a3038;
border: 1px solid #3a4048; border: 1px solid #3a4048;
+63 -2
View File
@@ -76,12 +76,32 @@ const state = {
live_max_step_m: 0.15, live_max_step_m: 0.15,
}; };
function checkWorkspace(side, position_m) {
// Controllo client-side: il target e' nella sfera di raggio ARM_MAX_REACH_M
// centrata sulla spalla? Ritorna {feasible, distance_m, max_m}.
const sh = SHOULDER[side];
if (!sh) return { feasible: true, distance_m: 0, max_m: ARM_MAX_REACH_M };
const dx = position_m[0] - sh.x;
const dy = position_m[1] - sh.y;
const dz = position_m[2] - sh.z;
const distance = Math.sqrt(dx*dx + dy*dy + dz*dz);
return {
feasible: distance <= ARM_MAX_REACH_M,
distance_m: distance,
max_m: ARM_MAX_REACH_M,
};
}
// Posizioni torso e spalle hardcoded (riferimento visivo, non IK) // Posizioni torso e spalle hardcoded (riferimento visivo, non IK)
const TORSO_POS = new THREE.Vector3(0, 0, 0.40); const TORSO_POS = new THREE.Vector3(0, 0, 0.40);
// Posizione approssimata delle spalle del G1 in base frame (zero-joint pose
// dell'URDF). Usato come fallback visivo quando /state e' offline e per il
// workspace check (raggiungibilita').
const SHOULDER = { const SHOULDER = {
right: new THREE.Vector3(0, -0.20, 0.60), right: new THREE.Vector3(0.0, -0.18, 1.05),
left: new THREE.Vector3(0, 0.20, 0.60), left: new THREE.Vector3(0.0, 0.18, 1.05),
}; };
const ARM_MAX_REACH_M = 0.62; // braccio teso G1 (upper+forearm+palm ~10+25+25 cm)
// ----- URDF loader ----- // ----- URDF loader -----
// Carica il modello G1 reale a partire dal URDF vendored sotto // Carica il modello G1 reale a partire dal URDF vendored sotto
@@ -346,6 +366,47 @@ function updateReadout() {
setInputIfNotFocused('p3W', rpy[2].toFixed(1)); setInputIfNotFocused('p3W', rpy[2].toFixed(1));
$('p3Status').textContent = state.target[side] ? '● modificata' : '— polso fermo'; $('p3Status').textContent = state.target[side] ? '● modificata' : '— polso fermo';
$('p3Status').className = 'p3-status' + (state.target[side] ? ' modified' : ''); $('p3Status').className = 'p3-status' + (state.target[side] ? ' modified' : '');
// Workspace feasibility coloring + indicator
applyFeasibilityToActiveGhost();
}
function applyFeasibilityToActiveGhost() {
const side = state.active_side;
if (!side) return;
const pose = state.target[side] || state.current[side];
if (!pose) {
// Niente posa, niente indicazione
setFeasibilityIndicator(null);
return;
}
const check = checkWorkspace(side, pose.position);
const ghost = state.ghosts[side];
if (ghost && ghost.userData.sphere) {
// verde se feasible, rosso se fuori workspace; opacita' invariata
const color = check.feasible ? 0x3ecf8e : 0xe74c3c;
ghost.userData.sphere.material.color.setHex(color);
}
setFeasibilityIndicator(check);
}
function setFeasibilityIndicator(check) {
const el = $('p3Feasibility');
if (!el) return;
if (!check) {
el.textContent = '';
el.className = 'p3-feasibility';
return;
}
const dist_mm = (check.distance_m * 1000).toFixed(0);
const max_mm = (check.max_m * 1000).toFixed(0);
if (check.feasible) {
el.textContent = `✓ raggiungibile (dist spalla ${dist_mm}/${max_mm} mm)`;
el.className = 'p3-feasibility ok';
} else {
const over_mm = (check.distance_m - check.max_m) * 1000;
el.textContent = `⚠ fuori workspace (${dist_mm}/${max_mm} mm — ${over_mm.toFixed(0)} mm oltre)`;
el.className = 'p3-feasibility error';
}
} }
// ----- polling ----- // ----- polling -----