antioch-py 2.2.3__py3-none-any.whl → 3.0.0__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 +101 -0
- antioch/{module/execution.py → execution.py} +1 -1
- antioch/{module/input.py → input.py} +2 -4
- antioch/{module/module.py → module.py} +17 -34
- antioch/{module/node.py → node.py} +17 -16
- {antioch_py-2.2.3.dist-info → antioch_py-3.0.0.dist-info}/METADATA +8 -11
- antioch_py-3.0.0.dist-info/RECORD +61 -0
- {antioch_py-2.2.3.dist-info → antioch_py-3.0.0.dist-info}/WHEEL +1 -1
- antioch_py-3.0.0.dist-info/licenses/LICENSE +21 -0
- common/ark/__init__.py +6 -16
- common/ark/ark.py +23 -62
- common/ark/hardware.py +1 -1
- common/ark/kinematics.py +1 -1
- common/ark/module.py +22 -0
- common/ark/node.py +46 -3
- common/ark/scheduler.py +2 -29
- common/ark/sim.py +1 -1
- {antioch/module → common/ark}/token.py +17 -0
- common/assets/rigging.usd +0 -0
- common/constants.py +63 -5
- common/core/__init__.py +37 -24
- common/core/auth.py +87 -112
- common/core/container.py +261 -0
- common/core/registry.py +131 -152
- common/core/rome.py +251 -0
- common/core/telemetry.py +176 -0
- common/core/types.py +219 -0
- common/message/__init__.py +19 -5
- common/message/annotation.py +174 -23
- common/message/array.py +25 -1
- common/message/camera.py +23 -1
- common/message/color.py +32 -6
- common/message/detection.py +40 -0
- common/message/foxglove.py +20 -0
- common/message/frame.py +71 -7
- common/message/image.py +58 -9
- common/message/imu.py +24 -4
- common/message/joint.py +69 -10
- common/message/log.py +52 -7
- common/message/pir.py +23 -8
- common/message/plot.py +57 -0
- common/message/point.py +55 -6
- common/message/point_cloud.py +55 -19
- common/message/pose.py +59 -19
- common/message/quaternion.py +105 -92
- common/message/radar.py +195 -29
- common/message/twist.py +34 -0
- common/message/types.py +40 -5
- common/message/vector.py +180 -245
- common/sim/__init__.py +49 -0
- common/{session/config.py → sim/objects.py} +97 -27
- common/sim/state.py +11 -0
- common/utils/comms.py +30 -12
- common/utils/logger.py +26 -7
- antioch/message.py +0 -87
- antioch/module/__init__.py +0 -53
- antioch/session/__init__.py +0 -152
- antioch/session/ark.py +0 -500
- antioch/session/asset.py +0 -65
- antioch/session/error.py +0 -80
- antioch/session/objects/__init__.py +0 -40
- antioch/session/objects/animation.py +0 -162
- antioch/session/objects/articulation.py +0 -180
- antioch/session/objects/basis_curve.py +0 -180
- antioch/session/objects/camera.py +0 -65
- antioch/session/objects/collision.py +0 -46
- antioch/session/objects/geometry.py +0 -58
- antioch/session/objects/ground_plane.py +0 -48
- antioch/session/objects/imu.py +0 -53
- antioch/session/objects/joint.py +0 -49
- antioch/session/objects/light.py +0 -123
- antioch/session/objects/pir_sensor.py +0 -98
- antioch/session/objects/radar.py +0 -62
- antioch/session/objects/rigid_body.py +0 -197
- antioch/session/objects/xform.py +0 -119
- antioch/session/record.py +0 -158
- antioch/session/scene.py +0 -1544
- antioch/session/session.py +0 -211
- antioch/session/task.py +0 -309
- antioch_py-2.2.3.dist-info/RECORD +0 -85
- antioch_py-2.2.3.dist-info/entry_points.txt +0 -2
- common/core/agent.py +0 -324
- common/core/task.py +0 -36
- common/message/velocity.py +0 -11
- common/rome/__init__.py +0 -9
- common/rome/client.py +0 -430
- common/rome/error.py +0 -16
- common/session/__init__.py +0 -31
- common/session/environment.py +0 -31
- common/session/sim.py +0 -129
- common/utils/usd.py +0 -12
- /antioch/{module/clock.py → clock.py} +0 -0
- {antioch_py-2.2.3.dist-info → antioch_py-3.0.0.dist-info}/top_level.txt +0 -0
- /common/message/{base.py → message.py} +0 -0
common/message/radar.py
CHANGED
|
@@ -1,31 +1,106 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
1
6
|
from pydantic import Field
|
|
2
7
|
|
|
3
|
-
from common.message.
|
|
8
|
+
from common.message.array import Array
|
|
9
|
+
from common.message.image import Image, ImageEncoding
|
|
10
|
+
from common.message.message import Message
|
|
4
11
|
from common.message.point_cloud import PointCloud
|
|
5
|
-
from common.message.vector import Vector3
|
|
6
12
|
|
|
7
13
|
|
|
8
|
-
class
|
|
9
|
-
"""
|
|
10
|
-
Single radar detection point with position and properties.
|
|
14
|
+
class RadarScan(Message):
|
|
11
15
|
"""
|
|
16
|
+
Radar scan data containing all detections from a single scan.
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
position: Vector3 = Field(description="3D position of detection in sensor frame")
|
|
15
|
-
range: float = Field(description="Range to target in meters")
|
|
16
|
-
azimuth: float = Field(description="Azimuth angle in radians")
|
|
17
|
-
elevation: float = Field(description="Elevation angle in radians")
|
|
18
|
-
velocity: float = Field(default=0.0, description="Radial velocity in m/s (positive = moving away)")
|
|
19
|
-
rcs: float = Field(default=0.0, description="Radar cross section in dBsm")
|
|
18
|
+
Data is stored as numpy arrays (via Array) for efficient processing.
|
|
20
19
|
|
|
20
|
+
Example:
|
|
21
|
+
```python
|
|
22
|
+
from common.message import RadarScan
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
# Access detection data efficiently via arrays
|
|
25
|
+
scan = radar.get_scan()
|
|
26
|
+
ranges = scan.ranges.to_numpy()
|
|
27
|
+
rcs = scan.rcs.to_numpy()
|
|
28
|
+
|
|
29
|
+
# Filter detections by range
|
|
30
|
+
close_mask = ranges < 10.0
|
|
31
|
+
close_rcs = rcs[close_mask]
|
|
32
|
+
|
|
33
|
+
# Convert to point cloud for visualization
|
|
34
|
+
point_cloud = scan.to_point_cloud(frame_id="radar_link")
|
|
35
|
+
```
|
|
25
36
|
"""
|
|
26
37
|
|
|
27
38
|
_type = "antioch/radar_scan"
|
|
28
|
-
|
|
39
|
+
|
|
40
|
+
# Raw spherical coordinates (stored as Arrays for serialization)
|
|
41
|
+
ranges: Array = Field(default_factory=lambda: Array.zeros(0), description="Range to targets in meters")
|
|
42
|
+
azimuths: Array = Field(default_factory=lambda: Array.zeros(0), description="Azimuth angles in radians")
|
|
43
|
+
elevations: Array = Field(default_factory=lambda: Array.zeros(0), description="Elevation angles in radians")
|
|
44
|
+
rcs: Array = Field(default_factory=lambda: Array.zeros(0), description="Radar cross section in dBsm")
|
|
45
|
+
velocities: Array = Field(default_factory=lambda: Array.zeros(0), description="Radial velocities in m/s")
|
|
46
|
+
|
|
47
|
+
# Cartesian positions (computed from spherical, stored for efficiency)
|
|
48
|
+
x: Array = Field(default_factory=lambda: Array.zeros(0), description="X positions in sensor frame")
|
|
49
|
+
y: Array = Field(default_factory=lambda: Array.zeros(0), description="Y positions in sensor frame")
|
|
50
|
+
z: Array = Field(default_factory=lambda: Array.zeros(0), description="Z positions in sensor frame")
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def num_detections(self) -> int:
|
|
54
|
+
"""
|
|
55
|
+
Number of detections in this scan.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
return len(self.ranges)
|
|
59
|
+
|
|
60
|
+
@classmethod
|
|
61
|
+
def from_arrays(
|
|
62
|
+
cls,
|
|
63
|
+
ranges: np.ndarray,
|
|
64
|
+
azimuths: np.ndarray,
|
|
65
|
+
elevations: np.ndarray,
|
|
66
|
+
rcs: np.ndarray,
|
|
67
|
+
velocities: np.ndarray,
|
|
68
|
+
x: np.ndarray,
|
|
69
|
+
y: np.ndarray,
|
|
70
|
+
z: np.ndarray,
|
|
71
|
+
) -> RadarScan:
|
|
72
|
+
"""
|
|
73
|
+
Create RadarScan directly from numpy arrays.
|
|
74
|
+
|
|
75
|
+
This is the most efficient way to create a RadarScan as it avoids
|
|
76
|
+
creating any intermediate Python objects.
|
|
77
|
+
|
|
78
|
+
:param ranges: Range values in meters.
|
|
79
|
+
:param azimuths: Azimuth angles in radians.
|
|
80
|
+
:param elevations: Elevation angles in radians.
|
|
81
|
+
:param rcs: RCS values in dBsm.
|
|
82
|
+
:param velocities: Radial velocities in m/s.
|
|
83
|
+
:param x: X positions in sensor frame.
|
|
84
|
+
:param y: Y positions in sensor frame.
|
|
85
|
+
:param z: Z positions in sensor frame.
|
|
86
|
+
:return: RadarScan instance.
|
|
87
|
+
:raises ValueError: If arrays have mismatched lengths.
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
n = len(ranges)
|
|
91
|
+
if not all(len(arr) == n for arr in [azimuths, elevations, rcs, velocities, x, y, z]):
|
|
92
|
+
raise ValueError("All arrays must have the same length")
|
|
93
|
+
|
|
94
|
+
return cls(
|
|
95
|
+
ranges=Array.from_numpy(ranges.astype(np.float32)),
|
|
96
|
+
azimuths=Array.from_numpy(azimuths.astype(np.float32)),
|
|
97
|
+
elevations=Array.from_numpy(elevations.astype(np.float32)),
|
|
98
|
+
rcs=Array.from_numpy(rcs.astype(np.float32)),
|
|
99
|
+
velocities=Array.from_numpy(velocities.astype(np.float32)),
|
|
100
|
+
x=Array.from_numpy(x.astype(np.float32)),
|
|
101
|
+
y=Array.from_numpy(y.astype(np.float32)),
|
|
102
|
+
z=Array.from_numpy(z.astype(np.float32)),
|
|
103
|
+
)
|
|
29
104
|
|
|
30
105
|
def to_point_cloud(self, frame_id: str = "radar") -> PointCloud:
|
|
31
106
|
"""
|
|
@@ -35,24 +110,115 @@ class RadarScan(Message):
|
|
|
35
110
|
:return: PointCloud with detection positions.
|
|
36
111
|
"""
|
|
37
112
|
|
|
38
|
-
if not self.detections:
|
|
39
|
-
return PointCloud(frame_id=frame_id, x=[], y=[], z=[])
|
|
40
|
-
|
|
41
113
|
return PointCloud(
|
|
42
114
|
frame_id=frame_id,
|
|
43
|
-
x=
|
|
44
|
-
y=
|
|
45
|
-
z=
|
|
115
|
+
x=self.x.to_list(),
|
|
116
|
+
y=self.y.to_list(),
|
|
117
|
+
z=self.z.to_list(),
|
|
46
118
|
)
|
|
47
119
|
|
|
48
|
-
|
|
49
|
-
|
|
120
|
+
def to_foxglove(self) -> dict[str, Any]:
|
|
121
|
+
"""
|
|
122
|
+
Convert to a JSON-serializable dict for Foxglove visualization.
|
|
123
|
+
|
|
124
|
+
:return: Dictionary with all scan data as lists.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
"num_detections": self.num_detections,
|
|
129
|
+
"ranges": self.ranges.to_list(),
|
|
130
|
+
"azimuths": self.azimuths.to_list(),
|
|
131
|
+
"elevations": self.elevations.to_list(),
|
|
132
|
+
"rcs": self.rcs.to_list(),
|
|
133
|
+
"velocities": self.velocities.to_list(),
|
|
134
|
+
"x": self.x.to_list(),
|
|
135
|
+
"y": self.y.to_list(),
|
|
136
|
+
"z": self.z.to_list(),
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class RangeMap(Message):
|
|
141
|
+
"""
|
|
142
|
+
1D range bin map for radar processing.
|
|
143
|
+
|
|
144
|
+
Represents radar signal strength across range bins, commonly used for
|
|
145
|
+
MTI (Moving Target Indication) processing and detection.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
_type = "antioch/range_map"
|
|
149
|
+
|
|
150
|
+
r_bins: int = Field(description="Number of range bins")
|
|
151
|
+
r_min_m: float = Field(description="Minimum range in meters (inclusive)")
|
|
152
|
+
r_max_m: float = Field(description="Maximum range in meters (inclusive)")
|
|
153
|
+
dr_m: float = Field(description="Range bin size in meters")
|
|
154
|
+
data: Array = Field(description="Float32 array of shape (r_bins,)")
|
|
155
|
+
|
|
156
|
+
def to_numpy(self) -> np.ndarray:
|
|
157
|
+
"""
|
|
158
|
+
Convert the range map to a 1D numpy array.
|
|
159
|
+
|
|
160
|
+
:return: float32 array with shape (r_bins,).
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
return self.data.to_numpy().reshape(self.r_bins)
|
|
164
|
+
|
|
165
|
+
def to_image(
|
|
166
|
+
self,
|
|
167
|
+
*,
|
|
168
|
+
transform: str = "log1p",
|
|
169
|
+
encoding: ImageEncoding = ImageEncoding.MONO8,
|
|
170
|
+
clip_percentile: float = 99.5,
|
|
171
|
+
height: int = 32,
|
|
172
|
+
) -> Image:
|
|
173
|
+
"""
|
|
174
|
+
Convert this range map into an Image for visualization.
|
|
175
|
+
|
|
176
|
+
The image is a horizontal bar where x-axis is range bins.
|
|
177
|
+
|
|
178
|
+
:param transform: "linear" or "log1p".
|
|
179
|
+
:param encoding: Image encoding to use (default MONO8).
|
|
180
|
+
:param clip_percentile: When encoding is MONO8, clip values to this percentile.
|
|
181
|
+
:param height: Height of the output image in pixels.
|
|
182
|
+
:return: Image representing the range map.
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
data = self.to_numpy()
|
|
186
|
+
if transform == "log1p":
|
|
187
|
+
values = np.log1p(data).astype(np.float32)
|
|
188
|
+
elif transform == "linear":
|
|
189
|
+
values = data.astype(np.float32, copy=False)
|
|
190
|
+
else:
|
|
191
|
+
raise ValueError(f"Unsupported transform '{transform}'")
|
|
192
|
+
|
|
193
|
+
if encoding == ImageEncoding.DEPTH_F32:
|
|
194
|
+
tiled = np.tile(values.reshape(1, -1), (height, 1))
|
|
195
|
+
return Image.from_numpy(tiled.astype(np.float32, copy=False), encoding=encoding)
|
|
196
|
+
|
|
197
|
+
if encoding != ImageEncoding.MONO8:
|
|
198
|
+
raise ValueError(f"Unsupported encoding '{encoding.value}' for range map image")
|
|
199
|
+
|
|
200
|
+
# Normalize to 8-bit
|
|
201
|
+
vmax = float(np.percentile(values, clip_percentile)) if values.size else 0.0
|
|
202
|
+
if vmax <= 0.0 or not np.isfinite(vmax):
|
|
203
|
+
mono = np.zeros((height, self.r_bins), dtype=np.uint8)
|
|
204
|
+
else:
|
|
205
|
+
scaled = np.clip(values / np.float32(vmax), 0.0, 1.0)
|
|
206
|
+
mono_row = (scaled * np.float32(255.0)).astype(np.uint8, copy=False)
|
|
207
|
+
mono = np.tile(mono_row.reshape(1, -1), (height, 1))
|
|
208
|
+
|
|
209
|
+
return Image.from_numpy(mono, encoding=encoding)
|
|
210
|
+
|
|
211
|
+
def to_foxglove(self) -> dict[str, Any]:
|
|
50
212
|
"""
|
|
51
|
-
|
|
213
|
+
Convert to a JSON-serializable dict for Foxglove visualization.
|
|
52
214
|
|
|
53
|
-
:
|
|
54
|
-
:return: Combined radar scan with all detections.
|
|
215
|
+
:return: Dictionary with range map metadata and data as list.
|
|
55
216
|
"""
|
|
56
217
|
|
|
57
|
-
|
|
58
|
-
|
|
218
|
+
return {
|
|
219
|
+
"r_bins": self.r_bins,
|
|
220
|
+
"r_min_m": self.r_min_m,
|
|
221
|
+
"r_max_m": self.r_max_m,
|
|
222
|
+
"dr_m": self.dr_m,
|
|
223
|
+
"data": self.data.to_list(),
|
|
224
|
+
}
|
common/message/twist.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from common.message.message import Message
|
|
4
|
+
from common.message.vector import Vector3
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Twist(Message):
|
|
8
|
+
"""
|
|
9
|
+
Linear and angular velocity (twist).
|
|
10
|
+
|
|
11
|
+
Represents the velocity of a rigid body in 3D space, combining both
|
|
12
|
+
linear velocity (translation) and angular velocity (rotation).
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
```python
|
|
16
|
+
from common.message import Twist, Vector3
|
|
17
|
+
|
|
18
|
+
# Create a twist for forward motion with rotation
|
|
19
|
+
twist = Twist(
|
|
20
|
+
linear=Vector3(x=1.0, y=0.0, z=0.0), # 1 m/s forward
|
|
21
|
+
angular=Vector3(x=0.0, y=0.0, z=0.1), # 0.1 rad/s yaw
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Zero velocity
|
|
25
|
+
stationary = Twist(
|
|
26
|
+
linear=Vector3.zeros(),
|
|
27
|
+
angular=Vector3.zeros(),
|
|
28
|
+
)
|
|
29
|
+
```
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
_type = "antioch/twist"
|
|
33
|
+
linear: Vector3 = Field(description="Linear velocity in m/s (x, y, z)")
|
|
34
|
+
angular: Vector3 = Field(description="Angular velocity in rad/s (x, y, z)")
|
common/message/types.py
CHANGED
|
@@ -1,37 +1,72 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from common.message.message import Message
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class Bool(Message):
|
|
5
7
|
"""
|
|
6
8
|
Boolean value message.
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
```python
|
|
12
|
+
from common.message import Bool
|
|
13
|
+
|
|
14
|
+
msg = Bool(value=True)
|
|
15
|
+
if msg.value:
|
|
16
|
+
print("Value is true")
|
|
17
|
+
```
|
|
7
18
|
"""
|
|
8
19
|
|
|
9
20
|
_type = "antioch/bool"
|
|
10
|
-
value: bool
|
|
21
|
+
value: bool = Field(description="Boolean value")
|
|
11
22
|
|
|
12
23
|
|
|
13
24
|
class Int(Message):
|
|
14
25
|
"""
|
|
15
26
|
Integer value message.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
from common.message import Int
|
|
31
|
+
|
|
32
|
+
msg = Int(value=42)
|
|
33
|
+
print(f"The answer is {msg.value}")
|
|
34
|
+
```
|
|
16
35
|
"""
|
|
17
36
|
|
|
18
37
|
_type = "antioch/int"
|
|
19
|
-
value: int
|
|
38
|
+
value: int = Field(description="Integer value")
|
|
20
39
|
|
|
21
40
|
|
|
22
41
|
class Float(Message):
|
|
23
42
|
"""
|
|
24
43
|
Float value message.
|
|
44
|
+
|
|
45
|
+
Example:
|
|
46
|
+
```python
|
|
47
|
+
from common.message import Float
|
|
48
|
+
|
|
49
|
+
msg = Float(value=3.14159)
|
|
50
|
+
print(f"Pi is approximately {msg.value:.2f}")
|
|
51
|
+
```
|
|
25
52
|
"""
|
|
26
53
|
|
|
27
54
|
_type = "antioch/float"
|
|
28
|
-
value: float
|
|
55
|
+
value: float = Field(description="Floating-point value")
|
|
29
56
|
|
|
30
57
|
|
|
31
58
|
class String(Message):
|
|
32
59
|
"""
|
|
33
60
|
String value message.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
```python
|
|
64
|
+
from common.message import String
|
|
65
|
+
|
|
66
|
+
msg = String(value="Hello, World!")
|
|
67
|
+
print(msg.value)
|
|
68
|
+
```
|
|
34
69
|
"""
|
|
35
70
|
|
|
36
71
|
_type = "antioch/string"
|
|
37
|
-
value: str
|
|
72
|
+
value: str = Field(description="String value")
|