dexcontrol 0.2.12__py3-none-any.whl → 0.3.4__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.
- dexcontrol/__init__.py +17 -8
- dexcontrol/apps/dualsense_teleop_base.py +1 -1
- dexcontrol/comm/__init__.py +51 -0
- dexcontrol/comm/rtc.py +401 -0
- dexcontrol/comm/subscribers.py +329 -0
- dexcontrol/config/core/chassis.py +9 -4
- dexcontrol/config/core/hand.py +1 -0
- dexcontrol/config/sensors/cameras/__init__.py +1 -2
- dexcontrol/config/sensors/cameras/zed_camera.py +2 -2
- dexcontrol/config/sensors/vega_sensors.py +12 -18
- dexcontrol/config/vega.py +4 -1
- dexcontrol/core/arm.py +66 -42
- dexcontrol/core/chassis.py +142 -120
- dexcontrol/core/component.py +107 -58
- dexcontrol/core/hand.py +119 -86
- dexcontrol/core/head.py +22 -33
- dexcontrol/core/misc.py +331 -158
- dexcontrol/core/robot_query_interface.py +467 -0
- dexcontrol/core/torso.py +5 -9
- dexcontrol/robot.py +245 -574
- dexcontrol/sensors/__init__.py +1 -2
- dexcontrol/sensors/camera/__init__.py +0 -2
- dexcontrol/sensors/camera/base_camera.py +150 -0
- dexcontrol/sensors/camera/rgb_camera.py +68 -64
- dexcontrol/sensors/camera/zed_camera.py +140 -164
- dexcontrol/sensors/imu/chassis_imu.py +81 -62
- dexcontrol/sensors/imu/zed_imu.py +54 -43
- dexcontrol/sensors/lidar/rplidar.py +16 -20
- dexcontrol/sensors/manager.py +4 -14
- dexcontrol/sensors/ultrasonic.py +15 -28
- dexcontrol/utils/__init__.py +0 -11
- dexcontrol/utils/comm_helper.py +110 -0
- dexcontrol/utils/constants.py +1 -1
- dexcontrol/utils/error_code.py +2 -4
- dexcontrol/utils/os_utils.py +172 -4
- dexcontrol/utils/pb_utils.py +6 -28
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.4.dist-info}/METADATA +16 -3
- dexcontrol-0.3.4.dist-info/RECORD +62 -0
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.4.dist-info}/WHEEL +1 -1
- dexcontrol/config/sensors/cameras/luxonis_camera.py +0 -51
- dexcontrol/proto/dexcontrol_msg_pb2.py +0 -73
- dexcontrol/proto/dexcontrol_msg_pb2.pyi +0 -220
- dexcontrol/proto/dexcontrol_query_pb2.py +0 -77
- dexcontrol/proto/dexcontrol_query_pb2.pyi +0 -162
- dexcontrol/sensors/camera/luxonis_camera.py +0 -169
- dexcontrol/utils/motion_utils.py +0 -199
- dexcontrol/utils/rate_limiter.py +0 -172
- dexcontrol/utils/rtc_utils.py +0 -144
- dexcontrol/utils/subscribers/__init__.py +0 -52
- dexcontrol/utils/subscribers/base.py +0 -281
- dexcontrol/utils/subscribers/camera.py +0 -332
- dexcontrol/utils/subscribers/decoders.py +0 -88
- dexcontrol/utils/subscribers/generic.py +0 -110
- dexcontrol/utils/subscribers/imu.py +0 -175
- dexcontrol/utils/subscribers/lidar.py +0 -172
- dexcontrol/utils/subscribers/protobuf.py +0 -111
- dexcontrol/utils/subscribers/rtc.py +0 -316
- dexcontrol/utils/zenoh_utils.py +0 -122
- dexcontrol-0.2.12.dist-info/RECORD +0 -75
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,71 +8,52 @@
|
|
|
8
8
|
# 2. Commercial License
|
|
9
9
|
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
10
|
|
|
11
|
-
"""ZED camera sensor implementation using RTC subscribers for RGB and
|
|
11
|
+
"""ZED camera sensor implementation using RTC or DexComm subscribers for RGB and depth."""
|
|
12
12
|
|
|
13
|
-
import logging
|
|
14
13
|
import time
|
|
15
|
-
from typing import Any
|
|
14
|
+
from typing import Any
|
|
16
15
|
|
|
17
16
|
import numpy as np
|
|
18
|
-
import
|
|
17
|
+
from loguru import logger
|
|
19
18
|
|
|
20
|
-
from dexcontrol.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
DepthCameraSubscriber,
|
|
25
|
-
RGBCameraSubscriber,
|
|
19
|
+
from dexcontrol.comm import (
|
|
20
|
+
create_camera_subscriber,
|
|
21
|
+
create_depth_subscriber,
|
|
22
|
+
create_rtc_camera_subscriber,
|
|
26
23
|
)
|
|
27
|
-
from dexcontrol.
|
|
28
|
-
from dexcontrol.
|
|
29
|
-
|
|
30
|
-
logger = logging.getLogger(__name__)
|
|
31
|
-
|
|
32
|
-
# Optional import for depth processing
|
|
33
|
-
try:
|
|
34
|
-
from dexsensor.serialization.camera import decode_depth
|
|
35
|
-
DEXSENSOR_AVAILABLE = True
|
|
36
|
-
except ImportError:
|
|
37
|
-
logger.warning("dexsensor not available. Depth data will be returned without decoding.")
|
|
38
|
-
decode_depth = None
|
|
39
|
-
DEXSENSOR_AVAILABLE = False
|
|
24
|
+
from dexcontrol.config.sensors.cameras import ZedCameraConfig
|
|
25
|
+
from dexcontrol.sensors.camera.base_camera import BaseCameraSensor
|
|
40
26
|
|
|
41
27
|
|
|
42
|
-
class ZedCameraSensor:
|
|
28
|
+
class ZedCameraSensor(BaseCameraSensor):
|
|
43
29
|
"""ZED camera sensor for multi-stream (RGB, Depth) data acquisition.
|
|
44
30
|
|
|
45
31
|
This sensor manages left RGB, right RGB, and depth data streams from a ZED
|
|
46
32
|
camera. It can be configured to use high-performance RTC subscribers for RGB
|
|
47
|
-
streams (`use_rtc=True`) or
|
|
48
|
-
|
|
33
|
+
streams (`use_rtc=True`) or standard DexComm subscribers. Both types provide
|
|
34
|
+
the same API interface, making them interchangeable.
|
|
49
35
|
"""
|
|
50
36
|
|
|
51
|
-
SubscriberType = Union[RTCSubscriber, DepthCameraSubscriber, RGBCameraSubscriber]
|
|
52
|
-
|
|
53
37
|
def __init__(
|
|
54
38
|
self,
|
|
55
39
|
configs: ZedCameraConfig,
|
|
56
|
-
zenoh_session: zenoh.Session,
|
|
57
40
|
) -> None:
|
|
58
41
|
"""Initialize the ZED camera sensor and its subscribers.
|
|
59
42
|
|
|
60
43
|
Args:
|
|
61
44
|
configs: Configuration object for the ZED camera.
|
|
62
|
-
zenoh_session: Active Zenoh session for communication.
|
|
63
45
|
"""
|
|
64
|
-
|
|
65
|
-
self._zenoh_session = zenoh_session
|
|
46
|
+
super().__init__(configs.name)
|
|
66
47
|
self._configs = configs
|
|
67
|
-
self._subscribers:
|
|
68
|
-
|
|
48
|
+
self._subscribers: dict[
|
|
49
|
+
str, Any | None
|
|
50
|
+
] = {} # Will hold either RTCSubscriberWrapper or Subscriber
|
|
69
51
|
|
|
70
52
|
self._create_subscribers()
|
|
71
|
-
self._query_camera_info()
|
|
72
53
|
|
|
73
54
|
def _create_subscriber(
|
|
74
|
-
self, stream_name: str, stream_config:
|
|
75
|
-
) ->
|
|
55
|
+
self, stream_name: str, stream_config: dict[str, Any]
|
|
56
|
+
) -> Any | None:
|
|
76
57
|
"""Factory method to create a subscriber based on stream type and config."""
|
|
77
58
|
try:
|
|
78
59
|
if not stream_config.get("enable", False):
|
|
@@ -85,50 +66,68 @@ class ZedCameraSensor:
|
|
|
85
66
|
if not topic:
|
|
86
67
|
logger.warning(f"'{self._name}': No 'topic' for depth stream.")
|
|
87
68
|
return None
|
|
88
|
-
logger.info(f"'{self._name}': Creating
|
|
89
|
-
|
|
69
|
+
logger.info(f"'{self._name}': Creating depth subscriber.")
|
|
70
|
+
# Use new DexComm integration
|
|
71
|
+
return create_depth_subscriber(
|
|
90
72
|
topic=topic,
|
|
91
|
-
zenoh_session=self._zenoh_session,
|
|
92
|
-
name=f"{self._name}_{stream_name}_subscriber",
|
|
93
|
-
enable_fps_tracking=self._configs.enable_fps_tracking,
|
|
94
|
-
fps_log_interval=self._configs.fps_log_interval,
|
|
95
73
|
)
|
|
96
74
|
|
|
97
|
-
# Create RGB subscriber (RTC or
|
|
75
|
+
# Create RGB subscriber (RTC or DexComm)
|
|
98
76
|
if self._configs.use_rtc:
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
77
|
+
# Check if we have RTC info from camera_info
|
|
78
|
+
rtc_url = self._get_rtc_signaling_url(stream_name)
|
|
79
|
+
if rtc_url:
|
|
80
|
+
logger.info(
|
|
81
|
+
f"'{self._name}': Creating RTC subscriber for '{stream_name}' with direct URL."
|
|
82
|
+
)
|
|
83
|
+
return create_rtc_camera_subscriber(
|
|
84
|
+
signaling_url=rtc_url,
|
|
85
|
+
)
|
|
86
|
+
else:
|
|
87
|
+
# Fallback to querying via info_key
|
|
88
|
+
info_key = stream_config.get("info_key")
|
|
89
|
+
if not info_key:
|
|
90
|
+
logger.warning(
|
|
91
|
+
f"'{self._name}': No RTC URL or info_key for stream '{stream_name}'."
|
|
92
|
+
)
|
|
93
|
+
return None
|
|
94
|
+
logger.info(
|
|
95
|
+
f"'{self._name}': Creating RTC subscriber for '{stream_name}' with info_key."
|
|
96
|
+
)
|
|
97
|
+
return create_rtc_camera_subscriber(
|
|
98
|
+
info_topic=info_key,
|
|
99
|
+
)
|
|
111
100
|
else:
|
|
112
101
|
topic = stream_config.get("topic")
|
|
113
102
|
if not topic:
|
|
114
|
-
logger.warning(
|
|
103
|
+
logger.warning(
|
|
104
|
+
f"'{self._name}': No 'topic' for Zenoh stream '{stream_name}'."
|
|
105
|
+
)
|
|
115
106
|
return None
|
|
116
|
-
logger.info(
|
|
117
|
-
|
|
107
|
+
logger.info(
|
|
108
|
+
f"'{self._name}': Creating RGB subscriber for '{stream_name}'."
|
|
109
|
+
)
|
|
110
|
+
# Use new DexComm integration
|
|
111
|
+
return create_camera_subscriber(
|
|
118
112
|
topic=topic,
|
|
119
|
-
zenoh_session=self._zenoh_session,
|
|
120
|
-
name=f"{self._name}_{stream_name}_subscriber",
|
|
121
|
-
enable_fps_tracking=self._configs.enable_fps_tracking,
|
|
122
|
-
fps_log_interval=self._configs.fps_log_interval,
|
|
123
113
|
)
|
|
124
114
|
|
|
125
115
|
except Exception as e:
|
|
126
|
-
logger.error(
|
|
116
|
+
logger.error(
|
|
117
|
+
f"Error creating subscriber for '{self._name}/{stream_name}': {e}"
|
|
118
|
+
)
|
|
127
119
|
return None
|
|
128
120
|
|
|
129
121
|
def _create_subscribers(self) -> None:
|
|
130
122
|
"""Create subscribers for all configured camera streams."""
|
|
131
123
|
subscriber_config = self._configs.subscriber_config
|
|
124
|
+
|
|
125
|
+
# Determine info endpoint for camera metadata
|
|
126
|
+
info_endpoint = self._determine_info_endpoint(subscriber_config)
|
|
127
|
+
|
|
128
|
+
# Query camera info first to potentially get RTC URLs
|
|
129
|
+
self._query_camera_info(info_endpoint)
|
|
130
|
+
|
|
132
131
|
stream_definitions = {
|
|
133
132
|
"left_rgb": subscriber_config.get("left_rgb", {}),
|
|
134
133
|
"right_rgb": subscriber_config.get("right_rgb", {}),
|
|
@@ -138,42 +137,28 @@ class ZedCameraSensor:
|
|
|
138
137
|
for name, config in stream_definitions.items():
|
|
139
138
|
self._subscribers[name] = self._create_subscriber(name, config)
|
|
140
139
|
|
|
141
|
-
def
|
|
142
|
-
"""
|
|
140
|
+
def _determine_info_endpoint(self, subscriber_config: dict) -> str | None:
|
|
141
|
+
"""Determine the info endpoint for querying camera metadata.
|
|
143
142
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
for s_name, s in self._subscribers.items()
|
|
147
|
-
if "rgb" in s_name and s is not None
|
|
148
|
-
]
|
|
149
|
-
|
|
150
|
-
if not enabled_rgb_streams:
|
|
151
|
-
logger.warning(f"'{self._name}': No enabled RGB streams to query for camera info.")
|
|
152
|
-
return
|
|
153
|
-
|
|
154
|
-
# Use the info_key from the first available RGB subscriber's config
|
|
155
|
-
first_stream_name = "left_rgb" if self._subscribers.get("left_rgb") else "right_rgb"
|
|
156
|
-
stream_config = self._configs.subscriber_config.get(first_stream_name, {})
|
|
157
|
-
info_key = stream_config.get("info_key")
|
|
158
|
-
|
|
159
|
-
if not info_key:
|
|
160
|
-
logger.warning(f"'{self._name}': Could not find info_key for camera info query.")
|
|
161
|
-
return
|
|
143
|
+
Args:
|
|
144
|
+
subscriber_config: Subscriber configuration dict
|
|
162
145
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
146
|
+
Returns:
|
|
147
|
+
Info endpoint or None
|
|
148
|
+
"""
|
|
149
|
+
# Try to find the first enabled RGB stream to get the base info endpoint
|
|
150
|
+
for stream_name in ["left_rgb", "right_rgb"]:
|
|
151
|
+
stream_config = subscriber_config.get(stream_name, {})
|
|
152
|
+
if stream_config.get("enable", False):
|
|
153
|
+
if self._configs.use_rtc:
|
|
154
|
+
info_key = stream_config.get("info_key")
|
|
155
|
+
if info_key:
|
|
156
|
+
return info_key
|
|
157
|
+
else:
|
|
158
|
+
topic = stream_config.get("topic")
|
|
159
|
+
if topic:
|
|
160
|
+
return self._derive_info_endpoint_from_topic(topic)
|
|
161
|
+
return None
|
|
177
162
|
|
|
178
163
|
def shutdown(self) -> None:
|
|
179
164
|
"""Shutdown all active subscribers for the camera sensor."""
|
|
@@ -182,7 +167,9 @@ class ZedCameraSensor:
|
|
|
182
167
|
if subscriber:
|
|
183
168
|
try:
|
|
184
169
|
subscriber.shutdown()
|
|
185
|
-
logger.debug(
|
|
170
|
+
logger.debug(
|
|
171
|
+
f"'{self._name}': Subscriber '{stream_name}' shut down."
|
|
172
|
+
)
|
|
186
173
|
except Exception as e:
|
|
187
174
|
logger.error(
|
|
188
175
|
f"Error shutting down '{stream_name}' subscriber for '{self._name}': {e}"
|
|
@@ -196,7 +183,9 @@ class ZedCameraSensor:
|
|
|
196
183
|
True if at least one subscriber is active, False otherwise.
|
|
197
184
|
"""
|
|
198
185
|
return any(
|
|
199
|
-
sub.
|
|
186
|
+
sub.get_latest() is not None
|
|
187
|
+
for sub in self._subscribers.values()
|
|
188
|
+
if sub is not None
|
|
200
189
|
)
|
|
201
190
|
|
|
202
191
|
def is_stream_active(self, stream_name: str) -> bool:
|
|
@@ -209,7 +198,7 @@ class ZedCameraSensor:
|
|
|
209
198
|
True if the specified stream's subscriber is active, False otherwise.
|
|
210
199
|
"""
|
|
211
200
|
subscriber = self._subscribers.get(stream_name)
|
|
212
|
-
return subscriber.
|
|
201
|
+
return subscriber.get_latest() is not None if subscriber else False
|
|
213
202
|
|
|
214
203
|
def wait_for_active(self, timeout: float = 5.0, require_all: bool = False) -> bool:
|
|
215
204
|
"""Wait for camera streams to become active.
|
|
@@ -229,8 +218,10 @@ class ZedCameraSensor:
|
|
|
229
218
|
|
|
230
219
|
if require_all:
|
|
231
220
|
for sub in enabled_subscribers:
|
|
232
|
-
if not sub.
|
|
233
|
-
logger.warning(
|
|
221
|
+
if not sub.wait_for_message(timeout):
|
|
222
|
+
logger.warning(
|
|
223
|
+
f"'{self._name}': Timed out waiting for subscriber '{sub.name}'."
|
|
224
|
+
)
|
|
234
225
|
return False
|
|
235
226
|
logger.info(f"'{self._name}': All enabled streams are active.")
|
|
236
227
|
return True
|
|
@@ -241,13 +232,14 @@ class ZedCameraSensor:
|
|
|
241
232
|
logger.info(f"'{self._name}': At least one stream is active.")
|
|
242
233
|
return True
|
|
243
234
|
time.sleep(0.1)
|
|
244
|
-
logger.warning(
|
|
235
|
+
logger.warning(
|
|
236
|
+
f"'{self._name}': Timed out waiting for any stream to become active."
|
|
237
|
+
)
|
|
245
238
|
return False
|
|
246
239
|
|
|
247
240
|
def get_obs(
|
|
248
|
-
self, obs_keys:
|
|
249
|
-
|
|
250
|
-
) -> Dict[str, Optional[np.ndarray]]:
|
|
241
|
+
self, obs_keys: list[str] | None = None, include_timestamp: bool = False
|
|
242
|
+
) -> dict[str, np.ndarray | None]:
|
|
251
243
|
"""Get the latest observation data from specified camera streams.
|
|
252
244
|
|
|
253
245
|
Args:
|
|
@@ -267,37 +259,49 @@ class ZedCameraSensor:
|
|
|
267
259
|
obs_out = {}
|
|
268
260
|
for key in keys_to_fetch:
|
|
269
261
|
subscriber = self._subscribers.get(key)
|
|
270
|
-
data = subscriber.
|
|
262
|
+
data = subscriber.get_latest() if subscriber else None
|
|
271
263
|
|
|
272
|
-
|
|
264
|
+
# DexComm returns dict with 'data' and 'timestamp' keys when timestamp is present
|
|
265
|
+
has_timestamp = isinstance(data, dict) and "timestamp" in data
|
|
273
266
|
|
|
274
267
|
if include_timestamp:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
268
|
+
# Always return a consistent structure
|
|
269
|
+
if has_timestamp:
|
|
270
|
+
obs_out[key] = {
|
|
271
|
+
"data": data.get("data") if isinstance(data, dict) else None,
|
|
272
|
+
"timestamp": data.get("timestamp")
|
|
273
|
+
if isinstance(data, dict)
|
|
274
|
+
else None,
|
|
275
|
+
}
|
|
276
|
+
else:
|
|
277
|
+
obs_out[key] = {"data": data, "timestamp": None}
|
|
278
278
|
else:
|
|
279
|
-
|
|
279
|
+
if has_timestamp:
|
|
280
|
+
# Extract payload when timestamp wrapper is present
|
|
281
|
+
obs_out[key] = data.get("data") if isinstance(data, dict) else None
|
|
282
|
+
else:
|
|
283
|
+
obs_out[key] = data
|
|
280
284
|
return obs_out
|
|
281
285
|
|
|
282
|
-
def get_left_rgb(self) ->
|
|
286
|
+
def get_left_rgb(self) -> np.ndarray | None:
|
|
283
287
|
"""Get the latest image from the left RGB stream.
|
|
284
288
|
|
|
285
289
|
Returns:
|
|
286
290
|
The latest left RGB image as a numpy array, or None if not available.
|
|
287
291
|
"""
|
|
288
292
|
subscriber = self._subscribers.get("left_rgb")
|
|
289
|
-
return subscriber.
|
|
293
|
+
return subscriber.get_latest() if subscriber else None
|
|
290
294
|
|
|
291
|
-
def get_right_rgb(self) ->
|
|
295
|
+
def get_right_rgb(self) -> np.ndarray | None:
|
|
292
296
|
"""Get the latest image from the right RGB stream.
|
|
293
297
|
|
|
294
298
|
Returns:
|
|
295
299
|
The latest right RGB image as a numpy array, or None if not available.
|
|
296
300
|
"""
|
|
297
301
|
subscriber = self._subscribers.get("right_rgb")
|
|
298
|
-
return subscriber.
|
|
302
|
+
return subscriber.get_latest() if subscriber else None
|
|
299
303
|
|
|
300
|
-
def get_depth(self) ->
|
|
304
|
+
def get_depth(self) -> np.ndarray | None:
|
|
301
305
|
"""Get the latest image from the depth stream.
|
|
302
306
|
|
|
303
307
|
The depth data is returned as a numpy array with values in meters.
|
|
@@ -306,29 +310,7 @@ class ZedCameraSensor:
|
|
|
306
310
|
The latest depth image as a numpy array, or None if not available.
|
|
307
311
|
"""
|
|
308
312
|
subscriber = self._subscribers.get("depth")
|
|
309
|
-
return subscriber.
|
|
310
|
-
|
|
311
|
-
@property
|
|
312
|
-
def fps(self) -> Dict[str, float]:
|
|
313
|
-
"""Get the current FPS measurement for each active stream.
|
|
314
|
-
|
|
315
|
-
Returns:
|
|
316
|
-
A dictionary mapping stream names to their FPS measurements.
|
|
317
|
-
"""
|
|
318
|
-
return {
|
|
319
|
-
name: sub.fps
|
|
320
|
-
for name, sub in self._subscribers.items()
|
|
321
|
-
if sub is not None
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
@property
|
|
325
|
-
def name(self) -> str:
|
|
326
|
-
"""Get the sensor name.
|
|
327
|
-
|
|
328
|
-
Returns:
|
|
329
|
-
Sensor name string.
|
|
330
|
-
"""
|
|
331
|
-
return self._name
|
|
313
|
+
return subscriber.get_latest() if subscriber else None
|
|
332
314
|
|
|
333
315
|
@property
|
|
334
316
|
def available_streams(self) -> list:
|
|
@@ -346,25 +328,11 @@ class ZedCameraSensor:
|
|
|
346
328
|
Returns:
|
|
347
329
|
List of stream names that are currently receiving data.
|
|
348
330
|
"""
|
|
349
|
-
return [
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
Returns:
|
|
356
|
-
True if dexsensor is available, False otherwise.
|
|
357
|
-
"""
|
|
358
|
-
return DEXSENSOR_AVAILABLE
|
|
359
|
-
|
|
360
|
-
@property
|
|
361
|
-
def camera_info(self) -> dict | None:
|
|
362
|
-
"""Get the camera info.
|
|
363
|
-
|
|
364
|
-
Returns:
|
|
365
|
-
Camera info dictionary if available, None otherwise.
|
|
366
|
-
"""
|
|
367
|
-
return self._camera_info
|
|
331
|
+
return [
|
|
332
|
+
name
|
|
333
|
+
for name, sub in self._subscribers.items()
|
|
334
|
+
if sub and sub.get_latest() is not None
|
|
335
|
+
]
|
|
368
336
|
|
|
369
337
|
@property
|
|
370
338
|
def height(self) -> dict[str, int]:
|
|
@@ -374,7 +342,10 @@ class ZedCameraSensor:
|
|
|
374
342
|
Height of the camera image.
|
|
375
343
|
"""
|
|
376
344
|
images = self.get_obs()
|
|
377
|
-
return {
|
|
345
|
+
return {
|
|
346
|
+
name: image.shape[0] if image is not None else 0
|
|
347
|
+
for name, image in images.items()
|
|
348
|
+
}
|
|
378
349
|
|
|
379
350
|
@property
|
|
380
351
|
def width(self) -> dict[str, int]:
|
|
@@ -384,8 +355,10 @@ class ZedCameraSensor:
|
|
|
384
355
|
Width of the camera image.
|
|
385
356
|
"""
|
|
386
357
|
images = self.get_obs()
|
|
387
|
-
return {
|
|
388
|
-
|
|
358
|
+
return {
|
|
359
|
+
name: image.shape[1] if image is not None else 0
|
|
360
|
+
for name, image in images.items()
|
|
361
|
+
}
|
|
389
362
|
|
|
390
363
|
@property
|
|
391
364
|
def resolution(self) -> dict[str, tuple[int, int]]:
|
|
@@ -395,4 +368,7 @@ class ZedCameraSensor:
|
|
|
395
368
|
Resolution of the camera image.
|
|
396
369
|
"""
|
|
397
370
|
images = self.get_obs()
|
|
398
|
-
return {
|
|
371
|
+
return {
|
|
372
|
+
name: (image.shape[0], image.shape[1]) if image is not None else (0, 0)
|
|
373
|
+
for name, image in images.items()
|
|
374
|
+
}
|