arbeiten am callibration

This commit is contained in:
chk
2026-06-13 00:00:18 +02:00
parent e0e4212a90
commit 1762a771cf
51 changed files with 30343 additions and 5 deletions

View File

@@ -736,6 +736,10 @@ function buildCompareLines() {
// ── Y-Achsen-Berechnung aus drei Positionen ───────────────────────────────────
/** Marker, die sich weniger als diesen Wert bewegen, werden ignoriert.
* Entspricht dem min_movement_mm-Parameter im Python-Skript. */
const Y_AXIS_MIN_MOVEMENT_MM = 10.0;
function computeAndShowYAxis() {
clearGroup(gYAxis);
@@ -755,6 +759,8 @@ function computeAndShowYAxis() {
const circumcenters = []; // [{id, C:[x,y,z]}] in mm
const normals = []; // [[nx,ny,nz]] Achsenrichtung je Marker
const markerData = []; // [{markerId, posA, posB, posC, circumcenter, normal}] für Speicherung
const skipped = []; // [{id, reason, maxMoveMm}]
for (const [id, ma] of mapA) {
const mb = mapB.get(id);
@@ -765,6 +771,22 @@ function computeAndShowYAxis() {
const P2 = mb.position_mm.map(Number);
const P3 = mc.position_mm.map(Number);
// ── Mindest-Bewegungs-Filter ─────────────────────────────────────────────
// Marker, die sich zwischen den drei Positionen kaum bewegen, liefern
// degenerate Umkreismittelpunkte und korrumpieren die Achsenschätzung.
// Dieselbe Logik wie im Python-Skript (min_movement_mm).
const maxMoveMm = Math.max(
Math.sqrt(dist2(P1, P2)),
Math.sqrt(dist2(P2, P3)),
Math.sqrt(dist2(P1, P3)),
);
if (maxMoveMm < Y_AXIS_MIN_MOVEMENT_MM) {
vlog(`Y-Achse: Marker ${id} übersprungen Bewegung zu gering` +
` (${maxMoveMm.toFixed(1)} mm < ${Y_AXIS_MIN_MOVEMENT_MM} mm, kein rotierender Marker)`, 'warn');
skipped.push({ id, reason: 'Bewegung zu gering', maxMoveMm: +maxMoveMm.toFixed(2) });
continue;
}
// Normalenvektor der Kreisebene = Achsenrichtung
const v1 = [P2[0]-P1[0], P2[1]-P1[1], P2[2]-P1[2]];
const v2 = [P3[0]-P1[0], P3[1]-P1[1], P3[2]-P1[2]];
@@ -795,6 +817,7 @@ function computeAndShowYAxis() {
(w1*P1[2] + w2*P2[2] + w3*P3[2]) / wSum,
];
circumcenters.push({ id, C });
markerData.push({ markerId: id, posA: P1, posB: P2, posC: P3, circumcenter: C, normal: n });
// Kreismittelpunkt (rose)
gYAxis.add(makeSphere(r2vArr(C), 0.007, 0xfb7185));
@@ -803,8 +826,11 @@ function computeAndShowYAxis() {
}
if (circumcenters.length === 0) {
vlog('Y-Achse: Keine gemeinsamen fremd-Marker in Pos A+B+C gefunden', 'warn');
window.parent.postMessage({ type: 'yaxis-measurement', axisDir: null }, '*');
const why = skipped.length
? `Alle ${skipped.length} Marker gefiltert (Bewegung < ${Y_AXIS_MIN_MOVEMENT_MM} mm)`
: 'Keine gemeinsamen fremd-Marker in Pos A+B+C gefunden';
vlog(`Y-Achse: ${why}`, 'warn');
window.parent.postMessage({ type: 'yaxis-measurement', axisDir: null, skipped }, '*');
return;
}
@@ -837,7 +863,11 @@ function computeAndShowYAxis() {
const fmt = v => (v >= 0 ? '+' : '') + v.toFixed(3) + '°';
const good = Math.abs(tiltXY) < 0.5 && Math.abs(tiltYZ) < 0.5;
vlog(`Y-Achse (${circumcenters.length} Marker): dir=[${axisDir.map(v => v.toFixed(4)).join(', ')}]`);
const usedIds = circumcenters.map(c => c.id);
const skippedIds = skipped.map(s => s.id);
vlog(`Y-Achse: ${usedIds.length} Marker genutzt (${usedIds.join(', ')})` +
(skippedIds.length ? ` · ${skippedIds.length} gefiltert (${skippedIds.join(', ')})` : ''));
vlog(` dir=[${axisDir.map(v => v.toFixed(4)).join(', ')}]`);
vlog(` Referenzpunkt: [${axisPoint.map(v => v.toFixed(1)).join(', ')}] mm`);
vlog(` Abw. von Y-Achse: XY ${fmt(tiltXY)} YZ ${fmt(tiltYZ)}`, good ? 'ok' : 'warn');
@@ -847,7 +877,14 @@ function computeAndShowYAxis() {
axisPoint,
tiltXY,
tiltYZ,
numMarkers: circumcenters.length,
numMarkers: circumcenters.length,
numMarkersCommon: circumcenters.length + skipped.length,
skipped,
// Für rotation_detection.json: Run-Referenzen und Marker-Rohdaten
runA: document.getElementById('sel-run-primary')?.value ?? null,
runB: document.getElementById('sel-run-compare')?.value ?? null,
runC: document.getElementById('sel-run-c')?.value ?? null,
markerData,
}, '*');
}

View File

@@ -60,7 +60,6 @@ body {
display: flex;
flex-direction: column;
gap: 3px;
padding-top: 4px;
flex-shrink: 0;
width: 148px;
}
@@ -92,6 +91,7 @@ body {
border-right: none;
border-left: 3px solid var(--accent);
padding-left: 13px; /* kompensiert den dickeren linken Rand */
margin-right: -1px; /* überlappt 1px ins Panel → kein Zoom-Spalt */
z-index: 1;
}

View File

@@ -565,6 +565,20 @@ function initArm(tab) {
log(`📐 Achse (${msg.numMarkers} Marker): dir=[${msg.axisDir.map(v => v.toFixed(4)).join(', ')}]` +
` XY=${fmt(msg.tiltXY)} YZ=${fmt(msg.tiltYZ)}`);
log(` Referenzpunkt: [${msg.axisPoint.map(v => v.toFixed(1)).join(', ')}] mm`);
// In rotation_detection.json speichern (anhängen)
fetch('/api/xaxis/save-rotation-detection', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
axis: { dir: msg.axisDir, referencePoint: msg.axisPoint, tiltXY_deg: msg.tiltXY, tiltYZ_deg: msg.tiltYZ },
runs: { A: msg.runA ?? null, B: msg.runB ?? null, C: msg.runC ?? null },
numMarkers: msg.numMarkers,
markers: msg.markerData ?? [],
}),
}).then(r => r.json())
.then(d => log(`💾 Gespeichert: ${d.file} (${d.total} Messungen)`))
.catch(e => log(`⚠ Speichern fehlgeschlagen: ${e.message}`));
}
});
}

