feat(pose_3d): doAlign + persistenza/caricamento calibrazione cross-camera
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
746662d534
commit
47ae8a6b2c
1 file changed
+77
@@ -784,6 +784,7 @@ async function pollState() {
|
||||
updateNuwaInputsFromCfg();
|
||||
updateD435InputsFromCfg();
|
||||
applyCloudParamsFromCfg(s.cloud_params);
|
||||
applyCrossCameraFromCfg(s.cross_camera);
|
||||
applyCalibratedD435UCS();
|
||||
// Applica TUTTI i giunti reali sul modello URDF (29 motori G1)
|
||||
state.joints = s.joints || null; // snapshot per tag locator save
|
||||
@@ -1972,6 +1973,75 @@ async function pollTags() {
|
||||
}
|
||||
}
|
||||
|
||||
// Costruisce una THREE.Matrix4 (row-major via .set) da {scale,R,t}: p' = s·R·p + t.
|
||||
function matrixFromAlign(cc) {
|
||||
if (!cc || !Array.isArray(cc.R) || cc.R.length !== 3 || !Array.isArray(cc.t)) return null;
|
||||
const s = Number.isFinite(cc.scale) ? cc.scale : 1;
|
||||
const R = cc.R, t = cc.t;
|
||||
return new THREE.Matrix4().set(
|
||||
s*R[0][0], s*R[0][1], s*R[0][2], t[0],
|
||||
s*R[1][0], s*R[1][1], s*R[1][2], t[1],
|
||||
s*R[2][0], s*R[2][1], s*R[2][2], t[2],
|
||||
0, 0, 0, 1,
|
||||
);
|
||||
}
|
||||
|
||||
async function doAlign() {
|
||||
const ids = ($('p3FrontTagIds')?.value || '0-49').trim();
|
||||
const alignId = parseInt($('p3AlignTagId')?.value, 10) || 0;
|
||||
const status = $('p3AlignStatus');
|
||||
if (status) { status.textContent = 'allineamento…'; status.style.color = 'var(--dim)'; }
|
||||
try {
|
||||
const r = await jsonFetch(
|
||||
`/api/pose_3d/align?ids=${encodeURIComponent(ids)}&align_id=${alignId}`,
|
||||
{ method: 'POST' });
|
||||
if (!r.found_in_both) {
|
||||
if (status) { status.textContent = r.error || 'tag non visto da entrambe'; status.style.color = 'var(--danger)'; }
|
||||
return;
|
||||
}
|
||||
state.align.matrix = matrixFromAlign(r);
|
||||
state.align.id = r.align_id;
|
||||
if (status) {
|
||||
status.textContent = `scala=${r.scale.toFixed(3)} · rmse=${r.rmse_mm.toFixed(1)}mm · n=${r.n_corners}`;
|
||||
status.style.color = 'var(--ok)';
|
||||
}
|
||||
applyNuwaCloudPlacement();
|
||||
} catch (e) {
|
||||
if (status) { status.textContent = `errore: ${e?.message || e}`; status.style.color = 'var(--danger)'; }
|
||||
}
|
||||
}
|
||||
|
||||
// Persistenza del solo flag + align_id (la matrice la salva il backend in /align).
|
||||
async function saveCrossCameraFlag() {
|
||||
try {
|
||||
await jsonFetch('/api/config', {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ pose_3d: { cross_camera: {
|
||||
enabled: $('p3AlignEnabled')?.checked === true,
|
||||
align_id: parseInt($('p3AlignTagId')?.value, 10) || 0,
|
||||
} } }),
|
||||
});
|
||||
} catch (e) { console.warn('[pose_3d] save cross_camera failed:', e?.message || e); }
|
||||
}
|
||||
|
||||
// Ripopola UI + matrice dalla config (guardato come applyCloudParamsFromCfg).
|
||||
function applyCrossCameraFromCfg(cc) {
|
||||
if (state.cross_camera_populated) return;
|
||||
if (!cc || Object.keys(cc).length === 0) { state.cross_camera_populated = true; return; }
|
||||
const en = $('p3AlignEnabled'); if (en) en.checked = cc.enabled === true;
|
||||
const id = $('p3AlignTagId'); if (id && Number.isFinite(cc.align_id)) id.value = String(cc.align_id);
|
||||
state.align.enabled = cc.enabled === true;
|
||||
state.align.id = cc.align_id || 0;
|
||||
const m = matrixFromAlign(cc);
|
||||
if (m) state.align.matrix = m;
|
||||
const status = $('p3AlignStatus');
|
||||
if (status && Number.isFinite(cc.rmse_mm)) {
|
||||
status.textContent = `caricata · scala=${(cc.scale||1).toFixed(3)} · rmse=${cc.rmse_mm.toFixed(1)}mm`;
|
||||
}
|
||||
state.cross_camera_populated = true;
|
||||
}
|
||||
|
||||
function bindFrontTagFinder() {
|
||||
const cb = $('p3ShowFrontTags');
|
||||
if (!cb) return;
|
||||
@@ -1990,6 +2060,13 @@ function bindFrontTagFinder() {
|
||||
|
||||
function bindCloudControls() {
|
||||
$('p3CloudAcquireBtn')?.addEventListener('click', acquireClouds);
|
||||
$('p3AlignBtn')?.addEventListener('click', doAlign);
|
||||
$('p3AlignEnabled')?.addEventListener('change', () => {
|
||||
state.align.enabled = $('p3AlignEnabled').checked === true;
|
||||
saveCrossCameraFlag();
|
||||
applyNuwaCloudPlacement();
|
||||
});
|
||||
$('p3AlignTagId')?.addEventListener('change', saveCrossCameraFlag);
|
||||
$('p3CloudClearBtn')?.addEventListener('click', clearClouds);
|
||||
$('p3ShowNuwaCloud')?.addEventListener('change', applyCloudVisibility);
|
||||
$('p3ShowD435Cloud')?.addEventListener('change', applyCloudVisibility);
|
||||
|
||||
Reference in new issue
Block a user