roboapi 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- roboapi/__init__.py +2 -0
- roboapi/calibration/__init__.py +6 -0
- roboapi/calibration/profile.py +139 -0
- roboapi/calibration/session.py +350 -0
- roboapi/cli.py +215 -0
- roboapi/config/__init__.py +22 -0
- roboapi/config/loader.py +119 -0
- roboapi/config/models.py +191 -0
- roboapi/embodiment.py +251 -0
- roboapi/errors.py +37 -0
- roboapi/geometry.py +337 -0
- roboapi/hardware/__init__.py +5 -0
- roboapi/hardware/base.py +113 -0
- roboapi/hardware/camera.py +167 -0
- roboapi/hardware/franka/__init__.py +5 -0
- roboapi/hardware/franka/arm.py +476 -0
- roboapi/hardware/franka/desk.py +555 -0
- roboapi/hardware/franka/feedback.py +196 -0
- roboapi/hardware/franka/guiding.py +104 -0
- roboapi/hardware/franka/ik.py +543 -0
- roboapi/hardware/franka/limits.py +110 -0
- roboapi/hardware/franka/settings.py +116 -0
- roboapi/hardware/robotiq.py +350 -0
- roboapi/hardware/ur/__init__.py +16 -0
- roboapi/hardware/ur/build.py +92 -0
- roboapi/hardware/ur/compliant.py +128 -0
- roboapi/hardware/ur/dashboard.py +103 -0
- roboapi/hardware/ur/feedback.py +159 -0
- roboapi/hardware/ur/ipc.py +206 -0
- roboapi/hardware/ur/native/CMakeLists.txt +37 -0
- roboapi/hardware/ur/native/dependencies.lock.json +13 -0
- roboapi/hardware/ur/native/include/ur_cpp/cartesian_speedl.hpp +170 -0
- roboapi/hardware/ur/native/include/ur_cpp/control_loop.hpp +126 -0
- roboapi/hardware/ur/native/include/ur_cpp/geom.hpp +72 -0
- roboapi/hardware/ur/native/include/ur_cpp/motion_primitives.hpp +350 -0
- roboapi/hardware/ur/native/include/ur_cpp/robot_io.hpp +26 -0
- roboapi/hardware/ur/native/include/ur_cpp/streaming_pose.hpp +369 -0
- roboapi/hardware/ur/native/src/control_loop.cpp +512 -0
- roboapi/hardware/ur/native/src/robot_io_urcl.cpp +143 -0
- roboapi/hardware/ur/native/tests/cartesian_speedl_unit.cpp +113 -0
- roboapi/hardware/ur/native/tests/control_modes_unit.cpp +137 -0
- roboapi/hardware/ur/native/tests/streaming_pose_unit.cpp +348 -0
- roboapi/hardware/ur/native/tests/velocity_governor_unit.cpp +65 -0
- roboapi/hardware/ur/native/worker.cpp +147 -0
- roboapi/hardware/ur/position.py +253 -0
- roboapi/hardware/ur/settings.py +95 -0
- roboapi/images.py +16 -0
- roboapi/initialize.py +137 -0
- roboapi/mcp.py +497 -0
- roboapi/motion.py +125 -0
- roboapi/naming.py +7 -0
- roboapi/operations.py +45 -0
- roboapi/policy/__init__.py +7 -0
- roboapi/policy/client.py +277 -0
- roboapi/policy/protocol.py +166 -0
- roboapi/policy/settings.py +79 -0
- roboapi/product-names.json +12 -0
- roboapi/records.py +149 -0
- roboapi/runtime.py +911 -0
- roboapi/semantics.py +6 -0
- roboapi/tasks.py +337 -0
- roboapi/web/__init__.py +5 -0
- roboapi/web/presets.py +44 -0
- roboapi/web/server.py +419 -0
- roboapi/web/static/assets/Bundler-LICENSE.txt +48 -0
- roboapi/web/static/assets/CalibrationScene-BO_spRoL.js +4220 -0
- roboapi/web/static/assets/Inter-OFL.txt +93 -0
- roboapi/web/static/assets/Inter-Variable-c8O0ljhh.ttf +0 -0
- roboapi/web/static/assets/Lucide-LICENSE.txt +43 -0
- roboapi/web/static/assets/Meshoptimizer-LICENSE.txt +23 -0
- roboapi/web/static/assets/MiSans-Regular-ES4DrbaF.woff2 +0 -0
- roboapi/web/static/assets/MiSans-Semibold-B34HIk9C.woff2 +0 -0
- roboapi/web/static/assets/PokeBrand-NOTICE.txt +20 -0
- roboapi/web/static/assets/React-LICENSE.txt +23 -0
- roboapi/web/static/assets/Three-LICENSE.txt +23 -0
- roboapi/web/static/assets/home-reference-CTpf8-oV.png +0 -0
- roboapi/web/static/assets/index-CGLhpUm0.js +28 -0
- roboapi/web/static/assets/index-QXqV-fDC.css +1 -0
- roboapi/web/static/assets/robotiq-ace-pro-2-EJuoOXTy.glb +0 -0
- roboapi/web/static/index.html +15 -0
- roboapi-0.4.2.dist-info/METADATA +205 -0
- roboapi-0.4.2.dist-info/RECORD +94 -0
- roboapi-0.4.2.dist-info/WHEEL +5 -0
- roboapi-0.4.2.dist-info/entry_points.txt +2 -0
- roboapi-0.4.2.dist-info/licenses/LICENSE +202 -0
- roboapi-0.4.2.dist-info/licenses/NOTICE +16 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Bundler-LICENSE.txt +48 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Inter-OFL.txt +93 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Lucide-LICENSE.txt +43 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Meshoptimizer-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/PokeBrand-NOTICE.txt +20 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/React-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/licenses/frontend/public/assets/Three-LICENSE.txt +23 -0
- roboapi-0.4.2.dist-info/top_level.txt +1 -0
roboapi/records.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"""Local execution records, independent of hardware and model transport."""
|
|
2
|
+
|
|
3
|
+
import collections
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
from .errors import PolicyUnavailable
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _inference_error_kind(exc: Exception) -> str:
|
|
19
|
+
"""Keep monitor errors useful without retaining arbitrary server or driver text."""
|
|
20
|
+
if not isinstance(exc, PolicyUnavailable):
|
|
21
|
+
return "runtime_error"
|
|
22
|
+
detail = str(exc).lower()
|
|
23
|
+
for name, marker in (
|
|
24
|
+
("connect_timeout", "connect timeout"),
|
|
25
|
+
("read_timeout", "read timeout"),
|
|
26
|
+
("transport_error", "transport error"),
|
|
27
|
+
("not_authorized", "model not authorized"),
|
|
28
|
+
("authentication_rejected", "authentication rejected"),
|
|
29
|
+
("http_error", "http error"),
|
|
30
|
+
("model_unavailable", "model unavailable"),
|
|
31
|
+
("invalid_response", "invalid response"),
|
|
32
|
+
):
|
|
33
|
+
if marker in detail:
|
|
34
|
+
return name
|
|
35
|
+
return "policy_unavailable"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _diagnostic_request_id(exc: Exception) -> str | None:
|
|
39
|
+
# Only the policy client's validated response-header form is accepted here.
|
|
40
|
+
match = re.search(r"(?:^|; )request_id=([A-Za-z0-9_.-]{1,128})(?:;|$)", str(exc))
|
|
41
|
+
return match.group(1) if match else None
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class RunRecords:
|
|
45
|
+
def __init__(self, cfg):
|
|
46
|
+
self.cfg = cfg
|
|
47
|
+
self.path = None
|
|
48
|
+
self.call_log = collections.deque(maxlen=200)
|
|
49
|
+
self.inference_events = collections.deque(maxlen=120)
|
|
50
|
+
self._inference_active = None
|
|
51
|
+
self._lock = threading.Lock()
|
|
52
|
+
self._activity = collections.deque(maxlen=120)
|
|
53
|
+
self._event_id = 0
|
|
54
|
+
|
|
55
|
+
def event(self, message: str, level: str = "info") -> None:
|
|
56
|
+
with self._lock:
|
|
57
|
+
self._event_id += 1
|
|
58
|
+
self._activity.append({"id": self._event_id, "t": time.time(),
|
|
59
|
+
"message": message, "level": level})
|
|
60
|
+
logger.log({"error": logging.ERROR, "warning": logging.WARNING}.get(level, logging.INFO), message)
|
|
61
|
+
|
|
62
|
+
def activity(self) -> list[dict]:
|
|
63
|
+
with self._lock:
|
|
64
|
+
return list(self._activity)
|
|
65
|
+
|
|
66
|
+
def open(self):
|
|
67
|
+
directory = Path(self.cfg.log_dir).expanduser()
|
|
68
|
+
directory.mkdir(parents=True, exist_ok=True)
|
|
69
|
+
self.path = directory / f"run-{time.strftime('%Y%m%d-%H%M%S')}.jsonl"
|
|
70
|
+
|
|
71
|
+
def write(self, chunk, result, image_age, image, *, state, control_trace) -> None:
|
|
72
|
+
if self.path is None:
|
|
73
|
+
return
|
|
74
|
+
record = {
|
|
75
|
+
"t": time.time(),
|
|
76
|
+
"instruction": chunk.instruction,
|
|
77
|
+
"state": state,
|
|
78
|
+
"chunk_head": chunk.arm[:3].round(5).tolist(),
|
|
79
|
+
"gripper_head": chunk.gripper[:3].round(4).tolist(),
|
|
80
|
+
"infer_ms": round(chunk.infer_ms, 2),
|
|
81
|
+
"chunk_len": len(chunk),
|
|
82
|
+
"image_age_ms": round(image_age * 1e3, 1),
|
|
83
|
+
"control_trace": control_trace,
|
|
84
|
+
**result,
|
|
85
|
+
}
|
|
86
|
+
try:
|
|
87
|
+
with self.path.open("a") as fh:
|
|
88
|
+
fh.write(json.dumps(record) + "\n")
|
|
89
|
+
if self.cfg.save_images:
|
|
90
|
+
self._save_image(image, record["t"])
|
|
91
|
+
except Exception as exc:
|
|
92
|
+
logger.warning("log write failed: %s", exc)
|
|
93
|
+
|
|
94
|
+
def _save_image(self, image: np.ndarray, stamp: float) -> None:
|
|
95
|
+
from PIL import Image
|
|
96
|
+
|
|
97
|
+
out = self.path.with_suffix("") / f"{stamp:.3f}.jpg"
|
|
98
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
99
|
+
Image.fromarray(image).save(out, quality=85)
|
|
100
|
+
|
|
101
|
+
def set_phase(self, phase: str, request_ms: float | None = None) -> None:
|
|
102
|
+
with self._lock:
|
|
103
|
+
if self._inference_active is not None:
|
|
104
|
+
self._inference_active["phase"] = phase
|
|
105
|
+
if request_ms is not None:
|
|
106
|
+
self._inference_active["request_ms"] = round(request_ms, 1)
|
|
107
|
+
|
|
108
|
+
def record_inference(self, started_at: float, started: float, *,
|
|
109
|
+
result: dict | None = None, exc: Exception | None = None) -> None:
|
|
110
|
+
monitor = result.pop("_monitor", {}) if result is not None else {}
|
|
111
|
+
with self._lock:
|
|
112
|
+
try:
|
|
113
|
+
active_request_ms = (self._inference_active or {}).get("request_ms")
|
|
114
|
+
phase = "complete" if exc is None else (
|
|
115
|
+
self._inference_active["phase"] if self._inference_active else "unknown"
|
|
116
|
+
)
|
|
117
|
+
self.inference_events.append({
|
|
118
|
+
"t": started_at,
|
|
119
|
+
"status": "ok" if exc is None else "error",
|
|
120
|
+
"phase": phase,
|
|
121
|
+
"infer_ms": round(result["infer_ms"], 1) if result is not None and result.get("infer_ms") is not None else None,
|
|
122
|
+
"request_ms": monitor.get("request_ms", active_request_ms),
|
|
123
|
+
"action_ms": monitor.get("action_ms"),
|
|
124
|
+
"step_ms": round((time.perf_counter() - started) * 1e3, 1),
|
|
125
|
+
"image_age_ms": monitor.get("image_age_ms"),
|
|
126
|
+
"request_id": monitor.get("request_id") if exc is None else _diagnostic_request_id(exc),
|
|
127
|
+
"output": monitor.get("output"),
|
|
128
|
+
"error_kind": _inference_error_kind(exc) if exc is not None else None,
|
|
129
|
+
})
|
|
130
|
+
finally:
|
|
131
|
+
self._inference_active = None
|
|
132
|
+
|
|
133
|
+
def begin_inference(self, timestamp, started):
|
|
134
|
+
with self._lock:
|
|
135
|
+
self._inference_active = {"t": timestamp, "started": started, "phase": "observation"}
|
|
136
|
+
|
|
137
|
+
def clear_errors(self):
|
|
138
|
+
with self._lock:
|
|
139
|
+
retained = [event for event in self.inference_events if event.get("status") != "error"]
|
|
140
|
+
self.inference_events.clear()
|
|
141
|
+
self.inference_events.extend(retained)
|
|
142
|
+
|
|
143
|
+
def snapshot(self):
|
|
144
|
+
with self._lock:
|
|
145
|
+
active = self._inference_active
|
|
146
|
+
current = ({"t": active["t"], "phase": active["phase"],
|
|
147
|
+
"elapsed_ms": round((time.perf_counter() - active["started"]) * 1e3, 1),
|
|
148
|
+
"request_ms": active.get("request_ms")} if active is not None else None)
|
|
149
|
+
return {"inference_active": current, "inference_events": list(self.inference_events)}
|