antioch-py 2.2.4__py3-none-any.whl → 3.0.1__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.

Files changed (94) hide show
  1. antioch/__init__.py +101 -0
  2. antioch/{module/execution.py → execution.py} +1 -1
  3. antioch/{module/input.py → input.py} +2 -4
  4. antioch/{module/module.py → module.py} +17 -34
  5. antioch/{module/node.py → node.py} +17 -16
  6. {antioch_py-2.2.4.dist-info → antioch_py-3.0.1.dist-info}/METADATA +8 -11
  7. antioch_py-3.0.1.dist-info/RECORD +61 -0
  8. {antioch_py-2.2.4.dist-info → antioch_py-3.0.1.dist-info}/WHEEL +1 -1
  9. antioch_py-3.0.1.dist-info/licenses/LICENSE +21 -0
  10. common/ark/__init__.py +6 -16
  11. common/ark/ark.py +23 -62
  12. common/ark/hardware.py +1 -1
  13. common/ark/kinematics.py +1 -1
  14. common/ark/module.py +22 -0
  15. common/ark/node.py +46 -3
  16. common/ark/scheduler.py +2 -29
  17. common/ark/sim.py +1 -1
  18. {antioch/module → common/ark}/token.py +17 -0
  19. common/assets/rigging.usd +0 -0
  20. common/constants.py +63 -5
  21. common/core/__init__.py +37 -24
  22. common/core/auth.py +87 -112
  23. common/core/container.py +261 -0
  24. common/core/registry.py +131 -152
  25. common/core/rome.py +251 -0
  26. common/core/telemetry.py +176 -0
  27. common/core/types.py +219 -0
  28. common/message/__init__.py +19 -5
  29. common/message/annotation.py +174 -23
  30. common/message/array.py +25 -1
  31. common/message/camera.py +23 -1
  32. common/message/color.py +32 -6
  33. common/message/detection.py +40 -0
  34. common/message/foxglove.py +20 -0
  35. common/message/frame.py +71 -7
  36. common/message/image.py +58 -9
  37. common/message/imu.py +24 -4
  38. common/message/joint.py +69 -10
  39. common/message/log.py +52 -7
  40. common/message/pir.py +23 -8
  41. common/message/plot.py +57 -0
  42. common/message/point.py +55 -6
  43. common/message/point_cloud.py +55 -19
  44. common/message/pose.py +59 -19
  45. common/message/quaternion.py +105 -92
  46. common/message/radar.py +195 -29
  47. common/message/twist.py +34 -0
  48. common/message/types.py +40 -5
  49. common/message/vector.py +180 -245
  50. common/sim/__init__.py +49 -0
  51. common/{session/config.py → sim/objects.py} +97 -27
  52. common/sim/state.py +11 -0
  53. common/utils/comms.py +30 -12
  54. common/utils/logger.py +26 -7
  55. antioch/message.py +0 -87
  56. antioch/module/__init__.py +0 -53
  57. antioch/session/__init__.py +0 -152
  58. antioch/session/ark.py +0 -500
  59. antioch/session/asset.py +0 -65
  60. antioch/session/error.py +0 -80
  61. antioch/session/objects/__init__.py +0 -40
  62. antioch/session/objects/animation.py +0 -162
  63. antioch/session/objects/articulation.py +0 -180
  64. antioch/session/objects/basis_curve.py +0 -180
  65. antioch/session/objects/camera.py +0 -65
  66. antioch/session/objects/collision.py +0 -46
  67. antioch/session/objects/geometry.py +0 -58
  68. antioch/session/objects/ground_plane.py +0 -48
  69. antioch/session/objects/imu.py +0 -53
  70. antioch/session/objects/joint.py +0 -49
  71. antioch/session/objects/light.py +0 -123
  72. antioch/session/objects/pir_sensor.py +0 -102
  73. antioch/session/objects/radar.py +0 -62
  74. antioch/session/objects/rigid_body.py +0 -197
  75. antioch/session/objects/xform.py +0 -119
  76. antioch/session/record.py +0 -158
  77. antioch/session/scene.py +0 -1544
  78. antioch/session/session.py +0 -211
  79. antioch/session/task.py +0 -309
  80. antioch_py-2.2.4.dist-info/RECORD +0 -85
  81. antioch_py-2.2.4.dist-info/entry_points.txt +0 -2
  82. common/core/agent.py +0 -324
  83. common/core/task.py +0 -36
  84. common/message/velocity.py +0 -11
  85. common/rome/__init__.py +0 -9
  86. common/rome/client.py +0 -435
  87. common/rome/error.py +0 -16
  88. common/session/__init__.py +0 -31
  89. common/session/environment.py +0 -31
  90. common/session/sim.py +0 -129
  91. common/utils/usd.py +0 -12
  92. /antioch/{module/clock.py → clock.py} +0 -0
  93. {antioch_py-2.2.4.dist-info → antioch_py-3.0.1.dist-info}/top_level.txt +0 -0
  94. /common/message/{base.py → message.py} +0 -0
