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.
- 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.4.dist-info → antioch_py-3.0.1.dist-info}/METADATA +8 -11
- antioch_py-3.0.1.dist-info/RECORD +61 -0
- {antioch_py-2.2.4.dist-info → antioch_py-3.0.1.dist-info}/WHEEL +1 -1
- antioch_py-3.0.1.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 -102
- 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.4.dist-info/RECORD +0 -85
- antioch_py-2.2.4.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 -435
- 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.4.dist-info → antioch_py-3.0.1.dist-info}/top_level.txt +0 -0
- /common/message/{base.py → message.py} +0 -0
common/message/image.py
CHANGED
|
@@ -3,13 +3,30 @@ from __future__ import annotations
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
|
+
from foxglove.schemas import RawImage as FoxgloveRawImage
|
|
7
|
+
from pydantic import Field
|
|
6
8
|
|
|
7
|
-
from common.message.
|
|
9
|
+
from common.message.message import Message
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class ImageEncoding(str, Enum):
|
|
11
13
|
"""
|
|
12
14
|
Image encodings with associated metadata.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
```python
|
|
18
|
+
from common.message import ImageEncoding
|
|
19
|
+
|
|
20
|
+
# Get bytes per pixel for an encoding
|
|
21
|
+
encoding = ImageEncoding.RGB8
|
|
22
|
+
bpp = encoding.bytes_per_pixel # 3
|
|
23
|
+
|
|
24
|
+
# Get numpy dtype for an encoding
|
|
25
|
+
dtype = encoding.numpy_dtype # np.uint8
|
|
26
|
+
|
|
27
|
+
# Get number of channels
|
|
28
|
+
channels = encoding.channels # 3
|
|
29
|
+
```
|
|
13
30
|
"""
|
|
14
31
|
|
|
15
32
|
# Monochrome images
|
|
@@ -92,17 +109,31 @@ class Image(Message):
|
|
|
92
109
|
"""
|
|
93
110
|
An image with raw data.
|
|
94
111
|
|
|
95
|
-
:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
Example:
|
|
113
|
+
```python
|
|
114
|
+
from common.message import Image, ImageEncoding
|
|
115
|
+
import numpy as np
|
|
116
|
+
|
|
117
|
+
# Create an image from a numpy array
|
|
118
|
+
rgb_array = np.zeros((480, 640, 3), dtype=np.uint8)
|
|
119
|
+
rgb_array[:, :, 0] = 255 # Red channel
|
|
120
|
+
image = Image.from_numpy(rgb_array, ImageEncoding.RGB8)
|
|
121
|
+
|
|
122
|
+
# Convert back to numpy for processing
|
|
123
|
+
array = image.to_numpy()
|
|
124
|
+
print(f"Shape: {array.shape}") # (480, 640, 3)
|
|
125
|
+
|
|
126
|
+
# Create a depth image
|
|
127
|
+
depth_array = np.ones((480, 640), dtype=np.float32) * 2.5
|
|
128
|
+
depth_image = Image.from_numpy(depth_array, ImageEncoding.DEPTH_F32)
|
|
129
|
+
```
|
|
99
130
|
"""
|
|
100
131
|
|
|
101
132
|
_type = "antioch/image"
|
|
102
|
-
encoding: ImageEncoding
|
|
103
|
-
width: int
|
|
104
|
-
height: int
|
|
105
|
-
data: bytes
|
|
133
|
+
encoding: ImageEncoding = Field(description="Pixel encoding format of the image")
|
|
134
|
+
width: int = Field(description="Image width in pixels")
|
|
135
|
+
height: int = Field(description="Image height in pixels")
|
|
136
|
+
data: bytes = Field(description="Raw image data as bytes")
|
|
106
137
|
|
|
107
138
|
@classmethod
|
|
108
139
|
def from_numpy(cls, array: np.ndarray, encoding: ImageEncoding | None = None) -> Image:
|
|
@@ -169,3 +200,21 @@ class Image(Message):
|
|
|
169
200
|
shape = (self.height, self.width) if self.encoding.channels == 1 else (self.height, self.width, self.encoding.channels)
|
|
170
201
|
array = np.frombuffer(self.data, dtype=self.encoding.numpy_dtype)
|
|
171
202
|
return array.reshape(shape)
|
|
203
|
+
|
|
204
|
+
def to_foxglove(self) -> FoxgloveRawImage:
|
|
205
|
+
"""
|
|
206
|
+
Convert to Foxglove RawImage for telemetry.
|
|
207
|
+
|
|
208
|
+
:return: Foxglove RawImage schema.
|
|
209
|
+
"""
|
|
210
|
+
|
|
211
|
+
step = self.width * self.encoding.bytes_per_pixel
|
|
212
|
+
return FoxgloveRawImage(
|
|
213
|
+
timestamp=None,
|
|
214
|
+
frame_id="",
|
|
215
|
+
width=self.width,
|
|
216
|
+
height=self.height,
|
|
217
|
+
encoding=self.encoding.value,
|
|
218
|
+
step=step,
|
|
219
|
+
data=self.data,
|
|
220
|
+
)
|
common/message/imu.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from common.message.message import Message
|
|
2
4
|
from common.message.quaternion import Quaternion
|
|
3
5
|
from common.message.vector import Vector3
|
|
4
6
|
|
|
@@ -6,9 +8,27 @@ from common.message.vector import Vector3
|
|
|
6
8
|
class ImuSample(Message):
|
|
7
9
|
"""
|
|
8
10
|
IMU sensor sample data.
|
|
11
|
+
|
|
12
|
+
Contains linear acceleration, angular velocity, and orientation from an
|
|
13
|
+
inertial measurement unit sensor.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
```python
|
|
17
|
+
from common.message import ImuSample, Vector3, Quaternion
|
|
18
|
+
|
|
19
|
+
# Create an IMU sample
|
|
20
|
+
sample = ImuSample(
|
|
21
|
+
linear_acceleration=Vector3(x=0.0, y=0.0, z=9.81), # Gravity
|
|
22
|
+
angular_velocity=Vector3(x=0.01, y=0.0, z=0.0),
|
|
23
|
+
orientation=Quaternion.identity(),
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Access acceleration components
|
|
27
|
+
accel_z = sample.linear_acceleration.z
|
|
28
|
+
```
|
|
9
29
|
"""
|
|
10
30
|
|
|
11
31
|
_type = "antioch/imu_sample"
|
|
12
|
-
linear_acceleration: Vector3
|
|
13
|
-
angular_velocity: Vector3
|
|
14
|
-
orientation: Quaternion
|
|
32
|
+
linear_acceleration: Vector3 = Field(description="Linear acceleration in m/s² (x, y, z)")
|
|
33
|
+
angular_velocity: Vector3 = Field(description="Angular velocity in rad/s (x, y, z)")
|
|
34
|
+
orientation: Quaternion = Field(description="Orientation as quaternion (w, x, y, z)")
|
common/message/joint.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from
|
|
1
|
+
from pydantic import Field
|
|
2
|
+
|
|
3
|
+
from common.message.message import Message
|
|
2
4
|
|
|
3
5
|
|
|
4
6
|
class JointState(Message):
|
|
@@ -7,12 +9,27 @@ class JointState(Message):
|
|
|
7
9
|
|
|
8
10
|
Represents the complete physical state of a joint including its position,
|
|
9
11
|
velocity, and measured effort (force/torque).
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
```python
|
|
15
|
+
from common.message import JointState
|
|
16
|
+
|
|
17
|
+
# Create a joint state
|
|
18
|
+
state = JointState(
|
|
19
|
+
name="shoulder_pan",
|
|
20
|
+
position=1.57,
|
|
21
|
+
velocity=0.1,
|
|
22
|
+
effort=5.0,
|
|
23
|
+
)
|
|
24
|
+
print(f"{state.name}: pos={state.position:.2f} rad")
|
|
25
|
+
```
|
|
10
26
|
"""
|
|
11
27
|
|
|
12
28
|
_type = "antioch/joint_state"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
29
|
+
name: str = Field(description="Name of the joint")
|
|
30
|
+
position: float = Field(description="Joint position in radians")
|
|
31
|
+
velocity: float = Field(description="Joint velocity in radians per second")
|
|
32
|
+
effort: float = Field(description="Joint effort (force/torque) in Nm")
|
|
16
33
|
|
|
17
34
|
|
|
18
35
|
class JointTarget(Message):
|
|
@@ -20,28 +37,70 @@ class JointTarget(Message):
|
|
|
20
37
|
Control target for a single joint.
|
|
21
38
|
|
|
22
39
|
Specifies desired position, velocity, and/or effort targets for a joint's
|
|
23
|
-
PD controller.
|
|
40
|
+
PD controller. The name field is required to identify which joint is being
|
|
41
|
+
targeted. Position, velocity, and effort are optional - omitted values are
|
|
42
|
+
not controlled.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
```python
|
|
46
|
+
from common.message import JointTarget
|
|
47
|
+
|
|
48
|
+
# Position control target
|
|
49
|
+
target = JointTarget(name="elbow", position=0.5)
|
|
50
|
+
|
|
51
|
+
# Velocity control target
|
|
52
|
+
vel_target = JointTarget(name="wrist", velocity=0.2)
|
|
53
|
+
|
|
54
|
+
# Combined position and velocity target
|
|
55
|
+
combined = JointTarget(name="shoulder", position=1.0, velocity=0.1)
|
|
56
|
+
```
|
|
24
57
|
"""
|
|
25
58
|
|
|
26
59
|
_type = "antioch/joint_target"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
60
|
+
name: str = Field(description="Name of the joint to control")
|
|
61
|
+
position: float | None = Field(default=None, description="Target position in radians")
|
|
62
|
+
velocity: float | None = Field(default=None, description="Target velocity in radians per second")
|
|
63
|
+
effort: float | None = Field(default=None, description="Target effort (force/torque) in Nm")
|
|
30
64
|
|
|
31
65
|
|
|
32
66
|
class JointStates(Message):
|
|
33
67
|
"""
|
|
34
68
|
Collection of joint states for an actuator group.
|
|
69
|
+
|
|
70
|
+
Example:
|
|
71
|
+
```python
|
|
72
|
+
from common.message import JointStates, JointState
|
|
73
|
+
|
|
74
|
+
# Create states for a robot arm
|
|
75
|
+
states = JointStates(states=[
|
|
76
|
+
JointState(name="joint1", position=0.0, velocity=0.0, effort=0.0),
|
|
77
|
+
JointState(name="joint2", position=1.57, velocity=0.1, effort=2.5),
|
|
78
|
+
])
|
|
79
|
+
|
|
80
|
+
for state in states.states:
|
|
81
|
+
print(f"{state.name}: {state.position:.2f} rad")
|
|
82
|
+
```
|
|
35
83
|
"""
|
|
36
84
|
|
|
37
85
|
_type = "antioch/joint_states"
|
|
38
|
-
states: list[JointState]
|
|
86
|
+
states: list[JointState] = Field(description="List of joint states")
|
|
39
87
|
|
|
40
88
|
|
|
41
89
|
class JointTargets(Message):
|
|
42
90
|
"""
|
|
43
91
|
Collection of joint targets for an actuator group.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
```python
|
|
95
|
+
from common.message import JointTargets, JointTarget
|
|
96
|
+
|
|
97
|
+
# Create targets for multiple joints
|
|
98
|
+
targets = JointTargets(targets=[
|
|
99
|
+
JointTarget(name="joint1", position=0.5),
|
|
100
|
+
JointTarget(name="joint2", position=1.0),
|
|
101
|
+
])
|
|
102
|
+
```
|
|
44
103
|
"""
|
|
45
104
|
|
|
46
105
|
_type = "antioch/joint_targets"
|
|
47
|
-
targets: list[JointTarget]
|
|
106
|
+
targets: list[JointTarget] = Field(description="List of joint targets")
|
common/message/log.py
CHANGED
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import time
|
|
2
2
|
from enum import Enum
|
|
3
3
|
|
|
4
|
+
from foxglove.schemas import Log as FoxgloveLog, LogLevel as FoxgloveLogLevel
|
|
4
5
|
from pydantic import Field
|
|
5
6
|
|
|
6
|
-
from common.message.
|
|
7
|
+
from common.message.message import Message
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
class LogLevel(str, Enum):
|
|
10
11
|
"""
|
|
11
12
|
Log level.
|
|
13
|
+
|
|
14
|
+
Example:
|
|
15
|
+
```python
|
|
16
|
+
from common.message import LogLevel
|
|
17
|
+
|
|
18
|
+
level = LogLevel.INFO
|
|
19
|
+
if level == LogLevel.ERROR:
|
|
20
|
+
print("Error occurred")
|
|
21
|
+
```
|
|
12
22
|
"""
|
|
13
23
|
|
|
14
24
|
DEBUG = "debug"
|
|
@@ -20,12 +30,47 @@ class LogLevel(str, Enum):
|
|
|
20
30
|
class Log(Message):
|
|
21
31
|
"""
|
|
22
32
|
Log entry structure.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
```python
|
|
36
|
+
from common.message import Log, LogLevel
|
|
37
|
+
|
|
38
|
+
# Create a log entry
|
|
39
|
+
log = Log(
|
|
40
|
+
let_us=1000000,
|
|
41
|
+
level=LogLevel.INFO,
|
|
42
|
+
message="Robot initialized successfully",
|
|
43
|
+
channel="robot.init",
|
|
44
|
+
)
|
|
45
|
+
```
|
|
23
46
|
"""
|
|
24
47
|
|
|
25
48
|
_type = "antioch/log"
|
|
26
|
-
timestamp_us: int = Field(default_factory=lambda: int(time.time_ns() // 1000))
|
|
27
|
-
let_us: int
|
|
28
|
-
level: LogLevel
|
|
29
|
-
message: str | None = None
|
|
30
|
-
channel: str | None = None
|
|
31
|
-
telemetry: bytes | None = None
|
|
49
|
+
timestamp_us: int = Field(default_factory=lambda: int(time.time_ns() // 1000), description="Wall clock timestamp in microseconds")
|
|
50
|
+
let_us: int = Field(description="Logical event time in microseconds")
|
|
51
|
+
level: LogLevel = Field(description="Log severity level")
|
|
52
|
+
message: str | None = Field(default=None, description="Log message text")
|
|
53
|
+
channel: str | None = Field(default=None, description="Log channel/category")
|
|
54
|
+
telemetry: bytes | None = Field(default=None, description="Optional serialized telemetry data")
|
|
55
|
+
|
|
56
|
+
def to_foxglove(self) -> FoxgloveLog:
|
|
57
|
+
"""
|
|
58
|
+
Convert to Foxglove Log for telemetry.
|
|
59
|
+
|
|
60
|
+
:return: Foxglove Log schema.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
level_map = {
|
|
64
|
+
LogLevel.DEBUG: FoxgloveLogLevel.Debug,
|
|
65
|
+
LogLevel.INFO: FoxgloveLogLevel.Info,
|
|
66
|
+
LogLevel.WARNING: FoxgloveLogLevel.Warning,
|
|
67
|
+
LogLevel.ERROR: FoxgloveLogLevel.Error,
|
|
68
|
+
}
|
|
69
|
+
return FoxgloveLog(
|
|
70
|
+
timestamp=None,
|
|
71
|
+
level=level_map[self.level],
|
|
72
|
+
message=self.message or "",
|
|
73
|
+
name=self.channel or "",
|
|
74
|
+
file="",
|
|
75
|
+
line=0,
|
|
76
|
+
)
|
common/message/pir.py
CHANGED
|
@@ -1,18 +1,33 @@
|
|
|
1
1
|
from pydantic import Field
|
|
2
2
|
|
|
3
|
-
from common.message.
|
|
3
|
+
from common.message.message import Message
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class PirStatus(Message):
|
|
7
7
|
"""
|
|
8
8
|
PIR sensor status containing detection state and signal information.
|
|
9
|
+
|
|
10
|
+
Passive Infrared (PIR) sensors detect motion by measuring changes in
|
|
11
|
+
infrared radiation in their field of view.
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
```python
|
|
15
|
+
from common.message import PirStatus
|
|
16
|
+
|
|
17
|
+
# Create a PIR status
|
|
18
|
+
status = PirStatus(
|
|
19
|
+
is_detected=True,
|
|
20
|
+
signal_strength=0.85,
|
|
21
|
+
threshold=0.5,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Check for motion
|
|
25
|
+
if status.is_detected:
|
|
26
|
+
print(f"Motion detected! Signal: {status.signal_strength}")
|
|
27
|
+
```
|
|
9
28
|
"""
|
|
10
29
|
|
|
11
30
|
_type = "antioch/pir_status"
|
|
12
|
-
is_detected: bool = Field(description="Whether motion is currently detected
|
|
13
|
-
signal_strength: float = Field(description="
|
|
14
|
-
|
|
15
|
-
# Multi-sensor status
|
|
16
|
-
sensor_states: list[bool] = Field(default_factory=list, description="Detection state per sensor (Center, Left, Right)")
|
|
17
|
-
sensor_signals: list[float] = Field(default_factory=list, description="Signal strength per sensor")
|
|
18
|
-
sensor_thresholds: list[float] = Field(default_factory=list, description="Detection threshold per sensor")
|
|
31
|
+
is_detected: bool = Field(description="Whether motion is currently detected")
|
|
32
|
+
signal_strength: float = Field(description="Analog signal strength [0.0, 1.0]")
|
|
33
|
+
threshold: float = Field(description="Detection threshold [0.0, 1.0]")
|
common/message/plot.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import Field
|
|
6
|
+
|
|
7
|
+
from common.message.message import Message
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PlotData(Message):
|
|
11
|
+
"""
|
|
12
|
+
Simple X/Y plot data for Foxglove visualization.
|
|
13
|
+
|
|
14
|
+
Use with Foxglove's Plot panel by selecting:
|
|
15
|
+
- X: .x[:]
|
|
16
|
+
- Y: .y[:]
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
```python
|
|
20
|
+
from common.message import PlotData
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
distances = np.linspace(0, 10, 128)
|
|
24
|
+
values = np.random.rand(128)
|
|
25
|
+
|
|
26
|
+
plot = PlotData.from_arrays(distances, values)
|
|
27
|
+
sim.logger.telemetry("radar/mti/radar_left", plot)
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
_type = "antioch/plot_data"
|
|
32
|
+
|
|
33
|
+
x: list[float] = Field(default_factory=list, description="X-axis values")
|
|
34
|
+
y: list[float] = Field(default_factory=list, description="Y-axis values")
|
|
35
|
+
|
|
36
|
+
@classmethod
|
|
37
|
+
def from_arrays(cls, x: Any, y: Any) -> PlotData:
|
|
38
|
+
"""
|
|
39
|
+
Create PlotData from arrays or lists.
|
|
40
|
+
|
|
41
|
+
:param x: X values (numpy array or list).
|
|
42
|
+
:param y: Y values (numpy array or list).
|
|
43
|
+
:return: PlotData instance.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
x_list = x.tolist() if hasattr(x, "tolist") else list(x)
|
|
47
|
+
y_list = y.tolist() if hasattr(y, "tolist") else list(y)
|
|
48
|
+
return cls(x=x_list, y=y_list)
|
|
49
|
+
|
|
50
|
+
def to_foxglove(self) -> dict[str, Any]:
|
|
51
|
+
"""
|
|
52
|
+
Convert to Foxglove-compatible dict.
|
|
53
|
+
|
|
54
|
+
:return: Dict with x and y arrays.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
return {"x": self.x, "y": self.y}
|
common/message/point.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from foxglove.schemas import Point2 as FoxglovePoint2, Point3 as FoxglovePoint3
|
|
4
|
+
from pydantic import Field
|
|
5
|
+
|
|
6
|
+
from common.message.message import Message
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class Point2(Message):
|
|
@@ -8,11 +11,25 @@ class Point2(Message):
|
|
|
8
11
|
A point representing a position in 2D space.
|
|
9
12
|
|
|
10
13
|
Used in image annotations and 2D coordinate systems.
|
|
14
|
+
|
|
15
|
+
Example:
|
|
16
|
+
```python
|
|
17
|
+
from common.message import Point2
|
|
18
|
+
|
|
19
|
+
# Create a 2D point
|
|
20
|
+
point = Point2(x=100.0, y=200.0)
|
|
21
|
+
|
|
22
|
+
# Create using factory method
|
|
23
|
+
origin = Point2.zero()
|
|
24
|
+
|
|
25
|
+
# Convert to Foxglove for visualization
|
|
26
|
+
foxglove_point = point.to_foxglove()
|
|
27
|
+
```
|
|
11
28
|
"""
|
|
12
29
|
|
|
13
30
|
_type = "antioch/point2"
|
|
14
|
-
x: float
|
|
15
|
-
y: float
|
|
31
|
+
x: float = Field(description="X coordinate")
|
|
32
|
+
y: float = Field(description="Y coordinate")
|
|
16
33
|
|
|
17
34
|
def __repr__(self) -> str:
|
|
18
35
|
"""
|
|
@@ -54,18 +71,41 @@ class Point2(Message):
|
|
|
54
71
|
|
|
55
72
|
return cls(x=0.0, y=0.0)
|
|
56
73
|
|
|
74
|
+
def to_foxglove(self) -> FoxglovePoint2:
|
|
75
|
+
"""
|
|
76
|
+
Convert to Foxglove Point2 for telemetry.
|
|
77
|
+
|
|
78
|
+
:return: Foxglove Point2 schema.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
return FoxglovePoint2(x=self.x, y=self.y)
|
|
82
|
+
|
|
57
83
|
|
|
58
84
|
class Point3(Message):
|
|
59
85
|
"""
|
|
60
86
|
A point representing a position in 3D space.
|
|
61
87
|
|
|
62
88
|
Used in 3D graphics and spatial coordinate systems.
|
|
89
|
+
|
|
90
|
+
Example:
|
|
91
|
+
```python
|
|
92
|
+
from common.message import Point3
|
|
93
|
+
|
|
94
|
+
# Create a 3D point
|
|
95
|
+
point = Point3(x=1.0, y=2.0, z=3.0)
|
|
96
|
+
|
|
97
|
+
# Create at origin
|
|
98
|
+
origin = Point3.zero()
|
|
99
|
+
|
|
100
|
+
# Create using factory method
|
|
101
|
+
p = Point3.new(x=0.5, y=1.0, z=0.0)
|
|
102
|
+
```
|
|
63
103
|
"""
|
|
64
104
|
|
|
65
105
|
_type = "antioch/point3"
|
|
66
|
-
x: float
|
|
67
|
-
y: float
|
|
68
|
-
z: float
|
|
106
|
+
x: float = Field(description="X coordinate")
|
|
107
|
+
y: float = Field(description="Y coordinate")
|
|
108
|
+
z: float = Field(description="Z coordinate")
|
|
69
109
|
|
|
70
110
|
def __repr__(self) -> str:
|
|
71
111
|
"""
|
|
@@ -107,3 +147,12 @@ class Point3(Message):
|
|
|
107
147
|
"""
|
|
108
148
|
|
|
109
149
|
return cls(x=0.0, y=0.0, z=0.0)
|
|
150
|
+
|
|
151
|
+
def to_foxglove(self) -> FoxglovePoint3:
|
|
152
|
+
"""
|
|
153
|
+
Convert to Foxglove Point3 for telemetry.
|
|
154
|
+
|
|
155
|
+
:return: Foxglove Point3 schema.
|
|
156
|
+
"""
|
|
157
|
+
|
|
158
|
+
return FoxglovePoint3(x=self.x, y=self.y, z=self.z)
|
common/message/point_cloud.py
CHANGED
|
@@ -1,25 +1,50 @@
|
|
|
1
1
|
import struct
|
|
2
2
|
|
|
3
|
+
from foxglove.schemas import (
|
|
4
|
+
PackedElementField,
|
|
5
|
+
PackedElementFieldNumericType as NumericType,
|
|
6
|
+
PointCloud as FoxglovePointCloud,
|
|
7
|
+
Pose as FoxglovePose,
|
|
8
|
+
Quaternion as FoxgloveQuaternion,
|
|
9
|
+
Vector3 as FoxgloveVector3,
|
|
10
|
+
)
|
|
3
11
|
from pydantic import Field, model_validator
|
|
4
12
|
|
|
5
|
-
from common.message.
|
|
13
|
+
from common.message.message import Message
|
|
6
14
|
|
|
7
15
|
|
|
8
16
|
class PointCloud(Message):
|
|
9
17
|
"""
|
|
10
18
|
A collection of 3D points.
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
:
|
|
20
|
+
Point clouds are used for representing 3D sensor data such as LiDAR
|
|
21
|
+
scans or depth camera point clouds.
|
|
22
|
+
|
|
23
|
+
Example:
|
|
24
|
+
```python
|
|
25
|
+
from common.message import PointCloud
|
|
26
|
+
|
|
27
|
+
# Create a point cloud
|
|
28
|
+
cloud = PointCloud(
|
|
29
|
+
frame_id="lidar_link",
|
|
30
|
+
x=[1.0, 2.0, 3.0],
|
|
31
|
+
y=[0.5, 1.0, 1.5],
|
|
32
|
+
z=[0.1, 0.2, 0.3],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
# Convert to Foxglove for visualization
|
|
36
|
+
foxglove_cloud = cloud.to_foxglove()
|
|
37
|
+
|
|
38
|
+
# Get packed bytes for transmission
|
|
39
|
+
data = cloud.to_bytes()
|
|
40
|
+
```
|
|
16
41
|
"""
|
|
17
42
|
|
|
18
43
|
_type = "antioch/point_cloud"
|
|
19
|
-
frame_id: str = Field(default="", description="Frame of reference")
|
|
20
|
-
x: list[float] = Field(description="X coordinates of points")
|
|
21
|
-
y: list[float] = Field(description="Y coordinates of points")
|
|
22
|
-
z: list[float] = Field(description="Z coordinates of points")
|
|
44
|
+
frame_id: str = Field(default="", description="Frame of reference for the point cloud")
|
|
45
|
+
x: list[float] = Field(description="X coordinates of points in meters")
|
|
46
|
+
y: list[float] = Field(description="Y coordinates of points in meters")
|
|
47
|
+
z: list[float] = Field(description="Z coordinates of points in meters")
|
|
23
48
|
|
|
24
49
|
@model_validator(mode="after")
|
|
25
50
|
def validate_array_lengths(self) -> "PointCloud":
|
|
@@ -47,17 +72,28 @@ class PointCloud(Message):
|
|
|
47
72
|
|
|
48
73
|
return bytes(data)
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
def combine(point_clouds: list["PointCloud"], frame_id: str = "") -> "PointCloud":
|
|
75
|
+
def to_foxglove(self) -> FoxglovePointCloud:
|
|
52
76
|
"""
|
|
53
|
-
|
|
77
|
+
Convert to Foxglove PointCloud for telemetry.
|
|
54
78
|
|
|
55
|
-
:
|
|
56
|
-
:param frame_id: Frame of reference for the combined point cloud.
|
|
57
|
-
:return: Combined point cloud with all points.
|
|
79
|
+
:return: Foxglove PointCloud schema.
|
|
58
80
|
"""
|
|
59
81
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return
|
|
82
|
+
# Pack point data as interleaved x, y, z floats
|
|
83
|
+
data = self.to_bytes()
|
|
84
|
+
|
|
85
|
+
return FoxglovePointCloud(
|
|
86
|
+
timestamp=None,
|
|
87
|
+
frame_id=self.frame_id,
|
|
88
|
+
pose=FoxglovePose(
|
|
89
|
+
position=FoxgloveVector3(x=0, y=0, z=0),
|
|
90
|
+
orientation=FoxgloveQuaternion(x=0, y=0, z=0, w=1),
|
|
91
|
+
),
|
|
92
|
+
point_stride=12, # 3 floats * 4 bytes
|
|
93
|
+
fields=[
|
|
94
|
+
PackedElementField(name="x", offset=0, type=NumericType.Float32),
|
|
95
|
+
PackedElementField(name="y", offset=4, type=NumericType.Float32),
|
|
96
|
+
PackedElementField(name="z", offset=8, type=NumericType.Float32),
|
|
97
|
+
],
|
|
98
|
+
data=data,
|
|
99
|
+
)
|