Marker - Callibration
This commit is contained in:
@@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|
||||||
|
|||||||
@@ -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 & 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"
|
||||||
|
|||||||
Reference in New Issue
Block a user