From 5f8e1a0189d37be4f1be1cd9129110faaaef7630 Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Tue, 16 Jun 2026 22:39:54 +0200 Subject: [PATCH] Marker mit einer Kamera - Gaurds --- public/boardViewer.html | 29 +++++++++++++---- public/yAxisCompute.js | 7 ++++ test/boardViewerHasXYZ.test.js | 59 ++++++++++++++++++++++++++++++++++ test/yAxisComputeJs.test.js | 39 ++++++++++++++++++++++ 4 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 test/boardViewerHasXYZ.test.js diff --git a/public/boardViewer.html b/public/boardViewer.html index 8c0fa52..a653815 100644 --- a/public/boardViewer.html +++ b/public/boardViewer.html @@ -216,6 +216,16 @@ function r2v(rx, ry, rz) { return new THREE.Vector3(rx * S, rz * S, -ry * S function r2vArr([rx, ry, rz]) { return r2v(rx, ry, rz); } function r2dir([dx, dy, dz]) { return new THREE.Vector3(dx, dz, -dy).normalize(); } +/** + * True wenn `arr` ein nutzbares [x,y,z] ist (z. B. position_mm). Marker, die + * 3b nicht triangulieren konnte (z. B. nur 1 Kamera), haben dieses Feld nicht + * — solche Marker werden an allen Stellen, die diese Funktion nutzen, einfach + * ignoriert statt einen Crash auszulösen. + */ +function hasXYZ(arr) { + return Array.isArray(arr) && arr.length >= 3 && arr.slice(0, 3).every(Number.isFinite); +} + // ── Renderer ────────────────────────────────────────────────────────────────── const canvas = document.getElementById('cv'); const renderer = new THREE.WebGLRenderer({ canvas, antialias: true }); @@ -403,6 +413,7 @@ function buildSkeletonFK(robot, angles) { } for (const obs of _measuredMarkers.markers) { + if (!hasXYZ(obs.position_mm)) continue; // z.B. 1-Kamera-Marker, nicht trianguliert const obsLink = (obs.link && obs.link !== 'Board') ? obs.link : markerIdToLink[obs.marker_id]; @@ -602,6 +613,7 @@ function buildScene(data) { ); for (const m of a0markers) { + if (!hasXYZ(m.position_mm)) continue; // z.B. 1-Kamera-Marker, nicht trianguliert nTriangulated++; // Kein künstlicher Offset – Kugelmittelpunkt exakt an triangulierter Position const mpos = r2vArr(m.position_mm); @@ -625,6 +637,7 @@ function buildScene(data) { !boardMarkers.some(bm => bm.id === m.marker_id) ); for (const m of unknownTriangulated) { + if (!hasXYZ(m.position_mm)) continue; // z.B. 1-Kamera-Marker, nicht trianguliert nUnknown++; const mpos = r2vArr(m.position_mm); gMeasured.add(makeSphere(mpos, 0.0055, 0x3b82f6)); @@ -733,9 +746,9 @@ function buildTable(data) { let dist = null, dz = null, edge = null; let state = 'none'; // 'tri', '1cam', 'unk' - if (meas) { + if (meas && hasXYZ(meas.position_mm)) { [x, y, z] = meas.position_mm; - [nx, ny, nz] = meas.normal; + if (Array.isArray(meas.normal)) [nx, ny, nz] = meas.normal; edge = meas.edge_length_mm; state = model ? 'tri' : 'unk'; @@ -745,7 +758,10 @@ function buildTable(data) { dist = Math.sqrt(dx*dx + dy*dy + ddz*ddz); dz = ddz; } - } else if (cameras.length > 0) { + } else if (cameras.length > 0 || meas) { + // meas ohne position_mm (z.B. 1-Kamera-Marker, noch nicht trianguliert) + // zählt wie "gesehen, aber nicht trianguliert" — gleicher Stand wie + // cameras.length>0, nur eben aus 3b statt aus den rohen Detektionen. state = '1cam'; } @@ -1049,7 +1065,8 @@ async function loadData(specificRunDir = null) { buildTable(data); // Fremd-Marker für Verbindungslinien merken (Marker, die nicht in Board-Link stehen) const bIds = new Set((data.robot?.links?.Board?.markers ?? []).map(m => m.id)); - _primaryFremdMarkers = (data.measuredMarkers?.markers ?? []).filter(m => !bIds.has(m.marker_id)); + _primaryFremdMarkers = (data.measuredMarkers?.markers ?? []) + .filter(m => !bIds.has(m.marker_id) && hasXYZ(m.position_mm)); const measTotal = data.measuredMarkers?.markers?.length ?? 0; vlog(`Basis: run=${data.runDir} gesamt=${measTotal} fremd=${_primaryFremdMarkers.length} boardIDs=${bIds.size}` + (_primaryFremdMarkers.length ? ` (${_primaryFremdMarkers.map(m => m.marker_id).join(' ')})` : '')); @@ -1087,7 +1104,7 @@ async function loadCompareData() { // Board-Marker-IDs aus Robot.json dieses Runs const boardIds = new Set((data.robot?.links?.Board?.markers ?? []).map(m => m.id)); for (const m of markers) { - if (!boardIds.has(m.marker_id)) { + if (!boardIds.has(m.marker_id) && hasXYZ(m.position_mm)) { _compareFremdMarkers.push(m); // für Linien gCompare.add(makeSphere(r2vArr(m.position_mm), 0.006, 0xf97316)); // orange Kugel } @@ -1120,7 +1137,7 @@ async function loadPositionC() { const markers = data.measuredMarkers?.markers ?? []; const boardIds = new Set((data.robot?.links?.Board?.markers ?? []).map(m => m.id)); for (const m of markers) { - if (!boardIds.has(m.marker_id)) { + if (!boardIds.has(m.marker_id) && hasXYZ(m.position_mm)) { _positionCFremdMarkers.push(m); gPositionC.add(makeSphere(r2vArr(m.position_mm), 0.006, 0x22d3ee)); // cyan } diff --git a/public/yAxisCompute.js b/public/yAxisCompute.js index cc1244a..08e5d4e 100644 --- a/public/yAxisCompute.js +++ b/public/yAxisCompute.js @@ -106,6 +106,13 @@ const mc = mapC.get(id); if (!mb || !mc) continue; + // Fehlende position_mm (z.B. Einzelkamera-Marker, von 3b nicht + // trianguliert) ignorieren statt crashen — Marker bleibt im skipped-Log. + if (!Array.isArray(ma.position_mm) || !Array.isArray(mb.position_mm) || !Array.isArray(mc.position_mm)) { + skipped.push({ id, reason: 'fehlende position_mm (z.B. Einzelkamera-Marker, nicht trianguliert)' }); + continue; + } + const P1 = ma.position_mm.map(Number); const P2 = mb.position_mm.map(Number); const P3 = mc.position_mm.map(Number); diff --git a/test/boardViewerHasXYZ.test.js b/test/boardViewerHasXYZ.test.js new file mode 100644 index 0000000..7f8bdd5 --- /dev/null +++ b/test/boardViewerHasXYZ.test.js @@ -0,0 +1,59 @@ +/** + * boardViewerHasXYZ.test.js + * ========================= + * Unit-Test für die reine Hilfsfunktion `hasXYZ()` aus public/boardViewer.html. + * + * boardViewer.html ist kein ladbares JS-Modul (Inline-