dexcontrol 0.2.3__py3-none-any.whl → 0.2.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 dexcontrol might be problematic. Click here for more details.
- dexcontrol/config/sensors/cameras/__init__.py +2 -0
- dexcontrol/config/sensors/cameras/rgb_camera.py +24 -7
- dexcontrol/config/sensors/cameras/zed_camera.py +34 -15
- dexcontrol/config/sensors/vega_sensors.py +1 -0
- dexcontrol/config/vega.py +6 -0
- dexcontrol/proto/dexcontrol_query_pb2.py +15 -15
- dexcontrol/proto/dexcontrol_query_pb2.pyi +6 -2
- dexcontrol/robot.py +24 -29
- dexcontrol/sensors/camera/rgb_camera.py +62 -29
- dexcontrol/sensors/camera/zed_camera.py +183 -189
- dexcontrol/sensors/lidar/rplidar.py +13 -30
- dexcontrol/utils/pb_utils.py +6 -1
- dexcontrol/utils/subscribers/camera.py +1 -1
- dexcontrol/utils/subscribers/lidar.py +16 -45
- dexcontrol/utils/subscribers/rtc.py +2 -1
- dexcontrol/utils/viz_utils.py +4 -4
- {dexcontrol-0.2.3.dist-info → dexcontrol-0.2.7.dist-info}/METADATA +40 -15
- {dexcontrol-0.2.3.dist-info → dexcontrol-0.2.7.dist-info}/RECORD +20 -20
- {dexcontrol-0.2.3.dist-info → dexcontrol-0.2.7.dist-info}/WHEEL +0 -0
- {dexcontrol-0.2.3.dist-info → dexcontrol-0.2.7.dist-info}/licenses/LICENSE +0 -0
|
@@ -13,16 +13,33 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
|
|
14
14
|
@dataclass
|
|
15
15
|
class RGBCameraConfig:
|
|
16
|
+
"""Configuration for the RGBCameraSensor.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
_target_: The target class to instantiate.
|
|
20
|
+
name: A unique name for the sensor instance.
|
|
21
|
+
enable: Whether the sensor is enabled.
|
|
22
|
+
use_rtc: If True, uses the high-performance RTCSubscriber. If False,
|
|
23
|
+
uses the standard RGBCameraSubscriber with a Zenoh topic.
|
|
24
|
+
enable_fps_tracking: If True, tracks and logs the FPS of the stream.
|
|
25
|
+
fps_log_interval: The interval in seconds for logging FPS.
|
|
26
|
+
subscriber_config: A dictionary containing the configuration for the
|
|
27
|
+
underlying subscriber (either RTC or standard).
|
|
28
|
+
"""
|
|
29
|
+
|
|
16
30
|
_target_: str = "dexcontrol.sensors.camera.rgb_camera.RGBCameraSensor"
|
|
17
31
|
name: str = "rgb_camera"
|
|
32
|
+
enable: bool = False
|
|
33
|
+
use_rtc: bool = True
|
|
18
34
|
enable_fps_tracking: bool = False
|
|
19
35
|
fps_log_interval: int = 30
|
|
20
|
-
|
|
36
|
+
|
|
21
37
|
subscriber_config: dict = field(
|
|
22
|
-
default_factory=lambda:
|
|
23
|
-
rgb
|
|
24
|
-
enable
|
|
25
|
-
info_key
|
|
26
|
-
|
|
27
|
-
|
|
38
|
+
default_factory=lambda: {
|
|
39
|
+
"rgb": {
|
|
40
|
+
"enable": True,
|
|
41
|
+
"info_key": "camera/rgb/info",
|
|
42
|
+
"topic": "camera/rgb",
|
|
43
|
+
}
|
|
44
|
+
}
|
|
28
45
|
)
|
|
@@ -13,24 +13,43 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
|
|
14
14
|
@dataclass
|
|
15
15
|
class ZedCameraConfig:
|
|
16
|
+
"""Configuration for the ZedCameraSensor.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
_target_: The target class to instantiate.
|
|
20
|
+
name: A unique name for the sensor instance.
|
|
21
|
+
enable: Whether the sensor is enabled.
|
|
22
|
+
use_rtc: If True, RGB streams use the high-performance RTCSubscriber.
|
|
23
|
+
If False, they use the standard RGBCameraSubscriber. Depth
|
|
24
|
+
always uses a standard Zenoh subscriber.
|
|
25
|
+
enable_fps_tracking: If True, tracks and logs the FPS of all streams.
|
|
26
|
+
fps_log_interval: The interval in seconds for logging FPS.
|
|
27
|
+
subscriber_config: A dictionary containing the configurations for each
|
|
28
|
+
individual stream (left_rgb, right_rgb, depth).
|
|
29
|
+
"""
|
|
30
|
+
|
|
16
31
|
_target_: str = "dexcontrol.sensors.camera.zed_camera.ZedCameraSensor"
|
|
17
32
|
name: str = "zed_camera"
|
|
33
|
+
enable: bool = False
|
|
34
|
+
use_rtc: bool = True
|
|
18
35
|
enable_fps_tracking: bool = False
|
|
19
36
|
fps_log_interval: int = 30
|
|
20
|
-
|
|
37
|
+
|
|
21
38
|
subscriber_config: dict = field(
|
|
22
|
-
default_factory=lambda:
|
|
23
|
-
left_rgb
|
|
24
|
-
enable
|
|
25
|
-
info_key
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
39
|
+
default_factory=lambda: {
|
|
40
|
+
"left_rgb": {
|
|
41
|
+
"enable": True,
|
|
42
|
+
"info_key": "camera/head/left_rgb/info",
|
|
43
|
+
"topic": "camera/head/left_rgb",
|
|
44
|
+
},
|
|
45
|
+
"right_rgb": {
|
|
46
|
+
"enable": True,
|
|
47
|
+
"info_key": "camera/head/right_rgb/info",
|
|
48
|
+
"topic": "camera/head/right_rgb",
|
|
49
|
+
},
|
|
50
|
+
"depth": {
|
|
51
|
+
"enable": True,
|
|
52
|
+
"topic": "camera/head/depth",
|
|
53
|
+
},
|
|
54
|
+
}
|
|
36
55
|
)
|
dexcontrol/config/vega.py
CHANGED
|
@@ -167,6 +167,12 @@ class Vega1Config(VegaConfig):
|
|
|
167
167
|
wheels_dist=0.45,
|
|
168
168
|
)
|
|
169
169
|
)
|
|
170
|
+
head: HeadConfig = field(
|
|
171
|
+
default_factory=lambda: HeadConfig(
|
|
172
|
+
joint_limit_lower=[-1.483, -2.792, -1.378],
|
|
173
|
+
joint_limit_upper=[1.483, 2.792, 1.483],
|
|
174
|
+
)
|
|
175
|
+
)
|
|
170
176
|
|
|
171
177
|
|
|
172
178
|
# Register variant configurations
|
|
@@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default()
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x64\x65xcontrol_query.proto\x12\ndexcontrol\"Z\n\nSetArmMode\x12)\n\x04mode\x18\x01 \x01(\x0e\x32\x1b.dexcontrol.SetArmMode.Mode\"!\n\x04Mode\x12\x0c\n\x08POSITION\x10\x00\x12\x0b\n\x07\x44ISABLE\x10\x01\"Z\n\x0bSetHeadMode\x12*\n\x04mode\x18\x01 \x01(\x0e\x32\x1c.dexcontrol.SetHeadMode.Mode\"\x1f\n\x04Mode\x12\n\n\x06\x45NABLE\x10\x00\x12\x0b\n\x07\x44ISABLE\x10\x01\"\x1a\n\x08SetEstop\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"\x18\n\x06SetLed\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"\x82\x01\n\nClearError\x12\x33\n\tcomponent\x18\x01 \x01(\x0e\x32 .dexcontrol.ClearError.Component\"?\n\tComponent\x12\x0c\n\x08LEFT_ARM\x10\x00\x12\r\n\tRIGHT_ARM\x10\x01\x12\x08\n\x04HEAD\x10\x02\x12\x0b\n\x07\x43HASSIS\x10\x03\"y\n\x0fRebootComponent\x12\x38\n\tcomponent\x18\x01 \x01(\x0e\x32%.dexcontrol.RebootComponent.Component\",\n\tComponent\x12\x07\n\x03\x41RM\x10\x00\x12\t\n\x05TORSO\x10\x01\x12\x0b\n\x07\x43HASSIS\x10\x02\"
|
|
28
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x64\x65xcontrol_query.proto\x12\ndexcontrol\"Z\n\nSetArmMode\x12)\n\x04mode\x18\x01 \x01(\x0e\x32\x1b.dexcontrol.SetArmMode.Mode\"!\n\x04Mode\x12\x0c\n\x08POSITION\x10\x00\x12\x0b\n\x07\x44ISABLE\x10\x01\"Z\n\x0bSetHeadMode\x12*\n\x04mode\x18\x01 \x01(\x0e\x32\x1c.dexcontrol.SetHeadMode.Mode\"\x1f\n\x04Mode\x12\n\n\x06\x45NABLE\x10\x00\x12\x0b\n\x07\x44ISABLE\x10\x01\"\x1a\n\x08SetEstop\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"\x18\n\x06SetLed\x12\x0e\n\x06\x65nable\x18\x01 \x01(\x08\"\x82\x01\n\nClearError\x12\x33\n\tcomponent\x18\x01 \x01(\x0e\x32 .dexcontrol.ClearError.Component\"?\n\tComponent\x12\x0c\n\x08LEFT_ARM\x10\x00\x12\r\n\tRIGHT_ARM\x10\x01\x12\x08\n\x04HEAD\x10\x02\x12\x0b\n\x07\x43HASSIS\x10\x03\"y\n\x0fRebootComponent\x12\x38\n\tcomponent\x18\x01 \x01(\x0e\x32%.dexcontrol.RebootComponent.Component\",\n\tComponent\x12\x07\n\x03\x41RM\x10\x00\x12\t\n\x05TORSO\x10\x01\x12\x0b\n\x07\x43HASSIS\x10\x02\"\x80\x01\n\x0f\x46irmwareVersion\x12\x18\n\x10hardware_version\x18\x01 \x01(\x05\x12\x18\n\x10software_version\x18\x02 \x01(\x05\x12\x14\n\x0c\x63ompile_time\x18\x03 \x01(\x05\x12\x11\n\tmain_hash\x18\x04 \x01(\t\x12\x10\n\x08sub_hash\x18\x05 \x01(\t\"\xb2\x01\n\x0fSoftwareVersion\x12J\n\x10\x66irmware_version\x18\x01 \x03(\x0b\x32\x30.dexcontrol.SoftwareVersion.FirmwareVersionEntry\x1aS\n\x14\x46irmwareVersionEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12*\n\x05value\x18\x02 \x01(\x0b\x32\x1b.dexcontrol.FirmwareVersion:\x02\x38\x01\"\x9d\x01\n\x14SingleComponentState\x12\x11\n\tconnected\x18\x01 \x01(\x08\x12,\n\x07\x65nabled\x18\x02 \x01(\x0e\x32\x1b.dexcontrol.ComponentStatus\x12\x30\n\x0b\x65rror_state\x18\x03 \x01(\x0e\x32\x1b.dexcontrol.ComponentStatus\x12\x12\n\nerror_code\x18\x04 \x01(\t\"\x9b\x01\n\x0f\x43omponentStates\x12\x37\n\x06states\x18\x01 \x03(\x0b\x32\'.dexcontrol.ComponentStates.StatesEntry\x1aO\n\x0bStatesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12/\n\x05value\x18\x02 \x01(\x0b\x32 .dexcontrol.SingleComponentState:\x02\x38\x01*0\n\x0f\x43omponentStatus\x12\n\n\x06NORMAL\x10\x00\x12\x06\n\x02NA\x10\x01\x12\t\n\x05\x45RROR\x10\x02\x62\x06proto3')
|
|
29
29
|
|
|
30
30
|
_globals = globals()
|
|
31
31
|
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
@@ -36,8 +36,8 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
36
36
|
_globals['_SOFTWAREVERSION_FIRMWAREVERSIONENTRY']._serialized_options = b'8\001'
|
|
37
37
|
_globals['_COMPONENTSTATES_STATESENTRY']._loaded_options = None
|
|
38
38
|
_globals['_COMPONENTSTATES_STATESENTRY']._serialized_options = b'8\001'
|
|
39
|
-
_globals['_COMPONENTSTATUS']._serialized_start=
|
|
40
|
-
_globals['_COMPONENTSTATUS']._serialized_end=
|
|
39
|
+
_globals['_COMPONENTSTATUS']._serialized_start=1162
|
|
40
|
+
_globals['_COMPONENTSTATUS']._serialized_end=1210
|
|
41
41
|
_globals['_SETARMMODE']._serialized_start=38
|
|
42
42
|
_globals['_SETARMMODE']._serialized_end=128
|
|
43
43
|
_globals['_SETARMMODE_MODE']._serialized_start=95
|
|
@@ -58,16 +58,16 @@ if not _descriptor._USE_C_DESCRIPTORS:
|
|
|
58
58
|
_globals['_REBOOTCOMPONENT']._serialized_end=530
|
|
59
59
|
_globals['_REBOOTCOMPONENT_COMPONENT']._serialized_start=486
|
|
60
60
|
_globals['_REBOOTCOMPONENT_COMPONENT']._serialized_end=530
|
|
61
|
-
_globals['_FIRMWAREVERSION']._serialized_start=
|
|
62
|
-
_globals['_FIRMWAREVERSION']._serialized_end=
|
|
63
|
-
_globals['_SOFTWAREVERSION']._serialized_start=
|
|
64
|
-
_globals['_SOFTWAREVERSION']._serialized_end=
|
|
65
|
-
_globals['_SOFTWAREVERSION_FIRMWAREVERSIONENTRY']._serialized_start=
|
|
66
|
-
_globals['_SOFTWAREVERSION_FIRMWAREVERSIONENTRY']._serialized_end=
|
|
67
|
-
_globals['_SINGLECOMPONENTSTATE']._serialized_start=
|
|
68
|
-
_globals['_SINGLECOMPONENTSTATE']._serialized_end=
|
|
69
|
-
_globals['_COMPONENTSTATES']._serialized_start=
|
|
70
|
-
_globals['_COMPONENTSTATES']._serialized_end=
|
|
71
|
-
_globals['_COMPONENTSTATES_STATESENTRY']._serialized_start=
|
|
72
|
-
_globals['_COMPONENTSTATES_STATESENTRY']._serialized_end=
|
|
61
|
+
_globals['_FIRMWAREVERSION']._serialized_start=533
|
|
62
|
+
_globals['_FIRMWAREVERSION']._serialized_end=661
|
|
63
|
+
_globals['_SOFTWAREVERSION']._serialized_start=664
|
|
64
|
+
_globals['_SOFTWAREVERSION']._serialized_end=842
|
|
65
|
+
_globals['_SOFTWAREVERSION_FIRMWAREVERSIONENTRY']._serialized_start=759
|
|
66
|
+
_globals['_SOFTWAREVERSION_FIRMWAREVERSIONENTRY']._serialized_end=842
|
|
67
|
+
_globals['_SINGLECOMPONENTSTATE']._serialized_start=845
|
|
68
|
+
_globals['_SINGLECOMPONENTSTATE']._serialized_end=1002
|
|
69
|
+
_globals['_COMPONENTSTATES']._serialized_start=1005
|
|
70
|
+
_globals['_COMPONENTSTATES']._serialized_end=1160
|
|
71
|
+
_globals['_COMPONENTSTATES_STATESENTRY']._serialized_start=1081
|
|
72
|
+
_globals['_COMPONENTSTATES_STATESENTRY']._serialized_end=1160
|
|
73
73
|
# @@protoc_insertion_point(module_scope)
|
|
@@ -86,14 +86,18 @@ class RebootComponent(_message.Message):
|
|
|
86
86
|
def __init__(self, component: _Optional[_Union[RebootComponent.Component, str]] = ...) -> None: ...
|
|
87
87
|
|
|
88
88
|
class FirmwareVersion(_message.Message):
|
|
89
|
-
__slots__ = ("hardware_version", "software_version", "main_hash")
|
|
89
|
+
__slots__ = ("hardware_version", "software_version", "compile_time", "main_hash", "sub_hash")
|
|
90
90
|
HARDWARE_VERSION_FIELD_NUMBER: _ClassVar[int]
|
|
91
91
|
SOFTWARE_VERSION_FIELD_NUMBER: _ClassVar[int]
|
|
92
|
+
COMPILE_TIME_FIELD_NUMBER: _ClassVar[int]
|
|
92
93
|
MAIN_HASH_FIELD_NUMBER: _ClassVar[int]
|
|
94
|
+
SUB_HASH_FIELD_NUMBER: _ClassVar[int]
|
|
93
95
|
hardware_version: int
|
|
94
96
|
software_version: int
|
|
97
|
+
compile_time: int
|
|
95
98
|
main_hash: str
|
|
96
|
-
|
|
99
|
+
sub_hash: str
|
|
100
|
+
def __init__(self, hardware_version: _Optional[int] = ..., software_version: _Optional[int] = ..., compile_time: _Optional[int] = ..., main_hash: _Optional[str] = ..., sub_hash: _Optional[str] = ...) -> None: ...
|
|
97
101
|
|
|
98
102
|
class SoftwareVersion(_message.Message):
|
|
99
103
|
__slots__ = ("firmware_version",)
|
dexcontrol/robot.py
CHANGED
|
@@ -51,6 +51,7 @@ from dexcontrol.sensors import Sensors
|
|
|
51
51
|
from dexcontrol.utils.constants import ROBOT_NAME_ENV_VAR
|
|
52
52
|
from dexcontrol.utils.os_utils import get_robot_model, resolve_key_name
|
|
53
53
|
from dexcontrol.utils.pb_utils import (
|
|
54
|
+
TYPE_SOFTWARE_VERSION,
|
|
54
55
|
ComponentStatus,
|
|
55
56
|
software_version_to_dict,
|
|
56
57
|
status_to_dict,
|
|
@@ -416,9 +417,12 @@ class Robot:
|
|
|
416
417
|
timeout_sec: Final[int] = 8
|
|
417
418
|
check_interval: Final[float] = 0.1 # Check every 100ms
|
|
418
419
|
|
|
419
|
-
|
|
420
|
+
status = console.status(
|
|
420
421
|
"[bold green]Waiting for components to become active..."
|
|
421
|
-
)
|
|
422
|
+
)
|
|
423
|
+
status.start()
|
|
424
|
+
|
|
425
|
+
try:
|
|
422
426
|
for name in component_names:
|
|
423
427
|
# Check if shutdown was triggered
|
|
424
428
|
if self._shutdown_called:
|
|
@@ -447,6 +451,8 @@ class Robot:
|
|
|
447
451
|
|
|
448
452
|
# Wait a short interval before checking again
|
|
449
453
|
time.sleep(check_interval)
|
|
454
|
+
finally:
|
|
455
|
+
status.stop()
|
|
450
456
|
|
|
451
457
|
if not all(actives):
|
|
452
458
|
self.shutdown()
|
|
@@ -527,34 +533,30 @@ class Robot:
|
|
|
527
533
|
f"Error stopping component {component.__class__.__name__}: {e}"
|
|
528
534
|
)
|
|
529
535
|
|
|
530
|
-
#
|
|
531
|
-
time.sleep(0.
|
|
536
|
+
# Brief delay to ensure all stop operations complete
|
|
537
|
+
time.sleep(0.1)
|
|
532
538
|
|
|
533
539
|
# Shutdown sensors first (they may have background threads)
|
|
534
540
|
try:
|
|
535
|
-
self.sensors
|
|
536
|
-
|
|
537
|
-
|
|
541
|
+
if hasattr(self, "sensors") and self.sensors is not None:
|
|
542
|
+
self.sensors.shutdown()
|
|
543
|
+
# Give time for sensor subscribers to undeclare cleanly
|
|
544
|
+
time.sleep(0.2)
|
|
538
545
|
except Exception as e: # pylint: disable=broad-except
|
|
539
546
|
logger.error(f"Error shutting down sensors: {e}")
|
|
540
547
|
|
|
541
|
-
# Additional delay for sensor shutdown to complete
|
|
542
|
-
time.sleep(0.2)
|
|
543
|
-
|
|
544
548
|
# Shutdown components in reverse order
|
|
545
549
|
for component in reversed(self._components):
|
|
546
550
|
if component is not None:
|
|
547
551
|
try:
|
|
548
552
|
component.shutdown()
|
|
549
|
-
# Small delay between each component shutdown to prevent race conditions
|
|
550
|
-
time.sleep(0.05)
|
|
551
553
|
except Exception as e: # pylint: disable=broad-except
|
|
552
554
|
logger.error(
|
|
553
555
|
f"Error shutting down component {component.__class__.__name__}: {e}"
|
|
554
556
|
)
|
|
555
557
|
|
|
556
|
-
#
|
|
557
|
-
time.sleep(0.
|
|
558
|
+
# Brief delay to allow component shutdown to complete
|
|
559
|
+
time.sleep(0.1)
|
|
558
560
|
|
|
559
561
|
# Enhanced Zenoh session close with better synchronization
|
|
560
562
|
zenoh_close_success = False
|
|
@@ -563,8 +565,8 @@ class Robot:
|
|
|
563
565
|
"""Close zenoh session in a separate thread."""
|
|
564
566
|
nonlocal zenoh_close_success
|
|
565
567
|
try:
|
|
566
|
-
#
|
|
567
|
-
time.sleep(0.
|
|
568
|
+
# Brief delay for Zenoh cleanup
|
|
569
|
+
time.sleep(0.1)
|
|
568
570
|
self._zenoh_session.close()
|
|
569
571
|
zenoh_close_success = True
|
|
570
572
|
except Exception as e: # pylint: disable=broad-except
|
|
@@ -574,20 +576,19 @@ class Robot:
|
|
|
574
576
|
close_thread = threading.Thread(target=_close_zenoh_session, daemon=False)
|
|
575
577
|
close_thread.start()
|
|
576
578
|
|
|
577
|
-
# Wait for close with
|
|
579
|
+
# Wait for close with reasonable timeout
|
|
578
580
|
close_thread.join(timeout=3.0)
|
|
579
581
|
|
|
580
582
|
if close_thread.is_alive():
|
|
581
583
|
logger.warning(
|
|
582
|
-
"Zenoh session close timed out after
|
|
584
|
+
"Zenoh session close timed out after 1s, continuing shutdown"
|
|
583
585
|
)
|
|
584
|
-
#
|
|
585
|
-
close_thread.daemon = True
|
|
586
|
+
# Thread will be left to finish in background, but won't block shutdown
|
|
586
587
|
elif zenoh_close_success:
|
|
587
588
|
logger.debug("Zenoh session closed cleanly")
|
|
588
589
|
|
|
589
|
-
#
|
|
590
|
-
time.sleep(0.
|
|
590
|
+
# Brief final delay for cleanup
|
|
591
|
+
time.sleep(0.5)
|
|
591
592
|
|
|
592
593
|
logger.info("Robot shutdown complete")
|
|
593
594
|
|
|
@@ -601,13 +602,7 @@ class Robot:
|
|
|
601
602
|
|
|
602
603
|
def get_software_version(
|
|
603
604
|
self, show: bool = True
|
|
604
|
-
) -> dict[
|
|
605
|
-
str,
|
|
606
|
-
dict[
|
|
607
|
-
Literal["hardware_version", "software_version", "main_hash"],
|
|
608
|
-
int | str,
|
|
609
|
-
],
|
|
610
|
-
]:
|
|
605
|
+
) -> dict[str, TYPE_SOFTWARE_VERSION]:
|
|
611
606
|
"""Retrieve software version information for all components.
|
|
612
607
|
|
|
613
608
|
Args:
|
|
@@ -8,64 +8,94 @@
|
|
|
8
8
|
# 2. Commercial License
|
|
9
9
|
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
10
|
|
|
11
|
-
"""RGB camera sensor implementation using RTC
|
|
11
|
+
"""RGB camera sensor implementation using RTC or Zenoh subscriber."""
|
|
12
12
|
|
|
13
13
|
import logging
|
|
14
|
+
from typing import Optional, Union
|
|
14
15
|
|
|
15
16
|
import numpy as np
|
|
16
17
|
import zenoh
|
|
17
18
|
|
|
19
|
+
from dexcontrol.config.sensors.cameras import RGBCameraConfig
|
|
18
20
|
from dexcontrol.utils.rtc_utils import create_rtc_subscriber_with_config
|
|
21
|
+
from dexcontrol.utils.subscribers.camera import RGBCameraSubscriber
|
|
22
|
+
from dexcontrol.utils.subscribers.rtc import RTCSubscriber
|
|
19
23
|
|
|
20
24
|
logger = logging.getLogger(__name__)
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
class RGBCameraSensor:
|
|
24
|
-
"""RGB camera sensor
|
|
28
|
+
"""RGB camera sensor that supports both RTC and standard Zenoh subscribers.
|
|
25
29
|
|
|
26
|
-
This sensor provides RGB image data from a camera
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
This sensor provides RGB image data from a camera. It can be configured to use
|
|
31
|
+
either a high-performance RTC subscriber for real-time video streams or a
|
|
32
|
+
standard Zenoh subscriber for raw image topics. The mode is controlled by the
|
|
33
|
+
`use_rtc` flag in the `RGBCameraConfig`.
|
|
29
34
|
"""
|
|
30
35
|
|
|
31
36
|
def __init__(
|
|
32
37
|
self,
|
|
33
|
-
configs,
|
|
38
|
+
configs: RGBCameraConfig,
|
|
34
39
|
zenoh_session: zenoh.Session,
|
|
35
|
-
*args,
|
|
36
|
-
**kwargs,
|
|
37
40
|
) -> None:
|
|
38
|
-
"""Initialize the RGB camera sensor.
|
|
41
|
+
"""Initialize the RGB camera sensor based on the provided configuration.
|
|
39
42
|
|
|
40
43
|
Args:
|
|
41
|
-
configs: Configuration for the RGB camera sensor.
|
|
44
|
+
configs: Configuration object for the RGB camera sensor.
|
|
42
45
|
zenoh_session: Active Zenoh session for communication.
|
|
43
46
|
"""
|
|
44
47
|
self._name = configs.name
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
config
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
48
|
+
self._subscriber: Optional[Union[RTCSubscriber, RGBCameraSubscriber]] = None
|
|
49
|
+
|
|
50
|
+
subscriber_config = configs.subscriber_config.get("rgb", {})
|
|
51
|
+
if not subscriber_config or not subscriber_config.get("enable", False):
|
|
52
|
+
logger.info(f"RGBCameraSensor '{self._name}' is disabled in config.")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
if configs.use_rtc:
|
|
57
|
+
logger.info(f"'{self._name}': Using RTC subscriber.")
|
|
58
|
+
self._subscriber = create_rtc_subscriber_with_config(
|
|
59
|
+
zenoh_session=zenoh_session,
|
|
60
|
+
config=subscriber_config,
|
|
61
|
+
name=f"{self._name}_subscriber",
|
|
62
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
63
|
+
fps_log_interval=configs.fps_log_interval,
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
logger.info(f"'{self._name}': Using standard Zenoh subscriber.")
|
|
67
|
+
topic = subscriber_config.get("topic")
|
|
68
|
+
if topic:
|
|
69
|
+
self._subscriber = RGBCameraSubscriber(
|
|
70
|
+
topic=topic,
|
|
71
|
+
zenoh_session=zenoh_session,
|
|
72
|
+
name=f"{self._name}_subscriber",
|
|
73
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
74
|
+
fps_log_interval=configs.fps_log_interval,
|
|
75
|
+
)
|
|
76
|
+
else:
|
|
77
|
+
logger.warning(
|
|
78
|
+
f"No 'topic' specified for '{self._name}' in non-RTC mode."
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if self._subscriber is None:
|
|
82
|
+
logger.warning(f"Failed to create subscriber for '{self._name}'.")
|
|
83
|
+
|
|
84
|
+
except Exception as e:
|
|
85
|
+
logger.error(f"Error creating subscriber for '{self._name}': {e}")
|
|
86
|
+
self._subscriber = None
|
|
58
87
|
|
|
59
88
|
def shutdown(self) -> None:
|
|
60
|
-
"""Shutdown the camera sensor."""
|
|
89
|
+
"""Shutdown the camera sensor and release all resources."""
|
|
61
90
|
if self._subscriber:
|
|
62
91
|
self._subscriber.shutdown()
|
|
92
|
+
logger.info(f"'{self._name}' sensor shut down.")
|
|
63
93
|
|
|
64
94
|
def is_active(self) -> bool:
|
|
65
95
|
"""Check if the camera sensor is actively receiving data.
|
|
66
96
|
|
|
67
97
|
Returns:
|
|
68
|
-
True if receiving data, False otherwise.
|
|
98
|
+
True if the subscriber exists and is receiving data, False otherwise.
|
|
69
99
|
"""
|
|
70
100
|
return self._subscriber.is_active() if self._subscriber else False
|
|
71
101
|
|
|
@@ -76,15 +106,18 @@ class RGBCameraSensor:
|
|
|
76
106
|
timeout: Maximum time to wait in seconds.
|
|
77
107
|
|
|
78
108
|
Returns:
|
|
79
|
-
True if sensor becomes active
|
|
109
|
+
True if the sensor becomes active within the timeout, False otherwise.
|
|
80
110
|
"""
|
|
81
|
-
|
|
111
|
+
if not self._subscriber:
|
|
112
|
+
logger.warning(f"'{self._name}': Cannot wait, no subscriber initialized.")
|
|
113
|
+
return False
|
|
114
|
+
return self._subscriber.wait_for_active(timeout)
|
|
82
115
|
|
|
83
116
|
def get_obs(self) -> np.ndarray | None:
|
|
84
|
-
"""Get the latest RGB image
|
|
117
|
+
"""Get the latest observation (RGB image) from the sensor.
|
|
85
118
|
|
|
86
119
|
Returns:
|
|
87
|
-
|
|
120
|
+
The latest RGB image as a numpy array (HxWxC) if available, otherwise None.
|
|
88
121
|
"""
|
|
89
122
|
return self._subscriber.get_latest_data() if self._subscriber else None
|
|
90
123
|
|