diff --git a/public/boardViewer.html b/public/boardViewer.html index 4f41cee..df3864e 100644 --- a/public/boardViewer.html +++ b/public/boardViewer.html @@ -372,17 +372,27 @@ function buildSkeletonFK(robot, angles) { // 5. Fehlerlinien: Modell-Marker → gemessene Position (aus 3b) if (_measuredMarkers?.markers?.length > 0) { + // Fallback-Lookup: marker_id → Link-Name, falls obs.link fehlt oder 'Board' ist + const markerIdToLink = {}; + for (const [lname, ldata] of Object.entries(links)) { + if (lname === 'Board') continue; + for (const m of (ldata.markers ?? [])) markerIdToLink[m.id] = lname; + } + for (const obs of _measuredMarkers.markers) { - if (obs.link === 'Board' || !obs.link) continue; - const ldata = links[obs.link]; - if (!ldata || !frames[obs.link]) continue; + const obsLink = (obs.link && obs.link !== 'Board') + ? obs.link + : markerIdToLink[obs.marker_id]; + if (!obsLink) continue; + const ldata = links[obsLink]; + if (!ldata || !frames[obsLink]) continue; const modelM = ldata.markers?.find(mm => mm.id === obs.marker_id); if (!modelM?.position) continue; const [lx, ly, lz] = modelM.position; - const modelPosW = new THREE.Vector3(lx * S, lz * S, -ly * S).applyMatrix4(frames[obs.link]); - const obsPosW = r2vArr(obs.position_mm); + const modelPosW = new THREE.Vector3(lx * S, lz * S, -ly * S).applyMatrix4(frames[obsLink]); + const obsPosW = r2vArr(obs.position_mm); gArmMarkers.add(makeLine(modelPosW, obsPosW, 0xff8800, 0.85)); - gArmMarkers.add(makeSphere(obsPosW, 0.007, LINK_COLORS[obs.link] ?? 0x3b82f6)); + gArmMarkers.add(makeSphere(obsPosW, 0.007, LINK_COLORS[obsLink] ?? 0x3b82f6)); } } } diff --git a/public/calibration.js b/public/calibration.js index e7b8522..d6c3865 100644 --- a/public/calibration.js +++ b/public/calibration.js @@ -1099,8 +1099,69 @@ function initMarker() { } } + // ── Homing-Run starten ─────────────────────────────────────────────────── + async function runMarkerHoming() { + const btn = document.getElementById('btn-marker-homing'); + const statusEl = document.getElementById('marker-homing-status'); + if (btn) btn.disabled = true; + if (statusEl) statusEl.textContent = '● Läuft …'; + logM('▶ Homing gestartet …'); + + try { + const response = await fetch('/api/homing/run', { method: 'POST' }); + if (!response.ok || !response.body) throw new Error(`HTTP ${response.status}`); + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buf = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buf += decoder.decode(value, { stream: true }); + const lines = buf.split('\n'); + buf = lines.pop(); + for (const line of lines) { + if (!line.startsWith('data: ')) continue; + let evt; + try { evt = JSON.parse(line.slice(6)); } catch { continue; } + + if (evt.type === 'log') { logM(evt.text ?? ''); } + if (evt.type === 'step') { logM(`[${evt.step}/${evt.total}] ${evt.text ?? ''}`); } + if (evt.type === 'analysis' && evt.key?.startsWith('state_') && evt.value) { + frameEl?.contentWindow?.postMessage({ type: 'homing-state', state: evt.value }, '*'); + } + if (evt.type === 'error') { + logM(`❌ ${evt.text ?? ''}`); + if (statusEl) statusEl.textContent = '✗ Fehler'; + } + if (evt.type === 'done') { + if (evt.state) { + if (statusEl) statusEl.textContent = '✓ Fertig'; + logM('✅ Homing abgeschlossen'); + } else { + if (statusEl) statusEl.textContent = '✗ Fehler'; + } + if (evt.runDir && frameEl?.contentWindow) { + frameEl.contentWindow.postMessage({ type: 'load-homing-run', runDir: evt.runDir }, '*'); + if (evt.state) { + frameEl.contentWindow.postMessage({ type: 'homing-state', state: evt.state }, '*'); + } + } + } + } + } + } catch (err) { + logM(`❌ ${err}`); + if (statusEl) statusEl.textContent = '✗ Fehler'; + } finally { + if (btn) btn.disabled = false; + } + } + // ── Event-Listener ──────────────────────────────────────────────────────── document.getElementById('btn-marker-reload')?.addEventListener('click', () => loadRobot()); + document.getElementById('btn-marker-homing')?.addEventListener('click', runMarkerHoming); linkSel?.addEventListener('change', () => updateMarkerDropdown()); idSel?.addEventListener('change', () => updateSpinLabel()); diff --git a/public/calibration_marker.html b/public/calibration_marker.html index 3b25320..2056f11 100644 --- a/public/calibration_marker.html +++ b/public/calibration_marker.html @@ -69,21 +69,36 @@
+ ++ Startet einen vollständigen Homing-Run (Foto aller Kameras → ArUco-Erkennung → Gelenk-Winkel). + Der Viewer wird anschliessend mit den neuen Messwerten aktualisiert. +
+- Zeigt das Roboter-Modell mit den Arm-Markern in der aktuellen robot.json-Konfiguration. - Sind Homing-Messwerte vorhanden (aus letztem Homing-Run), werden auch die beobachteten Marker - als Kugeln und die Abweichungs-Linien dargestellt. - Nach einer Spin-Änderung wird der Viewer automatisch neu geladen. + Zeigt das Roboter-Modell mit den Arm-Markern (aus robot.json) und — nach einem Homing-Run — + die beobachteten Marker als Kugeln mit Abweichungs-Linien. + Nach einer Spin-Änderung oder einem Homing-Run wird der Viewer automatisch neu geladen.