From b1950ffa5a756b081080dc79c069bc9057801502 Mon Sep 17 00:00:00 2001 From: chk <79915315+ChKendel@users.noreply.github.com> Date: Wed, 10 Jun 2026 18:08:18 +0200 Subject: [PATCH] board rotation - fix 0 --- public/calibration.js | 48 +++++++++++++++--- public/calibration_board.html | 25 ++++++--- .../2_estimate_camera_from_observations.py | 15 +++++- ...e_camera_from_observations.cpython-311.pyc | Bin 0 -> 46689 bytes server/server.js | 41 +++++++++++---- 5 files changed, 104 insertions(+), 25 deletions(-) create mode 100644 scripts/__pycache__/2_estimate_camera_from_observations.cpython-311.pyc diff --git a/public/calibration.js b/public/calibration.js index 953d79d..223572b 100644 --- a/public/calibration.js +++ b/public/calibration.js @@ -354,6 +354,27 @@ async function loadBoardTable() { // ── Board ───────────────────────────────────────────────────────────────────── +/** Befüllt alle Set-Dropdowns aus /api/robot/board-sets */ +async function populateBoardSetDropdowns() { + let sets = []; + try { + const r = await fetch('/api/robot/board-sets'); + if (r.ok) sets = (await r.json()).sets ?? []; + } catch { /* kein Server / noch keine Sets → leere Dropdowns */ } + + // Hilfsfunktion: + + + @@ -78,14 +84,21 @@ Sets justieren (zu 3b-Messung)

- Set verschieben - + + bleibt /  + + wird verschoben - Rotation (Z-Achse) + Translation → passt Set zu 3b-Messung + Rotation (Z-Achse) + Translation → passt verschobenes Set zu 3b-Messung
diff --git a/scripts/2_estimate_camera_from_observations.py b/scripts/2_estimate_camera_from_observations.py index f6197c7..4b28917 100644 --- a/scripts/2_estimate_camera_from_observations.py +++ b/scripts/2_estimate_camera_from_observations.py @@ -212,13 +212,15 @@ def get_marker_rotation(marker: Dict[str, Any]) -> np.ndarray: return np.eye(3, dtype=np.float32) -def load_marker_lookup(robot_json_path: str) -> Dict[int, Dict[str, Any]]: +def load_marker_lookup(robot_json_path: str, ref_set: Optional[str] = None) -> Dict[int, Dict[str, Any]]: """ Supports the new format: robot_data["links"]["Board"]["markers"] Fallback: robot_data["Marker"] + + ref_set: wenn angegeben, werden nur Marker mit passendem "set"-Feld als Referenz verwendet. """ robot_json_path = resolve_path(robot_json_path) with open(robot_json_path, "r", encoding="utf-8") as f: @@ -248,6 +250,10 @@ def load_marker_lookup(robot_json_path: str) -> Dict[int, Dict[str, Any]]: if marker_id < 0: continue + # Referenz-Set-Filter: nur Marker mit passendem set-Wert verwenden + if ref_set and str(marker.get("set", "")) != ref_set: + continue + if "position" not in marker: continue @@ -583,13 +589,18 @@ def main() -> None: parser.add_argument("--maxRmsPx", type=float, default=None, help="Optional soft warning threshold for final reprojection RMS in pixels") parser.add_argument("--epsJac", type=float, default=1e-6, help="Finite-difference epsilon") + parser.add_argument("--refSet", default=None, + help="Nur Marker dieses Sets als Referenz verwenden (z.B. 'A0', 'rail'). " + "Leer = alle Marker aus links.Board.") args = parser.parse_args() detection_path = resolve_path(args.input) robot_path = resolve_path(args.robot) detection = load_json(detection_path) - marker_lookup = load_marker_lookup(robot_path) + marker_lookup = load_marker_lookup(robot_path, ref_set=args.refSet) + if args.refSet: + print(f"[INFO] Referenz-Set: '{args.refSet}' → {len(marker_lookup)} Referenz-Marker") K, D = load_intrinsics_from_detection(detection) diff --git a/scripts/__pycache__/2_estimate_camera_from_observations.cpython-311.pyc b/scripts/__pycache__/2_estimate_camera_from_observations.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e51d550251f748f8e38151b71727c29ea40b15b GIT binary patch literal 46689 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 Gvj2Z!wMUi! literal 0 HcmV?d00001 diff --git a/server/server.js b/server/server.js index 906b668..419bd53 100755 --- a/server/server.js +++ b/server/server.js @@ -491,6 +491,8 @@ app.post('/api/board/run', async (req, res) => { if (!res.writableEnded) res.write(`data: ${JSON.stringify(obj)}\n\n`); }; + const { refSet } = req.body ?? {}; + // 1. Temp-Verzeichnis const ts = makeTimestamp(); const runDir = path.join(boardDataDir, ts); @@ -501,9 +503,14 @@ app.post('/api/board/run', async (req, res) => { // Robot-JSON laden und Marker-Anzahl loggen let robotData = null; try { robotData = JSON.parse(await fsPromises.readFile(ROBOT_JSON, 'utf8')); } catch {} - const boardMarkerCount = robotData?.links?.Board?.markers?.length ?? '?'; + const boardMarkers = robotData?.links?.Board?.markers ?? []; + const boardMarkerCount = boardMarkers.length; + const refMarkerCount = refSet + ? boardMarkers.filter(m => m.set === refSet).length + : boardMarkerCount; send({ type: 'log', text: `▶ Robot-JSON: ${ROBOT_JSON}` }); - send({ type: 'log', text: `▶ Board-Marker (A0): ${boardMarkerCount} Marker aus links.Board.markers` }); + send({ type: 'log', text: `▶ Board-Marker: ${boardMarkerCount} (links.Board.markers)` }); + send({ type: 'log', text: `▶ Referenz-Set: ${refSet ? `"${refSet}" (${refMarkerCount} Marker)` : 'alle'}` }); send({ type: 'log', text: '' }); // 2. Kameras ermitteln @@ -567,12 +574,9 @@ app.post('/api/board/run', async (req, res) => { } send({ type: 'log', text: '\n▷ 2_estimate_camera_from_observations' }); - const exit2 = await runScript([ - SCRIPT_2, - '-i', detJson, - '-robot', ROBOT_JSON, - '-outDir', runDir, - ], send); + const script2Args = [SCRIPT_2, '-i', detJson, '-robot', ROBOT_JSON, '-outDir', runDir]; + if (refSet) script2Args.push('--refSet', refSet); + const exit2 = await runScript(script2Args, send); if (exit2 !== 0) { send({ type: 'log', text: `❌ Script 2 Exit ${exit2}` }); } @@ -753,6 +757,23 @@ app.post('/api/robot/remove-marker', async (req, res) => { } }); +/** + * GET /api/robot/board-sets + * Gibt die einzigartigen "set"-Werte aller Marker in links.Board zurück. + * Wird vom Frontend genutzt, um Dropdowns zu befüllen. + */ +app.get('/api/robot/board-sets', async (req, res) => { + try { + const robot = JSON.parse(await fsPromises.readFile(ROBOT_JSON, 'utf8')); + const markers = robot?.links?.Board?.markers ?? []; + const sets = [...new Set(markers.map(m => m.set).filter(Boolean))].sort(); + return res.json({ sets }); + } catch (err) { + console.error('robot/board-sets error:', err); + return res.status(500).json({ error: String(err) }); + } +}); + /** * POST /api/robot/align-sets * Richtet alle Marker des angegebenen Sets rigid (2D-Rotation um Z + 3D-Translation) @@ -761,7 +782,7 @@ app.post('/api/robot/remove-marker', async (req, res) => { */ app.post('/api/robot/align-sets', async (req, res) => { try { - const { setToMove } = req.body ?? {}; + const { setToMove, setFixed } = req.body ?? {}; if (!setToMove) return res.status(400).json({ error: '"setToMove" ist erforderlich.' }); let extraMarkers = []; @@ -778,7 +799,7 @@ app.post('/api/robot/align-sets', async (req, res) => { if (result.error) return res.status(400).json(result); console.log( - `robot/align-sets set="${setToMove}" → ${result.numChanged} Marker verschoben` + + `robot/align-sets fixed="${setFixed ?? '–'}" move="${setToMove}" → ${result.numChanged} Marker` + ` (${result.numMatchingPts} Messpunkte) Δx=${result.transform.tx} Δy=${result.transform.ty}` + ` Δz=${result.transform.tz} mm θ=${result.transform.thetaDeg}°`, );