x-axis justierung: lines 3
This commit is contained in:
@@ -389,3 +389,102 @@ export async function assignMarkerId(robotPath, { markerId, set, link, extraMark
|
||||
change: { action: 'added', markerId: id, oldLink: null, oldSet: '', newLink: link, newSet: set ?? '' },
|
||||
};
|
||||
}
|
||||
|
||||
// ── Aktion 5: X-Achse übernehmen ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Rotiert alle Marker-Positionen in robot.json so, dass die übergebene Richtung
|
||||
* zur neuen X-Achse [1,0,0] wird. Rotation um den Schwerpunkt aller A0-Marker
|
||||
* (Origin bleibt erhalten).
|
||||
*
|
||||
* direction: [vx, vy, vz] – gemessene Ist-X-Richtung in Roboter-Koordinaten
|
||||
*
|
||||
* Algorithmus:
|
||||
* 1. Normalisiere direction, ggf. Vorzeichen so dass vx > 0
|
||||
* 2. Baue Orthonormalbasis: x_new = v,
|
||||
* z_new = Gram-Schmidt([0,0,1] gegen v),
|
||||
* y_new = cross(z_new, x_new)
|
||||
* 3. Rotationsmatrix R (old→new): Zeilen = [x_new, y_new, z_new]
|
||||
* 4. p_new = origin + R * (p_old − origin)
|
||||
*/
|
||||
export async function adoptXAxis(robotPath, { direction }) {
|
||||
const [vx, vy, vz] = direction.map(Number);
|
||||
const len = Math.sqrt(vx * vx + vy * vy + vz * vz);
|
||||
if (len < 1e-9) throw new Error('Richtungsvektor zu klein (fast Null-Vektor).');
|
||||
|
||||
// Normalisieren, immer positive X-Komponente
|
||||
let nx = vx / len, ny = vy / len, nz = vz / len;
|
||||
if (nx < 0) { nx = -nx; ny = -ny; nz = -nz; }
|
||||
|
||||
// ── Orthonormalbasis ──────────────────────────────────────────────────────
|
||||
// Z_new: Gram-Schmidt von [0,0,1] gegen x_new
|
||||
const dotZ = nz; // dot([0,0,1], [nx,ny,nz])
|
||||
let zx = -dotZ * nx, zy = -dotZ * ny, zz = 1 - dotZ * nz;
|
||||
let zlen = Math.sqrt(zx * zx + zy * zy + zz * zz);
|
||||
if (zlen < 1e-9) {
|
||||
// Sonderfall: x_new fast parallel zu Z – Fallback auf [0,1,0]
|
||||
const dotY = ny;
|
||||
zx = -dotY * nx; zy = 1 - dotY * ny; zz = -dotY * nz;
|
||||
zlen = Math.sqrt(zx * zx + zy * zy + zz * zz);
|
||||
}
|
||||
zx /= zlen; zy /= zlen; zz /= zlen;
|
||||
|
||||
// Y_new = cross(z_new, x_new) [rechte-Hand-Regel: ẑ × x̂ = ŷ]
|
||||
const yx = zy * nz - zz * ny;
|
||||
const yy = zz * nx - zx * nz;
|
||||
const yz = zx * ny - zy * nx;
|
||||
|
||||
// Rotationsfunktion: p_rot = R * p (R hat Zeilen = neue Achsen in alten Koordinaten)
|
||||
function rotVec(px, py, pz) {
|
||||
return [
|
||||
nx * px + ny * py + nz * pz, // neue X-Komponente
|
||||
yx * px + yy * py + yz * pz, // neue Y-Komponente
|
||||
zx * px + zy * py + zz * pz, // neue Z-Komponente
|
||||
];
|
||||
}
|
||||
|
||||
const robot = await readRobot(robotPath);
|
||||
const links = robot.links ?? {};
|
||||
|
||||
// ── Ursprung: Schwerpunkt aller A0-Marker ────────────────────────────────
|
||||
const a0Pos = [];
|
||||
for (const ld of Object.values(links)) {
|
||||
for (const m of (ld.markers ?? [])) {
|
||||
if (m.set === 'A0' && Array.isArray(m.position) && m.position.length >= 3) {
|
||||
a0Pos.push(m.position.map(Number));
|
||||
}
|
||||
}
|
||||
}
|
||||
let ox = 0, oy = 0, oz = 0;
|
||||
if (a0Pos.length > 0) {
|
||||
for (const [px, py, pz] of a0Pos) { ox += px; oy += py; oz += pz; }
|
||||
ox /= a0Pos.length; oy /= a0Pos.length; oz /= a0Pos.length;
|
||||
}
|
||||
|
||||
// ── Alle Marker rotieren ──────────────────────────────────────────────────
|
||||
let numChanged = 0;
|
||||
for (const ld of Object.values(links)) {
|
||||
for (const m of (ld.markers ?? [])) {
|
||||
if (!Array.isArray(m.position) || m.position.length < 3) continue;
|
||||
const [px, py, pz] = m.position.map(Number);
|
||||
const [rx, ry, rz] = rotVec(px - ox, py - oy, pz - oz);
|
||||
m.position = [
|
||||
Math.round((ox + rx) * 100) / 100,
|
||||
Math.round((oy + ry) * 100) / 100,
|
||||
Math.round((oz + rz) * 100) / 100,
|
||||
];
|
||||
numChanged++;
|
||||
}
|
||||
}
|
||||
|
||||
robot.links = links;
|
||||
await writeRobot(robotPath, robot);
|
||||
|
||||
return {
|
||||
numChanged,
|
||||
origin: [ox, oy, oz].map(v => Math.round(v * 10) / 10),
|
||||
newXAxis: [nx, ny, nz].map(v => Math.round(v * 10000) / 10000),
|
||||
angleXYdeg: Math.round(Math.atan2(ny, nx) * 18000 / Math.PI) / 100,
|
||||
angleXZdeg: Math.round(Math.atan2(nz, nx) * 18000 / Math.PI) / 100,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'url';
|
||||
import process from 'process';
|
||||
import { spawn } from 'child_process';
|
||||
import { WebcamClient } from './webcamClient.js';
|
||||
import { assignByZRange, removeMarkerAssignment, alignSetToMeasured, assignMarkerId } from './editRobot.js';
|
||||
import { assignByZRange, removeMarkerAssignment, alignSetToMeasured, assignMarkerId, adoptXAxis } from './editRobot.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -864,6 +864,31 @@ app.post('/api/robot/assign-id', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/robot/adopt-x-axis
|
||||
* Dreht alle Marker-Positionen in robot.json so, dass die gemessene Richtung
|
||||
* zur neuen X-Achse [1,0,0] wird. Rotation um den A0-Schwerpunkt.
|
||||
* Body: { direction: [vx, vy, vz] }
|
||||
*/
|
||||
app.post('/api/robot/adopt-x-axis', async (req, res) => {
|
||||
try {
|
||||
const { direction } = req.body ?? {};
|
||||
if (!Array.isArray(direction) || direction.length < 3) {
|
||||
return res.status(400).json({ error: '"direction" muss ein Array [vx,vy,vz] sein.' });
|
||||
}
|
||||
const result = await adoptXAxis(ROBOT_JSON, { direction });
|
||||
console.log(
|
||||
`robot/adopt-x-axis dir=[${direction.map(v => Number(v).toFixed(4)).join(', ')}]` +
|
||||
` → ${result.numChanged} Marker, Ursprung=[${result.origin.join(', ')}]` +
|
||||
` XY=${result.angleXYdeg}° XZ=${result.angleXZdeg}°`,
|
||||
);
|
||||
return res.json(result);
|
||||
} catch (err) {
|
||||
console.error('robot/adopt-x-axis error:', err);
|
||||
return res.status(500).json({ error: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/calibration/upload-npz
|
||||
* Liest {camera}_calibration.npz aus der aktuellen Session und
|
||||
|
||||
Reference in New Issue
Block a user