Compare commits
2 Commits
2e622de801
...
cce53cda54
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cce53cda54 | ||
|
|
275ab083fa |
File diff suppressed because it is too large
Load Diff
@@ -1,595 +0,0 @@
|
|||||||
{
|
|
||||||
"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,
|
|
||||||
"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":[300, -500,1450],
|
|
||||||
"cameraPosition_c":[600, -500,600],
|
|
||||||
"cameraTarget": [210, -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.70, 0.85, 1.0],
|
|
||||||
"backgroundStrength": 0.20,
|
|
||||||
"sunEnergy": 0.35,
|
|
||||||
"areaEnergy": 120,
|
|
||||||
"exposure": -1.5,
|
|
||||||
"lensDirt": true,
|
|
||||||
"lensDirtStrength": 0.08,
|
|
||||||
"dofEnabled": true,
|
|
||||||
"dofFStop": 11,
|
|
||||||
"arucoDust": false,
|
|
||||||
"arucoDustStrength": 0.00005,
|
|
||||||
"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.20, 0.20],
|
|
||||||
"roughness": 0.35,
|
|
||||||
"metallic": 0.0
|
|
||||||
},
|
|
||||||
"markerBlack": {
|
|
||||||
"baseColor": [0.04, 0.04, 0.04],
|
|
||||||
"roughness": 0.80,
|
|
||||||
"metallic": 0.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"skeletonDefaults": {
|
|
||||||
"radius": 4,
|
|
||||||
"color": [0.85, 0.20, 0.20]
|
|
||||||
},
|
|
||||||
"markerDefaults": {
|
|
||||||
"size": 25,
|
|
||||||
"thickness": 1,
|
|
||||||
"color": [0.04, 0.04, 0.04]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"defaultPosition__": {
|
|
||||||
"x": 10,
|
|
||||||
"y": 4,
|
|
||||||
"z": 20,
|
|
||||||
"a": 10,
|
|
||||||
"b": 2,
|
|
||||||
"c": 9,
|
|
||||||
"e": 1
|
|
||||||
},
|
|
||||||
"defaultPosition": {
|
|
||||||
"x": 180,
|
|
||||||
"y": 86,
|
|
||||||
"z": -120,
|
|
||||||
"a": -60,
|
|
||||||
"b": 22,
|
|
||||||
"c": 91,
|
|
||||||
"e": 10
|
|
||||||
},
|
|
||||||
"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
|
|
||||||
},
|
|
||||||
"robot_test_poses": {
|
|
||||||
"sim04": {"x": 70, "y": 50,"z": -70,"a": 120,"b": 50,"c": 30,"e": 20},
|
|
||||||
"sim05": {"x": 180,"y": 86,"z": -120,"a": -60,"b": 22,"c": 91,"e": 10},
|
|
||||||
"sim06": {"x": 80, "y": 20, "z": 80, "a": -120, "b": 23, "c": 9, "e": 3},
|
|
||||||
"sim07": {"x": 30, "y": -2, "z": 95, "a": 20, "b": 23, "c": 9, "e": 9},
|
|
||||||
"sim08": {"x": 50, "y": -2, "z": 95, "a": 20, "b": 60, "c": 9, "e": 3},
|
|
||||||
"sim09": {"x": 60, "y": -2, "z": 95, "a": 200, "b": 60, "c": 9, "e": 8},
|
|
||||||
"sim09a": {"x": 60, "y": -2, "z": 95, "a": 200, "b": 60, "c": 9, "e": 8},
|
|
||||||
"sim09b": {"x": 60, "y": -2, "z": 95, "a": 200, "b": 60, "c": 9, "e": 8},
|
|
||||||
"sim010": {"x": 120, "y": 60, "z": -110, "a": 20, "b": 30, "c": 180, "e": 4},
|
|
||||||
"sim011": {"x": 50, "y": 4, "z": 176, "a": 20, "b": 60, "c": 9, "e": 5},
|
|
||||||
"sim012": {"x": 50, "y": 0, "z": 178, "a": 210, "b": 80, "c": 90, "e": 6}
|
|
||||||
},
|
|
||||||
"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": 4,
|
|
||||||
"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.20, 0.20]
|
|
||||||
},
|
|
||||||
"markers":[
|
|
||||||
{"id":210,"position":[20, -20, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":211,"position":[250, -10, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":215,"position":[250, -90, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":214,"position":[350, -10, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":208,"position":[350, -90, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":206,"position":[650, -10, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":205,"position":[750, -90, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":207,"position":[750, -10, 0.3], "normal":[0,0,1]},
|
|
||||||
{"id":217,"position":[650, -90, 0.3], "normal":[0,0,1]},
|
|
||||||
|
|
||||||
{"id": 46, "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, "position": [344.23, -286.54, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 48, "position": [688.69, -320.72, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 49, "position": [1006.0, 158.33, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 50, "position": [573.41, 211.86, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 51, "position": [167.8, -172.08, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 52, "position": [94.68, 208.66, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 53, "position": [486.25, 212.24, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 54, "position": [342.27, -330.59, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 55, "position": [283.72, -262.58, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 56, "position": [498.68, 168.67, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 57, "position": [602.86, -364.05, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 58, "position": [50.09, -218.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 59, "position": [626.21, -278.75, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 60, "position": [434.36, 283.81, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 61, "position": [-22.42, 335.83, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 62, "position": [404.7, -175.1, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 63, "position": [777.4, -236.15, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 64, "position": [-21.27, -188.23, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 65, "position": [803.39, -297.37, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 66, "position": [209.75, -363.23, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 67, "position": [523.07, 267.04, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 68, "position": [573.73, 170.64, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 69, "position": [7.61, -281.21, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 70, "position": [601.87, 300.33, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 71, "position": [749.75, -284.01, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 72, "position": [440.99, 194.32, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 73, "position": [221.73, 333.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 74, "position": [93.78, 144.5, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 75, "position": [-25.7, 194.58, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 76, "position": [685.21, 166.8, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 77, "position": [18.19, 191.57, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 78, "position": [823.11, -344.38, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 79, "position": [312.3, -159.11, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 80, "position": [863.59, -335.92, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 81, "position": [132.14, 169.03, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 82, "position": [219.16, 297.24, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 83, "position": [44.16, 339.22, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 84, "position": [407.49, 258.42, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 85, "position": [504.58, -312.75, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 86, "position": [362.89, 292.01, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 87, "position": [943.63, -245.76, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 88, "position": [765.87, 316.04, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 89, "position": [988.02, -369.14, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 90, "position": [643.17, 316.43, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 91, "position": [723.35, 328.05, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 92, "position": [645.09, -184.84, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 93, "position": [934.88, 143.6, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 94, "position": [875.7, 173.65, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 95, "position": [186.04, -274.07, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 96, "position": [369.77, -186.49, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 97, "position": [304.35, -359.67, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 98, "position": [575.27, 315.06, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 99, "position": [959.16, -321.55, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 100, "position": [803.25, 172.36, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 101, "position": [117.7, 298.66, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 102, "position": [649.69, -223.0, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 103, "position": [105.71, -187.71, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 104, "position": [826.71, 239.16, -27.3], "normal": [0, 0, 1], "spin": 90},
|
|
||||||
{"id": 105, "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.20, 0.80, 0.20]
|
|
||||||
},
|
|
||||||
"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.20, 0.20, 0.90]
|
|
||||||
},
|
|
||||||
"markers": [
|
|
||||||
{
|
|
||||||
"id": 198,
|
|
||||||
"name": "aruco_198",
|
|
||||||
"position": [0, -160, 35],
|
|
||||||
"normal": [0, 0, 1],
|
|
||||||
"size": 25,
|
|
||||||
"spin": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 229,
|
|
||||||
"name": "aruco_229",
|
|
||||||
"position": [0, -250, 35],
|
|
||||||
"normal": [0, 0, 1],
|
|
||||||
"size": 25,
|
|
||||||
"spin": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 242,
|
|
||||||
"name": "aruco_242",
|
|
||||||
"position": [0, -250, -35],
|
|
||||||
"normal": [0, 0, -1],
|
|
||||||
"size": 25,
|
|
||||||
"spin": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 243,
|
|
||||||
"name": "aruco_243",
|
|
||||||
"position": [0, -285, 0],
|
|
||||||
"normal": [0, -1, 0],
|
|
||||||
"size": 25,
|
|
||||||
"spin": 0
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"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.90, 0.20, 0.20]
|
|
||||||
},
|
|
||||||
"model": [
|
|
||||||
{
|
|
||||||
"stlFile": "surfaces/Ellebogen.stl",
|
|
||||||
"originOfModel": [90,0,0],
|
|
||||||
"rotationOfModelDegree": [0,-90,-90],
|
|
||||||
"material": "defaultPlastic"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"markers": [
|
|
||||||
{"id": 244, "name": "aruco_244", "position": [125, 0, 0], "normal": [1, 0, 0], "size": 25, "spin": 0},
|
|
||||||
{"id": 245, "name": "aruco_245", "position": [90, 0, -35], "normal": [0, 0, -1], "size": 25, "spin": 0},
|
|
||||||
{"id": 246, "name": "aruco_246", "position": [90, 0, 35], "normal": [0, 0, 1], "size": 25},
|
|
||||||
{"id": 247, "name": "aruco_247", "position": [52.5, 0, 35], "normal": [0, 0, 1], "size": 25},
|
|
||||||
{"id": 248, "name": "aruco_248", "position": [52.5, 0, -35], "normal": [0, 0, -1], "size": 25},
|
|
||||||
{"id": 232, "name": "aruco_232", "position": [90, 24.75, -24.75], "normal": [0, 1, -1], "size": 25},
|
|
||||||
{"id": 231, "name": "aruco_231", "position": [90, 24.75, 24.75], "normal": [0, 1, 1], "size": 25}
|
|
||||||
]
|
|
||||||
|
|
||||||
},
|
|
||||||
"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.20]
|
|
||||||
},
|
|
||||||
"model": [
|
|
||||||
{
|
|
||||||
"stlFile": "surfaces/Unterarm.stl",
|
|
||||||
"originOfModel": [0,-250,0],
|
|
||||||
"rotationOfModelDegree": [180, 0, -90],
|
|
||||||
"material": "defaultPlastic"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"markers":[
|
|
||||||
|
|
||||||
{"id":120, "position":[24.75, -112, -24.75],"normal":[1,0,-1]},
|
|
||||||
{"id":122, "name": "aruco_122", "position":[-35,-112,0], "normal":[-1,0,0]},
|
|
||||||
{"id":218, "name": "aruco_218", "position":[35,-112,0], "normal":[1,0,0]},
|
|
||||||
{"id":113, "name": "aruco_113", "position":[0, -182, 30],"normal":[0,0,1]},
|
|
||||||
{"id":114, "name": "aruco_114", "position":[ 24.75, -182, -24.75],"normal":[ 1,0,-1]},
|
|
||||||
{"id":115, "name": "aruco_115", "position":[-24.75, -182, -24.75],"normal":[-1,0,-1]},
|
|
||||||
{"id":124, "name": "aruco_124", "position":[-35,-219,0], "normal":[-1,0,0]},
|
|
||||||
{"id":219, "name": "aruco_219", "position":[35,-219,0], "normal":[1,0,0]}
|
|
||||||
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"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.20, 0.20]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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.20, 0.80, 0.20]
|
|
||||||
},
|
|
||||||
"markers":[
|
|
||||||
{"id":40,"position":[12,-24,-17.1],"normal":[-10.98,0,-23.56]},
|
|
||||||
{"id":41,"position":[1.5,-2.2,25.8],"normal":[0,-25.6,9.5]},
|
|
||||||
{"id":42,"position":[13.9,-40,0],"normal":[1,-0.35,0.40], "spin": 27}
|
|
||||||
],
|
|
||||||
"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.20, 0.80, 0.20]
|
|
||||||
},
|
|
||||||
"markers":[
|
|
||||||
{"id":43,"position":[-12,-24,17.1],"normal":[10.98,0,23.56], "spin":90 },
|
|
||||||
{"id":44,"position":[-1.5,-2.2,-25.8],"normal":[0,-25.6,-9.5], "spin":90},
|
|
||||||
{"id":45,"position":[-13.9,-40,0],"normal":[-1,-0.35,-0.40], "spin": -27}
|
|
||||||
],
|
|
||||||
"model": [
|
|
||||||
{
|
|
||||||
"stlFile": "surfaces/Finger.stl",
|
|
||||||
"originOfModel": [-24,0,9.1],
|
|
||||||
"rotationOfModelDegree": [90, 90,0],
|
|
||||||
"material": "defaultPlastic"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 MiB |
@@ -8,374 +8,349 @@
|
|||||||
|
|
||||||
## Was ist Homing?
|
## Was ist Homing?
|
||||||
|
|
||||||
Homing = der Roboter weiss **nicht**, wo er ist.
|
Homing = der Roboter weiss **nicht**, wo er ist.
|
||||||
Die Kameras schauen auf das Board + die ArUco-Marker am Roboter und berechnen
|
Die Kameras schauen auf das Board + die ArUco-Marker am Roboter und berechnen
|
||||||
daraus die vollständige Pose aller Gelenke — ohne mechanische Endschalter.
|
daraus die vollständige Pose aller Gelenke — ohne mechanische Endschalter.
|
||||||
|
|
||||||
Homing läuft bei **jedem** Einschalten ab, also muss es schnell und robust sein.
|
Homing läuft bei **jedem** Einschalten ab: schnell, robust, vollautomatisch.
|
||||||
Kalibrierung hingegen läuft nur nach mechanischen Änderungen (≈ einmalig).
|
Kalibrierung hingegen läuft nur nach mechanischen Änderungen (≈ einmalig).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Voraussetzungen (Kalibrierung muss abgeschlossen sein)
|
## Kinematik-Kette (aus `robot.json → links`)
|
||||||
|
|
||||||
| Was | Wo gespeichert | Status |
|
```
|
||||||
|-----|----------------|--------|
|
Board (ROOT, fest) ← Referenz aller Kameras
|
||||||
| Kamera-Intrinsik (NPZ) | `data/calibration/camX_calibration.npz` | ✅ |
|
│
|
||||||
| Board-Marker-Positionen | `robot.json → links.Board.markers[]` | ✅ |
|
├── Base linear x axis=[1,0,0] ← Slider-Position
|
||||||
| X-Achsen-Richtung | `robot.json → links.*.position` (rotiert) | ✅ |
|
├── Arm1 revolute y axis=[-1,0,0] ← Schultergelenk
|
||||||
| **Arm1-Gelenkursprung Y/Z** | `robot.json → links.Arm1.jointToParent.origin[1,2]` | 🔶 in Arbeit |
|
├── Ellbow revolute z axis=[-1,0,0] ← Ellbogen
|
||||||
| Arm-Marker-Zuordnung | `robot.json → links.Arm1/Ellbow/Arm2/Hand.markers[]` | ❌ offen |
|
├── Arm2 revolute a axis=[0,-1,0] ← Unterarm-Drehung
|
||||||
|
├── Hand revolute b axis=[1,0,0] ← Handgelenk
|
||||||
|
├── Palm revolute c axis=[0,-1,0] ← Handfläche
|
||||||
|
└── FingerA/B linear e axis=±[1,0,0] ← Greifer (symmetrisch)
|
||||||
|
```
|
||||||
|
|
||||||
> **Schlüssel-Erkenntnis:** Sobald `Arm1.jointToParent.origin` korrekt gesetzt ist (aus
|
**Resultat-State:** `{ x_mm, y_deg, z_deg, a_deg, b_deg, c_deg, e_mm }`
|
||||||
> der Y-Achsen-Kalibrierung), ist die gesamte Kinematikkette in `robot.json` geometrisch
|
|
||||||
> definiert. Dann kann Homing starten.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Kinematik-Kette (aus `robot.json`)
|
## Voraussetzungen (Kalibrierung)
|
||||||
|
|
||||||
```
|
| Was | Mechanismus | Status |
|
||||||
Board (ROOT, fest)
|
|-----|-------------|--------|
|
||||||
│
|
| Kamera-Intrinsik (NPZ) | `calibration.html` → Tab Camera NPZ | ✅ fertig |
|
||||||
├── Base linear variable=x axis=[1,0,0] origin=[0,0,16]
|
| Board-Marker-Positionen | `calibration.html` → Tab Board | ✅ fertig |
|
||||||
│ → Slider-Position entlang Board-X
|
| X-Achsen-Richtung | `calibration.html` → Tab Robot X-Axis | ✅ fertig |
|
||||||
│
|
| **Arm1 Joint-Origin Y/Z** | `calibration.html` → Tab Arm1 → Button „Joint-Origin Y/Z übernehmen" | ✅ **Button vorhanden, ausführbar** |
|
||||||
├── Arm1 revolute variable=y axis=[-1,0,0] origin=[110, 108*, 45*]
|
| Arm-Marker in robot.json | Manuell eintragen (links.Arm1/Ellbow/Arm2/Hand.markers) | 🔶 Nutzer trägt ein |
|
||||||
│ → Schultergelenk (heben/senken) *aus Y-Achsen-Kalib.
|
|
||||||
│
|
|
||||||
├── Ellbow revolute variable=z axis=[-1,0,0] origin=[0,-250,0]
|
|
||||||
│ → Ellbogen (relativ zu Arm1-Ende)
|
|
||||||
│
|
|
||||||
├── Arm2 revolute variable=a axis=[0,-1,0] origin=[90,0,0]
|
|
||||||
│ → Unterarm-Drehung
|
|
||||||
│
|
|
||||||
├── Hand revolute variable=b axis=[1,0,0] origin=[0,-250,0]
|
|
||||||
│ → Handgelenk
|
|
||||||
│
|
|
||||||
├── Palm revolute variable=c axis=[0,-1,0] origin=[0,0,0]
|
|
||||||
│ → Handflächen-Rotation
|
|
||||||
│
|
|
||||||
└── FingerA/B linear variable=e axis=±[1,0,0] origin=[±4,-35,0]
|
|
||||||
→ Greifer (symmetrisch)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Gelenkvariablen:** `x` (mm), `y z a b c` (Grad), `e` (mm)
|
> **Kalibrierung gilt als abgeschlossen** sobald der Arm1-Button geklickt und
|
||||||
|
> die Arm-Marker eingetragen sind.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Homing-Ablauf (Schritt für Schritt)
|
## robot.json – Ladestrategie
|
||||||
|
|
||||||
### Schritt 1 — Snapshot aufnehmen
|
### Aktuell: lokale Datei
|
||||||
|
```
|
||||||
**Was:** Alle Kameras gleichzeitig ein Foto.
|
ROBOT_JSON = process.env.ROBOT_JSON || 'scripts/robot_1781069752019.json'
|
||||||
|
|
||||||
**Script:** — (direkt via Webcam-API)
|
|
||||||
|
|
||||||
**UI-Aktion:** Button `[Foto aufnehmen]`
|
|
||||||
|
|
||||||
**Ausgabe:**
|
|
||||||
- `cam0.jpg`, `cam1.jpg`, `cam2.jpg` im aktuellen Run-Verzeichnis
|
|
||||||
- **Snapshots-Sektion:** Kamerabilder erscheinen
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Schritt 2 — ArUco-Marker erkennen
|
|
||||||
|
|
||||||
**Was:** Pixel-Koordinaten aller sichtbaren Marker in jedem Kamerabild.
|
|
||||||
|
|
||||||
**Script:** `1_detect_aruco_observations.py`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python 1_detect_aruco_observations.py \
|
|
||||||
-i cam0.jpg cam1.jpg cam2.jpg \
|
|
||||||
-robot robot.json \
|
|
||||||
-outDir data/homing/TIMESTAMP/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output-Dateien:**
|
### Geplant: vom Driver per API
|
||||||
- `cam0_aruco_detection.json` — Marker-IDs + Pixel-Ecken + Kamera-Pose-Kandidaten
|
Der Driver-Service (ROBOT_URL) kennt die aktuelle Roboter-Konfiguration.
|
||||||
|
Lademechanismus (bereits implementiertes Muster aus `ROBOT_URL`/`BODYTRACKER_URL`):
|
||||||
|
|
||||||
**Snapshot CSV:** Tabelle: MarkerID | Kamera | px-Koordinaten | confidence
|
```
|
||||||
|
GET ROBOT_URL/api/robot/config → robot.json Inhalt
|
||||||
**Fehlerfälle:**
|
|
||||||
- Marker verdeckt → weniger Marker in CSV sichtbar
|
|
||||||
- Schlechte Beleuchtung → confidence niedrig
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Schritt 3 — Kamera-Posen schätzen
|
|
||||||
|
|
||||||
**Was:** Aus den Board-Markern (fix im Raum) die extrinsische Lage jeder Kamera
|
|
||||||
berechnen.
|
|
||||||
|
|
||||||
**Script:** `2_estimate_camera_from_observations.py`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python 2_estimate_camera_from_observations.py \
|
|
||||||
-i data/homing/TIMESTAMP/ \
|
|
||||||
-robot robot.json \
|
|
||||||
-outDir data/homing/TIMESTAMP/
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output-Dateien:**
|
**Implementierung (Backend `server.js`):**
|
||||||
- `cam0_camera_pose.json` — 4×4 Transformationsmatrix Kamera→Welt
|
```javascript
|
||||||
|
async function loadRobotConfig() {
|
||||||
**Analysis & Reasoning:** Reprojektions-Fehler pro Kamera (sollte < 3 px)
|
if (ROBOT_URL) {
|
||||||
|
// Vom Driver holen
|
||||||
**Fehlerfälle:**
|
const res = await fetch(new URL('/api/robot/config', ROBOT_URL));
|
||||||
- Zu wenig Board-Marker sichtbar (< 3) → Kamera-Pose nicht berechenbar
|
return res.json();
|
||||||
- Hoher Reprojektions-Fehler → Kalibrierung möglicherweise veraltet
|
}
|
||||||
|
// Fallback: lokale Datei
|
||||||
---
|
return JSON.parse(await fs.readFile(ROBOT_JSON, 'utf8'));
|
||||||
|
|
||||||
### Schritt 3b — 3D-Marker-Positionen triangulieren
|
|
||||||
|
|
||||||
**Was:** Aus den Kamera-Posen und den Pixel-Koordinaten die echten 3D-Positionen
|
|
||||||
aller Marker berechnen (inkl. Arm-Marker).
|
|
||||||
|
|
||||||
**Script:** `3b_corner_marker_poses.py`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python 3b_corner_marker_poses.py \
|
|
||||||
-i data/homing/TIMESTAMP/ \
|
|
||||||
-robot robot.json \
|
|
||||||
--outDir data/homing/TIMESTAMP/
|
|
||||||
```
|
|
||||||
|
|
||||||
**Output-Dateien:**
|
|
||||||
- `aruco_marker_poses.json` — für jeden Marker: `position_mm`, `normal`, `corners_m`, `link`
|
|
||||||
|
|
||||||
**Snapshot CSV:** Vollständige Marker-Tabelle mit triangulierten 3D-Positionen
|
|
||||||
|
|
||||||
**Fehlerfälle:**
|
|
||||||
- Marker nur in 1 Kamera → keine Triangulation möglich (braucht ≥ 2)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Schritt 4 — Gelenkwinkel bestimmen
|
|
||||||
|
|
||||||
Zwei Methoden — **4b** ist sequenziell und robuster:
|
|
||||||
|
|
||||||
#### Methode A — Vollständige State-Schätzung (schnell)
|
|
||||||
|
|
||||||
**Script:** `4_robotState_estimation_v6.py`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python 4_robotState_estimation_v6.py \
|
|
||||||
aruco_marker_poses.json \
|
|
||||||
--robot robot.json \
|
|
||||||
--output homing_state.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Schätzt alle Gelenke in einem Durchlauf mit geometrischen Gleichungen
|
|
||||||
(Kabsch-Fit, Projektions-Residuen, 2D-Winkel).
|
|
||||||
|
|
||||||
#### Methode B — Sequenziell Gelenk für Gelenk (robuster)
|
|
||||||
|
|
||||||
**Script:** `4b_revolute_angle.py` — einmal pro Gelenk, von root nach tip:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Slider-Position (x) aus Board-Markern bekannt → manuell oder aus 4a
|
|
||||||
python 4b_revolute_angle.py --robot robot.json --aruco aruco_marker_poses.json \
|
|
||||||
--link Arm1 --x-mm 180 --output state_arm1.json
|
|
||||||
|
|
||||||
python 4b_revolute_angle.py --robot robot.json --aruco aruco_marker_poses.json \
|
|
||||||
--link Ellbow --from-state state_arm1.json --output state_ellbow.json
|
|
||||||
|
|
||||||
python 4b_revolute_angle.py --robot robot.json --aruco aruco_marker_poses.json \
|
|
||||||
--link Arm2 --from-state state_ellbow.json --output state_arm2.json
|
|
||||||
|
|
||||||
python 4b_revolute_angle.py --robot robot.json --aruco aruco_marker_poses.json \
|
|
||||||
--link Hand --from-state state_arm2.json --output state_hand.json
|
|
||||||
```
|
|
||||||
|
|
||||||
Jedes `--from-state` enthält den akkumulierten Gelenk-State aus allen
|
|
||||||
vorherigen Schritten.
|
|
||||||
|
|
||||||
**Warum sequenziell?** Arm2-Winkel kann nur korrekt berechnet werden wenn
|
|
||||||
Arm1 und Ellbow bereits bekannt sind (der Weltachsenvektor des Arm2-Joints
|
|
||||||
ändert sich mit den vorherigen Gelenkwinkeln).
|
|
||||||
|
|
||||||
**Output-JSON-Struktur (pro Schritt):**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"link": "Arm1",
|
|
||||||
"joint": "y",
|
|
||||||
"mean_angle_deg": 23.4,
|
|
||||||
"circular_std_deg": 0.8,
|
|
||||||
"num_pairs": 6,
|
|
||||||
"joint_origin_world_mm": [290, 108, 45],
|
|
||||||
"joint_axis_world": [-1, 0, 0],
|
|
||||||
"accumulated_state": { "x": 180, "y": 23.4, "z": null, "a": null, ... }
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Analysis & Reasoning:** Gelenk-für-Gelenk-Ergebnis als JSON-Baum
|
**Konsequenz für Homing:** Das Homing-Script bekommt robot.json als
|
||||||
|
temporäre Datei (bereits vorhandenes Muster: `ROBOT_JSON` als Pfad an Python).
|
||||||
|
Falls ROBOT_URL konfiguriert: zuerst fetch → temp-Datei schreiben → Script aufrufen.
|
||||||
|
|
||||||
**Fehlerfälle:**
|
**Priorität:** Kann nach dem restlichen Homing implementiert werden.
|
||||||
- `circular_std_deg` > 5° → Marker-Konfiguration fragwürdig, Messung wiederholen
|
Solange ROBOT_URL nicht konfiguriert, läuft alles mit der lokalen Datei.
|
||||||
- `num_pairs` < 2 → Arm-Marker nicht genug sichtbar
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Schritt 5 (optional) — Kamera-Z verfeinern
|
## X-Position (Slider) – Bestimmung
|
||||||
|
|
||||||
**Was:** Kamera-Höhe (Z) ist aus Board-Markern schlecht bestimmt (Board ist flach).
|
Die Slider-Position `x` wird **nicht** manuell eingegeben, sondern aus den
|
||||||
Wenn Ellbow-Winkel bekannt → FK-Vorhersage als Z-Referenz nutzen.
|
triangulierten Marker-Positionen berechnet (nach Schritt 3b).
|
||||||
|
|
||||||
**Script:** `5_camera_z_refine.py`
|
**Ansatz:** Die absolute X-Position eines bekannten Arm-Markers im
|
||||||
|
Board-Koordinatensystem enthält direkt die Slider-Information —
|
||||||
|
alle anderen Gelenke sind rotatorisch und verschieben den Marker nicht
|
||||||
|
entlang der X-Achse des Boards.
|
||||||
|
|
||||||
```bash
|
Alternativ: Schwerpunkt der Board-nahen A0-Marker projiziert auf die X-Achse
|
||||||
python 5_camera_z_refine.py \
|
(robust, braucht keine Arm-Marker).
|
||||||
--angle state_ellbow.json \
|
|
||||||
--robot robot.json \
|
**In `4b_revolute_angle.py`:** `--x-mm` wird aus `aruco_marker_poses.json`
|
||||||
--aruco aruco_marker_poses.json \
|
berechnet und als erstes Argument übergeben. Alle weiteren 4b-Aufrufe
|
||||||
-pose cam0_camera_pose.json \
|
nutzen `--from-state` des vorherigen Schritts.
|
||||||
-pose cam1_camera_pose.json \
|
|
||||||
--outDir data/homing/TIMESTAMP/
|
---
|
||||||
|
|
||||||
|
## Homing-Ablauf (Script-Kette)
|
||||||
|
|
||||||
|
```
|
||||||
|
[Foto] → [1_detect] → [2_camera] → [3b_poses]
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
[4b Arm1]
|
||||||
|
[4b Ellbow]
|
||||||
|
[4b Arm2]
|
||||||
|
[4b Hand]
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
State-JSON
|
||||||
|
{x,y,z,a,b,c,e}
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
POST ROBOT_URL/api/state
|
||||||
```
|
```
|
||||||
|
|
||||||
**Output:** Korrigierte `*_camera_pose_v8.json` + `z_correction.json`
|
**Strategie:** `4b_revolute_angle.py` sequenziell, Link für Link von root nach tip.
|
||||||
|
X-Position (`--x-mm`) wird aus den triangulierten Board-Marker-Positionen bestimmt.
|
||||||
**Wann nötig:** Wenn Residuen in Schritt 4 systematisch in Z-Richtung driften.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Schritt 6 — Ergebnis senden
|
## Implementierungsplan: Homing-UI
|
||||||
|
|
||||||
**Was:** Vollständigen Joint-State an Roboter-Controller übermitteln.
|
### ⚠ Wichtig: Schritte 1–3b existieren bereits
|
||||||
|
|
||||||
**UI-Aktion:** Button `[An Roboter senden]`
|
Die Kette **Foto → 1_detect → 2_camera → 3b_poses** ist bereits vollständig
|
||||||
|
implementiert und produktiv in `POST /api/board/run` (`server/server.js`,
|
||||||
|
ca. Zeile 520–606). Sie erzeugt `<runDir>/aruco_marker_poses.json`.
|
||||||
|
|
||||||
**Body:** `{ x, y, z, a, b, c, e }`
|
**Die echten, funktionierenden Aufrufe (NICHT neu erfinden):**
|
||||||
→ `POST ROBOT_URL/api/state` (oder Motion-Controller-Protokoll)
|
|
||||||
|
|
||||||
**Result – Raw JSON:**
|
```javascript
|
||||||
```json
|
// Pro Kamera geloopt (jede Kamera hat eigene NPZ):
|
||||||
{
|
runScript([SCRIPT_1, '-i', imgPath, '-npz', npzPath, '-robot', ROBOT_JSON,
|
||||||
"status": "ok",
|
'-cameraId', camId, '-outDir', runDir, '--saveDebugImage']);
|
||||||
"timestamp": "2026-06-13T19:00:00",
|
|
||||||
"state": { "x": 180.0, "y": 23.4, "z": -12.1, "a": 5.0, "b": 0.0, "c": 0.0, "e": 0.0 },
|
runScript([SCRIPT_2, '-i', detJson, '-robot', ROBOT_JSON, '-outDir', runDir]);
|
||||||
"confidence": { "y_std_deg": 0.8, "z_std_deg": 1.2 }
|
|
||||||
|
// Einmal, nach allen Kameras (braucht ≥2 _camera_pose.json):
|
||||||
|
runScript([SCRIPT_3B, '--evalDir', runDir, '--robot', ROBOT_JSON]);
|
||||||
|
// → schreibt <runDir>/aruco_marker_poses.json
|
||||||
|
```
|
||||||
|
|
||||||
|
**Empfehlung:** Die Snapshot+1+2+3b-Logik aus `/api/board/run` in eine
|
||||||
|
gemeinsame Funktion `runBoardPipeline(runDir, send)` auslagern. Homing ruft
|
||||||
|
sie auf und hängt nur den 4b-Teil an. So gibt es keine Duplikate und keine
|
||||||
|
abweichenden Argumente.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 1 — Backend-Route `POST /api/homing/run`
|
||||||
|
|
||||||
|
**Datei:** `server/server.js` (neue Route) + `server/homingOrchestrator.js` (neue Datei)
|
||||||
|
|
||||||
|
**Ablauf als SSE-Stream:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// server/homingOrchestrator.js
|
||||||
|
export async function runHoming({ robotJsonPath, send }) {
|
||||||
|
|
||||||
|
// 1–3b: bestehende Board-Pipeline wiederverwenden
|
||||||
|
// (Foto + 1_detect + 2_camera + 3b_poses, pro Kamera geloopt)
|
||||||
|
send({ type: 'step', step: 1, text: 'Snapshot + Marker-Triangulation …' });
|
||||||
|
const runDir = await runBoardPipeline(robotJsonPath, send); // aus server.js ausgelagert
|
||||||
|
const arucoJson = path.join(runDir, 'aruco_marker_poses.json');
|
||||||
|
|
||||||
|
// 4. X-Position aus triangulierten Markern bestimmen
|
||||||
|
const xMm = estimateXFromMarkers(arucoJson); // siehe Abschnitt „X-Position"
|
||||||
|
|
||||||
|
// 4b. Gelenkwinkel sequenziell, Link für Link
|
||||||
|
const links = ['Arm1', 'Ellbow', 'Arm2', 'Hand'];
|
||||||
|
let fromState = null;
|
||||||
|
for (const link of links) {
|
||||||
|
send({ type: 'step', text: `Gelenkwinkel ${link} …` });
|
||||||
|
const args = [SCRIPT_4B,
|
||||||
|
'--robot', robotJsonPath, '--aruco', arucoJson,
|
||||||
|
'--link', link,
|
||||||
|
'--output', path.join(runDir, `state_${link}.json`),
|
||||||
|
];
|
||||||
|
if (fromState) args.push('--from-state', fromState);
|
||||||
|
else args.push('--x-mm', String(xMm));
|
||||||
|
const exit = await runScript(args, send);
|
||||||
|
if (exit !== 0) { send({ type: 'error', text: `4b ${link} Exit ${exit}` }); return; }
|
||||||
|
fromState = path.join(runDir, `state_${link}.json`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ergebnis: letztes state_*.json enthält den vollständigen accumulated_state
|
||||||
|
const finalState = JSON.parse(fs.readFileSync(fromState, 'utf8'));
|
||||||
|
send({ type: 'done', state: finalState.accumulated_state, runDir });
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Result – Tree View:** Gelenk-Werte als lesbare Tabelle
|
> **Hinweis:** `runScript()` gibt den Exit-Code zurück (nicht den Pfad).
|
||||||
|
> Der State-Pfad wird separat gemerkt (`fromState`).
|
||||||
|
|
||||||
---
|
**Route:**
|
||||||
|
```javascript
|
||||||
|
app.post('/api/homing/run', async (req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
|
const send = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||||
|
try {
|
||||||
|
await runHoming({ robotJsonPath: ROBOT_JSON, send });
|
||||||
|
} catch (err) {
|
||||||
|
send({ type: 'error', text: String(err) });
|
||||||
|
}
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
## UI-Seitenstruktur (analog index.html)
|
app.post('/api/homing/send-state', async (req, res) => {
|
||||||
|
// Sendet { x, y, z, a, b, c, e } an ROBOT_URL/api/state
|
||||||
```
|
});
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ AKTIONEN │
|
|
||||||
│ [Foto aufnehmen] [Homing berechnen] │
|
|
||||||
│ [An Roboter senden] │
|
|
||||||
├─────────────────────────────────────────┤
|
|
||||||
│ AUSGABE / LOG │
|
|
||||||
│ Schritt-für-Schritt Log aller Scripts │
|
|
||||||
├─────────────────────────────────────────┤
|
|
||||||
│ ANALYSIS & REASONING │
|
|
||||||
│ Kamera-Posen, Reprojektionsfehler, │
|
|
||||||
│ Gelenk-Schätzungen je Schritt als JSON │
|
|
||||||
├──────────────────┬──────────────────────┤
|
|
||||||
│ RESULT – RAW JSON │ RESULT – TREE │
|
|
||||||
│ Vollst. State-JSON │ Gelenk-Tabelle │
|
|
||||||
├─────────────────────────────────────────┤
|
|
||||||
│ SNAPSHOT CSV │
|
|
||||||
│ Tabelle aller Marker: ID, Position, │
|
|
||||||
│ Link, Residual, num_cameras │
|
|
||||||
├─────────────────────────────────────────┤
|
|
||||||
│ SNAPSHOTS (Bilder) │
|
|
||||||
│ Annotierte Kamerabilder mit Markern │
|
|
||||||
└─────────────────────────────────────────┘
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Offene Punkte (Voraussetzungen für Homing)
|
### Phase 2 — Frontend `public/homing.html`
|
||||||
|
|
||||||
### 🔶 A — Arm-Marker in robot.json eintragen
|
Neue Seite (zugänglich von `index.html` via Link-Button, wie `calibration.html`).
|
||||||
|
|
||||||
Die Arm-Links haben noch **keine Marker** in `robot.json` (links.Arm1/Ellbow/Arm2/Hand.markers).
|
**Sektionen (identisches Muster wie `index.html`):**
|
||||||
|
|
||||||
Mögliche Quellen:
|
```
|
||||||
- Aus der Y-Achsen-Kalibrierung: Marker 197, 218, 219 wurden als rotierend erkannt
|
┌──────────────────────────────────────────────────────┐
|
||||||
→ diese können mit relativen Positionen in den jeweiligen Links eingetragen werden
|
│ AKTIONEN │
|
||||||
- Mit `4b_revolute_angle.py` lässt sich pro Link prüfen, welche Marker
|
│ [📷 Foto & Homing berechnen] │
|
||||||
"zu diesem Link gehören" (paarweise Baseline-Methode)
|
│ [✅ An Roboter senden] (disabled bis Ergebnis) │
|
||||||
|
│ Status-Badge: ○ Warte ● Läuft ✓ Fertig ✗ Fehler │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ AUSGABE / LOG │
|
||||||
|
│ Schritt-für-Schritt Log aller Scripts (SSE-Stream) │
|
||||||
|
│ Fortschritt: ──── Schritt 3/6 ──── │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ ANALYSIS & REASONING │
|
||||||
|
│ Zwischenergebnisse je Script als JSON │
|
||||||
|
│ { camera_reprojection_px, arm1_std_deg, … } │
|
||||||
|
├──────────────────┬───────────────────────────────────┤
|
||||||
|
│ RESULT RAW JSON │ RESULT TREE VIEW │
|
||||||
|
│ { │ x (Slider): 180.0 mm │
|
||||||
|
│ "x": 180.0, │ y (Arm1): +23.4° │
|
||||||
|
│ "y": 23.4, │ z (Ellbow): -12.1° │
|
||||||
|
│ ... │ a (Arm2): +5.0° │
|
||||||
|
│ } │ b (Hand): 0.0° │
|
||||||
|
│ │ c (Palm): 0.0° │
|
||||||
|
│ │ e (Greifer): 0.0 mm │
|
||||||
|
├──────────────────┴───────────────────────────────────┤
|
||||||
|
│ SNAPSHOT CSV (Marker-Tabelle) │
|
||||||
|
│ ID │ Link │ x mm │ y mm │ z mm │ Residual │ │
|
||||||
|
│ 218 │ Arm2 │ 229.1 │ 118.5 │ 48.3 │ 2.1 mm │ │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ SNAPSHOTS (annotierte Kamerabilder) │
|
||||||
|
│ [cam0] [cam1] [cam2] │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
**Aktion:** Für jedes Arm-Segment mind. 2 Marker bestimmen und in robot.json eintragen.
|
**Schlüssel-Implementierungsdetails:**
|
||||||
|
|
||||||
### 🔶 B — Arm1.jointToParent.origin[Y, Z] finalisieren
|
```javascript
|
||||||
|
// homing.js (client)
|
||||||
|
|
||||||
Aus der Arm1-Y-Achsen-Kalibrierung (calibration_arm.html):
|
// SSE-Stream vom Backend empfangen
|
||||||
- Berechneter Wert: Y ≈ 115.6 mm, Z ≈ 50.3 mm
|
async function runHoming() {
|
||||||
- Aktueller Wert in robot.json: origin = [110, 108, 45]
|
const response = await fetch('/api/homing/run', { method: 'POST' });
|
||||||
- **Aktion:** Button „Joint-Origin Y/Z übernehmen" klicken → schreibt in robot.json
|
await readSseStream(response, appendLog, (evt) => {
|
||||||
|
if (evt.type === 'step') { updateProgress(evt); }
|
||||||
|
if (evt.type === 'analysis') { showAnalysis(evt.data); }
|
||||||
|
if (evt.type === 'done') {
|
||||||
|
showResult(evt.state);
|
||||||
|
enableSendButton(evt.state);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
### 🔶 C — X-Position (Slider) im Homing
|
// Ergebnis an Roboter senden
|
||||||
|
async function sendToRobot(state) {
|
||||||
Die Slider-Position `x` ist für `4b_revolute_angle.py --x-mm` nötig.
|
await fetch('/api/homing/send-state', {
|
||||||
Optionen:
|
method: 'POST',
|
||||||
1. Mechanischer Anschlag / Encoder → immer bekannt
|
body: JSON.stringify({ state }),
|
||||||
2. Aus Board-Markern schätzen (funktioniert wenn A0-Marker sichtbar)
|
});
|
||||||
3. Manuell eingeben (Fallback)
|
}
|
||||||
|
```
|
||||||
### 🔶 D — Homing-Seite implementieren
|
|
||||||
|
|
||||||
Eine neue `homing.html`-Seite (oder Tab in `calibration.html`) die:
|
|
||||||
- Die Script-Kette 1 → 2 → 3b → 4b(je Link) → (5) als SSE-Stream orchestriert
|
|
||||||
- Alle Ausgabe-Sektionen wie in index.html befüllt
|
|
||||||
- „An Roboter senden" Button mit finalem State-JSON verdrahtet
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Script-Abhängigkeitsgraph
|
### Phase 3 — robot.json via Driver-API
|
||||||
|
|
||||||
|
**Voraussetzung:** ROBOT_URL ist konfiguriert und der Driver hat `GET /api/robot/config`.
|
||||||
|
|
||||||
|
**Implementierung in `server.js`:**
|
||||||
|
```javascript
|
||||||
|
// Beim Start oder on-demand: robot.json vom Driver laden
|
||||||
|
async function fetchRobotConfig() {
|
||||||
|
if (!ROBOT_URL) return; // lokale Datei reicht
|
||||||
|
const res = await fetch(new URL('/api/robot/config', ROBOT_URL));
|
||||||
|
if (!res.ok) return; // Fallback auf lokale Datei
|
||||||
|
const data = await res.json();
|
||||||
|
// Temporär in data/robot/robot_live.json cachen
|
||||||
|
await fs.writeFile(ROBOT_JSON_LIVE, JSON.stringify(data, null, 2));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Auswirkung:** Nur `ROBOT_JSON` Variable ändern — alle Scripts bekommen
|
||||||
|
automatisch die aktuelle Konfiguration.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reihenfolge der Implementierung
|
||||||
|
|
||||||
```
|
```
|
||||||
Kamera-Snapshot
|
[Jetzt] Arm-Marker eintragen (Nutzer)
|
||||||
│
|
→ Arm1 Joint-Origin Button klicken (bereits ausführbar)
|
||||||
▼
|
|
||||||
[1] detect_aruco → cam*_aruco_detection.json
|
[0] Refactor: Snapshot+1+2+3b aus /api/board/run
|
||||||
│
|
in runBoardPipeline(runDir, send) auslagern
|
||||||
▼
|
→ wird von Board-Run UND Homing genutzt
|
||||||
[2] estimate_camera → cam*_camera_pose.json
|
|
||||||
│ (aus Board-Markern)
|
[1] POST /api/homing/run + homingOrchestrator.js
|
||||||
▼
|
→ runBoardPipeline() + 4b-Schleife als SSE
|
||||||
[3b] corner_marker_poses → aruco_marker_poses.json
|
→ SCRIPT_4B-Konstante in server.js ergänzen
|
||||||
│ (alle Marker trianguliert)
|
→ Minimale UI: nur Log + Raw JSON
|
||||||
├──────────────────────────────────────┐
|
|
||||||
▼ ▼
|
[2] public/homing.html
|
||||||
[4b] revolute Arm1 [4] state_v6 (alternativ)
|
→ Vollständige UI mit allen Sektionen
|
||||||
│
|
→ Link von index.html
|
||||||
[4b] revolute Ellbow ──► [5] camera_z_refine (optional, nutzt Ellbow)
|
|
||||||
│
|
[3] POST /api/homing/send-state
|
||||||
[4b] revolute Arm2
|
→ ROBOT_URL/api/state aufrufen (analog zu robotActions.js)
|
||||||
│
|
|
||||||
[4b] revolute Hand
|
[4] robot.json via Driver-API (wenn ROBOT_URL verfügbar)
|
||||||
│
|
→ Nur wenn Driver den Endpunkt implementiert
|
||||||
▼
|
|
||||||
State-JSON {x, y, z, a, b, c, e}
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
→ Roboter-Controller
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Status-Tabelle
|
## Status-Tabelle
|
||||||
|
|
||||||
| Schritt | Script | Status | Blockiert durch |
|
| Schritt | Was | Status |
|
||||||
|---------|--------|--------|-----------------|
|
|---------|-----|--------|
|
||||||
| 1 Snapshot | Webcam-API | ✅ vorhanden | — |
|
| Scripts 1, 2, 3b, 4b | Homing-Scripts | ✅ vorhanden |
|
||||||
| 2 Marker erkennen | `1_detect_aruco.py` | ✅ vorhanden | — |
|
| Kalibrierung | Kamera, Board, X-Achse | ✅ fertig |
|
||||||
| 3 Kamera-Pose | `2_estimate_camera.py` | ✅ vorhanden | — |
|
| Arm1 Joint-Origin | Button in calibration_arm.html | ✅ **ausführbar** |
|
||||||
| 3b Triangulation | `3b_corner_marker_poses.py` | ✅ vorhanden | — |
|
| Arm-Marker | robot.json links.Arm1/… .markers | 🔶 Nutzer trägt ein |
|
||||||
| 4 State-Schätzung | `4_robotState_estimation_v6.py` | ✅ vorhanden | Arm-Marker fehlen |
|
| `/api/homing/run` | Backend-Orchestrierung | ❌ zu implementieren |
|
||||||
| 4b Sequenziell | `4b_revolute_angle.py` | ✅ vorhanden | Arm-Marker fehlen |
|
| `homing.html` | Frontend-UI | ❌ zu implementieren |
|
||||||
| 5 Z-Verfeinern | `5_camera_z_refine.py` | ✅ vorhanden | Ellbow-Marker fehlen |
|
| `/api/homing/send-state` | State an Roboter | ❌ zu implementieren |
|
||||||
| **Arm1-Origin** | calibration_arm.html | 🔶 bereit | Joint-Origin Y/Z übernehmen |
|
| robot.json via API | Driver-Integration | ⏳ nach allem anderen |
|
||||||
| **Arm-Marker** | robot.json | ❌ fehlen | manuelle Zuordnung |
|
|
||||||
| **Homing-UI** | homing.html | ❌ fehlt | erst nach Markern sinnvoll |
|
|
||||||
|
|||||||
Reference in New Issue
Block a user