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 0000000..3e51d55 Binary files /dev/null and b/scripts/__pycache__/2_estimate_camera_from_observations.cpython-311.pyc differ 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}°`, );