weight apply = 0 running
This commit is contained in:
@@ -149,9 +149,9 @@ class ObservationQualityConfig:
|
|||||||
sharpness_ref: float = 2500.0
|
sharpness_ref: float = 2500.0
|
||||||
homography_ref: float = 0.18
|
homography_ref: float = 0.18
|
||||||
|
|
||||||
# factor f in min(1, q + f)
|
# factor f scales the effect of each quality indicator:
|
||||||
# f = 1 -> factor disabled / neutral
|
# f = 1 -> indicator is fully active
|
||||||
# f = 0 -> factor fully active
|
# f = 0 -> indicator is ignored (neutral weight = 1)
|
||||||
size_factor: float = 1.0
|
size_factor: float = 1.0
|
||||||
aspect_factor: float = 1.0
|
aspect_factor: float = 1.0
|
||||||
border_factor: float = 1.0
|
border_factor: float = 1.0
|
||||||
@@ -341,9 +341,12 @@ def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[s
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
def quality_factor(q: float, f: float) -> float:
|
def quality_factor(q: float, f: float) -> float:
|
||||||
# Saturating factor in [0, 1].
|
# Interpret f in [0, 1] as the per-indicator influence.
|
||||||
# f = 1.0 -> ignore this q-indicator completely.
|
# f = 1.0 -> indicator is fully active; q is applied.
|
||||||
return clamp01(q + f)
|
# f = 0.0 -> indicator is ignored and contributes a neutral weight of 1.0.
|
||||||
|
q = clamp01(q)
|
||||||
|
f = clamp01(f)
|
||||||
|
return 1.0 - f + f * q
|
||||||
|
|
||||||
|
|
||||||
def projective_homography_quality(image_points_px: np.ndarray, image_shape: Tuple[int, int], ref: float) -> float:
|
def projective_homography_quality(image_points_px: np.ndarray, image_shape: Tuple[int, int], ref: float) -> float:
|
||||||
|
|||||||
706
pipeline/2_Multiview_ersteVersion.py
Normal file
706
pipeline/2_Multiview_ersteVersion.py
Normal file
@@ -0,0 +1,706 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
============================================================
|
||||||
|
STEP 2b — Simultane Multiview-Optimierung für Roboterpose
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Ziel:
|
||||||
|
Aus mehreren ArUco-Detektionsdateien die gemeinsame
|
||||||
|
Roboterpose (x,y,z,a,b,c,e) schätzen und jede Kamera-Pose
|
||||||
|
sowie Marker-Weltpositionen ausgeben.
|
||||||
|
|
||||||
|
Eingabe:
|
||||||
|
--robot ../robot.json
|
||||||
|
--detections render_1a_aruco_detection.json render_1b_aruco_detection.json ...
|
||||||
|
--outDir .
|
||||||
|
|
||||||
|
Ausgabe:
|
||||||
|
multiview_pose.json
|
||||||
|
|
||||||
|
Hinweis:
|
||||||
|
Dieses Skript verwendet die Markerpositionen aus robot.json
|
||||||
|
als kinematische Constraints und optimiert gleichzeitig:
|
||||||
|
- Roboterzustand (x,y,z,a,b,c,e)
|
||||||
|
- Kameraextrinsische Parameter pro Bild
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import least_squares
|
||||||
|
|
||||||
|
STATE_KEYS = ["x", "y", "z", "a", "b", "c", "e"]
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# JSON helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_json(path: str) -> Dict[str, Any]:
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def save_json(data: Dict[str, Any], path: Path) -> None:
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# robot.json helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def resolve_scalar(value: Any, default: float = 0.0) -> float:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
if isinstance(value, (int, float)):
|
||||||
|
return float(value)
|
||||||
|
try:
|
||||||
|
return float(str(value).strip())
|
||||||
|
except ValueError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_vector(value: Any, default_len: int = 3) -> Tuple[float, ...]:
|
||||||
|
if value is None:
|
||||||
|
return tuple(0.0 for _ in range(default_len))
|
||||||
|
if isinstance(value, (int, float, str)):
|
||||||
|
return (resolve_scalar(value),) + tuple(0.0 for _ in range(default_len - 1))
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
resolved = [resolve_scalar(v) for v in value]
|
||||||
|
if len(resolved) < default_len:
|
||||||
|
resolved.extend([0.0] * (default_len - len(resolved)))
|
||||||
|
return tuple(resolved[:default_len])
|
||||||
|
return tuple(0.0 for _ in range(default_len))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_metric_scale(robot: Dict[str, Any]) -> float:
|
||||||
|
rendering_info = robot.get('renderingInfo', {}) or {}
|
||||||
|
metric = rendering_info.get('metric', 'mm')
|
||||||
|
return 0.001 if str(metric).strip().lower() == 'mm' else 1.0
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_axis(axis: Any) -> np.ndarray:
|
||||||
|
vec = np.asarray(axis, dtype=np.float64)
|
||||||
|
if vec.shape != (3,):
|
||||||
|
vec = vec.reshape(-1)[:3]
|
||||||
|
norm = np.linalg.norm(vec)
|
||||||
|
return vec / max(norm, 1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def euler_deg_to_matrix(euler_deg: Any) -> np.ndarray:
|
||||||
|
x_deg, y_deg, z_deg = resolve_vector(euler_deg, 3)
|
||||||
|
x = math.radians(x_deg)
|
||||||
|
y = math.radians(y_deg)
|
||||||
|
z = math.radians(z_deg)
|
||||||
|
|
||||||
|
cx = math.cos(x)
|
||||||
|
sx = math.sin(x)
|
||||||
|
cy = math.cos(y)
|
||||||
|
sy = math.sin(y)
|
||||||
|
cz = math.cos(z)
|
||||||
|
sz = math.sin(z)
|
||||||
|
|
||||||
|
Rx = np.array([
|
||||||
|
[1.0, 0.0, 0.0],
|
||||||
|
[0.0, cx, -sx],
|
||||||
|
[0.0, sx, cx]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
Ry = np.array([
|
||||||
|
[cy, 0.0, sy],
|
||||||
|
[0.0, 1.0, 0.0],
|
||||||
|
[-sy, 0.0, cy]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
Rz = np.array([
|
||||||
|
[cz, -sz, 0.0],
|
||||||
|
[sz, cz, 0.0],
|
||||||
|
[0.0, 0.0, 1.0]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
return Rz @ Ry @ Rx
|
||||||
|
|
||||||
|
|
||||||
|
def transform_from_translation_rotation(translation: Any, rotation_deg: Any) -> np.ndarray:
|
||||||
|
T = np.eye(4, dtype=np.float64)
|
||||||
|
pos = np.asarray(resolve_vector(translation, 3), dtype=np.float64)
|
||||||
|
T[:3, 3] = pos
|
||||||
|
T[:3, :3] = euler_deg_to_matrix(rotation_deg)
|
||||||
|
return T
|
||||||
|
|
||||||
|
|
||||||
|
def axis_angle_matrix(axis: Any, angle_deg: float) -> np.ndarray:
|
||||||
|
axis_vec = normalize_axis(axis)
|
||||||
|
theta = math.radians(angle_deg)
|
||||||
|
kx, ky, kz = axis_vec
|
||||||
|
c = math.cos(theta)
|
||||||
|
s = math.sin(theta)
|
||||||
|
v = 1.0 - c
|
||||||
|
R = np.array([
|
||||||
|
[kx * kx * v + c, kx * ky * v - kz * s, kx * kz * v + ky * s],
|
||||||
|
[ky * kx * v + kz * s, ky * ky * v + c, ky * kz * v - kx * s],
|
||||||
|
[kz * kx * v - ky * s, kz * ky * v + kx * s, kz * kz * v + c]
|
||||||
|
], dtype=np.float64)
|
||||||
|
T = np.eye(4, dtype=np.float64)
|
||||||
|
T[:3, :3] = R
|
||||||
|
return T
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Kinematics and marker extraction
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def extract_markers(robot: Dict[str, Any], scale: float) -> Dict[int, Dict[str, Any]]:
|
||||||
|
markers = {}
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
marker_defaults = (robot.get('renderingInfo', {}) or {}).get('markerDefaults', {}) or {}
|
||||||
|
default_size_mm = float(marker_defaults.get('size', 25.0))
|
||||||
|
|
||||||
|
for link_name, link_info in links.items():
|
||||||
|
for marker in link_info.get('markers', []) or []:
|
||||||
|
marker_id = int(marker.get('id', -1))
|
||||||
|
if marker_id < 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pos = resolve_vector(marker.get('position', [0, 0, 0]), 3)
|
||||||
|
size_mm = float(marker.get('size', default_size_mm))
|
||||||
|
markers[marker_id] = {
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'link_name': link_name,
|
||||||
|
'position_m': np.asarray([pos[0] * scale, pos[1] * scale, pos[2] * scale], dtype=np.float64),
|
||||||
|
'normal': normalize_axis(resolve_vector(marker.get('normal', [0, 0, 1]), 3)),
|
||||||
|
'spin_deg': float(marker.get('spin', 0.0)),
|
||||||
|
'size_m': size_mm * scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers
|
||||||
|
|
||||||
|
|
||||||
|
def marker_plane_axes(normal: np.ndarray, spin_deg: float) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
n = normalize_axis(normal)
|
||||||
|
candidate = np.array((0.0, 0.0, 1.0), dtype=np.float64)
|
||||||
|
if abs(np.dot(n, candidate)) > 0.99:
|
||||||
|
candidate = np.array((1.0, 0.0, 0.0), dtype=np.float64)
|
||||||
|
|
||||||
|
x_dir = np.cross(candidate, n)
|
||||||
|
x_dir /= max(np.linalg.norm(x_dir), 1e-9)
|
||||||
|
y_dir = np.cross(n, x_dir)
|
||||||
|
|
||||||
|
if abs(spin_deg) > 1e-6:
|
||||||
|
theta = math.radians(spin_deg)
|
||||||
|
cos_t = math.cos(theta)
|
||||||
|
sin_t = math.sin(theta)
|
||||||
|
x_rot = x_dir * cos_t + np.cross(n, x_dir) * sin_t + n * np.dot(n, x_dir) * (1.0 - cos_t)
|
||||||
|
y_rot = y_dir * cos_t + np.cross(n, y_dir) * sin_t + n * np.dot(n, y_dir) * (1.0 - cos_t)
|
||||||
|
return x_rot, y_rot
|
||||||
|
|
||||||
|
return x_dir, y_dir
|
||||||
|
|
||||||
|
|
||||||
|
def marker_object_corners(marker: Dict[str, Any]) -> np.ndarray:
|
||||||
|
half = marker['size_m'] * 0.5
|
||||||
|
x_dir, y_dir = marker_plane_axes(marker['normal'], marker['spin_deg'])
|
||||||
|
corners = np.stack([
|
||||||
|
-x_dir * half + y_dir * half,
|
||||||
|
x_dir * half + y_dir * half,
|
||||||
|
x_dir * half - y_dir * half,
|
||||||
|
-x_dir * half - y_dir * half
|
||||||
|
], axis=0)
|
||||||
|
return marker['position_m'].reshape(1, 3) + corners
|
||||||
|
|
||||||
|
|
||||||
|
def build_link_chain(robot: Dict[str, Any]) -> List[str]:
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
ordered: List[str] = []
|
||||||
|
remaining = set(links.keys())
|
||||||
|
|
||||||
|
while remaining:
|
||||||
|
progress = False
|
||||||
|
for name in list(remaining):
|
||||||
|
parent = links[name].get('parent')
|
||||||
|
if not parent or parent in ordered:
|
||||||
|
ordered.append(name)
|
||||||
|
remaining.remove(name)
|
||||||
|
progress = True
|
||||||
|
if not progress:
|
||||||
|
raise RuntimeError('Cycle detected in robot link tree or missing parent link')
|
||||||
|
return ordered
|
||||||
|
|
||||||
|
|
||||||
|
def compute_link_transforms(robot: Dict[str, Any], state: Dict[str, float], scale: float) -> Dict[str, np.ndarray]:
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
ordered_links = build_link_chain(robot)
|
||||||
|
transforms: Dict[str, np.ndarray] = {}
|
||||||
|
|
||||||
|
for link_name in ordered_links:
|
||||||
|
link_info = links[link_name] or {}
|
||||||
|
parent_name = link_info.get('parent')
|
||||||
|
parent_transform = transforms[parent_name] if parent_name else np.eye(4, dtype=np.float64)
|
||||||
|
|
||||||
|
mount_translation = np.asarray(resolve_vector(link_info.get('mountPosition', [0, 0, 0]), 3), dtype=np.float64) * scale
|
||||||
|
mount = transform_from_translation_rotation(
|
||||||
|
mount_translation,
|
||||||
|
link_info.get('mountRotation', [0, 0, 0])
|
||||||
|
)
|
||||||
|
|
||||||
|
joint_info = link_info.get('jointToParent', {}) or {}
|
||||||
|
joint_origin = np.asarray(resolve_vector(joint_info.get('origin', [0, 0, 0]), 3), dtype=np.float64) * scale
|
||||||
|
joint = transform_from_translation_rotation(
|
||||||
|
joint_origin,
|
||||||
|
joint_info.get('rotation', [0, 0, 0])
|
||||||
|
)
|
||||||
|
|
||||||
|
motion = np.eye(4, dtype=np.float64)
|
||||||
|
joint_type = str(joint_info.get('type', 'fixed')).strip().lower()
|
||||||
|
control_var = str(joint_info.get('variable', joint_info.get('control', ''))).strip().lower()
|
||||||
|
axis = resolve_vector(joint_info.get('axis', [1, 0, 0]), 3)
|
||||||
|
|
||||||
|
if joint_type == 'linear':
|
||||||
|
motion[:3, 3] = normalize_axis(axis) * state.get(control_var, 0.0) * scale
|
||||||
|
elif joint_type == 'revolute':
|
||||||
|
motion = axis_angle_matrix(axis, state.get(control_var, 0.0))
|
||||||
|
|
||||||
|
transforms[link_name] = parent_transform @ mount @ joint @ motion
|
||||||
|
|
||||||
|
return transforms
|
||||||
|
|
||||||
|
|
||||||
|
def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray:
|
||||||
|
link_transform = link_transforms[marker['link_name']]
|
||||||
|
local = np.ones(4, dtype=np.float64)
|
||||||
|
local[:3] = marker['position_m']
|
||||||
|
world = link_transform @ local
|
||||||
|
return world[:3]
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Camera / observation helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_intrinsics(detection_json: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
cam = detection_json['camera']
|
||||||
|
K = np.asarray(cam['camera_matrix'], dtype=np.float64)
|
||||||
|
D = np.asarray(cam.get('distortion_coefficients', [0, 0, 0, 0, 0]), dtype=np.float64).reshape(-1, 1)
|
||||||
|
return K, D
|
||||||
|
|
||||||
|
|
||||||
|
def collect_views_and_observations(
|
||||||
|
detection_files: List[str],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]]
|
||||||
|
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
||||||
|
views: List[Dict[str, Any]] = []
|
||||||
|
observations: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
|
for idx, det_path in enumerate(detection_files):
|
||||||
|
detection_json = load_json(det_path)
|
||||||
|
K, D = load_intrinsics(detection_json)
|
||||||
|
views.append({
|
||||||
|
'index': idx,
|
||||||
|
'source_file': os.path.abspath(det_path),
|
||||||
|
'camera_id': detection_json.get('camera', {}).get('camera_id', f'cam{idx+1}'),
|
||||||
|
'image_file': detection_json.get('image', {}).get('image_file'),
|
||||||
|
'K': K,
|
||||||
|
'D': D
|
||||||
|
})
|
||||||
|
|
||||||
|
for det in detection_json.get('detections', []) or []:
|
||||||
|
marker_id = int(det.get('marker_id', -1))
|
||||||
|
if marker_id < 0 or marker_id not in robot_markers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
image_points = det.get('image_points_px')
|
||||||
|
if isinstance(image_points, list) and len(image_points) == 4:
|
||||||
|
image_points = np.asarray(image_points, dtype=np.float64)
|
||||||
|
else:
|
||||||
|
center = resolve_vector(det.get('center_px', [0, 0]), 2)
|
||||||
|
image_points = np.asarray([center], dtype=np.float64)
|
||||||
|
|
||||||
|
confidence = float(det.get('confidence', 1.0))
|
||||||
|
marker = robot_markers[marker_id]
|
||||||
|
observations.append({
|
||||||
|
'view_index': idx,
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'marker_link_corners': marker_object_corners(marker),
|
||||||
|
'image_points_px': image_points,
|
||||||
|
'confidence': max(0.01, min(1.0, confidence))
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(views) == 0:
|
||||||
|
raise RuntimeError('No valid detection views found')
|
||||||
|
|
||||||
|
if len(observations) == 0:
|
||||||
|
raise RuntimeError('No marker observations matched robot.json markers')
|
||||||
|
|
||||||
|
return views, observations
|
||||||
|
|
||||||
|
|
||||||
|
def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray:
|
||||||
|
link_transform = link_transforms[marker['link_name']]
|
||||||
|
local = marker_object_corners(marker)
|
||||||
|
homogeneous = np.concatenate([local, np.ones((local.shape[0], 1), dtype=np.float64)], axis=1)
|
||||||
|
world = (link_transform @ homogeneous.T).T
|
||||||
|
return world[:, :3]
|
||||||
|
|
||||||
|
|
||||||
|
def initial_camera_guess(
|
||||||
|
view: Dict[str, Any],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
default_state: Dict[str, float],
|
||||||
|
scale: float,
|
||||||
|
robot: Dict[str, Any]
|
||||||
|
) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
object_points = []
|
||||||
|
image_points = []
|
||||||
|
|
||||||
|
link_transforms = compute_link_transforms(robot, default_state, scale)
|
||||||
|
|
||||||
|
for obs in observations:
|
||||||
|
if obs['view_index'] != view['index']:
|
||||||
|
continue
|
||||||
|
marker = robot_markers[obs['marker_id']]
|
||||||
|
object_points.append(compute_marker_world_corners(marker, link_transforms))
|
||||||
|
image_points.append(obs['image_points_px'])
|
||||||
|
|
||||||
|
if len(object_points) == 0:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
object_points = np.vstack(object_points)
|
||||||
|
image_points = np.vstack(image_points)
|
||||||
|
|
||||||
|
if object_points.shape[0] < 4:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
success, rvec, tvec = cv2.solvePnP(
|
||||||
|
object_points,
|
||||||
|
image_points,
|
||||||
|
view['K'],
|
||||||
|
view['D'],
|
||||||
|
flags=cv2.SOLVEPNP_ITERATIVE
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
return rvec, tvec
|
||||||
|
|
||||||
|
|
||||||
|
def project_points(
|
||||||
|
points_3d: np.ndarray,
|
||||||
|
rvec: np.ndarray,
|
||||||
|
tvec: np.ndarray,
|
||||||
|
K: np.ndarray,
|
||||||
|
D: np.ndarray
|
||||||
|
) -> np.ndarray:
|
||||||
|
projected, _ = cv2.projectPoints(points_3d, rvec, tvec, K, D)
|
||||||
|
return projected.reshape(-1, 2)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Optimization
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pack_parameters(robot_state: Dict[str, float], camera_params: List[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray:
|
||||||
|
state_vec = np.asarray([robot_state[k] for k in STATE_KEYS], dtype=np.float64)
|
||||||
|
cams = []
|
||||||
|
for rvec, tvec in camera_params:
|
||||||
|
cams.append(rvec.reshape(3))
|
||||||
|
cams.append(tvec.reshape(3))
|
||||||
|
return np.concatenate([state_vec] + cams)
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_parameters(params: np.ndarray, n_views: int) -> Tuple[Dict[str, float], List[Tuple[np.ndarray, np.ndarray]]]:
|
||||||
|
robot_state = {STATE_KEYS[i]: float(params[i]) for i in range(len(STATE_KEYS))}
|
||||||
|
camera_params = []
|
||||||
|
offset = len(STATE_KEYS)
|
||||||
|
for _ in range(n_views):
|
||||||
|
rvec = params[offset:offset + 3].reshape(3, 1)
|
||||||
|
tvec = params[offset + 3:offset + 6].reshape(3, 1)
|
||||||
|
camera_params.append((rvec, tvec))
|
||||||
|
offset += 6
|
||||||
|
return robot_state, camera_params
|
||||||
|
|
||||||
|
|
||||||
|
def residuals_for_parameters(
|
||||||
|
params: np.ndarray,
|
||||||
|
views: List[Dict[str, Any]],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
scale: float,
|
||||||
|
default_state: Dict[str, float]
|
||||||
|
) -> np.ndarray:
|
||||||
|
robot_state, camera_params = unpack_parameters(params, len(views))
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
|
||||||
|
residuals = []
|
||||||
|
for obs in observations:
|
||||||
|
marker = robot_markers[obs['marker_id']]
|
||||||
|
world_corners = compute_marker_world_corners(marker, link_transforms)
|
||||||
|
rvec, tvec = camera_params[obs['view_index']]
|
||||||
|
proj = project_points(world_corners, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D'])
|
||||||
|
diffs = proj - obs['image_points_px']
|
||||||
|
weight = math.sqrt(obs['confidence'])
|
||||||
|
residuals.extend((diffs * weight).reshape(-1))
|
||||||
|
|
||||||
|
for key in STATE_KEYS:
|
||||||
|
diff = robot_state[key] - default_state.get(key, 0.0)
|
||||||
|
if key in ('x', 'y', 'z', 'e'):
|
||||||
|
w = 0.001
|
||||||
|
else:
|
||||||
|
w = 0.01
|
||||||
|
residuals.append(diff * w)
|
||||||
|
|
||||||
|
return np.asarray(residuals, dtype=np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
def estimate_uncertainty(result: Any, n_params: int) -> np.ndarray:
|
||||||
|
if result.jac is None:
|
||||||
|
return np.full(n_params, float('nan'), dtype=np.float64)
|
||||||
|
J = result.jac
|
||||||
|
m, n = J.shape
|
||||||
|
JTJ = J.T @ J
|
||||||
|
try:
|
||||||
|
cov = np.linalg.pinv(JTJ)
|
||||||
|
except np.linalg.LinAlgError:
|
||||||
|
cov = np.linalg.pinv(JTJ + np.eye(n) * 1e-9)
|
||||||
|
residuals = result.fun
|
||||||
|
dof = max(1, m - n)
|
||||||
|
sigma2 = float(np.sum(residuals ** 2) / dof)
|
||||||
|
cov *= sigma2
|
||||||
|
return np.sqrt(np.diag(cov))
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Output assembly
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def camera_position_world(rvec: np.ndarray, tvec: np.ndarray) -> np.ndarray:
|
||||||
|
R, _ = cv2.Rodrigues(rvec)
|
||||||
|
return (-R.T @ tvec).reshape(3)
|
||||||
|
|
||||||
|
|
||||||
|
def build_output(
|
||||||
|
robot_state: Dict[str, float],
|
||||||
|
state_uncertainty: np.ndarray,
|
||||||
|
views: List[Dict[str, Any]],
|
||||||
|
camera_params: List[Tuple[np.ndarray, np.ndarray]],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
scale: float,
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
robot_json_path: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
|
||||||
|
marker_summary: Dict[int, Dict[str, Any]] = {}
|
||||||
|
for marker_id, marker in robot_markers.items():
|
||||||
|
marker_summary[marker_id] = {
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'link_name': marker['link_name'],
|
||||||
|
'position_world_m': compute_marker_world_position(marker, link_transforms).tolist(),
|
||||||
|
'size_m': marker['size_m'],
|
||||||
|
'observation_count': 0,
|
||||||
|
'mean_confidence': None,
|
||||||
|
'mean_reprojection_error_px': None,
|
||||||
|
'observations': []
|
||||||
|
}
|
||||||
|
|
||||||
|
per_marker_errors: Dict[int, List[float]] = {mid: [] for mid in marker_summary}
|
||||||
|
per_marker_confidences: Dict[int, List[float]] = {mid: [] for mid in marker_summary}
|
||||||
|
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
for obs in observations:
|
||||||
|
marker_id = obs['marker_id']
|
||||||
|
marker = robot_markers[marker_id]
|
||||||
|
object_points_m = compute_marker_world_corners(marker, link_transforms)
|
||||||
|
rvec, tvec = camera_params[obs['view_index']]
|
||||||
|
proj = project_points(object_points_m, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D'])
|
||||||
|
diffs = proj - obs['image_points_px']
|
||||||
|
errors = np.linalg.norm(diffs, axis=1)
|
||||||
|
repro_error = float(np.mean(errors))
|
||||||
|
per_marker_errors[marker_id].extend(errors.tolist())
|
||||||
|
per_marker_confidences[marker_id].append(obs['confidence'])
|
||||||
|
marker_summary[marker_id]['observation_count'] += 1
|
||||||
|
marker_summary[marker_id]['observations'].append({
|
||||||
|
'view_index': obs['view_index'],
|
||||||
|
'source_file': views[obs['view_index']]['source_file'],
|
||||||
|
'image_file': views[obs['view_index']]['image_file'],
|
||||||
|
'confidence': obs['confidence'],
|
||||||
|
'mean_reprojection_error_px': repro_error,
|
||||||
|
'corner_reprojection_errors_px': errors.tolist()
|
||||||
|
})
|
||||||
|
|
||||||
|
for marker_id, summary in marker_summary.items():
|
||||||
|
if summary['observation_count'] > 0:
|
||||||
|
summary['mean_confidence'] = float(np.mean(per_marker_confidences[marker_id]))
|
||||||
|
summary['mean_reprojection_error_px'] = float(np.mean(per_marker_errors[marker_id]))
|
||||||
|
|
||||||
|
camera_outputs = []
|
||||||
|
for idx, view in enumerate(views):
|
||||||
|
rvec, tvec = camera_params[idx]
|
||||||
|
cam_pos = camera_position_world(rvec, tvec)
|
||||||
|
observed_count = sum(1 for obs in observations if obs['view_index'] == idx)
|
||||||
|
camera_outputs.append({
|
||||||
|
'view_index': idx,
|
||||||
|
'source_file': view['source_file'],
|
||||||
|
'camera_id': view['camera_id'],
|
||||||
|
'camera_position_world_m': cam_pos.tolist(),
|
||||||
|
'rvec': rvec.reshape(-1).tolist(),
|
||||||
|
'tvec': tvec.reshape(-1).tolist(),
|
||||||
|
'intrinsics': {
|
||||||
|
'camera_matrix': view['K'].tolist(),
|
||||||
|
'distortion_coefficients': view['D'].reshape(-1).tolist()
|
||||||
|
},
|
||||||
|
'observation_count': observed_count
|
||||||
|
})
|
||||||
|
|
||||||
|
robot_pose_output = {
|
||||||
|
'state': {k: float(robot_state[k]) for k in STATE_KEYS},
|
||||||
|
'uncertainty': {
|
||||||
|
'x_mm': float(state_uncertainty[0]),
|
||||||
|
'y_mm': float(state_uncertainty[1]),
|
||||||
|
'z_mm': float(state_uncertainty[2]),
|
||||||
|
'a_deg': float(state_uncertainty[3]),
|
||||||
|
'b_deg': float(state_uncertainty[4]),
|
||||||
|
'c_deg': float(state_uncertainty[5]),
|
||||||
|
'e_mm': float(state_uncertainty[6])
|
||||||
|
},
|
||||||
|
'confidence': {
|
||||||
|
'x': float(math.exp(-state_uncertainty[0] / 10.0)),
|
||||||
|
'y': float(math.exp(-state_uncertainty[1] / 10.0)),
|
||||||
|
'z': float(math.exp(-state_uncertainty[2] / 10.0)),
|
||||||
|
'a': float(math.exp(-state_uncertainty[3] / 10.0)),
|
||||||
|
'b': float(math.exp(-state_uncertainty[4] / 10.0)),
|
||||||
|
'c': float(math.exp(-state_uncertainty[5] / 10.0)),
|
||||||
|
'e': float(math.exp(-state_uncertainty[6] / max(1.0, state_uncertainty[6])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'schema_version': '1.0',
|
||||||
|
'created_utc': datetime.datetime.utcnow().isoformat() + 'Z',
|
||||||
|
'source_robot_json': os.path.abspath(robot_json_path),
|
||||||
|
'source_detections': [view['source_file'] for view in views],
|
||||||
|
'robot_pose': robot_pose_output,
|
||||||
|
'camera_poses': camera_outputs,
|
||||||
|
'marker_positions': list(marker_summary.values())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description='Multiview optimization of robot pose and camera extrinsics')
|
||||||
|
parser.add_argument('--robot', required=True, help='Path to robot.json')
|
||||||
|
parser.add_argument('--detections', required=True, nargs='+', help='List of detection JSON files')
|
||||||
|
parser.add_argument('--outDir', required=True, help='Output directory')
|
||||||
|
parser.add_argument('--write-summary', action='store_true', help='Write summary file')
|
||||||
|
parser.add_argument('--max-iter', type=int, default=500, help='Maximum optimizer iterations')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
os.makedirs(args.outDir, exist_ok=True)
|
||||||
|
|
||||||
|
robot_json_path = os.path.abspath(args.robot)
|
||||||
|
robot = load_json(robot_json_path)
|
||||||
|
scale = parse_metric_scale(robot)
|
||||||
|
|
||||||
|
default_state = {
|
||||||
|
k: float(robot.get('defaultPosition', {}).get(k, 0.0) or 0.0)
|
||||||
|
for k in STATE_KEYS
|
||||||
|
}
|
||||||
|
|
||||||
|
robot_markers = extract_markers(robot, scale)
|
||||||
|
views, observations = collect_views_and_observations(args.detections, robot_markers)
|
||||||
|
|
||||||
|
camera_guesses = []
|
||||||
|
for view in views:
|
||||||
|
rvec, tvec = initial_camera_guess(view, observations, robot_markers, default_state, scale, robot)
|
||||||
|
camera_guesses.append((rvec, tvec))
|
||||||
|
|
||||||
|
x0 = pack_parameters(default_state, camera_guesses)
|
||||||
|
|
||||||
|
progress = {
|
||||||
|
'iter': 0,
|
||||||
|
'last_cost': None,
|
||||||
|
'last_print': time.time(),
|
||||||
|
'prev_x': x0.copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
def progress_callback(xk: np.ndarray) -> None:
|
||||||
|
progress['iter'] += 1
|
||||||
|
now = time.time()
|
||||||
|
if progress['iter'] == 1 or now - progress['last_print'] >= 1.0:
|
||||||
|
res = residuals_for_parameters(xk, views, observations, robot_markers, robot, scale, default_state)
|
||||||
|
cost = 0.5 * float(np.dot(res, res))
|
||||||
|
delta_cost = None
|
||||||
|
convergence = ''
|
||||||
|
if progress['last_cost'] is not None:
|
||||||
|
delta_cost = cost - progress['last_cost']
|
||||||
|
if abs(delta_cost) < 1e-3:
|
||||||
|
convergence = ' stable'
|
||||||
|
elif delta_cost < 0:
|
||||||
|
convergence = ' improving'
|
||||||
|
else:
|
||||||
|
convergence = ' worsening'
|
||||||
|
step_norm = float(np.linalg.norm(xk - progress['prev_x']))
|
||||||
|
print(
|
||||||
|
f'[Multiview] iter={progress["iter"]:4d} cost={cost:.4f}'
|
||||||
|
+ (f' delta={delta_cost:.4g}' if delta_cost is not None else '')
|
||||||
|
+ f' step={step_norm:.4g}'
|
||||||
|
+ convergence
|
||||||
|
)
|
||||||
|
progress['last_cost'] = cost
|
||||||
|
progress['last_print'] = now
|
||||||
|
progress['prev_x'] = xk.copy()
|
||||||
|
|
||||||
|
result = least_squares(
|
||||||
|
residuals_for_parameters,
|
||||||
|
x0,
|
||||||
|
args=(views, observations, robot_markers, robot, scale, default_state),
|
||||||
|
jac='2-point',
|
||||||
|
method='trf',
|
||||||
|
loss='soft_l1',
|
||||||
|
f_scale=1.0,
|
||||||
|
max_nfev=args.max_iter,
|
||||||
|
callback=progress_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
robot_state, camera_params = unpack_parameters(result.x, len(views))
|
||||||
|
uncertainties = estimate_uncertainty(result, len(result.x))
|
||||||
|
|
||||||
|
output = build_output(robot_state, uncertainties[:len(STATE_KEYS)], views, camera_params, observations, robot_markers, scale, robot, robot_json_path)
|
||||||
|
|
||||||
|
out_path = Path(args.outDir) / 'multiview_pose.json'
|
||||||
|
save_json(output, out_path)
|
||||||
|
|
||||||
|
print(f'Saved: {out_path}')
|
||||||
|
if args.write_summary:
|
||||||
|
summary_path = Path(args.outDir) / 'multiview_pose_summary.json'
|
||||||
|
summary = {
|
||||||
|
'final_cost': float(result.cost),
|
||||||
|
'status': int(result.status),
|
||||||
|
'message': result.message,
|
||||||
|
'robot_state': output['robot_pose'],
|
||||||
|
'camera_count': len(views),
|
||||||
|
'marker_count': len(robot_markers)
|
||||||
|
}
|
||||||
|
save_json(summary, summary_path)
|
||||||
|
print(f'Saved: {summary_path}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
1158
pipeline/2_Multiview_neu.py
Normal file
1158
pipeline/2_Multiview_neu.py
Normal file
File diff suppressed because it is too large
Load Diff
971
pipeline/2_Multiview_ohne_confidence.py
Normal file
971
pipeline/2_Multiview_ohne_confidence.py
Normal file
@@ -0,0 +1,971 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
============================================================
|
||||||
|
STEP 2b — Simultane Multiview-Optimierung für Roboterpose
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
Ziel:
|
||||||
|
Aus mehreren ArUco-Detektionsdateien die gemeinsame
|
||||||
|
Roboterpose (x,y,z,a,b,c,e) schätzen und jede Kamera-Pose
|
||||||
|
sowie Marker-Weltpositionen ausgeben.
|
||||||
|
|
||||||
|
Eingabe:
|
||||||
|
--robot ../robot.json
|
||||||
|
--detections render_1a_aruco_detection.json render_1b_aruco_detection.json ...
|
||||||
|
--outDir .
|
||||||
|
|
||||||
|
Ausgabe:
|
||||||
|
multiview_pose.json
|
||||||
|
|
||||||
|
Hinweis:
|
||||||
|
Dieses Skript verwendet die Markerpositionen aus robot.json
|
||||||
|
als kinematische Constraints und optimiert gleichzeitig:
|
||||||
|
- Roboterzustand (x,y,z,a,b,c,e)
|
||||||
|
- Kameraextrinsische Parameter pro Bild
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from scipy.optimize import least_squares
|
||||||
|
|
||||||
|
STATE_KEYS = ["x", "y", "z", "a", "b", "c", "e"]
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Constraint definitions and validation
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
class ConstraintResult:
|
||||||
|
"""Result of validating/applying a single constraint"""
|
||||||
|
def __init__(self, name: str, enabled: bool, reason: str = ""):
|
||||||
|
self.name = name
|
||||||
|
self.enabled = enabled
|
||||||
|
self.reason = reason
|
||||||
|
self.residuals = []
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
status = "✓ ENABLED" if self.enabled else "✗ DISABLED"
|
||||||
|
return f"{self.name:40s} {status:12s} {self.reason}"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_constraints(robot: Dict[str, Any], robot_markers: Dict[int, Dict[str, Any]]) -> Dict[str, ConstraintResult]:
|
||||||
|
"""
|
||||||
|
Validate which constraints can be applied based on robot geometry.
|
||||||
|
Returns a dict of constraint_name -> ConstraintResult
|
||||||
|
"""
|
||||||
|
results = {}
|
||||||
|
|
||||||
|
# --- Constraint 1: Rigid body distances within each link ---
|
||||||
|
rigid_body_result = ConstraintResult("RigidBodyDistances", False)
|
||||||
|
try:
|
||||||
|
rigid_body_count = 0
|
||||||
|
for link_name in ['Arm1', 'Ellbow', 'Arm2']:
|
||||||
|
link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name]
|
||||||
|
if len(link_markers) >= 2:
|
||||||
|
rigid_body_count += 1
|
||||||
|
if rigid_body_count >= 2:
|
||||||
|
rigid_body_result.enabled = True
|
||||||
|
rigid_body_result.reason = f"Found {rigid_body_count} links with 2+ markers each"
|
||||||
|
else:
|
||||||
|
rigid_body_result.reason = "Not enough rigid links with multiple markers"
|
||||||
|
except Exception as e:
|
||||||
|
rigid_body_result.reason = f"Error: {str(e)}"
|
||||||
|
results['RigidBodyDistances'] = rigid_body_result
|
||||||
|
|
||||||
|
# --- Constraint 2: Fixed X-distances between links (rotation around X-axis) ---
|
||||||
|
inter_link_x_result = ConstraintResult("InterLinkXDistances", False)
|
||||||
|
try:
|
||||||
|
links_with_markers = set(m['link_name'] for m in robot_markers.values())
|
||||||
|
x_rotated_links = []
|
||||||
|
for link_name in ['Arm1', 'Ellbow']:
|
||||||
|
if link_name in links_with_markers:
|
||||||
|
link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name]
|
||||||
|
if len(link_markers) >= 1:
|
||||||
|
x_rotated_links.append(link_name)
|
||||||
|
if len(x_rotated_links) >= 2:
|
||||||
|
inter_link_x_result.enabled = True
|
||||||
|
inter_link_x_result.reason = f"Found {len(x_rotated_links)} X-rotation links: {', '.join(x_rotated_links)}"
|
||||||
|
else:
|
||||||
|
inter_link_x_result.reason = "Not enough X-rotation links"
|
||||||
|
except Exception as e:
|
||||||
|
inter_link_x_result.reason = f"Error: {str(e)}"
|
||||||
|
results['InterLinkXDistances'] = inter_link_x_result
|
||||||
|
|
||||||
|
# --- Sanity check (not a hard constraint): Arm2 sin(a) dependency ---
|
||||||
|
arm2_sina_result = ConstraintResult("Arm2SinADependency", True, "Sanity check only (not enforced)")
|
||||||
|
try:
|
||||||
|
arm2_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm2']
|
||||||
|
if len(arm2_markers) >= 2:
|
||||||
|
z_values = set(m['position_m'][2] for m in arm2_markers)
|
||||||
|
if len(z_values) > 1:
|
||||||
|
arm2_sina_result.enabled = True
|
||||||
|
arm2_sina_result.reason = "Multiple Z-values detected; sin(a) dependency confirmed"
|
||||||
|
else:
|
||||||
|
arm2_sina_result.enabled = False
|
||||||
|
arm2_sina_result.reason = "No Z-variation in Arm2 markers (cannot use sin(a) constraint)"
|
||||||
|
else:
|
||||||
|
arm2_sina_result.enabled = False
|
||||||
|
arm2_sina_result.reason = "Not enough Arm2 markers"
|
||||||
|
except Exception as e:
|
||||||
|
arm2_sina_result.reason = f"Error: {str(e)}"
|
||||||
|
results['Arm2SinADependency'] = arm2_sina_result
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# JSON helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_json(path: str) -> Dict[str, Any]:
|
||||||
|
with open(path, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
|
||||||
|
|
||||||
|
def save_json(data: Dict[str, Any], path: Path) -> None:
|
||||||
|
with open(path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# robot.json helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def resolve_scalar(value: Any, default: float = 0.0) -> float:
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
if isinstance(value, (int, float)):
|
||||||
|
return float(value)
|
||||||
|
try:
|
||||||
|
return float(str(value).strip())
|
||||||
|
except ValueError:
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_vector(value: Any, default_len: int = 3) -> Tuple[float, ...]:
|
||||||
|
if value is None:
|
||||||
|
return tuple(0.0 for _ in range(default_len))
|
||||||
|
if isinstance(value, (int, float, str)):
|
||||||
|
return (resolve_scalar(value),) + tuple(0.0 for _ in range(default_len - 1))
|
||||||
|
if isinstance(value, (list, tuple)):
|
||||||
|
resolved = [resolve_scalar(v) for v in value]
|
||||||
|
if len(resolved) < default_len:
|
||||||
|
resolved.extend([0.0] * (default_len - len(resolved)))
|
||||||
|
return tuple(resolved[:default_len])
|
||||||
|
return tuple(0.0 for _ in range(default_len))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_metric_scale(robot: Dict[str, Any]) -> float:
|
||||||
|
rendering_info = robot.get('renderingInfo', {}) or {}
|
||||||
|
metric = rendering_info.get('metric', 'mm')
|
||||||
|
return 0.001 if str(metric).strip().lower() == 'mm' else 1.0
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_axis(axis: Any) -> np.ndarray:
|
||||||
|
vec = np.asarray(axis, dtype=np.float64)
|
||||||
|
if vec.shape != (3,):
|
||||||
|
vec = vec.reshape(-1)[:3]
|
||||||
|
norm = np.linalg.norm(vec)
|
||||||
|
return vec / max(norm, 1e-9)
|
||||||
|
|
||||||
|
|
||||||
|
def euler_deg_to_matrix(euler_deg: Any) -> np.ndarray:
|
||||||
|
x_deg, y_deg, z_deg = resolve_vector(euler_deg, 3)
|
||||||
|
x = math.radians(x_deg)
|
||||||
|
y = math.radians(y_deg)
|
||||||
|
z = math.radians(z_deg)
|
||||||
|
|
||||||
|
cx = math.cos(x)
|
||||||
|
sx = math.sin(x)
|
||||||
|
cy = math.cos(y)
|
||||||
|
sy = math.sin(y)
|
||||||
|
cz = math.cos(z)
|
||||||
|
sz = math.sin(z)
|
||||||
|
|
||||||
|
Rx = np.array([
|
||||||
|
[1.0, 0.0, 0.0],
|
||||||
|
[0.0, cx, -sx],
|
||||||
|
[0.0, sx, cx]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
Ry = np.array([
|
||||||
|
[cy, 0.0, sy],
|
||||||
|
[0.0, 1.0, 0.0],
|
||||||
|
[-sy, 0.0, cy]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
Rz = np.array([
|
||||||
|
[cz, -sz, 0.0],
|
||||||
|
[sz, cz, 0.0],
|
||||||
|
[0.0, 0.0, 1.0]
|
||||||
|
], dtype=np.float64)
|
||||||
|
|
||||||
|
return Rz @ Ry @ Rx
|
||||||
|
|
||||||
|
|
||||||
|
def transform_from_translation_rotation(translation: Any, rotation_deg: Any) -> np.ndarray:
|
||||||
|
T = np.eye(4, dtype=np.float64)
|
||||||
|
pos = np.asarray(resolve_vector(translation, 3), dtype=np.float64)
|
||||||
|
T[:3, 3] = pos
|
||||||
|
T[:3, :3] = euler_deg_to_matrix(rotation_deg)
|
||||||
|
return T
|
||||||
|
|
||||||
|
|
||||||
|
def axis_angle_matrix(axis: Any, angle_deg: float) -> np.ndarray:
|
||||||
|
axis_vec = normalize_axis(axis)
|
||||||
|
theta = math.radians(angle_deg)
|
||||||
|
kx, ky, kz = axis_vec
|
||||||
|
c = math.cos(theta)
|
||||||
|
s = math.sin(theta)
|
||||||
|
v = 1.0 - c
|
||||||
|
R = np.array([
|
||||||
|
[kx * kx * v + c, kx * ky * v - kz * s, kx * kz * v + ky * s],
|
||||||
|
[ky * kx * v + kz * s, ky * ky * v + c, ky * kz * v - kx * s],
|
||||||
|
[kz * kx * v - ky * s, kz * ky * v + kx * s, kz * kz * v + c]
|
||||||
|
], dtype=np.float64)
|
||||||
|
T = np.eye(4, dtype=np.float64)
|
||||||
|
T[:3, :3] = R
|
||||||
|
return T
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Kinematics and marker extraction
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def extract_markers(robot: Dict[str, Any], scale: float) -> Dict[int, Dict[str, Any]]:
|
||||||
|
markers = {}
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
marker_defaults = (robot.get('renderingInfo', {}) or {}).get('markerDefaults', {}) or {}
|
||||||
|
default_size_mm = float(marker_defaults.get('size', 25.0))
|
||||||
|
|
||||||
|
for link_name, link_info in links.items():
|
||||||
|
for marker in link_info.get('markers', []) or []:
|
||||||
|
marker_id = int(marker.get('id', -1))
|
||||||
|
if marker_id < 0:
|
||||||
|
continue
|
||||||
|
|
||||||
|
pos = resolve_vector(marker.get('position', [0, 0, 0]), 3)
|
||||||
|
size_mm = float(marker.get('size', default_size_mm))
|
||||||
|
markers[marker_id] = {
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'link_name': link_name,
|
||||||
|
'position_m': np.asarray([pos[0] * scale, pos[1] * scale, pos[2] * scale], dtype=np.float64),
|
||||||
|
'normal': normalize_axis(resolve_vector(marker.get('normal', [0, 0, 1]), 3)),
|
||||||
|
'spin_deg': float(marker.get('spin', 0.0)),
|
||||||
|
'size_m': size_mm * scale,
|
||||||
|
}
|
||||||
|
|
||||||
|
return markers
|
||||||
|
|
||||||
|
|
||||||
|
def marker_plane_axes(normal: np.ndarray, spin_deg: float) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
n = normalize_axis(normal)
|
||||||
|
candidate = np.array((0.0, 0.0, 1.0), dtype=np.float64)
|
||||||
|
if abs(np.dot(n, candidate)) > 0.99:
|
||||||
|
candidate = np.array((1.0, 0.0, 0.0), dtype=np.float64)
|
||||||
|
|
||||||
|
x_dir = np.cross(candidate, n)
|
||||||
|
x_dir /= max(np.linalg.norm(x_dir), 1e-9)
|
||||||
|
y_dir = np.cross(n, x_dir)
|
||||||
|
|
||||||
|
if abs(spin_deg) > 1e-6:
|
||||||
|
theta = math.radians(spin_deg)
|
||||||
|
cos_t = math.cos(theta)
|
||||||
|
sin_t = math.sin(theta)
|
||||||
|
x_rot = x_dir * cos_t + np.cross(n, x_dir) * sin_t + n * np.dot(n, x_dir) * (1.0 - cos_t)
|
||||||
|
y_rot = y_dir * cos_t + np.cross(n, y_dir) * sin_t + n * np.dot(n, y_dir) * (1.0 - cos_t)
|
||||||
|
return x_rot, y_rot
|
||||||
|
|
||||||
|
return x_dir, y_dir
|
||||||
|
|
||||||
|
|
||||||
|
def marker_object_corners(marker: Dict[str, Any]) -> np.ndarray:
|
||||||
|
half = marker['size_m'] * 0.5
|
||||||
|
x_dir, y_dir = marker_plane_axes(marker['normal'], marker['spin_deg'])
|
||||||
|
corners = np.stack([
|
||||||
|
-x_dir * half + y_dir * half,
|
||||||
|
x_dir * half + y_dir * half,
|
||||||
|
x_dir * half - y_dir * half,
|
||||||
|
-x_dir * half - y_dir * half
|
||||||
|
], axis=0)
|
||||||
|
return marker['position_m'].reshape(1, 3) + corners
|
||||||
|
|
||||||
|
|
||||||
|
def build_link_chain(robot: Dict[str, Any]) -> List[str]:
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
ordered: List[str] = []
|
||||||
|
remaining = set(links.keys())
|
||||||
|
|
||||||
|
while remaining:
|
||||||
|
progress = False
|
||||||
|
for name in list(remaining):
|
||||||
|
parent = links[name].get('parent')
|
||||||
|
if not parent or parent in ordered:
|
||||||
|
ordered.append(name)
|
||||||
|
remaining.remove(name)
|
||||||
|
progress = True
|
||||||
|
if not progress:
|
||||||
|
raise RuntimeError('Cycle detected in robot link tree or missing parent link')
|
||||||
|
return ordered
|
||||||
|
|
||||||
|
|
||||||
|
def compute_link_transforms(robot: Dict[str, Any], state: Dict[str, float], scale: float) -> Dict[str, np.ndarray]:
|
||||||
|
links = robot.get('links', {}) or {}
|
||||||
|
ordered_links = build_link_chain(robot)
|
||||||
|
transforms: Dict[str, np.ndarray] = {}
|
||||||
|
|
||||||
|
for link_name in ordered_links:
|
||||||
|
link_info = links[link_name] or {}
|
||||||
|
parent_name = link_info.get('parent')
|
||||||
|
parent_transform = transforms[parent_name] if parent_name else np.eye(4, dtype=np.float64)
|
||||||
|
|
||||||
|
mount_translation = np.asarray(resolve_vector(link_info.get('mountPosition', [0, 0, 0]), 3), dtype=np.float64) * scale
|
||||||
|
mount = transform_from_translation_rotation(
|
||||||
|
mount_translation,
|
||||||
|
link_info.get('mountRotation', [0, 0, 0])
|
||||||
|
)
|
||||||
|
|
||||||
|
joint_info = link_info.get('jointToParent', {}) or {}
|
||||||
|
joint_origin = np.asarray(resolve_vector(joint_info.get('origin', [0, 0, 0]), 3), dtype=np.float64) * scale
|
||||||
|
joint = transform_from_translation_rotation(
|
||||||
|
joint_origin,
|
||||||
|
joint_info.get('rotation', [0, 0, 0])
|
||||||
|
)
|
||||||
|
|
||||||
|
motion = np.eye(4, dtype=np.float64)
|
||||||
|
joint_type = str(joint_info.get('type', 'fixed')).strip().lower()
|
||||||
|
control_var = str(joint_info.get('variable', joint_info.get('control', ''))).strip().lower()
|
||||||
|
axis = resolve_vector(joint_info.get('axis', [1, 0, 0]), 3)
|
||||||
|
|
||||||
|
if joint_type == 'linear':
|
||||||
|
motion[:3, 3] = normalize_axis(axis) * state.get(control_var, 0.0) * scale
|
||||||
|
elif joint_type == 'revolute':
|
||||||
|
motion = axis_angle_matrix(axis, state.get(control_var, 0.0))
|
||||||
|
|
||||||
|
transforms[link_name] = parent_transform @ mount @ joint @ motion
|
||||||
|
|
||||||
|
return transforms
|
||||||
|
|
||||||
|
|
||||||
|
def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray:
|
||||||
|
link_transform = link_transforms[marker['link_name']]
|
||||||
|
local = np.ones(4, dtype=np.float64)
|
||||||
|
local[:3] = marker['position_m']
|
||||||
|
world = link_transform @ local
|
||||||
|
return world[:3]
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Camera / observation helpers
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def load_intrinsics(detection_json: Dict[str, Any]) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
cam = detection_json['camera']
|
||||||
|
K = np.asarray(cam['camera_matrix'], dtype=np.float64)
|
||||||
|
D = np.asarray(cam.get('distortion_coefficients', [0, 0, 0, 0, 0]), dtype=np.float64).reshape(-1, 1)
|
||||||
|
return K, D
|
||||||
|
|
||||||
|
|
||||||
|
def collect_views_and_observations(
|
||||||
|
detection_files: List[str],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]]
|
||||||
|
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
||||||
|
views: List[Dict[str, Any]] = []
|
||||||
|
observations: List[Dict[str, Any]] = []
|
||||||
|
|
||||||
|
for idx, det_path in enumerate(detection_files):
|
||||||
|
detection_json = load_json(det_path)
|
||||||
|
K, D = load_intrinsics(detection_json)
|
||||||
|
views.append({
|
||||||
|
'index': idx,
|
||||||
|
'source_file': os.path.abspath(det_path),
|
||||||
|
'camera_id': detection_json.get('camera', {}).get('camera_id', f'cam{idx+1}'),
|
||||||
|
'image_file': detection_json.get('image', {}).get('image_file'),
|
||||||
|
'K': K,
|
||||||
|
'D': D
|
||||||
|
})
|
||||||
|
|
||||||
|
for det in detection_json.get('detections', []) or []:
|
||||||
|
marker_id = int(det.get('marker_id', -1))
|
||||||
|
if marker_id < 0 or marker_id not in robot_markers:
|
||||||
|
continue
|
||||||
|
|
||||||
|
image_points = det.get('image_points_px')
|
||||||
|
if isinstance(image_points, list) and len(image_points) == 4:
|
||||||
|
image_points = np.asarray(image_points, dtype=np.float64)
|
||||||
|
else:
|
||||||
|
center = resolve_vector(det.get('center_px', [0, 0]), 2)
|
||||||
|
image_points = np.asarray([center], dtype=np.float64)
|
||||||
|
|
||||||
|
confidence = float(det.get('confidence', 1.0))
|
||||||
|
marker = robot_markers[marker_id]
|
||||||
|
observations.append({
|
||||||
|
'view_index': idx,
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'marker_link_corners': marker_object_corners(marker),
|
||||||
|
'image_points_px': image_points,
|
||||||
|
'confidence': max(0.01, min(1.0, confidence))
|
||||||
|
})
|
||||||
|
|
||||||
|
if len(views) == 0:
|
||||||
|
raise RuntimeError('No valid detection views found')
|
||||||
|
|
||||||
|
if len(observations) == 0:
|
||||||
|
raise RuntimeError('No marker observations matched robot.json markers')
|
||||||
|
|
||||||
|
return views, observations
|
||||||
|
|
||||||
|
|
||||||
|
def compute_marker_world_corners(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray:
|
||||||
|
link_transform = link_transforms[marker['link_name']]
|
||||||
|
local = marker_object_corners(marker)
|
||||||
|
homogeneous = np.concatenate([local, np.ones((local.shape[0], 1), dtype=np.float64)], axis=1)
|
||||||
|
world = (link_transform @ homogeneous.T).T
|
||||||
|
return world[:, :3]
|
||||||
|
|
||||||
|
|
||||||
|
def initial_camera_guess(
|
||||||
|
view: Dict[str, Any],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
default_state: Dict[str, float],
|
||||||
|
scale: float,
|
||||||
|
robot: Dict[str, Any]
|
||||||
|
) -> Tuple[np.ndarray, np.ndarray]:
|
||||||
|
object_points = []
|
||||||
|
image_points = []
|
||||||
|
|
||||||
|
link_transforms = compute_link_transforms(robot, default_state, scale)
|
||||||
|
|
||||||
|
for obs in observations:
|
||||||
|
if obs['view_index'] != view['index']:
|
||||||
|
continue
|
||||||
|
marker = robot_markers[obs['marker_id']]
|
||||||
|
object_points.append(compute_marker_world_corners(marker, link_transforms))
|
||||||
|
image_points.append(obs['image_points_px'])
|
||||||
|
|
||||||
|
if len(object_points) == 0:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
object_points = np.vstack(object_points)
|
||||||
|
image_points = np.vstack(image_points)
|
||||||
|
|
||||||
|
if object_points.shape[0] < 4:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
success, rvec, tvec = cv2.solvePnP(
|
||||||
|
object_points,
|
||||||
|
image_points,
|
||||||
|
view['K'],
|
||||||
|
view['D'],
|
||||||
|
flags=cv2.SOLVEPNP_ITERATIVE
|
||||||
|
)
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
return np.zeros((3, 1), dtype=np.float64), np.array([[0.0], [0.0], [1.0]], dtype=np.float64)
|
||||||
|
|
||||||
|
return rvec, tvec
|
||||||
|
|
||||||
|
|
||||||
|
def project_points(
|
||||||
|
points_3d: np.ndarray,
|
||||||
|
rvec: np.ndarray,
|
||||||
|
tvec: np.ndarray,
|
||||||
|
K: np.ndarray,
|
||||||
|
D: np.ndarray
|
||||||
|
) -> np.ndarray:
|
||||||
|
projected, _ = cv2.projectPoints(points_3d, rvec, tvec, K, D)
|
||||||
|
return projected.reshape(-1, 2)
|
||||||
|
|
||||||
|
|
||||||
|
def compute_soft_constraint_residuals(
|
||||||
|
robot_state: Dict[str, float],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
link_transforms: Dict[str, np.ndarray],
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
enabled_constraints: Dict[str, ConstraintResult]
|
||||||
|
) -> List[float]:
|
||||||
|
"""
|
||||||
|
Compute residuals from soft constraints (kinematic consistency, rigid body distances).
|
||||||
|
Returns a list of constraint residuals to append to the total residual vector.
|
||||||
|
"""
|
||||||
|
residuals = []
|
||||||
|
weight_scale = 0.1 # Weight for soft constraints relative to reprojection errors
|
||||||
|
|
||||||
|
# Constraint 1: Rigid body distances within each link
|
||||||
|
if enabled_constraints['RigidBodyDistances'].enabled:
|
||||||
|
for link_name in ['Arm1', 'Ellbow', 'Arm2']:
|
||||||
|
link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name]
|
||||||
|
if len(link_markers) < 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Compute all pairwise distances in world coords
|
||||||
|
for i in range(len(link_markers)):
|
||||||
|
for j in range(i + 1, len(link_markers)):
|
||||||
|
m_i = link_markers[i]
|
||||||
|
m_j = link_markers[j]
|
||||||
|
|
||||||
|
pos_i = compute_marker_world_position(m_i, link_transforms)
|
||||||
|
pos_j = compute_marker_world_position(m_j, link_transforms)
|
||||||
|
|
||||||
|
dist_world = np.linalg.norm(pos_i - pos_j)
|
||||||
|
|
||||||
|
# Reference distance in local coords
|
||||||
|
dist_local = np.linalg.norm(m_i['position_m'] - m_j['position_m'])
|
||||||
|
|
||||||
|
# Residual: difference should be zero (rigid body)
|
||||||
|
error = dist_world - dist_local
|
||||||
|
residuals.append(error * weight_scale * 0.1) # Very soft weight
|
||||||
|
|
||||||
|
# Constraint 2: Fixed X-distances between links (Arm1 <-> Ellbow)
|
||||||
|
if enabled_constraints['InterLinkXDistances'].enabled:
|
||||||
|
arm1_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm1']
|
||||||
|
ellbow_markers = [m for m in robot_markers.values() if m['link_name'] == 'Ellbow']
|
||||||
|
|
||||||
|
if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1:
|
||||||
|
# Get first marker from each link
|
||||||
|
m_arm1 = arm1_markers[0]
|
||||||
|
m_ellbow = ellbow_markers[0]
|
||||||
|
|
||||||
|
pos_arm1 = compute_marker_world_position(m_arm1, link_transforms)
|
||||||
|
pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms)
|
||||||
|
|
||||||
|
# X-distance in world should match reference (relative position)
|
||||||
|
# Since both rotate around X-axis at different points, we check consistency
|
||||||
|
x_diff_world = pos_ellbow[0] - pos_arm1[0]
|
||||||
|
x_diff_ref = m_ellbow['position_m'][0] - m_arm1['position_m'][0]
|
||||||
|
|
||||||
|
error = x_diff_world - x_diff_ref
|
||||||
|
residuals.append(error * weight_scale)
|
||||||
|
|
||||||
|
return residuals
|
||||||
|
|
||||||
|
|
||||||
|
def compute_marker_world_position(marker: Dict[str, Any], link_transforms: Dict[str, np.ndarray]) -> np.ndarray:
|
||||||
|
"""Compute the world position of a marker given current link transforms."""
|
||||||
|
link_transform = link_transforms[marker['link_name']]
|
||||||
|
local_pos = np.concatenate([marker['position_m'], [1.0]])
|
||||||
|
world_pos = (link_transform @ local_pos)[:3]
|
||||||
|
return world_pos
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Optimization
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def pack_parameters(robot_state: Dict[str, float], camera_params: List[Tuple[np.ndarray, np.ndarray]]) -> np.ndarray:
|
||||||
|
state_vec = np.asarray([robot_state[k] for k in STATE_KEYS], dtype=np.float64)
|
||||||
|
cams = []
|
||||||
|
for rvec, tvec in camera_params:
|
||||||
|
cams.append(rvec.reshape(3))
|
||||||
|
cams.append(tvec.reshape(3))
|
||||||
|
return np.concatenate([state_vec] + cams)
|
||||||
|
|
||||||
|
|
||||||
|
def unpack_parameters(params: np.ndarray, n_views: int) -> Tuple[Dict[str, float], List[Tuple[np.ndarray, np.ndarray]]]:
|
||||||
|
robot_state = {STATE_KEYS[i]: float(params[i]) for i in range(len(STATE_KEYS))}
|
||||||
|
camera_params = []
|
||||||
|
offset = len(STATE_KEYS)
|
||||||
|
for _ in range(n_views):
|
||||||
|
rvec = params[offset:offset + 3].reshape(3, 1)
|
||||||
|
tvec = params[offset + 3:offset + 6].reshape(3, 1)
|
||||||
|
camera_params.append((rvec, tvec))
|
||||||
|
offset += 6
|
||||||
|
return robot_state, camera_params
|
||||||
|
|
||||||
|
|
||||||
|
def residuals_for_parameters(
|
||||||
|
params: np.ndarray,
|
||||||
|
views: List[Dict[str, Any]],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
scale: float,
|
||||||
|
default_state: Dict[str, float],
|
||||||
|
enabled_constraints: Dict[str, ConstraintResult]
|
||||||
|
) -> np.ndarray:
|
||||||
|
robot_state, camera_params = unpack_parameters(params, len(views))
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
|
||||||
|
residuals = []
|
||||||
|
|
||||||
|
# Reprojection residuals (primary observation)
|
||||||
|
for obs in observations:
|
||||||
|
marker = robot_markers[obs['marker_id']]
|
||||||
|
world_corners = compute_marker_world_corners(marker, link_transforms)
|
||||||
|
rvec, tvec = camera_params[obs['view_index']]
|
||||||
|
proj = project_points(world_corners, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D'])
|
||||||
|
diffs = proj - obs['image_points_px']
|
||||||
|
weight = math.sqrt(obs['confidence'])
|
||||||
|
residuals.extend((diffs * weight).reshape(-1))
|
||||||
|
|
||||||
|
# Weak priors on robot state
|
||||||
|
for key in STATE_KEYS:
|
||||||
|
diff = robot_state[key] - default_state.get(key, 0.0)
|
||||||
|
if key in ('x', 'y', 'z', 'e'):
|
||||||
|
w = 0.001
|
||||||
|
else:
|
||||||
|
w = 0.01
|
||||||
|
residuals.append(diff * w)
|
||||||
|
|
||||||
|
# Soft constraints (kinematic consistency, rigid body constraints)
|
||||||
|
soft_constraint_residuals = compute_soft_constraint_residuals(
|
||||||
|
robot_state, robot_markers, link_transforms, robot, enabled_constraints
|
||||||
|
)
|
||||||
|
residuals.extend(soft_constraint_residuals)
|
||||||
|
|
||||||
|
return np.asarray(residuals, dtype=np.float64)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def estimate_uncertainty(result: Any, n_params: int) -> np.ndarray:
|
||||||
|
if result.jac is None:
|
||||||
|
return np.full(n_params, float('nan'), dtype=np.float64)
|
||||||
|
J = result.jac
|
||||||
|
m, n = J.shape
|
||||||
|
JTJ = J.T @ J
|
||||||
|
try:
|
||||||
|
cov = np.linalg.pinv(JTJ)
|
||||||
|
except np.linalg.LinAlgError:
|
||||||
|
cov = np.linalg.pinv(JTJ + np.eye(n) * 1e-9)
|
||||||
|
residuals = result.fun
|
||||||
|
dof = max(1, m - n)
|
||||||
|
sigma2 = float(np.sum(residuals ** 2) / dof)
|
||||||
|
cov *= sigma2
|
||||||
|
return np.sqrt(np.diag(cov))
|
||||||
|
|
||||||
|
|
||||||
|
def print_constraint_sanity_check(
|
||||||
|
robot_state: Dict[str, float],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
link_transforms: Dict[str, np.ndarray],
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
enabled_constraints: Dict[str, ConstraintResult]
|
||||||
|
) -> None:
|
||||||
|
"""
|
||||||
|
Print sanity checks for all constraints to verify the optimization result.
|
||||||
|
"""
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("CONSTRAINT SANITY CHECKS (after optimization)")
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
# Check 1: Rigid body distances
|
||||||
|
if enabled_constraints['RigidBodyDistances'].enabled:
|
||||||
|
print("\n1. RIGID BODY DISTANCES")
|
||||||
|
for link_name in ['Arm1', 'Ellbow', 'Arm2']:
|
||||||
|
link_markers = [m for m in robot_markers.values() if m['link_name'] == link_name]
|
||||||
|
if len(link_markers) < 2:
|
||||||
|
continue
|
||||||
|
|
||||||
|
max_error = 0.0
|
||||||
|
for i in range(len(link_markers)):
|
||||||
|
for j in range(i + 1, len(link_markers)):
|
||||||
|
m_i = link_markers[i]
|
||||||
|
m_j = link_markers[j]
|
||||||
|
|
||||||
|
pos_i = compute_marker_world_position(m_i, link_transforms)
|
||||||
|
pos_j = compute_marker_world_position(m_j, link_transforms)
|
||||||
|
|
||||||
|
dist_world = np.linalg.norm(pos_i - pos_j)
|
||||||
|
dist_local = np.linalg.norm(m_i['position_m'] - m_j['position_m'])
|
||||||
|
error = abs(dist_world - dist_local)
|
||||||
|
max_error = max(max_error, error)
|
||||||
|
|
||||||
|
status = "✓" if max_error < 1.0 else "⚠" if max_error < 5.0 else "✗"
|
||||||
|
print(f" {link_name:10s}: max_error = {max_error:.3f} mm {status}")
|
||||||
|
|
||||||
|
# Check 2: Inter-link X distances
|
||||||
|
if enabled_constraints['InterLinkXDistances'].enabled:
|
||||||
|
print("\n2. INTER-LINK X-DISTANCES")
|
||||||
|
arm1_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm1']
|
||||||
|
ellbow_markers = [m for m in robot_markers.values() if m['link_name'] == 'Ellbow']
|
||||||
|
|
||||||
|
if len(arm1_markers) >= 1 and len(ellbow_markers) >= 1:
|
||||||
|
m_arm1 = arm1_markers[0]
|
||||||
|
m_ellbow = ellbow_markers[0]
|
||||||
|
|
||||||
|
pos_arm1 = compute_marker_world_position(m_arm1, link_transforms)
|
||||||
|
pos_ellbow = compute_marker_world_position(m_ellbow, link_transforms)
|
||||||
|
|
||||||
|
x_diff_world = pos_ellbow[0] - pos_arm1[0]
|
||||||
|
x_diff_ref = m_ellbow['position_m'][0] - m_arm1['position_m'][0]
|
||||||
|
error = abs(x_diff_world - x_diff_ref)
|
||||||
|
|
||||||
|
status = "✓" if error < 1.0 else "⚠" if error < 5.0 else "✗"
|
||||||
|
print(f" Arm1 <-> Ellbow: error = {error:.3f} mm {status}")
|
||||||
|
|
||||||
|
# Check 3: Arm2 sin(a) dependency
|
||||||
|
if enabled_constraints['Arm2SinADependency'].enabled:
|
||||||
|
print("\n3. ARM2 sin(a) DEPENDENCY (sanity check)")
|
||||||
|
arm2_markers = [m for m in robot_markers.values() if m['link_name'] == 'Arm2']
|
||||||
|
if len(arm2_markers) >= 2:
|
||||||
|
# Check that markers with different Z values have different X spreads
|
||||||
|
a_rad = math.radians(robot_state['a'])
|
||||||
|
sin_a = math.sin(a_rad)
|
||||||
|
cos_a = math.cos(a_rad)
|
||||||
|
|
||||||
|
z_variations = {}
|
||||||
|
for m in arm2_markers:
|
||||||
|
z_local = m['position_m'][2]
|
||||||
|
x_local = m['position_m'][0]
|
||||||
|
pos_world = compute_marker_world_position(m, link_transforms)
|
||||||
|
x_world = pos_world[0]
|
||||||
|
|
||||||
|
# Expected: x_world = 90 + x_local * cos(a) - z_local * sin(a)
|
||||||
|
x_expected = 90 * (robot.get('renderingInfo', {}).get('metric', 'mm') == 'mm' and 0.09 or 0.09) + x_local * cos_a - z_local * sin_a
|
||||||
|
x_error = abs(x_world - x_expected)
|
||||||
|
|
||||||
|
if z_local not in z_variations:
|
||||||
|
z_variations[z_local] = []
|
||||||
|
z_variations[z_local].append(x_error)
|
||||||
|
|
||||||
|
max_error = max(max(errors) for errors in z_variations.values()) if z_variations else 0.0
|
||||||
|
status = "✓" if max_error < 5.0 else "⚠" if max_error < 10.0 else "⚠"
|
||||||
|
print(f" X-consistency with sin(a): max_error = {max_error:.3f} mm {status}")
|
||||||
|
print(f" (Note: this is a consistency check, not a hard constraint)")
|
||||||
|
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
|
||||||
|
def camera_position_world(rvec: np.ndarray, tvec: np.ndarray) -> np.ndarray:
|
||||||
|
R, _ = cv2.Rodrigues(rvec)
|
||||||
|
return (-R.T @ tvec).reshape(3)
|
||||||
|
|
||||||
|
|
||||||
|
def build_output(
|
||||||
|
robot_state: Dict[str, float],
|
||||||
|
state_uncertainty: np.ndarray,
|
||||||
|
views: List[Dict[str, Any]],
|
||||||
|
camera_params: List[Tuple[np.ndarray, np.ndarray]],
|
||||||
|
observations: List[Dict[str, Any]],
|
||||||
|
robot_markers: Dict[int, Dict[str, Any]],
|
||||||
|
scale: float,
|
||||||
|
robot: Dict[str, Any],
|
||||||
|
robot_json_path: str
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
|
||||||
|
marker_summary: Dict[int, Dict[str, Any]] = {}
|
||||||
|
for marker_id, marker in robot_markers.items():
|
||||||
|
marker_summary[marker_id] = {
|
||||||
|
'marker_id': marker_id,
|
||||||
|
'link_name': marker['link_name'],
|
||||||
|
'position_world_m': compute_marker_world_position(marker, link_transforms).tolist(),
|
||||||
|
'size_m': marker['size_m'],
|
||||||
|
'observation_count': 0,
|
||||||
|
'mean_confidence': None,
|
||||||
|
'mean_reprojection_error_px': None,
|
||||||
|
'observations': []
|
||||||
|
}
|
||||||
|
|
||||||
|
per_marker_errors: Dict[int, List[float]] = {mid: [] for mid in marker_summary}
|
||||||
|
per_marker_confidences: Dict[int, List[float]] = {mid: [] for mid in marker_summary}
|
||||||
|
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
for obs in observations:
|
||||||
|
marker_id = obs['marker_id']
|
||||||
|
marker = robot_markers[marker_id]
|
||||||
|
object_points_m = compute_marker_world_corners(marker, link_transforms)
|
||||||
|
rvec, tvec = camera_params[obs['view_index']]
|
||||||
|
proj = project_points(object_points_m, rvec, tvec, views[obs['view_index']]['K'], views[obs['view_index']]['D'])
|
||||||
|
diffs = proj - obs['image_points_px']
|
||||||
|
errors = np.linalg.norm(diffs, axis=1)
|
||||||
|
repro_error = float(np.mean(errors))
|
||||||
|
per_marker_errors[marker_id].extend(errors.tolist())
|
||||||
|
per_marker_confidences[marker_id].append(obs['confidence'])
|
||||||
|
marker_summary[marker_id]['observation_count'] += 1
|
||||||
|
marker_summary[marker_id]['observations'].append({
|
||||||
|
'view_index': obs['view_index'],
|
||||||
|
'source_file': views[obs['view_index']]['source_file'],
|
||||||
|
'image_file': views[obs['view_index']]['image_file'],
|
||||||
|
'confidence': obs['confidence'],
|
||||||
|
'mean_reprojection_error_px': repro_error,
|
||||||
|
'corner_reprojection_errors_px': errors.tolist()
|
||||||
|
})
|
||||||
|
|
||||||
|
for marker_id, summary in marker_summary.items():
|
||||||
|
if summary['observation_count'] > 0:
|
||||||
|
summary['mean_confidence'] = float(np.mean(per_marker_confidences[marker_id]))
|
||||||
|
summary['mean_reprojection_error_px'] = float(np.mean(per_marker_errors[marker_id]))
|
||||||
|
|
||||||
|
camera_outputs = []
|
||||||
|
for idx, view in enumerate(views):
|
||||||
|
rvec, tvec = camera_params[idx]
|
||||||
|
cam_pos = camera_position_world(rvec, tvec)
|
||||||
|
observed_count = sum(1 for obs in observations if obs['view_index'] == idx)
|
||||||
|
camera_outputs.append({
|
||||||
|
'view_index': idx,
|
||||||
|
'source_file': view['source_file'],
|
||||||
|
'camera_id': view['camera_id'],
|
||||||
|
'camera_position_world_m': cam_pos.tolist(),
|
||||||
|
'rvec': rvec.reshape(-1).tolist(),
|
||||||
|
'tvec': tvec.reshape(-1).tolist(),
|
||||||
|
'intrinsics': {
|
||||||
|
'camera_matrix': view['K'].tolist(),
|
||||||
|
'distortion_coefficients': view['D'].reshape(-1).tolist()
|
||||||
|
},
|
||||||
|
'observation_count': observed_count
|
||||||
|
})
|
||||||
|
|
||||||
|
robot_pose_output = {
|
||||||
|
'state': {k: float(robot_state[k]) for k in STATE_KEYS},
|
||||||
|
'uncertainty': {
|
||||||
|
'x_mm': float(state_uncertainty[0]),
|
||||||
|
'y_mm': float(state_uncertainty[1]),
|
||||||
|
'z_mm': float(state_uncertainty[2]),
|
||||||
|
'a_deg': float(state_uncertainty[3]),
|
||||||
|
'b_deg': float(state_uncertainty[4]),
|
||||||
|
'c_deg': float(state_uncertainty[5]),
|
||||||
|
'e_mm': float(state_uncertainty[6])
|
||||||
|
},
|
||||||
|
'confidence': {
|
||||||
|
'x': float(math.exp(-state_uncertainty[0] / 10.0)),
|
||||||
|
'y': float(math.exp(-state_uncertainty[1] / 10.0)),
|
||||||
|
'z': float(math.exp(-state_uncertainty[2] / 10.0)),
|
||||||
|
'a': float(math.exp(-state_uncertainty[3] / 10.0)),
|
||||||
|
'b': float(math.exp(-state_uncertainty[4] / 10.0)),
|
||||||
|
'c': float(math.exp(-state_uncertainty[5] / 10.0)),
|
||||||
|
'e': float(math.exp(-state_uncertainty[6] / max(1.0, state_uncertainty[6])))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
'schema_version': '1.0',
|
||||||
|
'created_utc': datetime.datetime.utcnow().isoformat() + 'Z',
|
||||||
|
'source_robot_json': os.path.abspath(robot_json_path),
|
||||||
|
'source_detections': [view['source_file'] for view in views],
|
||||||
|
'robot_pose': robot_pose_output,
|
||||||
|
'camera_poses': camera_outputs,
|
||||||
|
'marker_positions': list(marker_summary.values())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# Main
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
parser = argparse.ArgumentParser(description='Multiview optimization of robot pose and camera extrinsics')
|
||||||
|
parser.add_argument('--robot', required=True, help='Path to robot.json')
|
||||||
|
parser.add_argument('--detections', required=True, nargs='+', help='List of detection JSON files')
|
||||||
|
parser.add_argument('--outDir', required=True, help='Output directory')
|
||||||
|
parser.add_argument('--write-summary', action='store_true', help='Write summary file')
|
||||||
|
parser.add_argument('--max-iter', type=int, default=500, help='Maximum optimizer iterations')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
os.makedirs(args.outDir, exist_ok=True)
|
||||||
|
|
||||||
|
robot_json_path = os.path.abspath(args.robot)
|
||||||
|
robot = load_json(robot_json_path)
|
||||||
|
scale = parse_metric_scale(robot)
|
||||||
|
|
||||||
|
default_state = {
|
||||||
|
k: float(robot.get('defaultPosition', {}).get(k, 0.0) or 0.0)
|
||||||
|
for k in STATE_KEYS
|
||||||
|
}
|
||||||
|
|
||||||
|
robot_markers = extract_markers(robot, scale)
|
||||||
|
|
||||||
|
# Validate constraints
|
||||||
|
print("\n" + "=" * 70)
|
||||||
|
print("CONSTRAINT VALIDATION")
|
||||||
|
print("=" * 70)
|
||||||
|
enabled_constraints = validate_constraints(robot, robot_markers)
|
||||||
|
for constraint_name, result in enabled_constraints.items():
|
||||||
|
print(result)
|
||||||
|
print("=" * 70)
|
||||||
|
|
||||||
|
views, observations = collect_views_and_observations(args.detections, robot_markers)
|
||||||
|
|
||||||
|
camera_guesses = []
|
||||||
|
for view in views:
|
||||||
|
rvec, tvec = initial_camera_guess(view, observations, robot_markers, default_state, scale, robot)
|
||||||
|
camera_guesses.append((rvec, tvec))
|
||||||
|
|
||||||
|
x0 = pack_parameters(default_state, camera_guesses)
|
||||||
|
|
||||||
|
progress = {
|
||||||
|
'iter': 0,
|
||||||
|
'last_cost': None,
|
||||||
|
'last_print': time.time(),
|
||||||
|
'prev_x': x0.copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
def progress_callback(xk: np.ndarray) -> None:
|
||||||
|
progress['iter'] += 1
|
||||||
|
now = time.time()
|
||||||
|
if progress['iter'] == 1 or now - progress['last_print'] >= 1.0:
|
||||||
|
res = residuals_for_parameters(xk, views, observations, robot_markers, robot, scale, default_state, enabled_constraints)
|
||||||
|
cost = 0.5 * float(np.dot(res, res))
|
||||||
|
delta_cost = None
|
||||||
|
convergence = ''
|
||||||
|
if progress['last_cost'] is not None:
|
||||||
|
delta_cost = cost - progress['last_cost']
|
||||||
|
if abs(delta_cost) < 1e-3:
|
||||||
|
convergence = ' stable'
|
||||||
|
elif delta_cost < 0:
|
||||||
|
convergence = ' improving'
|
||||||
|
else:
|
||||||
|
convergence = ' worsening'
|
||||||
|
step_norm = float(np.linalg.norm(xk - progress['prev_x']))
|
||||||
|
print(
|
||||||
|
f'[Multiview] iter={progress["iter"]:4d} cost={cost:.4f}'
|
||||||
|
+ (f' delta={delta_cost:.4g}' if delta_cost is not None else '')
|
||||||
|
+ f' step={step_norm:.4g}'
|
||||||
|
+ convergence
|
||||||
|
)
|
||||||
|
progress['last_cost'] = cost
|
||||||
|
progress['last_print'] = now
|
||||||
|
progress['prev_x'] = xk.copy()
|
||||||
|
|
||||||
|
result = least_squares(
|
||||||
|
residuals_for_parameters,
|
||||||
|
x0,
|
||||||
|
args=(views, observations, robot_markers, robot, scale, default_state, enabled_constraints),
|
||||||
|
jac='2-point',
|
||||||
|
method='trf',
|
||||||
|
loss='soft_l1',
|
||||||
|
f_scale=1.0,
|
||||||
|
max_nfev=args.max_iter,
|
||||||
|
callback=progress_callback
|
||||||
|
)
|
||||||
|
|
||||||
|
robot_state, camera_params = unpack_parameters(result.x, len(views))
|
||||||
|
uncertainties = estimate_uncertainty(result, len(result.x))
|
||||||
|
|
||||||
|
# Print constraint sanity checks
|
||||||
|
link_transforms = compute_link_transforms(robot, robot_state, scale)
|
||||||
|
print_constraint_sanity_check(robot_state, robot_markers, link_transforms, robot, enabled_constraints)
|
||||||
|
|
||||||
|
output = build_output(robot_state, uncertainties[:len(STATE_KEYS)], views, camera_params, observations, robot_markers, scale, robot, robot_json_path)
|
||||||
|
|
||||||
|
out_path = Path(args.outDir) / 'multiview_pose.json'
|
||||||
|
save_json(output, out_path)
|
||||||
|
|
||||||
|
print(f'Saved: {out_path}')
|
||||||
|
if args.write_summary:
|
||||||
|
summary_path = Path(args.outDir) / 'multiview_pose_summary.json'
|
||||||
|
summary = {
|
||||||
|
'final_cost': float(result.cost),
|
||||||
|
'status': int(result.status),
|
||||||
|
'message': result.message,
|
||||||
|
'robot_state': output['robot_pose'],
|
||||||
|
'camera_count': len(views),
|
||||||
|
'marker_count': len(robot_markers)
|
||||||
|
}
|
||||||
|
save_json(summary, summary_path)
|
||||||
|
print(f'Saved: {summary_path}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
966
pipeline/multiview_pose_Weighted.json
Normal file
966
pipeline/multiview_pose_Weighted.json
Normal file
@@ -0,0 +1,966 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"created_utc": "2026-05-28T21:07:44.288252Z",
|
||||||
|
"source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json",
|
||||||
|
"source_detections": [
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json"
|
||||||
|
],
|
||||||
|
"robot_pose": {
|
||||||
|
"state": {
|
||||||
|
"x": 193.66787382210828,
|
||||||
|
"y": 61.23821620356439,
|
||||||
|
"z": -119.96654664833596,
|
||||||
|
"a": -107.97036991399465,
|
||||||
|
"b": 21.99999999999934,
|
||||||
|
"c": 91.00000000000014,
|
||||||
|
"e": 9.999999999999135
|
||||||
|
},
|
||||||
|
"uncertainty": {
|
||||||
|
"x_mm": 444.1920850838452,
|
||||||
|
"y_mm": 2085.804738658986,
|
||||||
|
"z_mm": 216.50850058940748,
|
||||||
|
"a_deg": 273.19721127112774,
|
||||||
|
"b_deg": 9704.418760205353,
|
||||||
|
"c_deg": 9704.420149983844,
|
||||||
|
"e_mm": 97044.43203671245
|
||||||
|
},
|
||||||
|
"confidence": {
|
||||||
|
"x": 5.116616350285866e-20,
|
||||||
|
"y": 2.598071909247533e-91,
|
||||||
|
"z": 3.9550801677927533e-10,
|
||||||
|
"a": 1.3651987041054823e-12,
|
||||||
|
"b": 0.0,
|
||||||
|
"c": 0.0,
|
||||||
|
"e": 0.36787944117144233
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"camera_poses": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
-77.74926115576925,
|
||||||
|
-73.0492976258425,
|
||||||
|
-147.8089302398559
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-4.597800910443656,
|
||||||
|
-21.216692970790984,
|
||||||
|
5.280603517092184
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
1.3186034894317018,
|
||||||
|
7.494557764024174,
|
||||||
|
-182.12830708406332
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
0.0906907584418463,
|
||||||
|
0.2776617967661424,
|
||||||
|
-0.7247751841355033
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-0.5104408724728655,
|
||||||
|
-3.1865020935343082,
|
||||||
|
-0.7599897453664757
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
0.18575285646205408,
|
||||||
|
0.031786357242087525,
|
||||||
|
-0.7583570784256441
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
0.2258549281270687,
|
||||||
|
-0.3663871310029323,
|
||||||
|
0.6799707410888476
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-3.4730218301317435,
|
||||||
|
0.5992584877895241,
|
||||||
|
0.35858647149574585
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
-0.1367303214358514,
|
||||||
|
0.0014859998076042164,
|
||||||
|
0.7930402247461223
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"marker_positions": [
|
||||||
|
{
|
||||||
|
"marker_id": 210,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.02,
|
||||||
|
-0.02,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.6024643807746477,
|
||||||
|
"mean_reprojection_error_px": 219.44641687346314,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.5042074435920213,
|
||||||
|
"mean_reprojection_error_px": 584.8502187266689,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
610.6060261840274,
|
||||||
|
566.4030412437936,
|
||||||
|
559.1036732421863,
|
||||||
|
603.2881342366682
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.6639531667043603,
|
||||||
|
"mean_reprojection_error_px": 43.90181819055642,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
30.0527741674556,
|
||||||
|
58.738230110803315,
|
||||||
|
62.58264430539862,
|
||||||
|
24.23362417856814
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.6392325320275614,
|
||||||
|
"mean_reprojection_error_px": 29.587213703164036,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
44.13807594611845,
|
||||||
|
1.1413153391747903,
|
||||||
|
31.635331629981838,
|
||||||
|
41.434131897381064
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 211,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.25,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.5607331763105995,
|
||||||
|
"mean_reprojection_error_px": 215.88919812501328,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.5607331763105995,
|
||||||
|
"mean_reprojection_error_px": 215.88919812501328,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
239.060567969831,
|
||||||
|
203.87810525007305,
|
||||||
|
193.21032729986996,
|
||||||
|
227.40779198027906
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 215,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.25,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.6915990015172901,
|
||||||
|
"mean_reprojection_error_px": 185.82669857910088,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.6816798505186958,
|
||||||
|
"mean_reprojection_error_px": 206.76829215011884,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
220.0957850912882,
|
||||||
|
186.544457069329,
|
||||||
|
194.82996305260656,
|
||||||
|
225.60296338725155
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7015181525158845,
|
||||||
|
"mean_reprojection_error_px": 164.88510500808295,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
197.75737719196465,
|
||||||
|
146.9529114839187,
|
||||||
|
132.41058001353588,
|
||||||
|
182.41955134291254
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 214,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.35000000000000003,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.5223267506441452,
|
||||||
|
"mean_reprojection_error_px": 430.28505616193627,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.5223267506441452,
|
||||||
|
"mean_reprojection_error_px": 430.28505616193627,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
435.27929209106946,
|
||||||
|
407.3352893643994,
|
||||||
|
427.77239968551254,
|
||||||
|
450.7532435067638
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 208,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.35000000000000003,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.5511135304821347,
|
||||||
|
"mean_reprojection_error_px": 104.93132940823983,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.5511135304821347,
|
||||||
|
"mean_reprojection_error_px": 104.93132940823983,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
101.05437746855068,
|
||||||
|
84.48184159814882,
|
||||||
|
111.54460519378607,
|
||||||
|
122.64449337247373
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 206,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.65,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.3243040576749168,
|
||||||
|
"mean_reprojection_error_px": 265.1269045088845,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.3243040576749168,
|
||||||
|
"mean_reprojection_error_px": 265.1269045088845,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
246.4689237594987,
|
||||||
|
270.74271590412576,
|
||||||
|
283.7439899333724,
|
||||||
|
259.551988438541
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 205,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.75,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.2813323639079811,
|
||||||
|
"mean_reprojection_error_px": 414.07105145074036,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.2813323639079811,
|
||||||
|
"mean_reprojection_error_px": 414.07105145074036,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
393.31517220079184,
|
||||||
|
414.88055122924146,
|
||||||
|
434.6820277122509,
|
||||||
|
413.40645466067724
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 207,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.75,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.270961511076242,
|
||||||
|
"mean_reprojection_error_px": 361.40219383857607,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.270961511076242,
|
||||||
|
"mean_reprojection_error_px": 361.40219383857607,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
343.99480245991305,
|
||||||
|
365.3864615302313,
|
||||||
|
379.1131712173157,
|
||||||
|
357.11434014684426
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 217,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.65,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.3364649702867649,
|
||||||
|
"mean_reprojection_error_px": 319.9894030743949,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.3364649702867649,
|
||||||
|
"mean_reprojection_error_px": 319.9894030743949,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
297.7784176112965,
|
||||||
|
321.2632775986662,
|
||||||
|
342.12879416557814,
|
||||||
|
318.78712292203863
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 198,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1936678738221083,
|
||||||
|
-0.04630507797358846,
|
||||||
|
0.15710136776571965
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.1708737557403893,
|
||||||
|
"mean_reprojection_error_px": 173.75585674911616,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.20939472520051636,
|
||||||
|
"mean_reprojection_error_px": 250.68402341711896,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
276.58098681044964,
|
||||||
|
248.3015862011881,
|
||||||
|
225.19824045129988,
|
||||||
|
252.6552802055382
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.13235278628026223,
|
||||||
|
"mean_reprojection_error_px": 96.82769008111335,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
86.14028865296076,
|
||||||
|
96.54841237858139,
|
||||||
|
117.89661118485392,
|
||||||
|
86.72544810805726
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 229,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1936678738221083,
|
||||||
|
-0.08961029442397768,
|
||||||
|
0.2359978710354143
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.2199830218620032,
|
||||||
|
"mean_reprojection_error_px": 111.48418089668428,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.2634483409899881,
|
||||||
|
"mean_reprojection_error_px": 158.57789833970793,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
183.23095634369403,
|
||||||
|
159.8980008563882,
|
||||||
|
134.10338119920908,
|
||||||
|
157.07925495954032
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.17651770273401826,
|
||||||
|
"mean_reprojection_error_px": 64.39046345366064,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
53.76348865479547,
|
||||||
|
65.98012218463036,
|
||||||
|
89.14976602616233,
|
||||||
|
48.66847694905442
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 242,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1936678738221083,
|
||||||
|
-0.15097424141151797,
|
||||||
|
0.2023160360184449
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 243,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1936678738221083,
|
||||||
|
-0.13713318542623254,
|
||||||
|
0.24983892702069976
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.783838885722734,
|
||||||
|
"mean_reprojection_error_px": 44.94196369988338,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.753480565397225,
|
||||||
|
"mean_reprojection_error_px": 82.27135266344914,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
104.64641625637452,
|
||||||
|
75.05759016037157,
|
||||||
|
54.73600619528254,
|
||||||
|
94.64539804176789
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7779633248368785,
|
||||||
|
"mean_reprojection_error_px": 17.133880995253847,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
13.52613306369719,
|
||||||
|
20.79198380803457,
|
||||||
|
20.738458388035028,
|
||||||
|
13.478948721248596
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.8200727669340984,
|
||||||
|
"mean_reprojection_error_px": 35.42065744094714,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
43.2051334803787,
|
||||||
|
2.291855750175984,
|
||||||
|
46.29738248992781,
|
||||||
|
49.88825804330606
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 244,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.31866787382210826,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 245,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.2836678738221083,
|
||||||
|
0.029915046239746786,
|
||||||
|
-0.018168379357383857
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 246,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.2836678738221083,
|
||||||
|
-0.029915046239746786,
|
||||||
|
0.018168379357383857
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.5848503519198133,
|
||||||
|
"mean_reprojection_error_px": 71.95677931061337,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.6846593913143055,
|
||||||
|
"mean_reprojection_error_px": 129.77061184774738,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
131.10152630147377,
|
||||||
|
148.01757225834027,
|
||||||
|
132.02269451379757,
|
||||||
|
107.940654317378
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.6496660357669276,
|
||||||
|
"mean_reprojection_error_px": 39.429662635023675,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
54.265219903618416,
|
||||||
|
25.613227047621212,
|
||||||
|
36.12153940947933,
|
||||||
|
41.71866417937572
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.4202256286782067,
|
||||||
|
"mean_reprojection_error_px": 46.67006344906903,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
17.76180722171543,
|
||||||
|
53.41185602621912,
|
||||||
|
87.55552599091499,
|
||||||
|
27.95106455742659
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 247,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.24616787382210828,
|
||||||
|
-0.029915046239746786,
|
||||||
|
0.018168379357383857
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.6103408077771776,
|
||||||
|
"mean_reprojection_error_px": 72.45582056932597,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.7451994373791682,
|
||||||
|
"mean_reprojection_error_px": 109.52982904634278,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
126.13937483803942,
|
||||||
|
126.72281250876043,
|
||||||
|
96.93499026995522,
|
||||||
|
88.3221385686161
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.6388783942274684,
|
||||||
|
"mean_reprojection_error_px": 42.48268761553851,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
66.85934117736593,
|
||||||
|
52.808931850254176,
|
||||||
|
27.34232661092379,
|
||||||
|
22.920150823610143
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.44694459172489637,
|
||||||
|
"mean_reprojection_error_px": 65.35494504609662,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
65.94881395992364,
|
||||||
|
79.79307781356299,
|
||||||
|
80.2686839721486,
|
||||||
|
35.40920443875125
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 124,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.20446625306675464,
|
||||||
|
-0.14213782157794694,
|
||||||
|
-0.16990066121932695
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.38376633052345943,
|
||||||
|
"mean_reprojection_error_px": 119.57328172201322,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.31559423864949376,
|
||||||
|
"mean_reprojection_error_px": 134.82416625853605,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
121.33281341523052,
|
||||||
|
103.50465151028168,
|
||||||
|
148.6485339972427,
|
||||||
|
165.81066611138934
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.4519384223974251,
|
||||||
|
"mean_reprojection_error_px": 104.32239718549039,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
97.26022837754157,
|
||||||
|
65.51154041781498,
|
||||||
|
119.230195720015,
|
||||||
|
135.28762422659
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 122,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.22220435967071417,
|
||||||
|
-0.08656453557061031,
|
||||||
|
-0.16036287025635595
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.5007571026495876,
|
||||||
|
"mean_reprojection_error_px": 100.5976968444762,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.19097609792206996,
|
||||||
|
"mean_reprojection_error_px": 191.04201527524393,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
161.2461896774225,
|
||||||
|
187.67710037351839,
|
||||||
|
219.07478119264024,
|
||||||
|
196.16998985739463
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.8023854845720537,
|
||||||
|
"mean_reprojection_error_px": 37.97273645764808,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
36.73735527126386,
|
||||||
|
66.77343554076022,
|
||||||
|
29.865847324866966,
|
||||||
|
18.51430769370129
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.5089097254546391,
|
||||||
|
"mean_reprojection_error_px": 72.77833880053663,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
79.87926104625087,
|
||||||
|
114.41534929802285,
|
||||||
|
63.25062596802093,
|
||||||
|
33.56811888985185
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 218,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.18286949457746193,
|
||||||
|
-0.029683137487597516,
|
||||||
|
-0.11301020464799262
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 101,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1624892762454371,
|
||||||
|
-0.0808799499047689,
|
||||||
|
-0.16381530379227105
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.30496613331262606,
|
||||||
|
"mean_reprojection_error_px": 266.775950196611,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.30496613331262606,
|
||||||
|
"mean_reprojection_error_px": 266.775950196611,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
251.08509253516107,
|
||||||
|
234.8164115324836,
|
||||||
|
283.5537072385155,
|
||||||
|
297.64858948028376
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 102,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.17776126974857978,
|
||||||
|
-0.12112440660686963,
|
||||||
|
-0.13937353791513551
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.7291583793913716,
|
||||||
|
"mean_reprojection_error_px": 139.1860336202907,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.8030005342619584,
|
||||||
|
"mean_reprojection_error_px": 274.10231703594735,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
237.94975046516782,
|
||||||
|
264.59434010512416,
|
||||||
|
308.4630690582601,
|
||||||
|
285.4021085152374
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7644345761691943,
|
||||||
|
"mean_reprojection_error_px": 72.4273672978813,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
46.62612295920637,
|
||||||
|
34.03763203823939,
|
||||||
|
100.37413904993618,
|
||||||
|
108.67157514414326
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.6200400277429619,
|
||||||
|
"mean_reprojection_error_px": 71.02841652704336,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
102.37939191515659,
|
||||||
|
31.002903116690042,
|
||||||
|
42.58563098754617,
|
||||||
|
108.14574008878068
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 219,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.18286949457746193,
|
||||||
|
-0.08522646866588529,
|
||||||
|
-0.20446477458093276
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
2144
pipeline/multiview_pose_neu.json
Normal file
2144
pipeline/multiview_pose_neu.json
Normal file
File diff suppressed because it is too large
Load Diff
966
pipeline/multiview_pose_ohne_confidence.json
Normal file
966
pipeline/multiview_pose_ohne_confidence.json
Normal file
@@ -0,0 +1,966 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"created_utc": "2026-05-28T21:06:06.508985Z",
|
||||||
|
"source_robot_json": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json",
|
||||||
|
"source_detections": [
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json"
|
||||||
|
],
|
||||||
|
"robot_pose": {
|
||||||
|
"state": {
|
||||||
|
"x": 178.4429017494112,
|
||||||
|
"y": 58.47337818356634,
|
||||||
|
"z": -115.44834336922801,
|
||||||
|
"a": -110.23287555471994,
|
||||||
|
"b": 21.99999999999879,
|
||||||
|
"c": 91.00000000000222,
|
||||||
|
"e": 10.000000000003752
|
||||||
|
},
|
||||||
|
"uncertainty": {
|
||||||
|
"x_mm": 452.93520846643963,
|
||||||
|
"y_mm": 2505.6853087014742,
|
||||||
|
"z_mm": 479.7547548672694,
|
||||||
|
"a_deg": 419.0184635434338,
|
||||||
|
"b_deg": 10357.838151169784,
|
||||||
|
"c_deg": 10357.838358456424,
|
||||||
|
"e_mm": 103578.27625405855
|
||||||
|
},
|
||||||
|
"confidence": {
|
||||||
|
"x": 2.1343902596830898e-20,
|
||||||
|
"y": 1.5117142415373693e-109,
|
||||||
|
"z": 1.460547647110293e-21,
|
||||||
|
"a": 6.342483509852416e-19,
|
||||||
|
"b": 0.0,
|
||||||
|
"c": 0.0,
|
||||||
|
"e": 0.36787944117144233
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"camera_poses": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
-152.34091569714903,
|
||||||
|
-19.013446299323782,
|
||||||
|
-99.72128351445228
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-5.499131960772023,
|
||||||
|
-21.91270066095422,
|
||||||
|
3.835322497331174
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
1.5121559708775556,
|
||||||
|
7.396415796967461,
|
||||||
|
-182.91147186295072
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 17
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
0.08962448933807897,
|
||||||
|
0.30899658581804224,
|
||||||
|
-0.6948843092613544
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-0.523540707058038,
|
||||||
|
-3.160660993109504,
|
||||||
|
-0.8426789395152159
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
0.17310377758414208,
|
||||||
|
0.03144977103054412,
|
||||||
|
-0.7452661514399899
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 10
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"camera_id": "cam1",
|
||||||
|
"camera_position_world_m": [
|
||||||
|
0.19930315742175841,
|
||||||
|
-0.4034279127468583,
|
||||||
|
0.6690051405865579
|
||||||
|
],
|
||||||
|
"rvec": [
|
||||||
|
-3.515407873484386,
|
||||||
|
0.6592488811642211,
|
||||||
|
0.3554510183912642
|
||||||
|
],
|
||||||
|
"tvec": [
|
||||||
|
-0.12629764596438484,
|
||||||
|
0.0026714931553666757,
|
||||||
|
0.7962948418902289
|
||||||
|
],
|
||||||
|
"intrinsics": {
|
||||||
|
"camera_matrix": [
|
||||||
|
[
|
||||||
|
1777.77783203125,
|
||||||
|
0.0,
|
||||||
|
640.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
1500.0,
|
||||||
|
360.0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
1.0
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"distortion_coefficients": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"observation_count": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"marker_positions": [
|
||||||
|
{
|
||||||
|
"marker_id": 210,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.02,
|
||||||
|
-0.02,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.6997700579082148,
|
||||||
|
"mean_reprojection_error_px": 219.98520262396713,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.5673043275049208,
|
||||||
|
"mean_reprojection_error_px": 582.6928891559149,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
608.4008310831335,
|
||||||
|
564.1978138064919,
|
||||||
|
556.9944685718621,
|
||||||
|
601.1784431621722
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7813266383036261,
|
||||||
|
"mean_reprojection_error_px": 39.955946109809304,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
50.05440600297249,
|
||||||
|
62.06318313444874,
|
||||||
|
46.054117736318254,
|
||||||
|
1.6520775654977295
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.7506792079160973,
|
||||||
|
"mean_reprojection_error_px": 37.30677260617712,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
63.966234566185975,
|
||||||
|
24.372791532782248,
|
||||||
|
11.26324714586789,
|
||||||
|
49.62481717987238
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 211,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.25,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.6412317666518965,
|
||||||
|
"mean_reprojection_error_px": 213.01123894247837,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.6412317666518965,
|
||||||
|
"mean_reprojection_error_px": 213.01123894247837,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
236.1031649719927,
|
||||||
|
200.91925610460427,
|
||||||
|
190.4144522672059,
|
||||||
|
224.6080824261106
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 215,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.25,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.8108527003549935,
|
||||||
|
"mean_reprojection_error_px": 198.10515794746527,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.7952557920267838,
|
||||||
|
"mean_reprojection_error_px": 204.57188538155356,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
217.65412604361978,
|
||||||
|
184.2181761177656,
|
||||||
|
192.91255827135913,
|
||||||
|
223.50268109346976
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.826449608683203,
|
||||||
|
"mean_reprojection_error_px": 191.63843051337702,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
226.90990661141322,
|
||||||
|
176.45008691276055,
|
||||||
|
156.32935430702966,
|
||||||
|
206.86437422230463
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 214,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.35000000000000003,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.6041252446922665,
|
||||||
|
"mean_reprojection_error_px": 443.42359136317395,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.6041252446922665,
|
||||||
|
"mean_reprojection_error_px": 443.42359136317395,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
448.5785815165172,
|
||||||
|
422.5680699902232,
|
||||||
|
440.44289571629525,
|
||||||
|
462.10481822966005
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 208,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.35000000000000003,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.6280424706698967,
|
||||||
|
"mean_reprojection_error_px": 104.44146786678962,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.6280424706698967,
|
||||||
|
"mean_reprojection_error_px": 104.44146786678962,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
99.73569678834396,
|
||||||
|
84.11026583409073,
|
||||||
|
111.81629138739532,
|
||||||
|
122.10361745732845
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 206,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.65,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.33820423087105295,
|
||||||
|
"mean_reprojection_error_px": 269.1974700827843,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.33820423087105295,
|
||||||
|
"mean_reprojection_error_px": 269.1974700827843,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
250.50345115135508,
|
||||||
|
274.79359034013294,
|
||||||
|
287.8396527117673,
|
||||||
|
263.6531861278821
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 205,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.75,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.28724459014346065,
|
||||||
|
"mean_reprojection_error_px": 418.6572411272094,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.28724459014346065,
|
||||||
|
"mean_reprojection_error_px": 418.6572411272094,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
397.9342521865105,
|
||||||
|
419.4901443591502,
|
||||||
|
439.2360756416858,
|
||||||
|
417.96849232149117
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 207,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.75,
|
||||||
|
-0.01,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.27228561618555186,
|
||||||
|
"mean_reprojection_error_px": 365.7887375890425,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.27228561618555186,
|
||||||
|
"mean_reprojection_error_px": 365.7887375890425,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
348.36626075065936,
|
||||||
|
369.7628063312705,
|
||||||
|
383.5097638627369,
|
||||||
|
361.51611941150327
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 217,
|
||||||
|
"link_name": "Board",
|
||||||
|
"position_world_m": [
|
||||||
|
0.65,
|
||||||
|
-0.09,
|
||||||
|
0.0003
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.35596573288593486,
|
||||||
|
"mean_reprojection_error_px": 324.2590328208219,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.35596573288593486,
|
||||||
|
"mean_reprojection_error_px": 324.2590328208219,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
302.09103568606685,
|
||||||
|
325.56745248682427,
|
||||||
|
346.3582043745394,
|
||||||
|
323.01943873585714
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 198,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1784429017494112,
|
||||||
|
-0.05382924293825046,
|
||||||
|
0.15468488162937843
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.16652042240877096,
|
||||||
|
"mean_reprojection_error_px": 173.03325228464936,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.20150745250271476,
|
||||||
|
"mean_reprojection_error_px": 247.8149724280193,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
273.59743355359444,
|
||||||
|
245.42488018268014,
|
||||||
|
222.46143771784855,
|
||||||
|
249.77613825795413
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.13153339231482716,
|
||||||
|
"mean_reprojection_error_px": 98.25153214127941,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
86.59176111374053,
|
||||||
|
97.5474468583387,
|
||||||
|
120.03541185467505,
|
||||||
|
88.83150873836338
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 229,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1784429017494112,
|
||||||
|
-0.100889763924023,
|
||||||
|
0.23140063858026605
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.2274747191640703,
|
||||||
|
"mean_reprojection_error_px": 110.21276026061363,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.26810902547334975,
|
||||||
|
"mean_reprojection_error_px": 155.73254773328267,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
180.08971330368811,
|
||||||
|
157.10232370463987,
|
||||||
|
131.63039756460225,
|
||||||
|
154.10775636020045
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.18684041285479083,
|
||||||
|
"mean_reprojection_error_px": 64.69297278794461,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
54.16045919019677,
|
||||||
|
65.65659031871526,
|
||||||
|
89.34107228321712,
|
||||||
|
49.613769359649325
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 242,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1784429017494112,
|
||||||
|
-0.1605575748858245,
|
||||||
|
0.19479801114688738
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 243,
|
||||||
|
"link_name": "Arm1",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1784429017494112,
|
||||||
|
-0.14902498312161308,
|
||||||
|
0.24293323034447747
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.9174550709990235,
|
||||||
|
"mean_reprojection_error_px": 44.17934855555228,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.8899728634608616,
|
||||||
|
"mean_reprojection_error_px": 78.81046188174007,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
101.13197340765323,
|
||||||
|
72.09114541671448,
|
||||||
|
50.99718387664307,
|
||||||
|
91.0215448259495
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.9132502955127223,
|
||||||
|
"mean_reprojection_error_px": 16.837449190455786,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
14.377732738720587,
|
||||||
|
19.045826627822404,
|
||||||
|
19.362443297536196,
|
||||||
|
14.563794097743955
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.9491420540234864,
|
||||||
|
"mean_reprojection_error_px": 36.89013459446098,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
44.535439947724775,
|
||||||
|
1.0820871934520055,
|
||||||
|
48.70513064720212,
|
||||||
|
53.23788058946502
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 244,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.3034429017494112,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 245,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.2684429017494112,
|
||||||
|
0.02934513796721045,
|
||||||
|
-0.019075190108761277
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 246,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.2684429017494112,
|
||||||
|
-0.02934513796721045,
|
||||||
|
0.019075190108761277
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.6792447690577089,
|
||||||
|
"mean_reprojection_error_px": 75.53581325353834,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.8015179238063419,
|
||||||
|
"mean_reprojection_error_px": 130.96780196095426,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
131.52551717948674,
|
||||||
|
149.219632082114,
|
||||||
|
133.91939902794635,
|
||||||
|
109.2066595542699
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7581700566520715,
|
||||||
|
"mean_reprojection_error_px": 40.470485652838775,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
54.08557804238714,
|
||||||
|
23.05261314724495,
|
||||||
|
38.690970054967124,
|
||||||
|
46.052781366755866
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.4780463267147134,
|
||||||
|
"mean_reprojection_error_px": 55.16915214682197,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
4.544326976345962,
|
||||||
|
67.214794241062,
|
||||||
|
103.3039176555024,
|
||||||
|
45.61356971437756
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 247,
|
||||||
|
"link_name": "Ellbow",
|
||||||
|
"position_world_m": [
|
||||||
|
0.23094290174941118,
|
||||||
|
-0.02934513796721045,
|
||||||
|
0.019075190108761277
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.7117000257529918,
|
||||||
|
"mean_reprojection_error_px": 71.76825320561402,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.8796777163094818,
|
||||||
|
"mean_reprojection_error_px": 109.30571474994146,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
125.10305772921419,
|
||||||
|
126.687286672494,
|
||||||
|
97.67977416118754,
|
||||||
|
87.7527404368701
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.7448005120854795,
|
||||||
|
"mean_reprojection_error_px": 41.53123348024193,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
62.91946766510497,
|
||||||
|
50.39956997542899,
|
||||||
|
30.245119355881712,
|
||||||
|
22.56077692455205
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.5106218488640142,
|
||||||
|
"mean_reprojection_error_px": 64.46781138665867,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
53.21697434340821,
|
||||||
|
81.71744187395875,
|
||||||
|
89.31098122106415,
|
||||||
|
33.62584810820357
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 124,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1905471840415046,
|
||||||
|
-0.14689057815962717,
|
||||||
|
-0.16571856986506908
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 2,
|
||||||
|
"mean_confidence": 0.4106676690676095,
|
||||||
|
"mean_reprojection_error_px": 110.73873242579455,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.34091855704160245,
|
||||||
|
"mean_reprojection_error_px": 135.68416308738642,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
122.09592215917837,
|
||||||
|
104.54432626694273,
|
||||||
|
149.5516106827851,
|
||||||
|
166.54479324063954
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.4804167810936165,
|
||||||
|
"mean_reprojection_error_px": 85.79330176420268,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
80.25443845468878,
|
||||||
|
44.769514846082515,
|
||||||
|
101.1257798229199,
|
||||||
|
117.02347393311956
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 122,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.2065917439726603,
|
||||||
|
-0.09049216814460652,
|
||||||
|
-0.1582492027972934
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.5690911450460799,
|
||||||
|
"mean_reprojection_error_px": 112.17658015146829,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.18665602922528673,
|
||||||
|
"mean_reprojection_error_px": 193.21424613028685,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
163.5154782260392,
|
||||||
|
189.8197178899714,
|
||||||
|
221.16013279196892,
|
||||||
|
198.36165561316793
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.9456847621099602,
|
||||||
|
"mean_reprojection_error_px": 59.619753741849934,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
62.57446736991388,
|
||||||
|
94.35842345190635,
|
||||||
|
52.23720146118015,
|
||||||
|
29.308922684399352
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.5749326438029929,
|
||||||
|
"mean_reprojection_error_px": 83.69574058226807,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
95.82678742449872,
|
||||||
|
125.24546180132528,
|
||||||
|
68.46920157539058,
|
||||||
|
45.24151152785769
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 218,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.16633861945731776,
|
||||||
|
-0.03350621972608657,
|
||||||
|
-0.1118025920534069
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 101,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.146660650151536,
|
||||||
|
-0.08689676917303694,
|
||||||
|
-0.16058631632445308
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 1,
|
||||||
|
"mean_confidence": 0.3325004465415643,
|
||||||
|
"mean_reprojection_error_px": 267.5418618263792,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.3325004465415643,
|
||||||
|
"mean_reprojection_error_px": 267.5418618263792,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
251.71309778665415,
|
||||||
|
235.71974142034514,
|
||||||
|
284.44360570880553,
|
||||||
|
298.291002389712
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 102,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.1637795636789253,
|
||||||
|
-0.12583826165265122,
|
||||||
|
-0.13527321767766717
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 3,
|
||||||
|
"mean_confidence": 0.8473641170835915,
|
||||||
|
"mean_reprojection_error_px": 135.5185983259116,
|
||||||
|
"observations": [
|
||||||
|
{
|
||||||
|
"view_index": 0,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a.png",
|
||||||
|
"confidence": 0.9494437061622057,
|
||||||
|
"mean_reprojection_error_px": 275.69603657656097,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
239.57198324805327,
|
||||||
|
266.35716033620514,
|
||||||
|
310.0207932359541,
|
||||||
|
286.8342094860313
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 1,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b.png",
|
||||||
|
"confidence": 0.8913851020933449,
|
||||||
|
"mean_reprojection_error_px": 62.952236808026484,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
32.059770721250835,
|
||||||
|
38.09936127795048,
|
||||||
|
90.48302392022927,
|
||||||
|
91.16679131267536
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"view_index": 2,
|
||||||
|
"source_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json",
|
||||||
|
"image_file": "C:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c.png",
|
||||||
|
"confidence": 0.7012635429952238,
|
||||||
|
"mean_reprojection_error_px": 67.90752159314737,
|
||||||
|
"corner_reprojection_errors_px": [
|
||||||
|
102.72093538416912,
|
||||||
|
41.99761561398122,
|
||||||
|
26.866949386310086,
|
||||||
|
100.04458598812904
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"marker_id": 219,
|
||||||
|
"link_name": "Arm2",
|
||||||
|
"position_world_m": [
|
||||||
|
0.16633861945731776,
|
||||||
|
-0.09182180091572817,
|
||||||
|
-0.201514870981736
|
||||||
|
],
|
||||||
|
"size_m": 0.025,
|
||||||
|
"observation_count": 0,
|
||||||
|
"mean_confidence": null,
|
||||||
|
"mean_reprojection_error_px": null,
|
||||||
|
"observations": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
72
pipeline/multiview_pose_summary.json
Normal file
72
pipeline/multiview_pose_summary.json
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
{
|
||||||
|
"schema_version": "1.0",
|
||||||
|
"created_utc": "2026-05-28T21:18:54.942233Z",
|
||||||
|
"source_robot_json": "c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\robot.json",
|
||||||
|
"source_detections": [
|
||||||
|
"c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1a_aruco_detection.json",
|
||||||
|
"c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1b_aruco_detection.json",
|
||||||
|
"c:\\Users\\kech\\SynologyDrive\\2026-AppServer-AppRobot\\appRobotRendering\\pipeline\\render_1c_aruco_detection.json"
|
||||||
|
],
|
||||||
|
"solver": {
|
||||||
|
"final_cost": 26976.87543820547,
|
||||||
|
"status": 0,
|
||||||
|
"message": "The maximum number of function evaluations is exceeded."
|
||||||
|
},
|
||||||
|
"robot_pose": {
|
||||||
|
"state": {
|
||||||
|
"x": 100.21525656040845,
|
||||||
|
"y": 29.397626553503173,
|
||||||
|
"z": -36.04825291833897,
|
||||||
|
"a": -120.21337951359496,
|
||||||
|
"b": 22.00000000000008,
|
||||||
|
"c": 90.99999999999989,
|
||||||
|
"e": 10.000000000000258
|
||||||
|
},
|
||||||
|
"uncertainty": {
|
||||||
|
"x_mm": 17194.235819424397,
|
||||||
|
"y_mm": 6014.1408132596725,
|
||||||
|
"z_mm": 3621.298501957444,
|
||||||
|
"a_deg": 8796.009134059024,
|
||||||
|
"b_deg": 12562.017200522667,
|
||||||
|
"c_deg": 12562.017199380893,
|
||||||
|
"e_mm": 125620.17196327279
|
||||||
|
},
|
||||||
|
"confidence": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 6.444409678452354e-262,
|
||||||
|
"z": 5.358019964976179e-158,
|
||||||
|
"a": 0.0,
|
||||||
|
"b": 0.0,
|
||||||
|
"c": 0.0,
|
||||||
|
"e": 0.36787944117144233
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"statistics": {
|
||||||
|
"observation_count": 34,
|
||||||
|
"camera_count": 3,
|
||||||
|
"marker_count": 23,
|
||||||
|
"observed_marker_count": 18,
|
||||||
|
"mean_detector_confidence": 0.5871913728875101,
|
||||||
|
"mean_weighted_confidence": 0.5774319023193206,
|
||||||
|
"mean_reprojection_error_px": 236.06726174073253,
|
||||||
|
"quality_means": {
|
||||||
|
"size": 0.8143773112577551,
|
||||||
|
"aspect": 0.834768084426124,
|
||||||
|
"border": 0.9237745098039215,
|
||||||
|
"homography": 0.7934741744632792
|
||||||
|
},
|
||||||
|
"quality_config": {
|
||||||
|
"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.01,
|
||||||
|
"aspect_factor": 0.01,
|
||||||
|
"border_factor": 0.01,
|
||||||
|
"center_factor": 0.01,
|
||||||
|
"sharpness_factor": 0.01,
|
||||||
|
"homography_factor": 0.01
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"created_utc": "2026-05-28T20:31:58Z",
|
"created_utc": "2026-05-28T21:11:29Z",
|
||||||
"vision_config": {
|
"vision_config": {
|
||||||
"MarkerType": "DICT_4X4_250",
|
"MarkerType": "DICT_4X4_250",
|
||||||
"MarkerSize": 0.025
|
"MarkerSize": 0.025
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
"detections": [
|
"detections": [
|
||||||
{
|
{
|
||||||
"observation_id": "11b87d79-31e1-4293-abe2-4327e2021077",
|
"observation_id": "e4f9bd9f-a867-4d2d-b0d3-c438f92fa01f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 102,
|
"marker_id": 102,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"confidence": 0.9494437061622057
|
"confidence": 0.9494437061622057
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "3f6119be-7ed5-4ba2-b0b8-f2eb85c0f4ac",
|
"observation_id": "3669c72d-6c64-46a9-9041-e0f332ed19a3",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 243,
|
"marker_id": 243,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"confidence": 0.8899728634608616
|
"confidence": 0.8899728634608616
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "a963cd6a-dea8-47d2-ba29-7410d1add84e",
|
"observation_id": "920dccb3-6db8-43fb-b0b3-036af4ed6acb",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 210,
|
"marker_id": 210,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
"confidence": 0.5673043275049208
|
"confidence": 0.5673043275049208
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "5da8c3b3-2b28-47b4-9132-c5225c5ba0bb",
|
"observation_id": "858fbb79-58d9-436a-9147-96b0a15fd61e",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 247,
|
"marker_id": 247,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -262,7 +262,7 @@
|
|||||||
"confidence": 0.8796777163094818
|
"confidence": 0.8796777163094818
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "b765e797-5dd9-4123-ac41-85b5606132a0",
|
"observation_id": "cafb1327-eefa-457c-9b1e-ed2d2ad78a8c",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 246,
|
"marker_id": 246,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
"confidence": 0.8015179238063419
|
"confidence": 0.8015179238063419
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "aca8152b-525f-45e7-9849-c46d2123a21c",
|
"observation_id": "cb014fcc-1cbe-4e59-afaa-6ea599b7f5ac",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 101,
|
"marker_id": 101,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -370,7 +370,7 @@
|
|||||||
"confidence": 0.3325004465415643
|
"confidence": 0.3325004465415643
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "f0a37d05-7187-4ae1-a53a-2b0260b751b4",
|
"observation_id": "a59f4cec-6038-48f8-b124-11a1b6e89cf3",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 215,
|
"marker_id": 215,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
"confidence": 0.7952557920267838
|
"confidence": 0.7952557920267838
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "38cfa10c-246e-439d-ba19-ab664d030ccc",
|
"observation_id": "0d4d4912-7df5-4189-80d0-cc1066edd2e1",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 124,
|
"marker_id": 124,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -478,7 +478,7 @@
|
|||||||
"confidence": 0.34091855704160245
|
"confidence": 0.34091855704160245
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "95edc7e5-0883-4ab5-8b6a-1f35d392d98a",
|
"observation_id": "47b5571d-68d0-4738-93c5-06c45c91168f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 229,
|
"marker_id": 229,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
"confidence": 0.26810902547334975
|
"confidence": 0.26810902547334975
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "602d2bac-2427-4211-86be-9a2b5905a4e4",
|
"observation_id": "3a5f11f4-1233-41b7-9ce8-db0acaa2855e",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 122,
|
"marker_id": 122,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -586,7 +586,7 @@
|
|||||||
"confidence": 0.18665602922528673
|
"confidence": 0.18665602922528673
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "308e1529-24a2-49e0-a7ad-6ac4fdead845",
|
"observation_id": "76947529-1679-4274-b547-56c7d61dde94",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 198,
|
"marker_id": 198,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -640,7 +640,7 @@
|
|||||||
"confidence": 0.20150745250271476
|
"confidence": 0.20150745250271476
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "0159ed42-fec0-4b1c-a383-9ca83f5a320f",
|
"observation_id": "7a157cbe-69a0-4f8d-b89d-af82d8470779",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 211,
|
"marker_id": 211,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -694,7 +694,7 @@
|
|||||||
"confidence": 0.6412317666518965
|
"confidence": 0.6412317666518965
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "4fa2ceb4-7915-4dba-b0f8-9700378eec0b",
|
"observation_id": "19c6229a-77c2-4846-86c2-2fe3cabd5ace",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 208,
|
"marker_id": 208,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -748,7 +748,7 @@
|
|||||||
"confidence": 0.6280424706698967
|
"confidence": 0.6280424706698967
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "d3bc0898-720e-4180-b70a-57a9adc4a30e",
|
"observation_id": "cf1fe906-7f6e-459b-96cc-a983a15d58b7",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 217,
|
"marker_id": 217,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -802,7 +802,7 @@
|
|||||||
"confidence": 0.35596573288593486
|
"confidence": 0.35596573288593486
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "d0d5506c-abf1-495f-bce9-33d4b374e599",
|
"observation_id": "249e0b3c-e2b1-40e4-baf8-073d85acc08c",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 206,
|
"marker_id": 206,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -856,7 +856,7 @@
|
|||||||
"confidence": 0.33820423087105295
|
"confidence": 0.33820423087105295
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "7b5443c9-fb76-4270-8c88-32c5a20953c1",
|
"observation_id": "152dc984-1ca4-4ca0-aad7-1bbbd8aba04f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 205,
|
"marker_id": 205,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -910,7 +910,7 @@
|
|||||||
"confidence": 0.28724459014346065
|
"confidence": 0.28724459014346065
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "ac8f8ef1-039a-4762-a4f6-8bdea9e3a4e4",
|
"observation_id": "66ce6541-9898-4bfc-9201-0d05fa7e6e62",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 207,
|
"marker_id": 207,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"created_utc": "2026-05-28T20:31:58Z",
|
"created_utc": "2026-05-28T21:11:29Z",
|
||||||
"vision_config": {
|
"vision_config": {
|
||||||
"MarkerType": "DICT_4X4_250",
|
"MarkerType": "DICT_4X4_250",
|
||||||
"MarkerSize": 0.025
|
"MarkerSize": 0.025
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
"detections": [
|
"detections": [
|
||||||
{
|
{
|
||||||
"observation_id": "e2619c8d-750d-4334-9b42-cb95426ed979",
|
"observation_id": "e57e88d8-4e83-459d-aacb-54dfb3d1a39f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 102,
|
"marker_id": 102,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"confidence": 0.8913851020933449
|
"confidence": 0.8913851020933449
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "d1d011f0-a579-448e-ace9-ea148ecbc5e7",
|
"observation_id": "3bb219ef-c7ff-4f0b-8cfa-284ead393c38",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 124,
|
"marker_id": 124,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"confidence": 0.4804167810936165
|
"confidence": 0.4804167810936165
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "1b5435e6-7883-434c-a9c7-63f4b8d750d1",
|
"observation_id": "26604964-f6c1-492f-964c-850826bc69cf",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 243,
|
"marker_id": 243,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
"confidence": 0.9132502955127223
|
"confidence": 0.9132502955127223
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "a673d83e-73ff-4d2d-a412-470b6da12d56",
|
"observation_id": "5feff871-616f-43e8-b817-f5d67116b577",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 122,
|
"marker_id": 122,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -262,7 +262,7 @@
|
|||||||
"confidence": 0.9456847621099602
|
"confidence": 0.9456847621099602
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "a755c678-9ad1-4af2-a744-a3c1a8ae0596",
|
"observation_id": "957db844-bb09-47b0-95a9-c2703a9f8a07",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 247,
|
"marker_id": 247,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
"confidence": 0.7448005120854795
|
"confidence": 0.7448005120854795
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "38397299-767f-490d-a47f-a3f09635e995",
|
"observation_id": "ce61eb47-425e-45a1-8079-a8da9414a07b",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 246,
|
"marker_id": 246,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -370,7 +370,7 @@
|
|||||||
"confidence": 0.7581700566520715
|
"confidence": 0.7581700566520715
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "273cc5e9-e6e4-4359-8fb1-02a33a935f0d",
|
"observation_id": "408b46a0-047b-4718-bc75-25fe6edef9ed",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 215,
|
"marker_id": 215,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
"confidence": 0.826449608683203
|
"confidence": 0.826449608683203
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "8da984fe-3152-43d6-8a39-b4e9266c2184",
|
"observation_id": "e971c366-cf09-4c98-8aba-6778415b54e2",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 210,
|
"marker_id": 210,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -478,7 +478,7 @@
|
|||||||
"confidence": 0.7813266383036261
|
"confidence": 0.7813266383036261
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "92d1be16-65a4-4855-a63c-4d21357d0a9d",
|
"observation_id": "898e0c56-a4d2-4cbf-945e-1fa1cbec6ad5",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 229,
|
"marker_id": 229,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -532,7 +532,7 @@
|
|||||||
"confidence": 0.18684041285479083
|
"confidence": 0.18684041285479083
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "5cbcbaf2-7581-4f25-81e3-85b4ce5a03b2",
|
"observation_id": "87adf180-14ed-459f-8d33-64e48ff2b517",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 198,
|
"marker_id": 198,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"schema_version": "1.0",
|
"schema_version": "1.0",
|
||||||
"created_utc": "2026-05-28T20:31:59Z",
|
"created_utc": "2026-05-28T21:11:30Z",
|
||||||
"vision_config": {
|
"vision_config": {
|
||||||
"MarkerType": "DICT_4X4_250",
|
"MarkerType": "DICT_4X4_250",
|
||||||
"MarkerSize": 0.025
|
"MarkerSize": 0.025
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
"detections": [
|
"detections": [
|
||||||
{
|
{
|
||||||
"observation_id": "6df4525f-58c7-4757-8a5e-130486fc123b",
|
"observation_id": "55dad250-7787-4d70-b26b-51bf9c63187f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 102,
|
"marker_id": 102,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -100,7 +100,7 @@
|
|||||||
"confidence": 0.7012635429952238
|
"confidence": 0.7012635429952238
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "c4dd28db-6041-4464-867a-96b25d671918",
|
"observation_id": "5af894e7-68a0-4872-ada4-689be1d630b6",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 122,
|
"marker_id": 122,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"confidence": 0.5749326438029929
|
"confidence": 0.5749326438029929
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "5bdf65ad-9a27-4a28-be1e-6e925df61c72",
|
"observation_id": "47253afe-906c-444d-af3f-db6e99351519",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 243,
|
"marker_id": 243,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -208,7 +208,7 @@
|
|||||||
"confidence": 0.9491420540234864
|
"confidence": 0.9491420540234864
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "37b9dec9-2578-4765-a40a-516fbe0424f5",
|
"observation_id": "4b090e16-a2e9-4cb6-81e4-307a04f04820",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 246,
|
"marker_id": 246,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -262,7 +262,7 @@
|
|||||||
"confidence": 0.4780463267147134
|
"confidence": 0.4780463267147134
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "a49c96d9-7884-4ddb-8c07-8f86130b5220",
|
"observation_id": "8d0cb44f-1cd1-41a3-a6bc-c39527148ec7",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 247,
|
"marker_id": 247,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
"confidence": 0.5106218488640142
|
"confidence": 0.5106218488640142
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "4da1fc9d-a7a9-4dd8-a39b-a667b8d5d165",
|
"observation_id": "cde0d217-048d-42a3-88bd-52c1d620984f",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 214,
|
"marker_id": 214,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
@@ -370,7 +370,7 @@
|
|||||||
"confidence": 0.6041252446922665
|
"confidence": 0.6041252446922665
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"observation_id": "a88abb43-d181-4584-b59b-28c65a2fff45",
|
"observation_id": "1e6148fd-0380-48d2-93f9-b4a546a35a7c",
|
||||||
"type": "aruco",
|
"type": "aruco",
|
||||||
"marker_id": 210,
|
"marker_id": 210,
|
||||||
"marker_size_m": 0.025,
|
"marker_size_m": 0.025,
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ python3 1_detect_aruco_observations.py --image render_1a.png -npz render.npz -ro
|
|||||||
python3 1_detect_aruco_observations.py --image render_1b.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir .
|
python3 1_detect_aruco_observations.py --image render_1b.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir .
|
||||||
python3 1_detect_aruco_observations.py --image render_1c.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir .
|
python3 1_detect_aruco_observations.py --image render_1c.png -npz render.npz -robot ../robot.json -cameraId cam1 -outDir .
|
||||||
|
|
||||||
python3 2_Multiview.py --robot ../robot.json --detections render_1a_aruco_detection.json render_1b_aruco_detection.json render_1c_aruco_detection.json --outDir .
|
python3 2_Multiview_ersteVersion.py --robot ../robot.json --detections render_1a_aruco_detection.json render_1b_aruco_detection.json render_1c_aruco_detection.json --outDir .
|
||||||
|
|||||||
Reference in New Issue
Block a user