Marker - Callibration

This commit is contained in:
chk
2026-06-15 22:51:00 +02:00
parent 15d4175fd1
commit f88e73c02f
3 changed files with 98 additions and 12 deletions

View File

@@ -372,17 +372,27 @@ function buildSkeletonFK(robot, angles) {
// 5. Fehlerlinien: Modell-Marker → gemessene Position (aus 3b) // 5. Fehlerlinien: Modell-Marker → gemessene Position (aus 3b)
if (_measuredMarkers?.markers?.length > 0) { 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) { for (const obs of _measuredMarkers.markers) {
if (obs.link === 'Board' || !obs.link) continue; const obsLink = (obs.link && obs.link !== 'Board')
const ldata = links[obs.link]; ? obs.link
if (!ldata || !frames[obs.link]) continue; : 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); const modelM = ldata.markers?.find(mm => mm.id === obs.marker_id);
if (!modelM?.position) continue; if (!modelM?.position) continue;
const [lx, ly, lz] = modelM.position; const [lx, ly, lz] = modelM.position;
const modelPosW = new THREE.Vector3(lx * S, lz * S, -ly * S).applyMatrix4(frames[obs.link]); const modelPosW = new THREE.Vector3(lx * S, lz * S, -ly * S).applyMatrix4(frames[obsLink]);
const obsPosW = r2vArr(obs.position_mm); const obsPosW = r2vArr(obs.position_mm);
gArmMarkers.add(makeLine(modelPosW, obsPosW, 0xff8800, 0.85)); 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));
} }
} }
} }

View File

@@ -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 ──────────────────────────────────────────────────────── // ── Event-Listener ────────────────────────────────────────────────────────
document.getElementById('btn-marker-reload')?.addEventListener('click', () => loadRobot()); document.getElementById('btn-marker-reload')?.addEventListener('click', () => loadRobot());
document.getElementById('btn-marker-homing')?.addEventListener('click', runMarkerHoming);
linkSel?.addEventListener('change', () => updateMarkerDropdown()); linkSel?.addEventListener('change', () => updateMarkerDropdown());
idSel?.addEventListener('change', () => updateSpinLabel()); idSel?.addEventListener('change', () => updateSpinLabel());

View File

@@ -69,21 +69,36 @@
<div id="marker-action-result" style="margin-top:10px;font-size:11px;min-height:18px;color:var(--muted)"></div> <div id="marker-action-result" style="margin-top:10px;font-size:11px;min-height:18px;color:var(--muted)"></div>
</div> </div>
<!-- ── Homing ───────────────────────────────────────────────────────────── -->
<div class="section full">
<h2>Homing</h2>
<p style="font-size:12px;color:var(--muted);margin-bottom:12px">
Startet einen vollständigen Homing-Run (Foto aller Kameras → ArUco-Erkennung → Gelenk-Winkel).
Der Viewer wird anschliessend mit den neuen Messwerten aktualisiert.
</p>
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap">
<button id="btn-marker-homing"
style="background:#1d4ed8;color:#fff;border:none;border-radius:3px;padding:6px 16px;cursor:pointer;font:inherit;font-size:12px">
Foto &amp; Homing berechnen
</button>
<span id="marker-homing-status" style="font-size:11px;color:var(--muted)"></span>
</div>
</div>
<!-- ── Log ──────────────────────────────────────────────────────────────── --> <!-- ── Log ──────────────────────────────────────────────────────────────── -->
<div class="section full"> <div class="section full">
<h2>Log</h2> <h2>Log</h2>
<textarea id="log-marker" readonly placeholder="(Aktionen erscheinen hier)" <textarea id="log-marker" readonly placeholder="(Aktionen und Homing-Output erscheinen hier)"
style="height:100px"></textarea> style="height:140px"></textarea>
</div> </div>
<!-- ── Viewer ───────────────────────────────────────────────────────────── --> <!-- ── Viewer ───────────────────────────────────────────────────────────── -->
<div class="section full"> <div class="section full">
<h2>Viewer</h2> <h2>Viewer</h2>
<p style="font-size:12px;color:var(--muted);margin-bottom:10px"> <p style="font-size:12px;color:var(--muted);margin-bottom:10px">
Zeigt das Roboter-Modell mit den Arm-Markern in der aktuellen robot.json-Konfiguration. Zeigt das Roboter-Modell mit den Arm-Markern (aus robot.json) und — nach einem Homing-Run —
Sind Homing-Messwerte vorhanden (aus letztem Homing-Run), werden auch die beobachteten Marker die beobachteten Marker als Kugeln mit Abweichungs-Linien.
als Kugeln und die Abweichungs-Linien dargestellt. Nach einer Spin-Änderung oder einem Homing-Run wird der Viewer automatisch neu geladen.
Nach einer Spin-Änderung wird der Viewer automatisch neu geladen.
</p> </p>
<iframe <iframe
id="marker-viewer-frame" id="marker-viewer-frame"