board rotation - fix 0
This commit is contained in:
@@ -354,6 +354,27 @@ async function loadBoardTable() {
|
|||||||
|
|
||||||
// ── Board ─────────────────────────────────────────────────────────────────────
|
// ── 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: <select> mit Optionen füllen
|
||||||
|
function fill(selId, placeholder, selected = '') {
|
||||||
|
const sel = document.getElementById(selId);
|
||||||
|
if (!sel) return;
|
||||||
|
sel.innerHTML = `<option value="">${placeholder}</option>` +
|
||||||
|
sets.map(s => `<option value="${s}"${s === selected ? ' selected' : ''}>${s}</option>`).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
fill('board-ref-set', 'alle');
|
||||||
|
fill('act-align-fixed', '– bleibt –');
|
||||||
|
fill('act-align-move', '– verschoben –');
|
||||||
|
}
|
||||||
|
|
||||||
function initBoard() {
|
function initBoard() {
|
||||||
const logBoard = document.getElementById('log-board');
|
const logBoard = document.getElementById('log-board');
|
||||||
|
|
||||||
@@ -363,15 +384,23 @@ function initBoard() {
|
|||||||
logBoard.scrollTop = logBoard.scrollHeight;
|
logBoard.scrollTop = logBoard.scrollHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dropdowns mit Sets aus robot.json befüllen
|
||||||
|
populateBoardSetDropdowns();
|
||||||
|
|
||||||
// Tabelle beim ersten Öffnen des Tabs befüllen
|
// Tabelle beim ersten Öffnen des Tabs befüllen
|
||||||
loadBoardTable();
|
loadBoardTable();
|
||||||
|
|
||||||
document.getElementById('btn-board-run').addEventListener('click', async () => {
|
document.getElementById('btn-board-run').addEventListener('click', async () => {
|
||||||
logB('Board-Erkennung wird gestartet …');
|
const refSet = document.getElementById('board-ref-set')?.value ?? '';
|
||||||
|
logB(`Board-Erkennung wird gestartet … Referenz: ${refSet || 'alle'}`);
|
||||||
const btn = document.getElementById('btn-board-run');
|
const btn = document.getElementById('btn-board-run');
|
||||||
btn.disabled = true;
|
btn.disabled = true;
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/board/run', { method: 'POST' });
|
const response = await fetch('/api/board/run', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ refSet: refSet || undefined }),
|
||||||
|
});
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const raw = await response.text().catch(() => '');
|
const raw = await response.text().catch(() => '');
|
||||||
let msg;
|
let msg;
|
||||||
@@ -390,8 +419,9 @@ function initBoard() {
|
|||||||
if (frame?.contentWindow) {
|
if (frame?.contentWindow) {
|
||||||
frame.contentWindow.postMessage({ type: 'reload' }, '*');
|
frame.contentWindow.postMessage({ type: 'reload' }, '*');
|
||||||
}
|
}
|
||||||
// Marker-Tabelle aktualisieren
|
// Marker-Tabelle + Set-Dropdowns aktualisieren
|
||||||
loadBoardTable();
|
loadBoardTable();
|
||||||
|
populateBoardSetDropdowns();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logB(`❌ Beendet mit Exit-Code ${evt.exitCode}`);
|
logB(`❌ Beendet mit Exit-Code ${evt.exitCode}`);
|
||||||
@@ -484,11 +514,15 @@ function initBoard() {
|
|||||||
|
|
||||||
// ── Aktion 3: Sets justieren (Kabsch 2D+Z) ─────────────────────────────────
|
// ── Aktion 3: Sets justieren (Kabsch 2D+Z) ─────────────────────────────────
|
||||||
document.getElementById('btn-act-align').addEventListener('click', async () => {
|
document.getElementById('btn-act-align').addEventListener('click', async () => {
|
||||||
const setToMove = document.getElementById('act-align-set').value.trim();
|
const setFixed = document.getElementById('act-align-fixed').value;
|
||||||
|
const setToMove = document.getElementById('act-align-move').value;
|
||||||
const result = document.getElementById('act-result');
|
const result = document.getElementById('act-result');
|
||||||
|
|
||||||
if (!setToMove) {
|
if (!setToMove) {
|
||||||
result.innerHTML = '<span style="color:#f87171">⚠ Bitte Set-Name eingeben (z.B. "rail").</span>'; return;
|
result.innerHTML = '<span style="color:#f87171">⚠ Bitte Set auswählen, das verschoben werden soll.</span>'; return;
|
||||||
|
}
|
||||||
|
if (setFixed && setFixed === setToMove) {
|
||||||
|
result.innerHTML = '<span style="color:#f87171">⚠ "Bleibt" und "verschoben" dürfen nicht dasselbe Set sein.</span>'; return;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.innerHTML = '<span style="color:#555b6e">Justierung läuft …</span>';
|
result.innerHTML = '<span style="color:#555b6e">Justierung läuft …</span>';
|
||||||
@@ -496,7 +530,7 @@ function initBoard() {
|
|||||||
const r = await fetch('/api/robot/align-sets', {
|
const r = await fetch('/api/robot/align-sets', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ setToMove }),
|
body: JSON.stringify({ setFixed: setFixed || undefined, setToMove }),
|
||||||
});
|
});
|
||||||
const data = await r.json();
|
const data = await r.json();
|
||||||
if (!r.ok || data.error) {
|
if (!r.ok || data.error) {
|
||||||
@@ -505,7 +539,7 @@ function initBoard() {
|
|||||||
const t = data.transform;
|
const t = data.transform;
|
||||||
result.innerHTML =
|
result.innerHTML =
|
||||||
`<span style="color:#22c55e">✅ Set "${setToMove}": ${data.numChanged} Marker verschoben` +
|
`<span style="color:#22c55e">✅ Set "${setToMove}": ${data.numChanged} Marker verschoben` +
|
||||||
` (${data.numMatchingPts} Messpunkte)</span>` +
|
` (${data.numMatchingPts} Messpunkte)${setFixed ? ` – "${setFixed}" bleibt` : ''}</span>` +
|
||||||
`  Δx=${t.tx} mm Δy=${t.ty} mm Δz=${t.tz} mm` +
|
`  Δx=${t.tx} mm Δy=${t.ty} mm Δz=${t.tz} mm` +
|
||||||
` θ=${t.thetaDeg}°`;
|
` θ=${t.thetaDeg}°`;
|
||||||
loadBoardTable();
|
loadBoardTable();
|
||||||
|
|||||||
@@ -14,9 +14,15 @@
|
|||||||
<span class="info-label">Letzter Run</span>
|
<span class="info-label">Letzter Run</span>
|
||||||
<span class="info-value" id="board-last-run">–</span>
|
<span class="info-value" id="board-last-run">–</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls" style="margin-top: 16px;">
|
<div class="controls" style="margin-top: 16px; display:flex; align-items:center; gap:10px; flex-wrap:wrap;">
|
||||||
<button id="btn-board-run">Board erkennen</button>
|
<button id="btn-board-run">Board erkennen</button>
|
||||||
<button disabled title="Folgt später">Ergebnis anzeigen</button>
|
<label style="display:flex;align-items:center;gap:6px;font-size:12px;color:var(--muted)">
|
||||||
|
Referenz:
|
||||||
|
<select id="board-ref-set"
|
||||||
|
style="background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 8px;font:inherit;font-size:12px;cursor:pointer">
|
||||||
|
<option value="">alle</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -78,14 +84,21 @@
|
|||||||
Sets justieren (zu 3b-Messung)
|
Sets justieren (zu 3b-Messung)
|
||||||
</p>
|
</p>
|
||||||
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--text)">
|
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--text)">
|
||||||
Set verschieben
|
<select id="act-align-fixed"
|
||||||
<input id="act-align-set" type="text" placeholder="rail"
|
style="background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 8px;font:inherit;font-size:12px;cursor:pointer">
|
||||||
style="width:90px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
<option value="">– bleibt –</option>
|
||||||
|
</select>
|
||||||
|
bleibt / 
|
||||||
|
<select id="act-align-move"
|
||||||
|
style="background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 8px;font:inherit;font-size:12px;cursor:pointer">
|
||||||
|
<option value="">– verschoben –</option>
|
||||||
|
</select>
|
||||||
|
wird verschoben
|
||||||
<button id="btn-act-align"
|
<button id="btn-act-align"
|
||||||
style="background:#1e293b;color:#c8cdd8;border:1px solid #4a9eff;border-radius:3px;padding:4px 14px;cursor:pointer;font:inherit;font-size:12px">
|
style="background:#1e293b;color:#c8cdd8;border:1px solid #4a9eff;border-radius:3px;padding:4px 14px;cursor:pointer;font:inherit;font-size:12px">
|
||||||
Justieren
|
Justieren
|
||||||
</button>
|
</button>
|
||||||
<span style="color:var(--muted);font-size:10px">Rotation (Z-Achse) + Translation → passt Set zu 3b-Messung</span>
|
<span style="color:var(--muted);font-size:10px">Rotation (Z-Achse) + Translation → passt verschobenes Set zu 3b-Messung</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -212,13 +212,15 @@ def get_marker_rotation(marker: Dict[str, Any]) -> np.ndarray:
|
|||||||
return np.eye(3, dtype=np.float32)
|
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:
|
Supports the new format:
|
||||||
robot_data["links"]["Board"]["markers"]
|
robot_data["links"]["Board"]["markers"]
|
||||||
|
|
||||||
Fallback:
|
Fallback:
|
||||||
robot_data["Marker"]
|
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)
|
robot_json_path = resolve_path(robot_json_path)
|
||||||
with open(robot_json_path, "r", encoding="utf-8") as f:
|
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:
|
if marker_id < 0:
|
||||||
continue
|
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:
|
if "position" not in marker:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -583,13 +589,18 @@ def main() -> None:
|
|||||||
parser.add_argument("--maxRmsPx", type=float, default=None,
|
parser.add_argument("--maxRmsPx", type=float, default=None,
|
||||||
help="Optional soft warning threshold for final reprojection RMS in pixels")
|
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("--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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
detection_path = resolve_path(args.input)
|
detection_path = resolve_path(args.input)
|
||||||
robot_path = resolve_path(args.robot)
|
robot_path = resolve_path(args.robot)
|
||||||
|
|
||||||
detection = load_json(detection_path)
|
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)
|
K, D = load_intrinsics_from_detection(detection)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -491,6 +491,8 @@ app.post('/api/board/run', async (req, res) => {
|
|||||||
if (!res.writableEnded) res.write(`data: ${JSON.stringify(obj)}\n\n`);
|
if (!res.writableEnded) res.write(`data: ${JSON.stringify(obj)}\n\n`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const { refSet } = req.body ?? {};
|
||||||
|
|
||||||
// 1. Temp-Verzeichnis
|
// 1. Temp-Verzeichnis
|
||||||
const ts = makeTimestamp();
|
const ts = makeTimestamp();
|
||||||
const runDir = path.join(boardDataDir, ts);
|
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
|
// Robot-JSON laden und Marker-Anzahl loggen
|
||||||
let robotData = null;
|
let robotData = null;
|
||||||
try { robotData = JSON.parse(await fsPromises.readFile(ROBOT_JSON, 'utf8')); } catch {}
|
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: `▶ 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: '' });
|
send({ type: 'log', text: '' });
|
||||||
|
|
||||||
// 2. Kameras ermitteln
|
// 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' });
|
send({ type: 'log', text: '\n▷ 2_estimate_camera_from_observations' });
|
||||||
const exit2 = await runScript([
|
const script2Args = [SCRIPT_2, '-i', detJson, '-robot', ROBOT_JSON, '-outDir', runDir];
|
||||||
SCRIPT_2,
|
if (refSet) script2Args.push('--refSet', refSet);
|
||||||
'-i', detJson,
|
const exit2 = await runScript(script2Args, send);
|
||||||
'-robot', ROBOT_JSON,
|
|
||||||
'-outDir', runDir,
|
|
||||||
], send);
|
|
||||||
if (exit2 !== 0) {
|
if (exit2 !== 0) {
|
||||||
send({ type: 'log', text: `❌ Script 2 Exit ${exit2}` });
|
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
|
* POST /api/robot/align-sets
|
||||||
* Richtet alle Marker des angegebenen Sets rigid (2D-Rotation um Z + 3D-Translation)
|
* 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) => {
|
app.post('/api/robot/align-sets', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { setToMove } = req.body ?? {};
|
const { setToMove, setFixed } = req.body ?? {};
|
||||||
if (!setToMove) return res.status(400).json({ error: '"setToMove" ist erforderlich.' });
|
if (!setToMove) return res.status(400).json({ error: '"setToMove" ist erforderlich.' });
|
||||||
|
|
||||||
let extraMarkers = [];
|
let extraMarkers = [];
|
||||||
@@ -778,7 +799,7 @@ app.post('/api/robot/align-sets', async (req, res) => {
|
|||||||
if (result.error) return res.status(400).json(result);
|
if (result.error) return res.status(400).json(result);
|
||||||
|
|
||||||
console.log(
|
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}` +
|
` (${result.numMatchingPts} Messpunkte) Δx=${result.transform.tx} Δy=${result.transform.ty}` +
|
||||||
` Δz=${result.transform.tz} mm θ=${result.transform.thetaDeg}°`,
|
` Δz=${result.transform.tz} mm θ=${result.transform.thetaDeg}°`,
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user