antioch-py 1.9.7__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.
Potentially problematic release.
This version of antioch-py might be problematic. Click here for more details.
- antioch/__init__.py +0 -0
- antioch/message.py +87 -0
- antioch/module/__init__.py +53 -0
- antioch/module/clock.py +62 -0
- antioch/module/execution.py +278 -0
- antioch/module/input.py +127 -0
- antioch/module/module.py +218 -0
- antioch/module/node.py +357 -0
- antioch/module/token.py +42 -0
- antioch/session/__init__.py +150 -0
- antioch/session/ark.py +504 -0
- antioch/session/asset.py +65 -0
- antioch/session/error.py +80 -0
- antioch/session/record.py +158 -0
- antioch/session/scene.py +1500 -0
- antioch/session/session.py +220 -0
- antioch/session/task.py +323 -0
- antioch/session/views/__init__.py +40 -0
- antioch/session/views/animation.py +189 -0
- antioch/session/views/articulation.py +245 -0
- antioch/session/views/basis_curve.py +186 -0
- antioch/session/views/camera.py +92 -0
- antioch/session/views/collision.py +75 -0
- antioch/session/views/geometry.py +74 -0
- antioch/session/views/ground_plane.py +63 -0
- antioch/session/views/imu.py +73 -0
- antioch/session/views/joint.py +64 -0
- antioch/session/views/light.py +175 -0
- antioch/session/views/pir_sensor.py +140 -0
- antioch/session/views/radar.py +73 -0
- antioch/session/views/rigid_body.py +282 -0
- antioch/session/views/xform.py +119 -0
- antioch_py-1.9.7.dist-info/METADATA +24 -0
- antioch_py-1.9.7.dist-info/RECORD +99 -0
- antioch_py-1.9.7.dist-info/WHEEL +5 -0
- antioch_py-1.9.7.dist-info/entry_points.txt +2 -0
- antioch_py-1.9.7.dist-info/top_level.txt +2 -0
- common/__init__.py +0 -0
- common/ark/__init__.py +60 -0
- common/ark/ark.py +128 -0
- common/ark/hardware.py +121 -0
- common/ark/kinematics.py +31 -0
- common/ark/module.py +85 -0
- common/ark/node.py +94 -0
- common/ark/scheduler.py +439 -0
- common/ark/sim.py +33 -0
- common/assets/__init__.py +3 -0
- common/constants.py +47 -0
- common/core/__init__.py +52 -0
- common/core/agent.py +296 -0
- common/core/auth.py +305 -0
- common/core/registry.py +331 -0
- common/core/task.py +36 -0
- common/message/__init__.py +59 -0
- common/message/annotation.py +89 -0
- common/message/array.py +500 -0
- common/message/base.py +517 -0
- common/message/camera.py +91 -0
- common/message/color.py +139 -0
- common/message/frame.py +50 -0
- common/message/image.py +171 -0
- common/message/imu.py +14 -0
- common/message/joint.py +47 -0
- common/message/log.py +31 -0
- common/message/pir.py +15 -0
- common/message/point.py +109 -0
- common/message/point_cloud.py +63 -0
- common/message/pose.py +148 -0
- common/message/quaternion.py +273 -0
- common/message/radar.py +58 -0
- common/message/types.py +37 -0
- common/message/vector.py +786 -0
- common/rome/__init__.py +9 -0
- common/rome/client.py +430 -0
- common/rome/error.py +16 -0
- common/session/__init__.py +54 -0
- common/session/environment.py +31 -0
- common/session/sim.py +240 -0
- common/session/views/__init__.py +263 -0
- common/session/views/animation.py +73 -0
- common/session/views/articulation.py +184 -0
- common/session/views/basis_curve.py +102 -0
- common/session/views/camera.py +147 -0
- common/session/views/collision.py +59 -0
- common/session/views/geometry.py +102 -0
- common/session/views/ground_plane.py +41 -0
- common/session/views/imu.py +66 -0
- common/session/views/joint.py +81 -0
- common/session/views/light.py +96 -0
- common/session/views/pir_sensor.py +104 -0
- common/session/views/radar.py +82 -0
- common/session/views/rigid_body.py +236 -0
- common/session/views/viewport.py +21 -0
- common/session/views/xform.py +39 -0
- common/utils/__init__.py +4 -0
- common/utils/comms.py +571 -0
- common/utils/logger.py +123 -0
- common/utils/time.py +42 -0
- common/utils/usd.py +12 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
from antioch.session.ark import Ark
|
|
2
|
+
from antioch.session.asset import Asset
|
|
3
|
+
from antioch.session.error import (
|
|
4
|
+
SessionArkError,
|
|
5
|
+
SessionAssetError,
|
|
6
|
+
SessionAuthError,
|
|
7
|
+
SessionError,
|
|
8
|
+
SessionHardwareError,
|
|
9
|
+
SessionSimRpcClientError,
|
|
10
|
+
SessionSimRpcInternalError,
|
|
11
|
+
SessionSimRpcInterruptedError,
|
|
12
|
+
SessionSimRpcNotConnectedError,
|
|
13
|
+
SessionTaskError,
|
|
14
|
+
SessionValidationError,
|
|
15
|
+
)
|
|
16
|
+
from antioch.session.scene import Scene
|
|
17
|
+
from antioch.session.session import Session, SessionContainer
|
|
18
|
+
from antioch.session.task import Task, TaskOutcome
|
|
19
|
+
from antioch.session.views import Articulation, BasisCurve, Camera, Geometry, GroundPlane, Imu, Joint, Light, Radar, RigidBody, XForm
|
|
20
|
+
from common.ark import (
|
|
21
|
+
ArkInfo,
|
|
22
|
+
ArkMetadata,
|
|
23
|
+
ArkReference,
|
|
24
|
+
ArkVersionReference,
|
|
25
|
+
AssetReference,
|
|
26
|
+
AssetVersionReference,
|
|
27
|
+
Environment,
|
|
28
|
+
Kinematics,
|
|
29
|
+
)
|
|
30
|
+
from common.ark.hardware import CameraHardware, Hardware, HardwareType, ImuHardware, RadarHardware
|
|
31
|
+
from common.ark.kinematics import Joint as ArkJoint, Link as ArkLink
|
|
32
|
+
from common.core import (
|
|
33
|
+
Agent,
|
|
34
|
+
AgentError,
|
|
35
|
+
AgentResponse,
|
|
36
|
+
AgentStateResponse,
|
|
37
|
+
AgentValidationError,
|
|
38
|
+
ArkStateResponse,
|
|
39
|
+
AuthError,
|
|
40
|
+
AuthHandler,
|
|
41
|
+
ContainerSource,
|
|
42
|
+
ContainerState,
|
|
43
|
+
Organization,
|
|
44
|
+
)
|
|
45
|
+
from common.message import ImuSample, JointState, JointStates, JointTarget, JointTargets, Pose
|
|
46
|
+
from common.session.sim import SimulationInfo, SimulationState, SimulationTime
|
|
47
|
+
from common.session.views.articulation import ArticulationJointConfig
|
|
48
|
+
from common.session.views.camera import CameraConfig, DistortionModel
|
|
49
|
+
from common.session.views.geometry import GeometryType, MeshApproximation
|
|
50
|
+
from common.session.views.imu import ImuConfig
|
|
51
|
+
from common.session.views.joint import JointAxis, JointType
|
|
52
|
+
from common.session.views.light import LightType
|
|
53
|
+
from common.session.views.radar import RadarConfig
|
|
54
|
+
from common.session.views.rigid_body import BodyType
|
|
55
|
+
from common.session.views.viewport import SetActiveViewportCamera, SetCameraView
|
|
56
|
+
|
|
57
|
+
__all__ = [
|
|
58
|
+
# Core containers
|
|
59
|
+
"Agent",
|
|
60
|
+
"AgentError",
|
|
61
|
+
"AgentValidationError",
|
|
62
|
+
"AuthError",
|
|
63
|
+
"AuthHandler",
|
|
64
|
+
"Organization",
|
|
65
|
+
"Ark",
|
|
66
|
+
"Asset",
|
|
67
|
+
"Camera",
|
|
68
|
+
"Scene",
|
|
69
|
+
"SessionContainer",
|
|
70
|
+
"Task",
|
|
71
|
+
"TaskOutcome",
|
|
72
|
+
# View containers
|
|
73
|
+
"Articulation",
|
|
74
|
+
"Camera",
|
|
75
|
+
"Geometry",
|
|
76
|
+
"GroundPlane",
|
|
77
|
+
"Imu",
|
|
78
|
+
"Joint",
|
|
79
|
+
"Light",
|
|
80
|
+
"Radar",
|
|
81
|
+
"RigidBody",
|
|
82
|
+
"XForm",
|
|
83
|
+
"BasisCurve",
|
|
84
|
+
"SetActiveViewportCamera",
|
|
85
|
+
"SetCameraView",
|
|
86
|
+
# Session client and errors
|
|
87
|
+
"Session",
|
|
88
|
+
"SessionError",
|
|
89
|
+
"SessionArkError",
|
|
90
|
+
"SessionAssetError",
|
|
91
|
+
"SessionAuthError",
|
|
92
|
+
"SessionHardwareError",
|
|
93
|
+
"SessionSimRpcClientError",
|
|
94
|
+
"SessionSimRpcInternalError",
|
|
95
|
+
"SessionSimRpcInterruptedError",
|
|
96
|
+
"SessionSimRpcNotConnectedError",
|
|
97
|
+
"SessionTaskError",
|
|
98
|
+
"SessionValidationError",
|
|
99
|
+
# Ark types
|
|
100
|
+
"ArkInfo",
|
|
101
|
+
"ArkMetadata",
|
|
102
|
+
"ArkReference",
|
|
103
|
+
"ArkVersionReference",
|
|
104
|
+
"AssetReference",
|
|
105
|
+
"AssetVersionReference",
|
|
106
|
+
"Environment",
|
|
107
|
+
"Kinematics",
|
|
108
|
+
# Ark kinematics
|
|
109
|
+
"ArkJoint",
|
|
110
|
+
"ArkLink",
|
|
111
|
+
# Hardware types
|
|
112
|
+
"Hardware",
|
|
113
|
+
"HardwareType",
|
|
114
|
+
"CameraHardware",
|
|
115
|
+
"ImuHardware",
|
|
116
|
+
"RadarHardware",
|
|
117
|
+
# Configuration types
|
|
118
|
+
"ArticulationJointConfig",
|
|
119
|
+
"CameraConfig",
|
|
120
|
+
"ImuConfig",
|
|
121
|
+
"RadarConfig",
|
|
122
|
+
# Joint types
|
|
123
|
+
"JointState",
|
|
124
|
+
"JointStates",
|
|
125
|
+
"JointTarget",
|
|
126
|
+
"JointTargets",
|
|
127
|
+
# Geometry types
|
|
128
|
+
"Pose",
|
|
129
|
+
# Camera types
|
|
130
|
+
"DistortionModel",
|
|
131
|
+
# Sensor types
|
|
132
|
+
"ImuSample",
|
|
133
|
+
# Enums
|
|
134
|
+
"BodyType",
|
|
135
|
+
"GeometryType",
|
|
136
|
+
"JointAxis",
|
|
137
|
+
"JointType",
|
|
138
|
+
"LightType",
|
|
139
|
+
"MeshApproximation",
|
|
140
|
+
# Simulation types
|
|
141
|
+
"SimulationInfo",
|
|
142
|
+
"SimulationState",
|
|
143
|
+
"SimulationTime",
|
|
144
|
+
# Agent types
|
|
145
|
+
"AgentResponse",
|
|
146
|
+
"AgentStateResponse",
|
|
147
|
+
"ArkStateResponse",
|
|
148
|
+
"ContainerSource",
|
|
149
|
+
"ContainerState",
|
|
150
|
+
]
|
antioch/session/ark.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import TYPE_CHECKING, cast
|
|
3
|
+
|
|
4
|
+
from antioch.module.token import TokenType
|
|
5
|
+
from antioch.session.error import SessionArkError, SessionAssetError
|
|
6
|
+
from antioch.session.record import NodeOutputRecorder
|
|
7
|
+
from antioch.session.session import SessionContainer
|
|
8
|
+
from antioch.session.views.articulation import Articulation
|
|
9
|
+
from antioch.session.views.camera import Camera
|
|
10
|
+
from antioch.session.views.imu import Imu
|
|
11
|
+
from antioch.session.views.pir_sensor import PirSensor
|
|
12
|
+
from antioch.session.views.radar import Radar
|
|
13
|
+
from common.ark import Ark as ArkDefinition, ArkReference, Environment, HardwareAccessMode
|
|
14
|
+
from common.ark.hardware import ActuatorGroupHardware, CameraHardware, HardwareType, ImuHardware, PirHardware, RadarHardware
|
|
15
|
+
from common.ark.scheduler import NodeCompleteEvent, NodeStartEvent, OnlineScheduler
|
|
16
|
+
from common.ark.sim import SimNodeComplete, SimNodeStart
|
|
17
|
+
from common.core import (
|
|
18
|
+
Agent,
|
|
19
|
+
ArkStateResponse,
|
|
20
|
+
ContainerSource,
|
|
21
|
+
get_ark_version_reference,
|
|
22
|
+
list_local_arks,
|
|
23
|
+
list_remote_arks,
|
|
24
|
+
load_local_ark,
|
|
25
|
+
pull_remote_ark,
|
|
26
|
+
)
|
|
27
|
+
from common.message import JointStates, JointTargets, Pose
|
|
28
|
+
from common.utils.comms import CommsAsyncSubscriber, CommsPublisher
|
|
29
|
+
from common.utils.time import now_us, us_to_s
|
|
30
|
+
from common.utils.usd import sanitize_usd_path
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from antioch.session.scene import Scene
|
|
34
|
+
|
|
35
|
+
NODE_START_PUBLISHER_PATH = "_ark/node_start/{module}/{node}"
|
|
36
|
+
NODE_COMPLETE_SUBSCRIBER_PATH = "_ark/node_complete/{module}/{node}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class Ark(SessionContainer):
|
|
40
|
+
"""
|
|
41
|
+
Complete runtime for an Ark in simulation.
|
|
42
|
+
|
|
43
|
+
Manages entire Ark lifecycle from loading through execution. The runtime is designed to be
|
|
44
|
+
robust and deterministic:
|
|
45
|
+
- Events/time increase monotonically via the deterministic scheduler
|
|
46
|
+
- Each execution is processed exactly once via execution tracking
|
|
47
|
+
- Multiple nodes can execute at the same LET
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
scene = Scene()
|
|
51
|
+
|
|
52
|
+
# Build and start Ark
|
|
53
|
+
ark = scene.add_ark(
|
|
54
|
+
name="my_robot",
|
|
55
|
+
version="1.0.0",
|
|
56
|
+
debug=True
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Step simulation
|
|
60
|
+
scene.step(dt=0.01) # Delegates to ark.step()
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(
|
|
64
|
+
self,
|
|
65
|
+
path: str,
|
|
66
|
+
scene: "Scene",
|
|
67
|
+
name: str,
|
|
68
|
+
version: str,
|
|
69
|
+
world_pose: Pose | None = None,
|
|
70
|
+
local_pose: Pose | None = None,
|
|
71
|
+
source: ContainerSource = ContainerSource.LOCAL,
|
|
72
|
+
debug: bool = False,
|
|
73
|
+
timeout: float = 30.0,
|
|
74
|
+
):
|
|
75
|
+
"""
|
|
76
|
+
Initialize and build complete Ark runtime.
|
|
77
|
+
|
|
78
|
+
:param path: USD path where the Ark will be built.
|
|
79
|
+
:param scene: Scene instance this Ark belongs to.
|
|
80
|
+
:param name: Name of the Ark.
|
|
81
|
+
:param version: Version of the Ark.
|
|
82
|
+
:param world_pose: Optional world pose.
|
|
83
|
+
:param local_pose: Optional local pose.
|
|
84
|
+
:param source: Container image source.
|
|
85
|
+
:param debug: Enable debug mode.
|
|
86
|
+
:param timeout: Timeout in seconds for the Ark to start.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
super().__init__()
|
|
90
|
+
self._path = path
|
|
91
|
+
self._scene = scene
|
|
92
|
+
self._agent = Agent()
|
|
93
|
+
|
|
94
|
+
# Load Ark definition
|
|
95
|
+
self._ark_def = load_local_ark(name, version)
|
|
96
|
+
self._ark_ref = get_ark_version_reference(name, version)
|
|
97
|
+
self._base_path = f"{path}/{sanitize_usd_path(self._ark_def.name)}"
|
|
98
|
+
|
|
99
|
+
# Start Ark via agent
|
|
100
|
+
self._agent.start_ark(
|
|
101
|
+
ark=self._ark_def,
|
|
102
|
+
source=source,
|
|
103
|
+
environment=Environment.SIM,
|
|
104
|
+
debug=debug,
|
|
105
|
+
timeout=timeout,
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
# Initialize scheduler
|
|
109
|
+
self._scheduler = OnlineScheduler(self._ark_def.edges, self._ark_def.modules)
|
|
110
|
+
self._next_event = self._scheduler.next()
|
|
111
|
+
self._node_start_times_us: dict[tuple[str, str], int] = {}
|
|
112
|
+
|
|
113
|
+
# Create publishers and subscribers for node coordination
|
|
114
|
+
self._node_start_publishers: dict[tuple[str, str], CommsPublisher] = {}
|
|
115
|
+
self._node_complete_subscribers: dict[tuple[str, str], CommsAsyncSubscriber] = {}
|
|
116
|
+
for module in self._ark_def.modules:
|
|
117
|
+
for node_name in module.nodes:
|
|
118
|
+
start_path = NODE_START_PUBLISHER_PATH.format(module=module.name, node=node_name)
|
|
119
|
+
complete_path = NODE_COMPLETE_SUBSCRIBER_PATH.format(module=module.name, node=node_name)
|
|
120
|
+
self._node_start_publishers[(module.name, node_name)] = self._session.comms.declare_publisher(start_path)
|
|
121
|
+
self._node_complete_subscribers[(module.name, node_name)] = self._session.comms.declare_async_subscriber(complete_path)
|
|
122
|
+
|
|
123
|
+
# Build in scene
|
|
124
|
+
self._load_ark_asset(world_pose=world_pose, local_pose=local_pose)
|
|
125
|
+
self._build_hardware()
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def path(self) -> str:
|
|
129
|
+
"""
|
|
130
|
+
Get the USD path where this Ark is built.
|
|
131
|
+
|
|
132
|
+
:return: The USD path of this Ark.
|
|
133
|
+
"""
|
|
134
|
+
|
|
135
|
+
return self._path
|
|
136
|
+
|
|
137
|
+
@property
|
|
138
|
+
def definition(self) -> ArkDefinition:
|
|
139
|
+
"""
|
|
140
|
+
Get the Ark definition.
|
|
141
|
+
|
|
142
|
+
:return: The Ark definition.
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
return self._ark_def
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def name(self) -> str:
|
|
149
|
+
"""
|
|
150
|
+
Get the Ark name.
|
|
151
|
+
|
|
152
|
+
:return: The Ark name.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
return self._ark_def.name
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def state(self) -> ArkStateResponse:
|
|
159
|
+
"""
|
|
160
|
+
Get current state of all modules.
|
|
161
|
+
|
|
162
|
+
:return: Ark state with module information.
|
|
163
|
+
"""
|
|
164
|
+
|
|
165
|
+
return self._agent.get_ark_state()
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def list_local() -> list[ArkReference]:
|
|
169
|
+
"""
|
|
170
|
+
List all locally available Arks.
|
|
171
|
+
|
|
172
|
+
:return: List of ArkReference objects from local storage.
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
return list_local_arks()
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def list_remote() -> list[ArkReference]:
|
|
179
|
+
"""
|
|
180
|
+
List all Arks from remote registry.
|
|
181
|
+
|
|
182
|
+
Requires authentication. Call session.login() first if not authenticated.
|
|
183
|
+
|
|
184
|
+
:return: List of ArkReference objects from remote registry.
|
|
185
|
+
:raises SessionAuthError: If not authenticated.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
return list_remote_arks()
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def pull(name: str, version: str, overwrite: bool = False) -> ArkDefinition:
|
|
192
|
+
"""
|
|
193
|
+
Pull an Ark from remote registry to local storage.
|
|
194
|
+
|
|
195
|
+
Requires authentication. Call session.login() first if not authenticated.
|
|
196
|
+
If the Ark already exists locally, returns the existing Ark unless overwrite=True.
|
|
197
|
+
|
|
198
|
+
:param name: Name of the Ark.
|
|
199
|
+
:param version: Version of the Ark.
|
|
200
|
+
:param overwrite: Overwrite local Ark if it already exists.
|
|
201
|
+
:return: The loaded Ark definition.
|
|
202
|
+
:raises SessionAuthError: If not authenticated.
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
return pull_remote_ark(name=name, version=version, overwrite=overwrite)
|
|
206
|
+
|
|
207
|
+
def step(self, dt_us: int = 1_000_000) -> None:
|
|
208
|
+
"""
|
|
209
|
+
Step the Ark simulation forward by exactly dt_us microseconds.
|
|
210
|
+
|
|
211
|
+
:param dt_us: Amount of time to step in microseconds (default 1 second).
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
current_sim_time_us = self._scene.time_us
|
|
215
|
+
target_sim_time_us = current_sim_time_us + dt_us
|
|
216
|
+
while current_sim_time_us < target_sim_time_us:
|
|
217
|
+
event = self._next_event
|
|
218
|
+
|
|
219
|
+
# Step physics directly to target time and break
|
|
220
|
+
if event.let_us > target_sim_time_us:
|
|
221
|
+
self._scene._step_physics(target_sim_time_us - current_sim_time_us)
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
# Step physics to event time if needed
|
|
225
|
+
if event.let_us > current_sim_time_us:
|
|
226
|
+
self._scene._step_physics(event.let_us - current_sim_time_us)
|
|
227
|
+
current_sim_time_us = self._scene.time_us
|
|
228
|
+
|
|
229
|
+
# Node start: read hardware for node and send non-blocking start to nodes
|
|
230
|
+
if isinstance(event, NodeStartEvent):
|
|
231
|
+
hardware_reads = self._read_node_hardware(event.module, event.node)
|
|
232
|
+
self._node_start_times_us[(event.module, event.node)] = now_us()
|
|
233
|
+
self._node_start_publishers[(event.module, event.node)].publish(
|
|
234
|
+
SimNodeStart(
|
|
235
|
+
module_name=event.module,
|
|
236
|
+
node_name=event.node,
|
|
237
|
+
start_let_us=event.start_let_us,
|
|
238
|
+
start_timestamp_us=self._node_start_times_us[(event.module, event.node)],
|
|
239
|
+
input_tokens=event.input_tokens,
|
|
240
|
+
hardware_reads=hardware_reads,
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Node complete: wait for completion message or budget to elapse and write hardware for node
|
|
245
|
+
elif isinstance(event, NodeCompleteEvent):
|
|
246
|
+
node_config = next(m for m in self._ark_def.modules if m.name == event.module).nodes[event.node]
|
|
247
|
+
node_complete_subscriber = self._node_complete_subscribers[(event.module, event.node)]
|
|
248
|
+
target_time_us = self._node_start_times_us[(event.module, event.node)] + node_config.budget_us
|
|
249
|
+
while True:
|
|
250
|
+
remaining_time_s = us_to_s(max(0, target_time_us - now_us()))
|
|
251
|
+
complete = node_complete_subscriber.recv_timeout(SimNodeComplete, timeout=remaining_time_s)
|
|
252
|
+
if complete is None and now_us() > target_time_us:
|
|
253
|
+
break
|
|
254
|
+
if complete is not None and complete.completion_let_us == event.completion_let_us:
|
|
255
|
+
if complete.hardware_writes is not None:
|
|
256
|
+
self._write_node_hardware(event.module, event.node, complete.hardware_writes)
|
|
257
|
+
break
|
|
258
|
+
|
|
259
|
+
# Fetch next event
|
|
260
|
+
self._next_event = self._scheduler.next()
|
|
261
|
+
|
|
262
|
+
def get_articulation(self) -> Articulation:
|
|
263
|
+
"""
|
|
264
|
+
Get the articulation for the Ark.
|
|
265
|
+
|
|
266
|
+
:return: The articulation instance.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
return self._articulation
|
|
270
|
+
|
|
271
|
+
def get_camera(self, module_name: str, hardware_name: str) -> Camera:
|
|
272
|
+
"""
|
|
273
|
+
Get camera hardware view.
|
|
274
|
+
|
|
275
|
+
:param module_name: Module name.
|
|
276
|
+
:param hardware_name: Hardware name.
|
|
277
|
+
:return: Camera view.
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
if (module_name, hardware_name) not in self._cameras:
|
|
281
|
+
raise SessionArkError(f"Camera '{hardware_name}' not found in module '{module_name}'")
|
|
282
|
+
return self._cameras[(module_name, hardware_name)]
|
|
283
|
+
|
|
284
|
+
def get_imu(self, module_name: str, hardware_name: str) -> Imu:
|
|
285
|
+
"""
|
|
286
|
+
Get IMU hardware view.
|
|
287
|
+
|
|
288
|
+
:param module_name: Module name.
|
|
289
|
+
:param hardware_name: Hardware name.
|
|
290
|
+
:return: IMU view.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
if (module_name, hardware_name) not in self._imus:
|
|
294
|
+
raise SessionArkError(f"IMU '{hardware_name}' not found in module '{module_name}'")
|
|
295
|
+
return self._imus[(module_name, hardware_name)]
|
|
296
|
+
|
|
297
|
+
def get_radar(self, module_name: str, hardware_name: str) -> Radar:
|
|
298
|
+
"""
|
|
299
|
+
Get radar hardware view.
|
|
300
|
+
|
|
301
|
+
:param module_name: Module name.
|
|
302
|
+
:param hardware_name: Hardware name.
|
|
303
|
+
:return: Radar view.
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
if (module_name, hardware_name) not in self._radars:
|
|
307
|
+
raise SessionArkError(f"Radar '{hardware_name}' not found in module '{module_name}'")
|
|
308
|
+
return self._radars[(module_name, hardware_name)]
|
|
309
|
+
|
|
310
|
+
def get_pir(self, module_name: str, hardware_name: str) -> PirSensor:
|
|
311
|
+
"""
|
|
312
|
+
Get PIR sensor hardware view.
|
|
313
|
+
|
|
314
|
+
:param module_name: Module name.
|
|
315
|
+
:param hardware_name: Hardware name.
|
|
316
|
+
:return: PIR sensor view.
|
|
317
|
+
"""
|
|
318
|
+
|
|
319
|
+
if (module_name, hardware_name) not in self._pirs:
|
|
320
|
+
raise SessionArkError(f"PIR sensor '{hardware_name}' not found in module '{module_name}'")
|
|
321
|
+
return self._pirs[(module_name, hardware_name)]
|
|
322
|
+
|
|
323
|
+
def list_hardware(self) -> dict[str, list[tuple[str, str]]]:
|
|
324
|
+
"""
|
|
325
|
+
List all hardware in the Ark.
|
|
326
|
+
|
|
327
|
+
:return: Dictionary of hardware (module name, hardware name) keyed by hardware type.
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
"CAMERA": list(self._cameras.keys()),
|
|
332
|
+
"IMU": list(self._imus.keys()),
|
|
333
|
+
"PIR": list(self._pirs.keys()),
|
|
334
|
+
"RADAR": list(self._radars.keys()),
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
def record_node_output(
|
|
338
|
+
self,
|
|
339
|
+
module_name: str,
|
|
340
|
+
node_name: str,
|
|
341
|
+
output_name: str,
|
|
342
|
+
token_type: TokenType | None = TokenType.DATA,
|
|
343
|
+
last_n: int = 10,
|
|
344
|
+
) -> NodeOutputRecorder:
|
|
345
|
+
"""
|
|
346
|
+
Create a recorder for monitoring node output tokens.
|
|
347
|
+
|
|
348
|
+
:param module_name: Module name.
|
|
349
|
+
:param node_name: Node name within module.
|
|
350
|
+
:param output_name: Output name within node.
|
|
351
|
+
:param token_type: Token type to record (default: TokenType.DATA, None = all types).
|
|
352
|
+
:param last_n: Number of recent tokens to buffer.
|
|
353
|
+
:return: NodeOutputRecorder instance.
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
return NodeOutputRecorder(
|
|
357
|
+
comms=self._session.comms,
|
|
358
|
+
ark_def=self._ark_def,
|
|
359
|
+
module_name=module_name,
|
|
360
|
+
node_name=node_name,
|
|
361
|
+
output_name=output_name,
|
|
362
|
+
token_type=token_type,
|
|
363
|
+
last_n=last_n,
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
def _read_node_hardware(self, module_name: str, node_name: str) -> dict[str, bytes]:
|
|
367
|
+
"""
|
|
368
|
+
Read hardware state for node execution.
|
|
369
|
+
|
|
370
|
+
:param module_name: Module name.
|
|
371
|
+
:param node_name: Node name.
|
|
372
|
+
:return: Hardware reads keyed by hardware name.
|
|
373
|
+
"""
|
|
374
|
+
|
|
375
|
+
hardware_reads = {}
|
|
376
|
+
node_def = next(m for m in self._ark_def.modules if m.name == module_name).nodes[node_name]
|
|
377
|
+
for hardware_name, access_mode in node_def.hardware_access.items():
|
|
378
|
+
if access_mode not in (HardwareAccessMode.READ, HardwareAccessMode.READ_WRITE):
|
|
379
|
+
continue
|
|
380
|
+
|
|
381
|
+
# Read from camera
|
|
382
|
+
if camera := self._cameras.get((module_name, hardware_name)):
|
|
383
|
+
camera_frame = camera.get_frame()
|
|
384
|
+
if camera_frame is not None:
|
|
385
|
+
hardware_reads[hardware_name] = camera_frame.pack()
|
|
386
|
+
|
|
387
|
+
# Read from IMU
|
|
388
|
+
elif imu := self._imus.get((module_name, hardware_name)):
|
|
389
|
+
imu_sample = imu.get_sample()
|
|
390
|
+
if imu_sample is not None:
|
|
391
|
+
hardware_reads[hardware_name] = imu_sample.pack()
|
|
392
|
+
|
|
393
|
+
# Read from radar
|
|
394
|
+
elif radar := self._radars.get((module_name, hardware_name)):
|
|
395
|
+
radar_scan = radar.get_scan()
|
|
396
|
+
if radar_scan is not None:
|
|
397
|
+
hardware_reads[hardware_name] = radar_scan.pack()
|
|
398
|
+
|
|
399
|
+
# Read from PIR sensor
|
|
400
|
+
elif pir := self._pirs.get((module_name, hardware_name)):
|
|
401
|
+
pir_status = pir.get_detection_status()
|
|
402
|
+
if pir_status is not None:
|
|
403
|
+
hardware_reads[hardware_name] = pir_status.pack()
|
|
404
|
+
|
|
405
|
+
# Read from actuator group
|
|
406
|
+
else:
|
|
407
|
+
joint_states = self._articulation.get_joint_states()
|
|
408
|
+
if joint_states is not None:
|
|
409
|
+
hardware_reads[hardware_name] = JointStates(states=joint_states).pack()
|
|
410
|
+
|
|
411
|
+
return hardware_reads
|
|
412
|
+
|
|
413
|
+
def _write_node_hardware(self, module_name: str, node_name: str, hardware_writes: dict[str, bytes]) -> None:
|
|
414
|
+
"""
|
|
415
|
+
Write hardware state from node execution.
|
|
416
|
+
|
|
417
|
+
:param module_name: Module name.
|
|
418
|
+
:param node_name: Node name.
|
|
419
|
+
:param hardware_writes: Hardware writes keyed by hardware name.
|
|
420
|
+
"""
|
|
421
|
+
|
|
422
|
+
node_def = next(m for m in self._ark_def.modules if m.name == module_name).nodes[node_name]
|
|
423
|
+
for hardware_name, write_data in hardware_writes.items():
|
|
424
|
+
access_mode = node_def.hardware_access.get(hardware_name)
|
|
425
|
+
if access_mode not in (HardwareAccessMode.WRITE, HardwareAccessMode.READ_WRITE):
|
|
426
|
+
continue
|
|
427
|
+
|
|
428
|
+
# Write to actuator group
|
|
429
|
+
targets = JointTargets.unpack(write_data)
|
|
430
|
+
self._articulation.set_joint_targets(joint_targets=targets.targets)
|
|
431
|
+
|
|
432
|
+
def _build_hardware(self) -> None:
|
|
433
|
+
"""
|
|
434
|
+
Create articulation and sensor views from hardware configs.
|
|
435
|
+
"""
|
|
436
|
+
|
|
437
|
+
actuator_group_configs = [
|
|
438
|
+
cast(ActuatorGroupHardware, hardware.config)
|
|
439
|
+
for hardware in self._ark_def.hardware
|
|
440
|
+
if hardware.type == HardwareType.ACTUATOR_GROUP
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
# Create single articulation view with all joint configs from actuator groups
|
|
444
|
+
self._articulation = self._scene.add_articulation(
|
|
445
|
+
path=self._base_path,
|
|
446
|
+
joint_configs=sum([config.config.joint_configs for config in actuator_group_configs], []),
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
# Create sensor views
|
|
450
|
+
self._cameras: dict[tuple[str, str], Camera] = {}
|
|
451
|
+
self._imus: dict[tuple[str, str], Imu] = {}
|
|
452
|
+
self._pirs: dict[tuple[str, str], PirSensor] = {}
|
|
453
|
+
self._radars: dict[tuple[str, str], Radar] = {}
|
|
454
|
+
for hardware in self._ark_def.hardware:
|
|
455
|
+
if hardware.type == HardwareType.CAMERA:
|
|
456
|
+
camera_hw = cast(CameraHardware, hardware.config)
|
|
457
|
+
self._cameras[(camera_hw.module, camera_hw.name)] = self._scene.add_camera(
|
|
458
|
+
path=f"{self._base_path}{camera_hw.path}",
|
|
459
|
+
config=camera_hw.config,
|
|
460
|
+
local_pose=camera_hw.pose,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
elif hardware.type == HardwareType.IMU:
|
|
464
|
+
imu_hw = cast(ImuHardware, hardware.config)
|
|
465
|
+
self._imus[(imu_hw.module, imu_hw.name)] = self._scene.add_imu(
|
|
466
|
+
path=f"{self._base_path}{imu_hw.path}",
|
|
467
|
+
config=imu_hw.config,
|
|
468
|
+
local_pose=imu_hw.pose,
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
elif hardware.type == HardwareType.PIR:
|
|
472
|
+
pir_hw = cast(PirHardware, hardware.config)
|
|
473
|
+
self._pirs[(pir_hw.module, pir_hw.name)] = self._scene.add_pir_sensor(
|
|
474
|
+
path=f"{self._base_path}{pir_hw.path}",
|
|
475
|
+
config=pir_hw.config,
|
|
476
|
+
local_pose=pir_hw.pose,
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
elif hardware.type == HardwareType.RADAR:
|
|
480
|
+
radar_hw = cast(RadarHardware, hardware.config)
|
|
481
|
+
self._radars[(radar_hw.module, radar_hw.name)] = self._scene.add_radar(
|
|
482
|
+
path=f"{self._base_path}{radar_hw.path}",
|
|
483
|
+
config=radar_hw.config,
|
|
484
|
+
local_pose=radar_hw.pose,
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
def _load_ark_asset(self, world_pose: Pose | None = None, local_pose: Pose | None = None) -> None:
|
|
488
|
+
"""
|
|
489
|
+
Load the asset for the Ark.
|
|
490
|
+
|
|
491
|
+
:param world_pose: Optional world pose.
|
|
492
|
+
:param local_pose: Optional local pose.
|
|
493
|
+
:raises SessionAssetError: If the asset file is not found.
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
if (asset_path := self._ark_ref.asset_path) is None or not os.path.exists(asset_path):
|
|
497
|
+
raise SessionAssetError(f"Asset file not found for Ark {self._ark_def.name}")
|
|
498
|
+
|
|
499
|
+
self._scene.add_asset(
|
|
500
|
+
path=self._base_path,
|
|
501
|
+
asset_file_path=asset_path,
|
|
502
|
+
world_pose=world_pose,
|
|
503
|
+
local_pose=local_pose,
|
|
504
|
+
)
|