Roadmap planen
This commit is contained in:
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 |
@@ -12,370 +12,318 @@ 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.
|
### Phase 1 — Backend-Route `POST /api/homing/run`
|
||||||
|
|
||||||
**UI-Aktion:** Button `[An Roboter senden]`
|
**Datei:** `server/server.js` (neue Route) + `server/homingOrchestrator.js` (neue Datei)
|
||||||
|
|
||||||
**Body:** `{ x, y, z, a, b, c, e }`
|
**Ablauf als SSE-Stream:**
|
||||||
→ `POST ROBOT_URL/api/state` (oder Motion-Controller-Protokoll)
|
|
||||||
|
|
||||||
**Result – Raw JSON:**
|
```javascript
|
||||||
```json
|
// server/homingOrchestrator.js
|
||||||
{
|
export async function runHoming({ robotJsonPath, boardDataDir, send, options }) {
|
||||||
"status": "ok",
|
|
||||||
"timestamp": "2026-06-13T19:00:00",
|
// 1. Snapshot (Webcam-API)
|
||||||
"state": { "x": 180.0, "y": 23.4, "z": -12.1, "a": 5.0, "b": 0.0, "c": 0.0, "e": 0.0 },
|
send({ type: 'step', step: 1, text: 'Foto aufnehmen …' });
|
||||||
"confidence": { "y_std_deg": 0.8, "z_std_deg": 1.2 }
|
const runDir = await takeSnapshot(); // WEBCAM_URL
|
||||||
|
|
||||||
|
// 2. Marker erkennen
|
||||||
|
send({ type: 'step', step: 2, text: 'Marker erkennen …' });
|
||||||
|
await runScript(['1_detect_aruco_observations.py',
|
||||||
|
'-i', ...camImages, '-robot', robotJsonPath, '-outDir', runDir], send);
|
||||||
|
|
||||||
|
// 3. Kamera-Posen
|
||||||
|
send({ type: 'step', step: 3, text: 'Kamera-Posen schätzen …' });
|
||||||
|
await runScript(['2_estimate_camera_from_observations.py',
|
||||||
|
'-i', runDir, '-robot', robotJsonPath, '-outDir', runDir], send);
|
||||||
|
|
||||||
|
// 3b. 3D-Triangulation
|
||||||
|
send({ type: 'step', step: 4, text: '3D-Positionen triangulieren …' });
|
||||||
|
await runScript(['3b_corner_marker_poses.py',
|
||||||
|
'-i', runDir, '-robot', robotJsonPath, '--outDir', runDir], send);
|
||||||
|
|
||||||
|
// 4. X-Position aus triangulierten Board-Markern bestimmen
|
||||||
|
const xMm = estimateXFromMarkers(arucoJson); // aus position_mm der A0-Marker
|
||||||
|
|
||||||
|
// 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 = ['4b_revolute_angle.py',
|
||||||
|
'--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));
|
||||||
|
fromState = await runScript(args, send);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ergebnis
|
||||||
|
const finalState = JSON.parse(fs.readFileSync(fromState));
|
||||||
|
send({ type: 'done', state: finalState.accumulated_state, runDir });
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Result – Tree View:** Gelenk-Werte als lesbare Tabelle
|
**Route:**
|
||||||
|
```javascript
|
||||||
|
app.post('/api/homing/run', async (req, res) => {
|
||||||
|
// SSE-Header
|
||||||
|
res.setHeader('Content-Type', 'text/event-stream');
|
||||||
|
const send = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`);
|
||||||
|
await runHoming({ robotJsonPath: ROBOT_JSON, boardDataDir, send });
|
||||||
|
res.end();
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/homing/send-state', async (req, res) => {
|
||||||
|
// Sendet { x, y, z, a, b, c, e } an ROBOT_URL/api/state
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## UI-Seitenstruktur (analog index.html)
|
### Phase 2 — Frontend `public/homing.html`
|
||||||
|
|
||||||
|
Neue Seite (zugänglich von `index.html` via Link-Button, wie `calibration.html`).
|
||||||
|
|
||||||
|
**Sektionen (identisches Muster wie `index.html`):**
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────┐
|
┌──────────────────────────────────────────────────────┐
|
||||||
│ AKTIONEN │
|
│ AKTIONEN │
|
||||||
│ [Foto aufnehmen] [Homing berechnen] │
|
│ [📷 Foto & Homing berechnen] │
|
||||||
│ [An Roboter senden] │
|
│ [✅ An Roboter senden] (disabled bis Ergebnis) │
|
||||||
├─────────────────────────────────────────┤
|
│ Status-Badge: ○ Warte ● Läuft ✓ Fertig ✗ Fehler │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
│ AUSGABE / LOG │
|
│ AUSGABE / LOG │
|
||||||
│ Schritt-für-Schritt Log aller Scripts │
|
│ Schritt-für-Schritt Log aller Scripts (SSE-Stream) │
|
||||||
├─────────────────────────────────────────┤
|
│ Fortschritt: ──── Schritt 3/6 ──── │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
│ ANALYSIS & REASONING │
|
│ ANALYSIS & REASONING │
|
||||||
│ Kamera-Posen, Reprojektionsfehler, │
|
│ Zwischenergebnisse je Script als JSON │
|
||||||
│ Gelenk-Schätzungen je Schritt als JSON │
|
│ { camera_reprojection_px, arm1_std_deg, … } │
|
||||||
├──────────────────┬──────────────────────┤
|
├──────────────────┬───────────────────────────────────┤
|
||||||
│ RESULT – RAW JSON │ RESULT – TREE │
|
│ RESULT RAW JSON │ RESULT TREE VIEW │
|
||||||
│ Vollst. State-JSON │ Gelenk-Tabelle │
|
│ { │ x (Slider): 180.0 mm │
|
||||||
├─────────────────────────────────────────┤
|
│ "x": 180.0, │ y (Arm1): +23.4° │
|
||||||
│ SNAPSHOT CSV │
|
│ "y": 23.4, │ z (Ellbow): -12.1° │
|
||||||
│ Tabelle aller Marker: ID, Position, │
|
│ ... │ a (Arm2): +5.0° │
|
||||||
│ Link, Residual, num_cameras │
|
│ } │ b (Hand): 0.0° │
|
||||||
├─────────────────────────────────────────┤
|
│ │ c (Palm): 0.0° │
|
||||||
│ SNAPSHOTS (Bilder) │
|
│ │ e (Greifer): 0.0 mm │
|
||||||
│ Annotierte Kamerabilder mit Markern │
|
├──────────────────┴───────────────────────────────────┤
|
||||||
└─────────────────────────────────────────┘
|
│ 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] │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Schlüssel-Implementierungsdetails:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// homing.js (client)
|
||||||
|
|
||||||
|
// SSE-Stream vom Backend empfangen
|
||||||
|
async function runHoming() {
|
||||||
|
const response = await fetch('/api/homing/run', { method: 'POST' });
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ergebnis an Roboter senden
|
||||||
|
async function sendToRobot(state) {
|
||||||
|
await fetch('/api/homing/send-state', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ state }),
|
||||||
|
});
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Offene Punkte (Voraussetzungen für Homing)
|
### Phase 3 — robot.json via Driver-API
|
||||||
|
|
||||||
### 🔶 A — Arm-Marker in robot.json eintragen
|
**Voraussetzung:** ROBOT_URL ist konfiguriert und der Driver hat `GET /api/robot/config`.
|
||||||
|
|
||||||
Die Arm-Links haben noch **keine Marker** in `robot.json` (links.Arm1/Ellbow/Arm2/Hand.markers).
|
**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));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
Mögliche Quellen:
|
**Auswirkung:** Nur `ROBOT_JSON` Variable ändern — alle Scripts bekommen
|
||||||
- Aus der Y-Achsen-Kalibrierung: Marker 197, 218, 219 wurden als rotierend erkannt
|
automatisch die aktuelle Konfiguration.
|
||||||
→ diese können mit relativen Positionen in den jeweiligen Links eingetragen werden
|
|
||||||
- Mit `4b_revolute_angle.py` lässt sich pro Link prüfen, welche Marker
|
|
||||||
"zu diesem Link gehören" (paarweise Baseline-Methode)
|
|
||||||
|
|
||||||
**Aktion:** Für jedes Arm-Segment mind. 2 Marker bestimmen und in robot.json eintragen.
|
|
||||||
|
|
||||||
### 🔶 B — Arm1.jointToParent.origin[Y, Z] finalisieren
|
|
||||||
|
|
||||||
Aus der Arm1-Y-Achsen-Kalibrierung (calibration_arm.html):
|
|
||||||
- Berechneter Wert: Y ≈ 115.6 mm, Z ≈ 50.3 mm
|
|
||||||
- Aktueller Wert in robot.json: origin = [110, 108, 45]
|
|
||||||
- **Aktion:** Button „Joint-Origin Y/Z übernehmen" klicken → schreibt in robot.json
|
|
||||||
|
|
||||||
### 🔶 C — X-Position (Slider) im Homing
|
|
||||||
|
|
||||||
Die Slider-Position `x` ist für `4b_revolute_angle.py --x-mm` nötig.
|
|
||||||
Optionen:
|
|
||||||
1. Mechanischer Anschlag / Encoder → immer bekannt
|
|
||||||
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
|
## 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
|
[1] POST /api/homing/run + homingOrchestrator.js
|
||||||
│
|
→ Script-Kette als SSE orchestrieren
|
||||||
▼
|
→ Minimale UI: nur Log + Raw JSON
|
||||||
[2] estimate_camera → cam*_camera_pose.json
|
|
||||||
│ (aus Board-Markern)
|
[2] public/homing.html
|
||||||
▼
|
→ Vollständige UI mit allen Sektionen
|
||||||
[3b] corner_marker_poses → aruco_marker_poses.json
|
→ Link von index.html
|
||||||
│ (alle Marker trianguliert)
|
|
||||||
├──────────────────────────────────────┐
|
[3] POST /api/homing/send-state
|
||||||
▼ ▼
|
→ ROBOT_URL/api/state aufrufen (analog zu robotActions.js)
|
||||||
[4b] revolute Arm1 [4] state_v6 (alternativ)
|
|
||||||
│
|
[4] robot.json via Driver-API (wenn ROBOT_URL verfügbar)
|
||||||
[4b] revolute Ellbow ──► [5] camera_z_refine (optional, nutzt Ellbow)
|
→ Nur wenn Driver den Endpunkt implementiert
|
||||||
│
|
|
||||||
[4b] revolute Arm2
|
|
||||||
│
|
|
||||||
[4b] revolute Hand
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
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