Claude studie mit mm
This commit is contained in:
@@ -93,7 +93,7 @@ Zusätzlich wird ein Boxplot gespeichert:
|
||||
python benchmark/camera_count/analyze.py --metric max_abs_deg
|
||||
```
|
||||
|
||||
Verfügbare Metriken: `mean_abs_deg`, `max_abs_deg`, `mean_abs_mm`, `max_abs_mm`
|
||||
Verfügbare Metriken: `finger_error_mm` (Standard), `mean_abs_deg`, `max_abs_deg`, `mean_abs_mm`, `max_abs_mm`
|
||||
|
||||
---
|
||||
|
||||
@@ -102,7 +102,7 @@ Verfügbare Metriken: `mean_abs_deg`, `max_abs_deg`, `mean_abs_mm`, `max_abs_mm`
|
||||
| Pfad | Inhalt |
|
||||
|---|---|
|
||||
| `benchmark/camera_count/results/camera_study.json` | Alle Einzelergebnisse (Szene, k, Subset, Fehler) |
|
||||
| `benchmark/camera_count/results/camera_study.csv` | Dasselbe als CSV (Excel, Pandas) |
|
||||
| `benchmark/camera_count/results/camera_study.csv` | Dasselbe als CSV — Spalten inkl. `finger_error_mm` |
|
||||
| `benchmark/camera_count/results/camera_count_vs_error.png` | Boxplot |
|
||||
| `data/camera_study/SceneX/k3_abc/` | Pipeline-Zwischenergebnisse pro Subset |
|
||||
|
||||
|
||||
@@ -92,9 +92,10 @@ def main() -> None:
|
||||
ap = argparse.ArgumentParser(description="Auswertung der Kamera-Anzahl-Studie")
|
||||
ap.add_argument("--input", default=str(RESULTS_DIR / "camera_study.json"))
|
||||
ap.add_argument("--metric",
|
||||
choices=["mean_abs_deg", "max_abs_deg", "mean_abs_mm", "max_abs_mm"],
|
||||
default="mean_abs_deg",
|
||||
help="Metrik für Tabelle und Plot (Standard: mean_abs_deg)")
|
||||
choices=["mean_abs_deg", "max_abs_deg", "mean_abs_mm", "max_abs_mm",
|
||||
"finger_error_mm"],
|
||||
default="finger_error_mm",
|
||||
help="Metrik für Tabelle und Plot (Standard: finger_error_mm)")
|
||||
ap.add_argument("--out-plot",
|
||||
default=str(RESULTS_DIR / "camera_count_vs_error.png"))
|
||||
args = ap.parse_args()
|
||||
|
||||
@@ -76,12 +76,13 @@ def run_pipeline(scene_dir: Path, cams: list[str], eval_dir: Path, robot: str) -
|
||||
return True
|
||||
|
||||
|
||||
def eval_pose(robot_state: Path, gt: Path) -> dict | None:
|
||||
def eval_pose(robot_state: Path, gt: Path, robot: str) -> dict | None:
|
||||
out = robot_state.with_suffix(".eval.json")
|
||||
cmd = [
|
||||
PY, str(ROOT / "benchmark" / "eval_pose.py"),
|
||||
str(robot_state), str(gt),
|
||||
"--out", str(out),
|
||||
"--robot", robot,
|
||||
"--tolDeg", "999", "--tolMm", "999",
|
||||
]
|
||||
subprocess.run(cmd, cwd=str(ROOT), capture_output=True, text=True)
|
||||
@@ -153,7 +154,7 @@ def main() -> None:
|
||||
print(f" {label}: FEHLER — übersprungen")
|
||||
continue
|
||||
|
||||
ev = eval_pose(rs, gt)
|
||||
ev = eval_pose(rs, gt, args.robot)
|
||||
if not ev:
|
||||
print(f" {label}: Auswertung fehlgeschlagen")
|
||||
continue
|
||||
@@ -167,10 +168,13 @@ def main() -> None:
|
||||
"max_abs_deg": s.get("max_abs_deg"),
|
||||
"mean_abs_mm": s.get("mean_abs_mm"),
|
||||
"max_abs_mm": s.get("max_abs_mm"),
|
||||
"finger_error_mm": s.get("finger_error_mm"),
|
||||
}
|
||||
all_results.append(row)
|
||||
errs.append(row["mean_abs_deg"] or 0.0)
|
||||
print(f" {label}: mean={row['mean_abs_deg']:.3f}° max={row['max_abs_deg']:.3f}°")
|
||||
fe = row["finger_error_mm"]
|
||||
fe_str = f" finger={fe:.2f}mm" if fe is not None else ""
|
||||
print(f" {label}: mean={row['mean_abs_deg']:.3f}° max={row['max_abs_deg']:.3f}°{fe_str}")
|
||||
|
||||
if errs:
|
||||
print(f" k={k} Zusammenfassung: mean={mean(errs):.3f}° "
|
||||
@@ -180,13 +184,14 @@ def main() -> None:
|
||||
Path(args.out).write_text(json.dumps(all_results, indent=2), encoding="utf-8")
|
||||
|
||||
with open(args.csv, "w", encoding="utf-8") as f:
|
||||
f.write("scene,k,subset,mean_abs_deg,max_abs_deg,mean_abs_mm,max_abs_mm\n")
|
||||
f.write("scene,k,subset,mean_abs_deg,max_abs_deg,mean_abs_mm,max_abs_mm,finger_error_mm\n")
|
||||
for r in all_results:
|
||||
f.write(f"{r['scene']},{r['k']},{r['subset']},"
|
||||
f"{r['mean_abs_deg'] or ''},"
|
||||
f"{r['max_abs_deg'] or ''},"
|
||||
f"{r['mean_abs_mm'] or ''},"
|
||||
f"{r['max_abs_mm'] or ''}\n")
|
||||
f"{r['max_abs_mm'] or ''},"
|
||||
f"{r['finger_error_mm'] or ''}\n")
|
||||
|
||||
print(f"\n[DONE] {len(all_results)} Ergebnisse -> {args.out}")
|
||||
|
||||
|
||||
@@ -17,10 +17,17 @@ from __future__ import annotations
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from typing import Any, Dict
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "pipeline"))
|
||||
from robot_fk import RobotFK # noqa: E402
|
||||
|
||||
LINEAR = {"x", "e"}
|
||||
JOINTS = ["x", "y", "z", "a", "b", "c", "e"]
|
||||
FINGER_LINK = "FingerA"
|
||||
|
||||
|
||||
def load_estimate(path: str) -> Dict[str, Dict[str, Any]]:
|
||||
@@ -51,7 +58,23 @@ def joint_error(v: str, est: float, gt: float) -> float:
|
||||
return abs(((est - gt + 180.0) % 360.0) - 180.0)
|
||||
|
||||
|
||||
def evaluate(estimate_path: str, gt_path: str) -> Dict[str, Any]:
|
||||
def finger_error_mm(est: Dict[str, Any], gt: Dict[str, float],
|
||||
robot_path: Optional[str]) -> Optional[float]:
|
||||
"""Euklidischer Abstand der FingerA-Position (FK) zwischen Schätzung und GT."""
|
||||
if not robot_path:
|
||||
return None
|
||||
fk = RobotFK.from_file(robot_path)
|
||||
est_vals = {v: est[v]["value"] for v in JOINTS}
|
||||
gt_vals = {v: gt.get(v, 0.0) for v in JOINTS}
|
||||
t_est = fk.compute(est_vals)
|
||||
t_gt = fk.compute(gt_vals)
|
||||
p_est = t_est[FINGER_LINK][:3, 3]
|
||||
p_gt = t_gt[FINGER_LINK][:3, 3]
|
||||
return float(np.linalg.norm(p_est - p_gt))
|
||||
|
||||
|
||||
def evaluate(estimate_path: str, gt_path: str,
|
||||
robot_path: Optional[str] = None) -> Dict[str, Any]:
|
||||
est = load_estimate(estimate_path)
|
||||
gt = load_gt(gt_path)
|
||||
|
||||
@@ -74,6 +97,7 @@ def evaluate(estimate_path: str, gt_path: str) -> Dict[str, Any]:
|
||||
"max_abs_deg": max(ang_errs) if ang_errs else None,
|
||||
"mean_abs_mm": (sum(lin_errs) / len(lin_errs)) if lin_errs else None,
|
||||
"max_abs_mm": max(lin_errs) if lin_errs else None,
|
||||
"finger_error_mm": finger_error_mm(est, gt, robot_path),
|
||||
}
|
||||
return {"rows": rows, "summary": summary}
|
||||
|
||||
@@ -83,11 +107,13 @@ def main() -> int:
|
||||
ap.add_argument("estimate", help="robot_state.json")
|
||||
ap.add_argument("gt", help="simulation/SceneX/pose.json")
|
||||
ap.add_argument("--out", default=None)
|
||||
ap.add_argument("--robot", default=None,
|
||||
help="robot.json — aktiviert FK-basierten Fingerfehler in mm")
|
||||
ap.add_argument("--tolDeg", type=float, default=2.0)
|
||||
ap.add_argument("--tolMm", type=float, default=3.0)
|
||||
args = ap.parse_args()
|
||||
|
||||
res = evaluate(args.estimate, args.gt)
|
||||
res = evaluate(args.estimate, args.gt, robot_path=args.robot)
|
||||
print(f"{'joint':>6} | {'est':>9} | {'gt':>9} | {'error':>9} | obs | nMk")
|
||||
print("-" * 58)
|
||||
worst = 0.0
|
||||
@@ -102,6 +128,9 @@ def main() -> int:
|
||||
mm = f"{s['mean_abs_mm']:.2f}" if s["mean_abs_mm"] is not None else "-"
|
||||
xm = f"{s['max_abs_mm']:.2f}" if s["max_abs_mm"] is not None else "-"
|
||||
print(f"angles: mean {md}deg / max {xd}deg | linear: mean {mm}mm / max {xm}mm")
|
||||
fe = s.get("finger_error_mm")
|
||||
if fe is not None:
|
||||
print(f"finger ({FINGER_LINK}) position error: {fe:.2f} mm")
|
||||
|
||||
if args.out:
|
||||
json.dump(res, open(args.out, "w", encoding="utf-8"), indent=2)
|
||||
|
||||
Reference in New Issue
Block a user