Y-Axis checks

This commit is contained in:
chk
2026-06-13 06:13:31 +02:00
parent 1762a771cf
commit 7f17427e0a
7 changed files with 765 additions and 110 deletions

View File

@@ -149,6 +149,8 @@
}
}
</script>
<!-- reine Berechnungslogik (kein DOM/Three.js) auch von Jest-Tests genutzt -->
<script src="/yAxisCompute.js"></script>
</head>
<body>
@@ -736,10 +738,6 @@ 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);
@@ -751,134 +749,56 @@ function computeAndShowYAxis() {
return;
}
const mapA = new Map(_primaryFremdMarkers .map(m => [m.marker_id, m]));
const mapB = new Map(_compareFremdMarkers .map(m => [m.marker_id, m]));
const mapC = new Map(_positionCFremdMarkers.map(m => [m.marker_id, m]));
// ── Berechnung via yAxisCompute.js (kein DOM/Three.js) ───────────────────
const result = YAxisCompute.computeYAxis(
_primaryFremdMarkers,
_compareFremdMarkers,
_positionCFremdMarkers,
);
function dist2(P, Q) { return (P[0]-Q[0])**2 + (P[1]-Q[1])**2 + (P[2]-Q[2])**2; }
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);
const mc = mapC.get(id);
if (!mb || !mc) continue;
const P1 = ma.position_mm.map(Number);
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]];
const cross = [
v1[1]*v2[2] - v1[2]*v2[1],
v1[2]*v2[0] - v1[0]*v2[2],
v1[0]*v2[1] - v1[1]*v2[0],
];
const crossLen = Math.sqrt(cross[0]**2 + cross[1]**2 + cross[2]**2);
if (crossLen < 1e-3) {
vlog(`Y-Achse: Marker ${id} degenerat (Punkte zu nahe / kollinear)`, 'warn');
continue;
}
const n = cross.map(c => c / crossLen);
normals.push(n);
// Umkreismittelpunkt (bary­zentrischer Ansatz)
const a2 = dist2(P2, P3), b2 = dist2(P1, P3), c2 = dist2(P1, P2);
const w1 = a2*(b2+c2-a2), w2 = b2*(a2+c2-b2), w3 = c2*(a2+b2-c2);
const wSum = w1 + w2 + w3;
if (Math.abs(wSum) < 1e-6) {
vlog(`Y-Achse: Marker ${id} Umkreis undefiniert`, 'warn');
continue;
}
const C = [
(w1*P1[0] + w2*P2[0] + w3*P3[0]) / wSum,
(w1*P1[1] + w2*P2[1] + w3*P3[1]) / wSum,
(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));
// Bogen-Linie B→C (cyan)
gYAxis.add(makeLine(r2vArr(P2), r2vArr(P3), 0x22d3ee, 0.6));
}
if (circumcenters.length === 0) {
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 }, '*');
if (!result.ok) {
vlog(`Y-Achse: ${result.reason}`, 'warn');
window.parent.postMessage({ type: 'yaxis-measurement', axisDir: null, skipped: result.skipped }, '*');
return;
}
// Achsenrichtung: Mittlere Normale (Vorzeichen angleichen)
const ref = normals[0];
const aligned = normals.map(n => {
const dot = n[0]*ref[0] + n[1]*ref[1] + n[2]*ref[2];
return dot >= 0 ? n : n.map(c => -c);
});
const meanN = [0, 1, 2].map(i => aligned.reduce((s, n) => s + n[i], 0) / aligned.length);
const meanNLen = Math.sqrt(meanN[0]**2 + meanN[1]**2 + meanN[2]**2);
const axisDir = meanN.map(c => c / meanNLen); // Roboter-Koordinaten
const { axisDir, axisPoint, tiltXY, tiltYZ, skipped, markerData } = result;
// Referenzpunkt: Schwerpunkt der Umkreismittelpunkte
const axisPoint = [0, 1, 2].map(i =>
circumcenters.reduce((s, c) => s + c.C[i], 0) / circumcenters.length
);
// ── Visualisierung (Three.js) ─────────────────────────────────────────────
for (const { posB: P2, posC: P3, circumcenter: C } of markerData) {
gYAxis.add(makeSphere(r2vArr(C), 0.007, 0xfb7185)); // Umkreismittelpunkt (rose)
gYAxis.add(makeLine(r2vArr(P2), r2vArr(P3), 0x22d3ee, 0.6)); // Bogen B→C (cyan)
}
// Achse als Linie ±500 mm visualisieren (magenta)
const L = 500;
const p1mm = axisPoint.map((v, i) => v - L * axisDir[i]);
const p2mm = axisPoint.map((v, i) => v + L * axisDir[i]);
gYAxis.add(makeLine(r2vArr(p1mm), r2vArr(p2mm), 0xe879f9, 0.9));
gYAxis.add(makeSphere(r2vArr(axisPoint), 0.011, 0xe879f9));
// Abweichung von der idealen Y-Achse [0,1,0] in Roboter-Koordinaten
const [ax, ay, az] = axisDir;
const tiltXY = Math.atan2(ax, ay) * 180 / Math.PI; // Kippung in XY-Ebene
const tiltYZ = Math.atan2(az, ay) * 180 / Math.PI; // Kippung in YZ-Ebene
// ── Logging ───────────────────────────────────────────────────────────────
const fmt = v => (v >= 0 ? '+' : '') + v.toFixed(3) + '°';
const good = Math.abs(tiltXY) < 0.5 && Math.abs(tiltYZ) < 0.5;
const usedIds = circumcenters.map(c => c.id);
const usedIds = markerData.map(m => m.markerId);
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');
if (skippedIds.length) {
skipped.forEach(s => vlog(` ↳ Marker ${s.id} übersprungen: ${s.reason} (${s.maxMoveMm} mm)`, 'warn'));
}
window.parent.postMessage({
type: 'yaxis-measurement',
type: 'yaxis-measurement',
axisDir,
axisPoint,
tiltXY,
tiltYZ,
numMarkers: circumcenters.length,
numMarkersCommon: circumcenters.length + skipped.length,
numMarkers: result.numMarkers,
numMarkersCommon: result.numMarkersCommon,
skipped,
// Für rotation_detection.json: Run-Referenzen und Marker-Rohdaten
runA: document.getElementById('sel-run-primary')?.value ?? null,