View File

@@ -20,6 +20,20 @@
</div>
</div>
<!-- ── Aktionen ───────────────────────────────────────────────────────────── -->
<div class="section full">
<h2>Aktionen</h2>
<div style="margin-top:14px;display:flex;align-items:center;gap:20px;flex-wrap:wrap">
<button id="btn-arm1-ccw" style="font-size:18px;padding:6px 22px" title="Bieps rauf">
⤴ Rauf
</button>
<span style="color:var(--muted);font-size:11px">Roboter-Bieps drehen (Schrittweite folgt)</span>
<button id="btn-arm1-cw" style="font-size:18px;padding:6px 22px" title="Bieps runter">
Runter ⤵
</button>
</div>
</div>
<div class="section full">
<h2>Ausgabe / Log</h2>
<textarea id="log-arm1" readonly placeholder="(Ausgabe erscheint hier)"></textarea>

View File

@@ -20,6 +20,20 @@
</div>
</div>
<!-- ── Aktionen ───────────────────────────────────────────────────────────── -->
<div class="section full">
<h2>Aktionen</h2>
<div style="margin-top:14px;display:flex;align-items:center;gap:20px;flex-wrap:wrap">
<button id="btn-arm2-ccw" style="font-size:18px;padding:6px 22px" title="Unterarm rauf">
⤴ Rauf
</button>
<span style="color:var(--muted);font-size:11px">Roboter-Unterarm drehen (Schrittweite folgt)</span>
<button id="btn-arm2-cw" style="font-size:18px;padding:6px 22px" title="Unterarm runter">
Runter ⤵
</button>
</div>
</div>
<div class="section full">
<h2>Ausgabe / Log</h2>
<textarea id="log-arm2" readonly placeholder="(Ausgabe erscheint hier)"></textarea>