Compare commits
2 Commits
45513cf714
...
aba07a5bca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aba07a5bca | ||
|
|
6b3053dd76 |
@@ -403,4 +403,77 @@ function initBoard() {
|
||||
btn.disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
// ── Aktion 1: Z-Bereich Zuordnung ───────────────────────────────────────────
|
||||
document.getElementById('btn-act-assign').addEventListener('click', async () => {
|
||||
const zMin = document.getElementById('act-z-min').value;
|
||||
const zMax = document.getElementById('act-z-max').value;
|
||||
const set = document.getElementById('act-set').value.trim();
|
||||
const link = document.getElementById('act-link').value.trim();
|
||||
const result = document.getElementById('act-result');
|
||||
|
||||
if (zMin === '' || zMax === '') {
|
||||
result.innerHTML = '<span style="color:#f87171">⚠ Bitte z-Bereich eingeben.</span>'; return;
|
||||
}
|
||||
if (!set && !link) {
|
||||
result.innerHTML = '<span style="color:#f87171">⚠ Bitte mindestens Set oder Link angeben.</span>'; return;
|
||||
}
|
||||
|
||||
result.innerHTML = '<span style="color:#555b6e">Zuordnung läuft …</span>';
|
||||
try {
|
||||
const r = await fetch('/api/robot/assign-by-z', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ zMin: parseFloat(zMin), zMax: parseFloat(zMax), set, link }),
|
||||
});
|
||||
const data = await r.json();
|
||||
if (!r.ok || data.error) {
|
||||
result.innerHTML = `<span style="color:#f87171">❌ ${data.error ?? `HTTP ${r.status}`}</span>`; return;
|
||||
}
|
||||
if (data.numChanged === 0) {
|
||||
result.innerHTML = '<span style="color:#555b6e">Keine Marker im Z-Bereich gefunden.</span>'; return;
|
||||
}
|
||||
const moved = data.changes.filter(c => c.oldLink !== c.newLink).length;
|
||||
result.innerHTML = `<span style="color:#22c55e">✅ ${data.numChanged} Marker geändert` +
|
||||
(moved ? `, ${moved} verschoben` : '') + `.</span>` +
|
||||
` IDs: ${data.changes.map(c => c.markerId).join(', ')}`;
|
||||
loadBoardTable();
|
||||
} catch (err) {
|
||||
result.innerHTML = `<span style="color:#f87171">❌ ${err}</span>`;
|
||||
}
|
||||
});
|
||||
|
||||
// ── Aktion 2: Zuordnung entfernen ───────────────────────────────────────────
|
||||
document.getElementById('btn-act-remove').addEventListener('click', async () => {
|
||||
const markerId = document.getElementById('act-rm-id').value.trim();
|
||||
const removeFrom = document.querySelector('input[name="act-rm-from"]:checked')?.value;
|
||||
const result = document.getElementById('act-result');
|
||||
|
||||
if (!markerId) {
|
||||
result.innerHTML = '<span style="color:#f87171">⚠ Bitte Marker-ID eingeben.</span>'; return;
|
||||
}
|
||||
|
||||
result.innerHTML = '<span style="color:#555b6e">Verarbeite …</span>';
|
||||
try {
|
||||
const r = await fetch('/api/robot/remove-marker', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ markerId: parseInt(markerId, 10), removeFrom }),
|
||||
});
|
||||
const data = await r.json();
|
||||
if (!r.ok) {
|
||||
result.innerHTML = `<span style="color:#f87171">❌ ${data.error ?? `HTTP ${r.status}`}</span>`; return;
|
||||
}
|
||||
if (!data.changed) {
|
||||
result.innerHTML = `<span style="color:#f87171">❌ ${data.error}</span>`; return;
|
||||
}
|
||||
const info = data.action === 'set-removed'
|
||||
? `Set "${data.oldSet}" entfernt (bleibt in Link "${data.link}")`
|
||||
: `Aus Link "${data.link}" entfernt`;
|
||||
result.innerHTML = `<span style="color:#22c55e">✅ Marker ${markerId}: ${info}</span>`;
|
||||
loadBoardTable();
|
||||
} catch (err) {
|
||||
result.innerHTML = `<span style="color:#f87171">❌ ${err}</span>`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,6 +20,62 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ── Aktionen ─────────────────────────────────────────────────────────── -->
|
||||
<div class="section full">
|
||||
<h2>Aktionen</h2>
|
||||
|
||||
<!-- Aktion 1: Z-Bereich Zuordnung -->
|
||||
<div style="margin-top:14px">
|
||||
<p style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">
|
||||
Z-Bereich Zuordnung
|
||||
</p>
|
||||
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--text)">
|
||||
z von
|
||||
<input id="act-z-min" type="number" step="0.5" placeholder="-30"
|
||||
style="width:72px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
||||
bis
|
||||
<input id="act-z-max" type="number" step="0.5" placeholder="-24"
|
||||
style="width:72px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
||||
mm  →  Set
|
||||
<input id="act-set" type="text" placeholder="A0"
|
||||
style="width:72px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
||||
Link
|
||||
<input id="act-link" type="text" placeholder="Board"
|
||||
style="width:90px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
||||
<button id="btn-act-assign"
|
||||
style="background:#1e293b;color:#c8cdd8;border:1px solid #4a9eff;border-radius:3px;padding:4px 14px;cursor:pointer;font:inherit;font-size:12px">
|
||||
Zuordnen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Aktion 2: Zuordnung entfernen -->
|
||||
<div style="margin-top:14px">
|
||||
<p style="font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:8px">
|
||||
Zuordnung entfernen
|
||||
</p>
|
||||
<div style="display:flex;flex-wrap:wrap;align-items:center;gap:8px;font-size:12px;color:var(--text)">
|
||||
ID
|
||||
<input id="act-rm-id" type="number" placeholder="z.B. 46"
|
||||
style="width:90px;background:#1e293b;border:1px solid #334155;color:#c8cdd8;border-radius:3px;padding:3px 7px;font:inherit;font-size:12px">
|
||||
aus
|
||||
<label style="cursor:pointer;display:flex;align-items:center;gap:4px">
|
||||
<input type="radio" name="act-rm-from" value="set" checked> Set
|
||||
</label>
|
||||
<label style="cursor:pointer;display:flex;align-items:center;gap:4px">
|
||||
<input type="radio" name="act-rm-from" value="link"> Link <span style="color:var(--muted);font-size:10px">(nur falls Set leer)</span>
|
||||
</label>
|
||||
<button id="btn-act-remove"
|
||||
style="background:#1e293b;color:#c8cdd8;border:1px solid #ef4444;border-radius:3px;padding:4px 14px;cursor:pointer;font:inherit;font-size:12px">
|
||||
Entfernen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ergebnis-Zeile -->
|
||||
<div id="act-result" style="margin-top:10px;font-size:11px;min-height:18px;color:var(--muted)"></div>
|
||||
</div>
|
||||
|
||||
<div class="section full">
|
||||
<h2>Ausgabe / Log</h2>
|
||||
<textarea id="log-board" readonly placeholder="(Ausgabe erscheint hier)"></textarea>
|
||||
|
||||
433
scripts/robot_1781069752019_v0.json
Normal file
433
scripts/robot_1781069752019_v0.json
Normal file
@@ -0,0 +1,433 @@
|
||||
{
|
||||
"coordinateSystem": {"handedness": "right", "x": "right", "y": "backward", "z": "up"},
|
||||
"units": {"length": "mm", "rotation": "degree"},
|
||||
"vision_config": {"MarkerType": "DICT_4X4_250", "MarkerSize": 0.025},
|
||||
"renderingInfo": {
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"renderDefaults": {"width": 1280, "height": 720, "dofFStop": 11},
|
||||
"cameraPosition__1": [-10, -800, 500],
|
||||
"cameraPosition__2": [-500, 300, 1200],
|
||||
"cameraPosition__3": [-200, -900, 200],
|
||||
"cameraPosition__4": [1200, 200, 300],
|
||||
"cameraPosition_a": [-300, -800, 500],
|
||||
"cameraPosition": [-200, 200, 1400],
|
||||
"cameraPosition_c": [600, -500, 600],
|
||||
"cameraTarget": [200, -200, 180],
|
||||
"cameraUpVector": [0, 0, 1],
|
||||
"lightPosition": [-500, -500, 500],
|
||||
"lightTarget": [0, 0, 0],
|
||||
"lightUpVector": [0, 0, 1],
|
||||
"metric": "mm",
|
||||
"showSkeleton": true,
|
||||
"showMarkers": true,
|
||||
"backgroundColor": [0.7, 0.85, 1.0],
|
||||
"backgroundStrength": 0.2,
|
||||
"sunEnergy": 0.35,
|
||||
"areaEnergy": 120,
|
||||
"exposure": -1.5,
|
||||
"lensDirt": true,
|
||||
"lensDirtStrength": 0.08,
|
||||
"dofEnabled": true,
|
||||
"dofFStop": 11.0,
|
||||
"arucoDust": true,
|
||||
"arucoDustStrength": 1.6,
|
||||
"markerOffsetMaxMm": 4.0,
|
||||
"markerOffsetSeed": 0,
|
||||
"markerRotationMaxDeg": 3,
|
||||
"motionBlur": true,
|
||||
"motionBlurMaxPx": 5.5,
|
||||
"focalErrorPct": 0.5,
|
||||
"principalErrorPx": 3.0,
|
||||
"residualDistortion": [0.02, -0.01],
|
||||
"localizedBlur": false,
|
||||
"localizedBlurStrength": 0.15,
|
||||
"vignette": true,
|
||||
"vignetteStrength": 0.08,
|
||||
"sensorNoise": true,
|
||||
"sensorNoiseStrength": 0.01,
|
||||
"lensDistortion": true,
|
||||
"lensDistortionStrength": 0.002,
|
||||
"materials": {
|
||||
"wood": {"baseColor": [0.72, 0.52, 0.33], "roughness": 0.85, "metallic": 0.0},
|
||||
"plaWhite": {"baseColor": [0.95, 0.95, 0.95], "roughness": 0.45, "metallic": 0.0},
|
||||
"steel": {"baseColor": [0.72, 0.72, 0.75], "roughness": 0.25, "metallic": 1.0},
|
||||
"powderCoatBlue": {"baseColor": [0.15, 0.25, 0.7], "roughness": 0.55, "metallic": 0.0},
|
||||
"defaultPlastic": {"baseColor": [0.95, 0.95, 0.95], "roughness": 0.4, "metallic": 0.0},
|
||||
"skeletonRed": {"baseColor": [0.85, 0.2, 0.2], "roughness": 0.35, "metallic": 0.0},
|
||||
"markerBlack": {"baseColor": [0.04, 0.04, 0.04], "roughness": 0.8, "metallic": 0.0}
|
||||
},
|
||||
"skeletonDefaults": {"radius": 4, "color": [0.85, 0.2, 0.2]},
|
||||
"markerDefaults": {"size": 25, "thickness": 1, "color": [0.04, 0.04, 0.04]},
|
||||
"defaultPosition": {"x": 80, "y": 20, "z": 80, "a": -120, "b": 23, "c": 9, "e": 3}
|
||||
},
|
||||
"defaultPosition__": {"x": 10, "y": 4, "z": 20, "a": 10, "b": 2, "c": 9, "e": 1},
|
||||
"defaultPosition": {"x": 50, "y": 4, "z": 176, "a": 20, "b": 60, "c": 9, "e": 5},
|
||||
"recognized": {"x": null, "y": null, "z": null, "a": null, "b": null, "c": null, "e": null},
|
||||
"constraint_rules": {
|
||||
"rigid_distance": {"enabled": true, "mode": "mst", "weight": 1.0},
|
||||
"joint_axis_projection": {"enabled": true, "max_pairs": 2, "weight": 0.35},
|
||||
"chain_axis_projection": {"enabled": false, "max_depth": 3, "max_pairs": 2, "weight": 0.15},
|
||||
"axis_alignment_threshold": 0.95
|
||||
},
|
||||
"observation_weighting": {"enabled": true, "distance_weight": true, "marker_size_weight": true, "view_angle_weight": true},
|
||||
"multiview_calculation": {
|
||||
"combine_mode": "mean",
|
||||
"size_ref_px": 50.0,
|
||||
"border_ref_px": 120.0,
|
||||
"center_ref_norm": 0.01,
|
||||
"sharpness_ref": 2500.0,
|
||||
"homography_ref": 0.18,
|
||||
"size_factor": 0.3,
|
||||
"aspect_factor": 0.3,
|
||||
"border_factor": 0.01,
|
||||
"center_factor": 0.01,
|
||||
"sharpness_factor": 0.5,
|
||||
"homography_factor": 0.2,
|
||||
"normal_visibility_factor": 0.01,
|
||||
"spin_factor": 0.3,
|
||||
"weight_floor": 0.3
|
||||
},
|
||||
"pose_estimation": {
|
||||
"method": "hybrid",
|
||||
"marker_observation": "corner_pose",
|
||||
"use_normals": true,
|
||||
"normal_weight": 100.0,
|
||||
"robust_loss": "huber",
|
||||
"huber_delta_mm": 8.0,
|
||||
"max_iterations": 200,
|
||||
"min_cameras_per_marker": 2,
|
||||
"finger_block_joints": ["b", "c", "e"],
|
||||
"per_link_method": {}
|
||||
},
|
||||
"robot_test_poses": {
|
||||
"4": {"x": 70, "y": 50, "z": -70, "a": 120, "b": 50, "c": 30, "e": 20},
|
||||
"5": {"x": 180, "y": 86, "z": -120, "a": -60, "b": 22, "c": 91, "e": 10},
|
||||
"6": {"x": 80, "y": 20, "z": 80, "a": -120, "b": 23, "c": 9, "e": 3},
|
||||
"7": {"x": 30, "y": -2, "z": 95, "a": 20, "b": 23, "c": 9, "e": 9},
|
||||
"8": {"x": 50, "y": -2, "z": 95, "a": 20, "b": 60, "c": 9, "e": 3},
|
||||
"9": {"x": 60, "y": -2, "z": 95, "a": 200, "b": 60, "c": 9, "e": 8},
|
||||
"9a": {
|
||||
"x": 60,
|
||||
"y": -2,
|
||||
"z": 95,
|
||||
"a": 200,
|
||||
"b": 60,
|
||||
"c": 9,
|
||||
"e": 8,
|
||||
"rendering": {"width": 1440, "height": 1080, "dofFStop": 11}
|
||||
},
|
||||
"9b": {
|
||||
"x": 60,
|
||||
"y": -2,
|
||||
"z": 95,
|
||||
"a": 200,
|
||||
"b": 60,
|
||||
"c": 9,
|
||||
"e": 8,
|
||||
"rendering": {"width": 4896, "height": 3264, "dofFStop": 5.6}
|
||||
},
|
||||
"10": {"x": 120, "y": 60, "z": -110, "a": 20, "b": 30, "c": 180, "e": 4},
|
||||
"11": {"x": 50, "y": 4, "z": 176, "a": 20, "b": 60, "c": 9, "e": 5},
|
||||
"12": {"x": 50, "y": 0, "z": 178, "a": 210, "b": 80, "c": 90, "e": 6}
|
||||
},
|
||||
"test_camera_positions": {
|
||||
"a": [-300, -800, 800],
|
||||
"b": [300, -900, 1200],
|
||||
"c": [300, -900, 400],
|
||||
"d": [700, -800, 400],
|
||||
"e": [1200, -900, 400],
|
||||
"f": [500, -300, 1400],
|
||||
"g": [-200, 200, 1400]
|
||||
},
|
||||
"test_camera_targets": {
|
||||
"a": [210, -100, 180],
|
||||
"b": [310, -80, 180],
|
||||
"c": [210, -100, 150],
|
||||
"d": [210, -100, 150],
|
||||
"e": [210, -100, 50],
|
||||
"f": [200, -200, 180],
|
||||
"g": [200, -200, 180]
|
||||
},
|
||||
"movements": {"x": null, "y": null, "z": null, "a": null, "b": null, "c": null, "e": null},
|
||||
"state_pose_params": {
|
||||
"numbers_of_Elements_to_consider_start": 3,
|
||||
"numbers_of_Elements_to_consider_final": 5,
|
||||
"solver_in_between_geometrical": false,
|
||||
"solver_after_geometrical": false,
|
||||
"geometric_passes_per_stage": 2,
|
||||
"revolute_search_coarse_deg": 5.0,
|
||||
"revolute_search_fine_deg": 1.0,
|
||||
"root_pose_min_markers": 3,
|
||||
"use_marker_normals_flip_tiebreak": true,
|
||||
"normal_flip_weight": 0.05
|
||||
},
|
||||
"links": {
|
||||
"Board": {
|
||||
"parent": null,
|
||||
"size": [1000, 200, 25],
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"skeleton": {"from": [0, 0, 16], "to": [1000, 0, 16], "radius": 4, "color": [0.85, 0.2, 0.2]},
|
||||
"markers": [
|
||||
{
|
||||
"id": 46,
|
||||
"set": "A0",
|
||||
"position": [536.71, 185.44, -27.3],
|
||||
"normal": [0, 0, 1],
|
||||
"spin": 90,
|
||||
"info": "is placed on a white paper, A0_60Arucos_25mm_Seet223.pdf, with the following marker placements:"
|
||||
},
|
||||
{"id": 47, "set": "A0", "position": [344.23, -286.54, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 48, "set": "A0", "position": [688.69, -320.72, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 49, "set": "A0", "position": [1006.0, 158.33, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 50, "set": "A0", "position": [573.41, 211.86, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 51, "set": "A0", "position": [167.8, -172.08, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 52, "set": "A0", "position": [94.68, 208.66, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 53, "set": "A0", "position": [486.25, 212.24, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 54, "set": "A0", "position": [342.27, -330.59, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 55, "set": "A0", "position": [283.72, -262.58, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 56, "set": "A0", "position": [498.68, 168.67, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 57, "set": "A0", "position": [602.86, -364.05, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 58, "set": "A0", "position": [50.09, -218.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 59, "set": "A0", "position": [626.21, -278.75, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 60, "set": "A0", "position": [434.36, 283.81, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 61, "set": "A0", "position": [-22.42, 335.83, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 62, "set": "A0", "position": [404.7, -175.1, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 63, "set": "A0", "position": [777.4, -236.15, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 64, "set": "A0", "position": [-21.27, -188.23, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 65, "set": "A0", "position": [803.39, -297.37, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 66, "set": "A0", "position": [209.75, -363.23, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 67, "set": "A0", "position": [523.07, 267.04, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 68, "set": "A0", "position": [573.73, 170.64, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 69, "set": "A0", "position": [7.61, -281.21, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 70, "set": "A0", "position": [601.87, 300.33, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 71, "set": "A0", "position": [749.75, -284.01, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 72, "set": "A0", "position": [440.99, 194.32, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 73, "set": "A0", "position": [221.73, 333.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 74, "set": "A0", "position": [93.78, 144.5, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 75, "set": "A0", "position": [-25.7, 194.58, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 76, "set": "A0", "position": [685.21, 166.8, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 77, "set": "A0", "position": [18.19, 191.57, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 78, "set": "A0", "position": [823.11, -344.38, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 79, "set": "A0", "position": [312.3, -159.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 80, "set": "A0", "position": [863.59, -335.92, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 81, "set": "A0", "position": [132.14, 169.03, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 82, "set": "A0", "position": [219.16, 297.24, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 83, "set": "A0", "position": [44.16, 339.22, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 84, "set": "A0", "position": [407.49, 258.42, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 85, "set": "A0", "position": [504.58, -312.75, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 86, "set": "A0", "position": [362.89, 292.01, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 87, "set": "A0", "position": [943.63, -245.76, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 88, "set": "A0", "position": [765.87, 316.04, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 89, "set": "A0", "position": [988.02, -369.14, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 90, "set": "A0", "position": [643.17, 316.43, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 91, "set": "A0", "position": [723.35, 328.05, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 92, "set": "A0", "position": [645.09, -184.84, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 93, "set": "A0", "position": [934.88, 143.6, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 94, "set": "A0", "position": [875.7, 173.65, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 95, "set": "A0", "position": [186.04, -274.07, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 96, "set": "A0", "position": [369.77, -186.49, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 97, "set": "A0", "position": [304.35, -359.67, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 98, "set": "A0", "position": [575.27, 315.06, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 99, "set": "A0", "position": [959.16, -321.55, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 100, "set": "A0", "position": [803.25, 172.36, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 101, "set": "A0", "position": [117.7, 298.66, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 102, "set": "A0", "position": [649.69, -223.0, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 103, "set": "A0", "position": [105.71, -187.71, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 104, "set": "A0", "position": [826.71, 239.16, -27.3], "normal": [0, 0, 1], "spin": 90},
|
||||
{"id": 105, "set": "A0", "position": [524.84, -266.25, -27.3], "normal": [0, 0, 1], "spin": 90}
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Board.stl",
|
||||
"originOfModel": [0, 0, 0],
|
||||
"rotationOfModelDegree": [0, 0, -90],
|
||||
"material": "wood"
|
||||
},
|
||||
{
|
||||
"stlFile": "surfaces/BoardRail.stl",
|
||||
"originOfModel": [0, 0, 0],
|
||||
"rotationOfModelDegree": [0, 0, -90],
|
||||
"material": "steel"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Base": {
|
||||
"parent": "Board",
|
||||
"size": [150, 200, 150],
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Slider",
|
||||
"type": "linear",
|
||||
"axis": [1, 0, 0],
|
||||
"origin": [0, 0, 16],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "x"
|
||||
},
|
||||
"skeleton": {"from": [0, 108, 45], "to": [110, 108, 45], "radius": 4, "color": [0.2, 0.8, 0.2]},
|
||||
"markers": [],
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Base.stl",
|
||||
"originOfModel": [-30, 0, -35],
|
||||
"rotationOfModelDegree": [0, 0, 0],
|
||||
"material": "plaWhite"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Arm1": {
|
||||
"parent": "Base",
|
||||
"size": [70, 250, 70],
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Joint1",
|
||||
"type": "revolute",
|
||||
"axis": [-1, 0, 0],
|
||||
"origin": [110, 108, 45],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "y"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [0, -250, 0], "radius": 4, "color": [0.2, 0.2, 0.9]},
|
||||
"markers": [ ],
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Holm.stl",
|
||||
"originOfModel__": [-25, 29, -28.5],
|
||||
"originOfModel": [-29, 25, 28.5],
|
||||
"rotationOfModelDegree__": [0, 0, 0],
|
||||
"rotationOfModelDegree": [180, 0, -90],
|
||||
"material": "powderCoatBlue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"Ellbow": {
|
||||
"parent": "Arm1",
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Joint2",
|
||||
"type": "revolute",
|
||||
"axis": [-1, 0, 0],
|
||||
"origin": [0, -250, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "z"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [90, 0, 0], "radius": 4, "color": [0.9, 0.2, 0.2]},
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Ellebogen.stl",
|
||||
"originOfModel": [90, 0, 0],
|
||||
"rotationOfModelDegree": [0, -90, -90],
|
||||
"material": "defaultPlastic"
|
||||
}
|
||||
],
|
||||
"markers": [
|
||||
]
|
||||
},
|
||||
"Arm2": {
|
||||
"parent": "Ellbow",
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Joint3",
|
||||
"type": "revolute",
|
||||
"axis": [0, -1, 0],
|
||||
"origin": [90, 0, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "a"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [0, -250, 0], "radius": 4, "color": [0.95, 0.85, 0.2]},
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Unterarm.stl",
|
||||
"originOfModel": [0, -250, 0],
|
||||
"rotationOfModelDegree": [180, 0, -90],
|
||||
"material": "defaultPlastic"
|
||||
}
|
||||
],
|
||||
"markers": [
|
||||
]
|
||||
},
|
||||
"Hand": {
|
||||
"parent": "Arm2",
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Joint4",
|
||||
"type": "revolute",
|
||||
"axis": [1, 0, 0],
|
||||
"origin": [0, -250, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "b"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [0, -35, 0], "radius": 4, "color": [0.95, 0.55, 0.15]}
|
||||
},
|
||||
"Palm": {
|
||||
"parent": "Hand",
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Joint3",
|
||||
"type": "revolute",
|
||||
"axis": [0, -1, 0],
|
||||
"origin": [0, 0, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "c"
|
||||
},
|
||||
"skeleton": {"from": [-50, -35, 0], "to": [50, -35, 0], "radius": 7, "color": [0.95, 0.2, 0.2]}
|
||||
},
|
||||
"FingerA": {
|
||||
"parent": "Palm",
|
||||
"size": [80, 60, 20],
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Slider",
|
||||
"type": "linear",
|
||||
"axis": [1, 0, 0],
|
||||
"origin": [4, -35, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "e"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [0, -60, 0], "radius": 4, "color": [0.2, 0.8, 0.2]},
|
||||
"markers": [
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Finger.stl",
|
||||
"originOfModel": [24, 0, -9.1],
|
||||
"rotationOfModelDegree": [90, -90, 0],
|
||||
"material": "defaultPlastic"
|
||||
}
|
||||
]
|
||||
},
|
||||
"FingerB": {
|
||||
"parent": "Palm",
|
||||
"size": [80, 60, 20],
|
||||
"mountPosition": [0, 0, 0],
|
||||
"mountRotation": [0, 0, 0],
|
||||
"jointToParent": {
|
||||
"name": "Slider",
|
||||
"type": "linear",
|
||||
"axis": [-1, 0, 0],
|
||||
"origin": [-4, -35, 0],
|
||||
"rotation": [0, 0, 0],
|
||||
"variable": "e"
|
||||
},
|
||||
"skeleton": {"from": [0, 0, 0], "to": [0, -60, 0], "radius": 4, "color": [0.2, 0.8, 0.2]},
|
||||
"markers": [
|
||||
],
|
||||
"model": [
|
||||
{
|
||||
"stlFile": "surfaces/Finger.stl",
|
||||
"originOfModel": [-24, 0, 9.1],
|
||||
"rotationOfModelDegree": [90, 90, 0],
|
||||
"material": "defaultPlastic"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
144
server/editRobot.js
Normal file
144
server/editRobot.js
Normal file
@@ -0,0 +1,144 @@
|
||||
/**
|
||||
* editRobot.js
|
||||
* Hilfsfunktionen zum Bearbeiten von robot_xxx.json.
|
||||
* Alle Schreibvorgänge machen ein Backup-Kommentar in der Datei (nein, aber
|
||||
* atomisches Write per Temp-Datei ist hier nicht nötig – die Datei wird direkt
|
||||
* überschrieben; bei Bedarf Backup-Strategie ergänzen).
|
||||
*/
|
||||
import fsPromises from 'fs/promises';
|
||||
|
||||
// ── I/O ───────────────────────────────────────────────────────────────────────
|
||||
|
||||
async function readRobot(robotPath) {
|
||||
return JSON.parse(await fsPromises.readFile(robotPath, 'utf8'));
|
||||
}
|
||||
|
||||
async function writeRobot(robotPath, data) {
|
||||
await fsPromises.writeFile(robotPath, JSON.stringify(data, null, 2), 'utf8');
|
||||
}
|
||||
|
||||
// ── Aktion 1: Marker nach Z-Bereich zuordnen ─────────────────────────────────
|
||||
|
||||
/**
|
||||
* Weist allen Markern, deren z-Position (mm) zwischen zMin und zMax liegt,
|
||||
* das angegebene Set und/oder den angegebenen Link zu.
|
||||
*
|
||||
* - set (optional): Setzt den set-Wert des Markers.
|
||||
* - link (optional): Verschiebt den Marker in diesen Link (wird ggf. angelegt).
|
||||
*
|
||||
* Gibt { numChanged, changes[] } zurück.
|
||||
* changes[]: { markerId, oldLink, newLink, oldSet, newSet }
|
||||
*/
|
||||
export async function assignByZRange(robotPath, { zMin, zMax, set, link }) {
|
||||
const robot = await readRobot(robotPath);
|
||||
const links = robot.links ?? {};
|
||||
const changes = [];
|
||||
|
||||
// Snapshot aller Marker (mit aktuellem Link-Name) – vor der Mutation
|
||||
const snapshot = [];
|
||||
for (const [linkName, linkData] of Object.entries(links)) {
|
||||
for (const marker of (linkData.markers ?? [])) {
|
||||
if (Array.isArray(marker.position)) {
|
||||
snapshot.push({ id: marker.id, currentLink: linkName });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Nur Marker im Z-Fenster bearbeiten
|
||||
const zLo = Number(zMin);
|
||||
const zHi = Number(zMax);
|
||||
|
||||
for (const { id, currentLink } of snapshot) {
|
||||
const srcLinkData = links[currentLink];
|
||||
const idx = (srcLinkData?.markers ?? []).findIndex(m => Number(m.id) === id);
|
||||
if (idx === -1) continue;
|
||||
|
||||
const marker = srcLinkData.markers[idx];
|
||||
const z = Number(marker.position[2]);
|
||||
if (z < zLo || z > zHi) continue;
|
||||
|
||||
const change = {
|
||||
markerId: marker.id,
|
||||
oldLink: currentLink,
|
||||
oldSet: marker.set ?? '',
|
||||
newLink: currentLink,
|
||||
newSet: marker.set ?? '',
|
||||
};
|
||||
|
||||
// Set setzen
|
||||
if (set !== undefined && set !== '') {
|
||||
marker.set = set;
|
||||
change.newSet = set;
|
||||
}
|
||||
|
||||
// Link wechseln
|
||||
if (link && link !== currentLink) {
|
||||
// Aus altem Link entfernen
|
||||
srcLinkData.markers.splice(idx, 1);
|
||||
|
||||
// Ziel-Link anlegen falls noch nicht vorhanden
|
||||
if (!links[link]) links[link] = { markers: [] };
|
||||
if (!links[link].markers) links[link].markers = [];
|
||||
links[link].markers.push(marker);
|
||||
change.newLink = link;
|
||||
}
|
||||
|
||||
changes.push(change);
|
||||
}
|
||||
|
||||
if (changes.length > 0) {
|
||||
robot.links = links;
|
||||
await writeRobot(robotPath, robot);
|
||||
}
|
||||
|
||||
return { numChanged: changes.length, changes };
|
||||
}
|
||||
|
||||
// ── Aktion 2: Set oder Link-Zuordnung entfernen ───────────────────────────────
|
||||
|
||||
/**
|
||||
* Entfernt die Set- oder Link-Zuordnung eines Markers.
|
||||
*
|
||||
* removeFrom: 'set' → löscht nur den set-Wert (Marker bleibt im Link)
|
||||
* removeFrom: 'link' → entfernt Marker komplett aus dem Link
|
||||
* (nur wenn set bereits leer/nicht gesetzt)
|
||||
*
|
||||
* Gibt { changed, markerId, action, link, [oldSet], [error] } zurück.
|
||||
*/
|
||||
export async function removeMarkerAssignment(robotPath, { markerId, removeFrom }) {
|
||||
const id = Number(markerId);
|
||||
const robot = await readRobot(robotPath);
|
||||
const links = robot.links ?? {};
|
||||
|
||||
for (const [linkName, linkData] of Object.entries(links)) {
|
||||
const markers = linkData.markers ?? [];
|
||||
const idx = markers.findIndex(m => Number(m.id) === id);
|
||||
if (idx === -1) continue;
|
||||
|
||||
const marker = markers[idx];
|
||||
|
||||
if (removeFrom === 'set') {
|
||||
const oldSet = marker.set ?? '';
|
||||
delete marker.set;
|
||||
await writeRobot(robotPath, robot);
|
||||
return { changed: true, markerId: id, action: 'set-removed', link: linkName, oldSet };
|
||||
}
|
||||
|
||||
if (removeFrom === 'link') {
|
||||
if (marker.set) {
|
||||
return {
|
||||
changed: false,
|
||||
markerId: id,
|
||||
error: `Marker ${id} hat noch Set "${marker.set}". Bitte zuerst Set entfernen.`,
|
||||
};
|
||||
}
|
||||
markers.splice(idx, 1);
|
||||
await writeRobot(robotPath, robot);
|
||||
return { changed: true, markerId: id, action: 'link-removed', link: linkName };
|
||||
}
|
||||
|
||||
return { changed: false, error: `Unbekannte Aktion: "${removeFrom}"` };
|
||||
}
|
||||
|
||||
return { changed: false, error: `Marker-ID ${id} nicht gefunden.` };
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'url';
|
||||
import process from 'process';
|
||||
import { spawn } from 'child_process';
|
||||
import { WebcamClient } from './webcamClient.js';
|
||||
import { assignByZRange, removeMarkerAssignment } from './editRobot.js';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
@@ -689,6 +690,54 @@ app.get('/api/board/latest', async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── Robot-JSON bearbeiten ─────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* POST /api/robot/assign-by-z
|
||||
* Weist allen Markern in [zMin, zMax] mm das angegebene Set und/oder Link zu.
|
||||
* Body: { zMin, zMax, set?, link? }
|
||||
*/
|
||||
app.post('/api/robot/assign-by-z', async (req, res) => {
|
||||
try {
|
||||
const { zMin, zMax, set, link } = req.body ?? {};
|
||||
if (zMin == null || zMax == null) {
|
||||
return res.status(400).json({ error: 'zMin und zMax sind erforderlich' });
|
||||
}
|
||||
if (!set && !link) {
|
||||
return res.status(400).json({ error: 'Mindestens set oder link muss angegeben werden' });
|
||||
}
|
||||
const result = await assignByZRange(ROBOT_JSON, { zMin, zMax, set, link });
|
||||
console.log(`robot/assign-by-z z=[${zMin}..${zMax}] set="${set}" link="${link}" → ${result.numChanged} geändert`);
|
||||
return res.json(result);
|
||||
} catch (err) {
|
||||
console.error('robot/assign-by-z error:', err);
|
||||
return res.status(500).json({ error: String(err) });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/robot/remove-marker
|
||||
* Entfernt Set oder Link-Zuordnung eines Markers.
|
||||
* Body: { markerId, removeFrom } removeFrom: 'set' | 'link'
|
||||
*/
|
||||
app.post('/api/robot/remove-marker', async (req, res) => {
|
||||
try {
|
||||
const { markerId, removeFrom } = req.body ?? {};
|
||||
if (markerId == null) {
|
||||
return res.status(400).json({ error: 'markerId ist erforderlich' });
|
||||
}
|
||||
if (!['set', 'link'].includes(removeFrom)) {
|
||||
return res.status(400).json({ error: 'removeFrom muss "set" oder "link" sein' });
|
||||
}
|
||||
const result = await removeMarkerAssignment(ROBOT_JSON, { markerId, removeFrom });
|
||||
console.log(`robot/remove-marker id=${markerId} from=${removeFrom} → changed=${result.changed}`);
|
||||
return res.json(result);
|
||||
} catch (err) {
|
||||
console.error('robot/remove-marker 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