@@ -1,152 +0,0 @@
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.objects import Articulation, BasisCurve, Camera, Geometry, GroundPlane, Imu, Joint, Light, Radar, RigidBody, XForm
17
- from antioch.session.scene import Scene
18
- from antioch.session.session import Session, SessionContainer
19
- from antioch.session.task import Task, TaskOutcome
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.config import (
47
- ArticulationJointConfig,
48
- BodyType,
49
- CameraConfig,
50
- DistortionModel,
51
- GeometryType,
52
- ImuConfig,
53
- JointAxis,
54
- JointType,
55
- LightType,
56
- MeshApproximation,
57
- RadarConfig,
58
- )
59
- from common.session.sim import SimulationInfo, SimulationState, SimulationTime
60
-
61
- __all__ = [
62
- # Core containers
63
- "Agent",
64
- "AgentError",
65
- "AgentValidationError",
66
- "AuthError",
67
- "AuthHandler",
68
- "Organization",
69
- "Ark",
70
- "Asset",
71
- "Camera",
72
- "Scene",
73
- "SessionContainer",
74
- "Task",
75
- "TaskOutcome",
76
- # Object containers
77
- "Articulation",
78
- "Camera",
79
- "Geometry",
80
- "GroundPlane",
81
- "Imu",
82
- "Joint",
83
- "Light",
84
- "Radar",
85
- "RigidBody",
86
- "XForm",
87
- "BasisCurve",
88
- # Session client and errors
89
- "Session",
90
- "SessionError",
91
- "SessionArkError",
92
- "SessionAssetError",
93
- "SessionAuthError",
94
- "SessionHardwareError",
95
- "SessionSimRpcClientError",
96
- "SessionSimRpcInternalError",
97
- "SessionSimRpcInterruptedError",
98
- "SessionSimRpcNotConnectedError",
99
- "SessionTaskError",
100
- "SessionValidationError",
101
- # Ark types
102
- "ArkInfo",
103
- "ArkMetadata",
104
- "ArkReference",
105
- "ArkVersionReference",
106
- "AssetReference",
107
- "AssetVersionReference",
108
- "Environment",
109
- "Kinematics",
110
- # Ark kinematics
111
- "ArkJoint",
112
- "ArkLink",
113
- # Hardware types
114
- "Hardware",
115
- "HardwareType",
116
- "CameraHardware",
117
- "ImuHardware",
118
- "RadarHardware",
119
- # Configuration types
120
- "ArticulationJointConfig",
121
- "CameraConfig",
122
- "ImuConfig",
123
- "RadarConfig",
124
- # Joint types
125
- "JointState",
126
- "JointStates",
127
- "JointTarget",
128
- "JointTargets",
129
- # Geometry types
130
- "Pose",
131
- # Camera types
132
- "DistortionModel",
133
- # Sensor types
134
- "ImuSample",
135
- # Enums
136
- "BodyType",
137
- "GeometryType",
138
- "JointAxis",
139
- "JointType",
140
- "LightType",
141
- "MeshApproximation",
142
- # Simulation types
143
- "SimulationInfo",
144
- "SimulationState",
145
- "SimulationTime",
146
- # Agent types
147
- "AgentResponse",
148
- "AgentStateResponse",
149
- "ArkStateResponse",
150
- "ContainerSource",
151
- "ContainerState",
152
- ]
antioch/session/ark.py DELETED
@@ -1,500 +0,0 @@
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.objects.articulation import Articulation
7
- from antioch.session.objects.camera import Camera
8
- from antioch.session.objects.imu import Imu
9
- from antioch.session.objects.pir_sensor import PirSensor
10
- from antioch.session.objects.radar import Radar
11
- from antioch.session.record import NodeOutputRecorder
12
- from antioch.session.session import SessionContainer
13
- from common.ark import Ark as ArkDefinition, ArkReference, Environment, HardwareAccessMode
14
- from common.ark.hardware import ActuatorGroupHardware, HardwareType
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
- self._cameras[(hardware.module, hardware.name)] = self._scene.add_camera(
457
- path=f"{self._base_path}{hardware.path}",
458
- config=hardware.config,
459
- local_pose=hardware.pose,
460
- )
461
-
462
- elif hardware.type == HardwareType.IMU:
463
- self._imus[(hardware.module, hardware.name)] = self._scene.add_imu(
464
- path=f"{self._base_path}{hardware.path}",
465
- config=hardware.config,
466
- local_pose=hardware.pose,
467
- )
468
-
469
- elif hardware.type == HardwareType.PIR:
470
- self._pirs[(hardware.module, hardware.name)] = self._scene.add_pir_sensor(
471
- path=f"{self._base_path}{hardware.path}",
472
- config=hardware.config,
473
- local_pose=hardware.pose,
474
- )
475
-
476
- elif hardware.type == HardwareType.RADAR:
477
- self._radars[(hardware.module, hardware.name)] = self._scene.add_radar(
478
- path=f"{self._base_path}{hardware.path}",
479
- config=hardware.config,
480
- local_pose=hardware.pose,
481
- )
482
-
483
- def _load_ark_asset(self, world_pose: Pose | None = None, local_pose: Pose | None = None) -> None:
484
- """
485
- Load the asset for the Ark.
486
-
487
- :param world_pose: Optional world pose.
488
- :param local_pose: Optional local pose.
489
- :raises SessionAssetError: If the asset file is not found.
490
- """
491
-
492
- if (asset_path := self._ark_ref.asset_path) is None or not os.path.exists(asset_path):
493
- raise SessionAssetError(f"Asset file not found for Ark {self._ark_def.name}")
494
-
495
- self._scene.add_asset(
496
- path=self._base_path,
497
- asset_file_path=asset_path,
498
- world_pose=world_pose,
499
- local_pose=local_pose,
500
- )