dexcontrol 0.2.1__py3-none-any.whl → 0.2.3__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/__init__.py +14 -3
- dexcontrol/apps/dualsense_teleop_base.py +16 -11
- dexcontrol/config/__init__.py +10 -5
- dexcontrol/config/core/__init__.py +8 -3
- dexcontrol/config/core/arm.py +8 -3
- dexcontrol/config/core/chassis.py +10 -5
- dexcontrol/config/core/hand.py +14 -9
- dexcontrol/config/core/head.py +8 -3
- dexcontrol/config/core/misc.py +8 -3
- dexcontrol/config/core/torso.py +8 -3
- dexcontrol/config/sensors/__init__.py +8 -3
- dexcontrol/config/sensors/cameras/__init__.py +9 -4
- dexcontrol/config/sensors/cameras/rgb_camera.py +18 -5
- dexcontrol/config/sensors/cameras/zed_camera.py +36 -0
- dexcontrol/config/sensors/imu/__init__.py +10 -5
- dexcontrol/config/sensors/imu/chassis_imu.py +21 -0
- dexcontrol/config/sensors/imu/zed_imu.py +21 -0
- dexcontrol/config/sensors/lidar/__init__.py +8 -3
- dexcontrol/config/sensors/lidar/rplidar.py +9 -3
- dexcontrol/config/sensors/ultrasonic/__init__.py +8 -3
- dexcontrol/config/sensors/ultrasonic/ultrasonic.py +9 -3
- dexcontrol/config/sensors/vega_sensors.py +34 -21
- dexcontrol/config/vega.py +14 -6
- dexcontrol/core/__init__.py +9 -0
- dexcontrol/core/arm.py +21 -6
- dexcontrol/core/chassis.py +8 -3
- dexcontrol/core/component.py +26 -6
- dexcontrol/core/hand.py +8 -3
- dexcontrol/core/head.py +18 -3
- dexcontrol/core/misc.py +94 -16
- dexcontrol/core/torso.py +8 -3
- dexcontrol/proto/dexcontrol_msg_pb2.py +17 -15
- dexcontrol/proto/dexcontrol_msg_pb2.pyi +24 -0
- dexcontrol/robot.py +82 -28
- dexcontrol/sensors/__init__.py +13 -8
- dexcontrol/sensors/camera/__init__.py +11 -6
- dexcontrol/sensors/camera/rgb_camera.py +33 -24
- dexcontrol/sensors/camera/zed_camera.py +364 -0
- dexcontrol/sensors/imu/__init__.py +13 -8
- dexcontrol/sensors/imu/chassis_imu.py +155 -0
- dexcontrol/sensors/imu/{nine_axis_imu.py → zed_imu.py} +41 -26
- dexcontrol/sensors/lidar/__init__.py +11 -1
- dexcontrol/sensors/lidar/rplidar.py +8 -3
- dexcontrol/sensors/manager.py +22 -9
- dexcontrol/sensors/ultrasonic.py +8 -3
- dexcontrol/utils/__init__.py +8 -3
- dexcontrol/utils/constants.py +10 -0
- dexcontrol/utils/io_utils.py +8 -3
- dexcontrol/utils/motion_utils.py +8 -3
- dexcontrol/utils/os_utils.py +23 -4
- dexcontrol/utils/pb_utils.py +8 -3
- dexcontrol/utils/rate_limiter.py +8 -3
- dexcontrol/utils/rtc_utils.py +144 -0
- dexcontrol/utils/subscribers/__init__.py +11 -3
- dexcontrol/utils/subscribers/base.py +26 -5
- dexcontrol/utils/subscribers/camera.py +10 -6
- dexcontrol/utils/subscribers/decoders.py +8 -3
- dexcontrol/utils/subscribers/generic.py +8 -3
- dexcontrol/utils/subscribers/imu.py +8 -3
- dexcontrol/utils/subscribers/lidar.py +8 -3
- dexcontrol/utils/subscribers/protobuf.py +8 -3
- dexcontrol/utils/subscribers/rtc.py +315 -0
- dexcontrol/utils/timer.py +8 -3
- dexcontrol/utils/trajectory_utils.py +8 -3
- dexcontrol/utils/viz_utils.py +8 -3
- dexcontrol/utils/zenoh_utils.py +83 -0
- dexcontrol-0.2.3.dist-info/METADATA +265 -0
- dexcontrol-0.2.3.dist-info/RECORD +72 -0
- {dexcontrol-0.2.1.dist-info → dexcontrol-0.2.3.dist-info}/WHEEL +1 -2
- dexcontrol-0.2.3.dist-info/licenses/LICENSE +184 -0
- dexcontrol/config/sensors/cameras/gemini_camera.py +0 -16
- dexcontrol/config/sensors/imu/gemini_imu.py +0 -15
- dexcontrol/config/sensors/imu/nine_axis_imu.py +0 -15
- dexcontrol/sensors/camera/gemini_camera.py +0 -139
- dexcontrol/sensors/imu/gemini_imu.py +0 -139
- dexcontrol/utils/reset_orbbec_camera_usb.py +0 -98
- dexcontrol-0.2.1.dist-info/METADATA +0 -369
- dexcontrol-0.2.1.dist-info/RECORD +0 -72
- dexcontrol-0.2.1.dist-info/licenses/LICENSE +0 -188
- dexcontrol-0.2.1.dist-info/licenses/NOTICE +0 -13
- dexcontrol-0.2.1.dist-info/top_level.txt +0 -1
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
|
-
"""
|
|
11
|
+
"""ZED IMU sensor implementation using Zenoh subscriber."""
|
|
7
12
|
|
|
8
13
|
import numpy as np
|
|
9
14
|
import zenoh
|
|
@@ -11,11 +16,13 @@ import zenoh
|
|
|
11
16
|
from dexcontrol.utils.subscribers.imu import IMUSubscriber
|
|
12
17
|
|
|
13
18
|
|
|
14
|
-
class
|
|
15
|
-
"""
|
|
19
|
+
class ZedIMUSensor:
|
|
20
|
+
"""ZED IMU sensor using Zenoh subscriber.
|
|
16
21
|
|
|
17
|
-
This sensor provides
|
|
18
|
-
orientation, and magnetometer data using the IMUSubscriber for efficient data handling.
|
|
22
|
+
This sensor provides IMU data from ZED cameras including acceleration, angular velocity,
|
|
23
|
+
orientation quaternion, and magnetometer data using the IMUSubscriber for efficient data handling.
|
|
24
|
+
The ZED IMU typically provides 9-axis data (accelerometer, gyroscope, magnetometer) with
|
|
25
|
+
quaternion orientation.
|
|
19
26
|
"""
|
|
20
27
|
|
|
21
28
|
def __init__(
|
|
@@ -23,13 +30,11 @@ class NineAxisIMUSensor:
|
|
|
23
30
|
configs,
|
|
24
31
|
zenoh_session: zenoh.Session,
|
|
25
32
|
) -> None:
|
|
26
|
-
"""Initialize the
|
|
33
|
+
"""Initialize the ZED IMU sensor.
|
|
27
34
|
|
|
28
35
|
Args:
|
|
29
|
-
|
|
36
|
+
configs: Configuration object containing topic, name, and other settings.
|
|
30
37
|
zenoh_session: Active Zenoh session for communication.
|
|
31
|
-
name: Name for the sensor instance.
|
|
32
|
-
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
33
38
|
"""
|
|
34
39
|
self._name = configs.name
|
|
35
40
|
|
|
@@ -42,13 +47,12 @@ class NineAxisIMUSensor:
|
|
|
42
47
|
fps_log_interval=configs.fps_log_interval,
|
|
43
48
|
)
|
|
44
49
|
|
|
45
|
-
|
|
46
50
|
def shutdown(self) -> None:
|
|
47
|
-
"""Shutdown the IMU sensor."""
|
|
51
|
+
"""Shutdown the ZED IMU sensor."""
|
|
48
52
|
self._subscriber.shutdown()
|
|
49
53
|
|
|
50
54
|
def is_active(self) -> bool:
|
|
51
|
-
"""Check if the IMU sensor is actively receiving data.
|
|
55
|
+
"""Check if the ZED IMU sensor is actively receiving data.
|
|
52
56
|
|
|
53
57
|
Returns:
|
|
54
58
|
True if receiving data, False otherwise.
|
|
@@ -56,7 +60,7 @@ class NineAxisIMUSensor:
|
|
|
56
60
|
return self._subscriber.is_active()
|
|
57
61
|
|
|
58
62
|
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
59
|
-
"""Wait for the IMU sensor to start receiving data.
|
|
63
|
+
"""Wait for the ZED IMU sensor to start receiving data.
|
|
60
64
|
|
|
61
65
|
Args:
|
|
62
66
|
timeout: Maximum time to wait in seconds.
|
|
@@ -67,30 +71,33 @@ class NineAxisIMUSensor:
|
|
|
67
71
|
return self._subscriber.wait_for_active(timeout)
|
|
68
72
|
|
|
69
73
|
def get_obs(self, obs_keys: list[str] | None = None) -> dict[str, np.ndarray] | None:
|
|
70
|
-
"""Get observation data for the
|
|
74
|
+
"""Get observation data for the ZED IMU sensor.
|
|
71
75
|
|
|
72
76
|
Args:
|
|
73
77
|
obs_keys: List of observation keys to retrieve. If None, returns all available data.
|
|
74
|
-
Valid keys: ['ang_vel', 'acc', '
|
|
78
|
+
Valid keys: ['ang_vel', 'acc', 'quat']
|
|
75
79
|
|
|
76
80
|
Returns:
|
|
77
81
|
Dictionary with observation data including all IMU measurements.
|
|
82
|
+
Keys are mapped as follows:
|
|
83
|
+
- 'ang_vel': Angular velocity from 'angular_velocity'
|
|
84
|
+
- 'acc': Linear acceleration from 'acceleration'
|
|
85
|
+
- 'quat': Orientation quaternion from 'orientation'
|
|
78
86
|
"""
|
|
79
87
|
if obs_keys is None:
|
|
80
|
-
obs_keys = ['ang_vel', 'acc', '
|
|
88
|
+
obs_keys = ['ang_vel', 'acc', 'quat']
|
|
81
89
|
|
|
82
90
|
data = self._subscriber.get_latest_data()
|
|
83
91
|
if data is None:
|
|
84
92
|
return None
|
|
85
93
|
|
|
86
94
|
obs_out = {}
|
|
95
|
+
|
|
87
96
|
for key in obs_keys:
|
|
88
97
|
if key == 'ang_vel':
|
|
89
98
|
obs_out[key] = data['angular_velocity']
|
|
90
99
|
elif key == 'acc':
|
|
91
100
|
obs_out[key] = data['acceleration']
|
|
92
|
-
elif key == 'mag':
|
|
93
|
-
obs_out[key] = data['magnetometer']
|
|
94
101
|
elif key == 'quat':
|
|
95
102
|
obs_out[key] = data['orientation']
|
|
96
103
|
else:
|
|
@@ -99,7 +106,7 @@ class NineAxisIMUSensor:
|
|
|
99
106
|
return obs_out
|
|
100
107
|
|
|
101
108
|
def get_acceleration(self) -> np.ndarray | None:
|
|
102
|
-
"""Get the latest linear acceleration.
|
|
109
|
+
"""Get the latest linear acceleration from ZED IMU.
|
|
103
110
|
|
|
104
111
|
Returns:
|
|
105
112
|
Linear acceleration [x, y, z] in m/s² if available, None otherwise.
|
|
@@ -107,7 +114,7 @@ class NineAxisIMUSensor:
|
|
|
107
114
|
return self._subscriber.get_acceleration()
|
|
108
115
|
|
|
109
116
|
def get_angular_velocity(self) -> np.ndarray | None:
|
|
110
|
-
"""Get the latest angular velocity.
|
|
117
|
+
"""Get the latest angular velocity from ZED IMU.
|
|
111
118
|
|
|
112
119
|
Returns:
|
|
113
120
|
Angular velocity [x, y, z] in rad/s if available, None otherwise.
|
|
@@ -115,7 +122,7 @@ class NineAxisIMUSensor:
|
|
|
115
122
|
return self._subscriber.get_angular_velocity()
|
|
116
123
|
|
|
117
124
|
def get_orientation(self) -> np.ndarray | None:
|
|
118
|
-
"""Get the latest orientation quaternion.
|
|
125
|
+
"""Get the latest orientation quaternion from ZED IMU.
|
|
119
126
|
|
|
120
127
|
Returns:
|
|
121
128
|
Orientation quaternion [x, y, z, w] if available, None otherwise.
|
|
@@ -123,13 +130,21 @@ class NineAxisIMUSensor:
|
|
|
123
130
|
return self._subscriber.get_orientation()
|
|
124
131
|
|
|
125
132
|
def get_magnetometer(self) -> np.ndarray | None:
|
|
126
|
-
"""Get the latest magnetometer reading.
|
|
133
|
+
"""Get the latest magnetometer reading from ZED IMU.
|
|
127
134
|
|
|
128
135
|
Returns:
|
|
129
136
|
Magnetic field [x, y, z] in µT if available, None otherwise.
|
|
130
137
|
"""
|
|
131
138
|
return self._subscriber.get_magnetometer()
|
|
132
139
|
|
|
140
|
+
def has_magnetometer(self) -> bool:
|
|
141
|
+
"""Check if the ZED IMU has magnetometer data available.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
True if magnetometer data is available, False otherwise.
|
|
145
|
+
"""
|
|
146
|
+
return self._subscriber.has_magnetometer()
|
|
147
|
+
|
|
133
148
|
@property
|
|
134
149
|
def fps(self) -> float:
|
|
135
150
|
"""Get the current FPS measurement.
|
|
@@ -141,7 +156,7 @@ class NineAxisIMUSensor:
|
|
|
141
156
|
|
|
142
157
|
@property
|
|
143
158
|
def name(self) -> str:
|
|
144
|
-
"""Get the IMU name.
|
|
159
|
+
"""Get the ZED IMU name.
|
|
145
160
|
|
|
146
161
|
Returns:
|
|
147
162
|
IMU name string.
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
|
+
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
|
+
|
|
1
11
|
from .rplidar import RPLidarSensor
|
|
2
12
|
|
|
3
|
-
__all__ = ['RPLidarSensor']
|
|
13
|
+
__all__ = ['RPLidarSensor']
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""LIDAR sensor implementations using Zenoh subscribers.
|
|
7
12
|
|
dexcontrol/sensors/manager.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Sensor manager for managing robot sensors with Zenoh communication.
|
|
7
12
|
|
|
@@ -21,10 +26,10 @@ from omegaconf import DictConfig, OmegaConf
|
|
|
21
26
|
from dexcontrol.config.sensors.vega_sensors import VegaSensorsConfig
|
|
22
27
|
|
|
23
28
|
if TYPE_CHECKING:
|
|
24
|
-
from dexcontrol.sensors.camera.gemini_camera import GeminiCameraSensor
|
|
25
29
|
from dexcontrol.sensors.camera.rgb_camera import RGBCameraSensor
|
|
26
|
-
from dexcontrol.sensors.
|
|
27
|
-
from dexcontrol.sensors.imu.
|
|
30
|
+
from dexcontrol.sensors.camera.zed_camera import ZedCameraSensor
|
|
31
|
+
from dexcontrol.sensors.imu.chassis_imu import ChassisIMUSensor
|
|
32
|
+
from dexcontrol.sensors.imu.zed_imu import ZedIMUSensor
|
|
28
33
|
from dexcontrol.sensors.lidar.rplidar import RPLidarSensor
|
|
29
34
|
from dexcontrol.sensors.ultrasonic import UltrasonicSensor
|
|
30
35
|
|
|
@@ -43,13 +48,13 @@ class Sensors:
|
|
|
43
48
|
|
|
44
49
|
if TYPE_CHECKING:
|
|
45
50
|
# Type annotations for dynamically created sensor attributes
|
|
46
|
-
head_camera:
|
|
51
|
+
head_camera: ZedCameraSensor
|
|
47
52
|
base_left_camera: RGBCameraSensor
|
|
48
53
|
base_right_camera: RGBCameraSensor
|
|
49
54
|
base_front_camera: RGBCameraSensor
|
|
50
55
|
base_back_camera: RGBCameraSensor
|
|
51
|
-
base_imu:
|
|
52
|
-
head_imu:
|
|
56
|
+
base_imu: ChassisIMUSensor
|
|
57
|
+
head_imu: ZedIMUSensor
|
|
53
58
|
lidar: RPLidarSensor
|
|
54
59
|
ultrasonic: UltrasonicSensor
|
|
55
60
|
|
|
@@ -87,6 +92,10 @@ class Sensors:
|
|
|
87
92
|
logger.debug(f"Sensor {name} config is None")
|
|
88
93
|
return None
|
|
89
94
|
|
|
95
|
+
if not config.enable:
|
|
96
|
+
logger.debug(f"Sensor {name} is disabled")
|
|
97
|
+
return None
|
|
98
|
+
|
|
90
99
|
if not (hasattr(config, '_target_') and config._target_):
|
|
91
100
|
return None
|
|
92
101
|
|
|
@@ -118,9 +127,13 @@ class Sensors:
|
|
|
118
127
|
try:
|
|
119
128
|
if hasattr(sensor, "shutdown"):
|
|
120
129
|
sensor.shutdown()
|
|
130
|
+
# Small delay between each sensor shutdown to prevent race conditions
|
|
131
|
+
time.sleep(0.05)
|
|
121
132
|
except Exception as e:
|
|
122
133
|
logger.error(f"Error shutting down sensor: {e}")
|
|
123
134
|
|
|
135
|
+
# Additional delay to ensure all sensor subscribers have been undeclared
|
|
136
|
+
time.sleep(0.1)
|
|
124
137
|
logger.info("All sensors shut down")
|
|
125
138
|
|
|
126
139
|
def get_active_sensors(self) -> list[str]:
|
dexcontrol/sensors/ultrasonic.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Ultrasonic sensor implementations using Zenoh subscribers.
|
|
7
12
|
|
dexcontrol/utils/__init__.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
from .subscribers import (
|
|
7
12
|
BaseZenohSubscriber,
|
dexcontrol/utils/constants.py
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
|
+
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
|
+
|
|
1
11
|
"""Constants used throughout the dexcontrol package."""
|
|
2
12
|
|
|
3
13
|
from typing import Final
|
dexcontrol/utils/io_utils.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
from typing import Any
|
|
7
12
|
|
dexcontrol/utils/motion_utils.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
|
|
7
12
|
import numpy as np
|
dexcontrol/utils/os_utils.py
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
|
+
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
|
+
|
|
1
11
|
"""Operating system utility functions."""
|
|
2
12
|
|
|
3
13
|
import os
|
|
14
|
+
import re
|
|
4
15
|
from typing import Final
|
|
5
16
|
|
|
6
|
-
from loguru import logger
|
|
7
|
-
|
|
8
17
|
from dexcontrol.utils.constants import ROBOT_NAME_ENV_VAR
|
|
9
18
|
|
|
10
19
|
|
|
@@ -23,6 +32,10 @@ def resolve_key_name(key: str) -> str:
|
|
|
23
32
|
# Remove leading slash if present
|
|
24
33
|
key = key.lstrip("/")
|
|
25
34
|
|
|
35
|
+
# Check if robot name is already present at the beginning
|
|
36
|
+
if key.startswith(f"{robot_name}/"):
|
|
37
|
+
return key
|
|
38
|
+
|
|
26
39
|
# Combine robot name and key with single slash
|
|
27
40
|
return f"{robot_name}/{key}"
|
|
28
41
|
|
|
@@ -30,10 +43,16 @@ def resolve_key_name(key: str) -> str:
|
|
|
30
43
|
def get_robot_model() -> str:
|
|
31
44
|
"""Get the robot model from the environment variable."""
|
|
32
45
|
robot_model_abb_mapping = dict(vg="vega")
|
|
33
|
-
robot_name = os.getenv(ROBOT_NAME_ENV_VAR
|
|
46
|
+
robot_name = os.getenv(ROBOT_NAME_ENV_VAR)
|
|
47
|
+
if robot_name is None:
|
|
48
|
+
raise ValueError(
|
|
49
|
+
f"Robot name is not set, please set the environment variable {ROBOT_NAME_ENV_VAR}"
|
|
50
|
+
)
|
|
51
|
+
if not re.match(r"^dm/[a-zA-Z0-9]{12}-(?:\d+|rc\d+)$", robot_name):
|
|
52
|
+
raise ValueError(f"Robot name is not in the correct format: {robot_name}")
|
|
53
|
+
|
|
34
54
|
robot_model_abb = robot_name.split("/")[-1].split("-")[0][:2]
|
|
35
55
|
if robot_model_abb not in robot_model_abb_mapping:
|
|
36
56
|
raise ValueError(f"Unknown robot model: {robot_model_abb}")
|
|
37
57
|
model = robot_model_abb_mapping[robot_model_abb] + "-" + robot_name.split("-")[-1]
|
|
38
|
-
logger.info(f"The current robot model is: {model}")
|
|
39
58
|
return model
|
dexcontrol/utils/pb_utils.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Utility functions for Python data structures and protobuf messages."""
|
|
7
12
|
|
dexcontrol/utils/rate_limiter.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Rate limiter utility for maintaining consistent execution rates."""
|
|
7
12
|
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
|
+
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
|
+
|
|
11
|
+
"""RTC utilities for dexcontrol.
|
|
12
|
+
|
|
13
|
+
This module provides utility functions for creating RTC subscribers
|
|
14
|
+
that first query Zenoh for connection information.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import zenoh
|
|
18
|
+
from loguru import logger
|
|
19
|
+
|
|
20
|
+
from dexcontrol.utils.subscribers.rtc import RTCSubscriber
|
|
21
|
+
from dexcontrol.utils.zenoh_utils import query_zenoh_json
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def query_rtc_info(
|
|
25
|
+
zenoh_session: zenoh.Session,
|
|
26
|
+
info_topic: str,
|
|
27
|
+
timeout: float = 2.0,
|
|
28
|
+
max_retries: int = 1,
|
|
29
|
+
retry_delay: float = 0.5,
|
|
30
|
+
) -> dict | None:
|
|
31
|
+
"""Query Zenoh for RTC connection information.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
zenoh_session: Active Zenoh session for communication.
|
|
35
|
+
info_topic: Zenoh topic to query for RTC info.
|
|
36
|
+
timeout: Maximum time to wait for a response in seconds.
|
|
37
|
+
max_retries: Maximum number of retry attempts.
|
|
38
|
+
retry_delay: Initial delay between retries (doubles each retry).
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Dictionary containing host and port information if successful, None otherwise.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
# Use the general Zenoh query function
|
|
45
|
+
info = query_zenoh_json(
|
|
46
|
+
zenoh_session=zenoh_session,
|
|
47
|
+
topic=info_topic,
|
|
48
|
+
timeout=timeout,
|
|
49
|
+
max_retries=max_retries,
|
|
50
|
+
retry_delay=retry_delay,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
return info
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def create_rtc_subscriber_from_zenoh(
|
|
57
|
+
zenoh_session: zenoh.Session,
|
|
58
|
+
info_topic: str,
|
|
59
|
+
name: str = "rtc_subscriber",
|
|
60
|
+
enable_fps_tracking: bool = True,
|
|
61
|
+
fps_log_interval: int = 100,
|
|
62
|
+
query_timeout: float = 2.0,
|
|
63
|
+
max_retries: int = 1,
|
|
64
|
+
) -> RTCSubscriber | None:
|
|
65
|
+
"""Create a RTC subscriber by first querying Zenoh for connection info.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
zenoh_session: Active Zenoh session for communication.
|
|
69
|
+
info_topic: Zenoh topic to query for RTC connection info.
|
|
70
|
+
name: Name for logging purposes.
|
|
71
|
+
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
72
|
+
fps_log_interval: Number of frames between FPS calculations.
|
|
73
|
+
query_timeout: Maximum time to wait for Zenoh query response.
|
|
74
|
+
max_retries: Maximum number of retry attempts for Zenoh query.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
RTCSubscriber instance if successful, None otherwise.
|
|
78
|
+
"""
|
|
79
|
+
# Query Zenoh for RTC connection information
|
|
80
|
+
rtc_info = query_rtc_info(zenoh_session, info_topic, query_timeout, max_retries)
|
|
81
|
+
|
|
82
|
+
if rtc_info is None:
|
|
83
|
+
logger.error("Failed to get RTC connection info from Zenoh")
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
url = rtc_info.get("signaling_url")
|
|
87
|
+
|
|
88
|
+
if not url:
|
|
89
|
+
logger.error(f"Invalid RTC info: url={url}")
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
# Construct WebSocket URL
|
|
93
|
+
ws_url = url
|
|
94
|
+
logger.info(f"Creating RTC subscriber with URL: {ws_url}")
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# Create and return the RTC subscriber
|
|
98
|
+
subscriber = RTCSubscriber(
|
|
99
|
+
url=ws_url,
|
|
100
|
+
name=name,
|
|
101
|
+
enable_fps_tracking=enable_fps_tracking,
|
|
102
|
+
fps_log_interval=fps_log_interval,
|
|
103
|
+
)
|
|
104
|
+
return subscriber
|
|
105
|
+
except Exception as e:
|
|
106
|
+
logger.error(f"Failed to create RTC subscriber: {e}")
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def create_rtc_subscriber_with_config(
|
|
111
|
+
zenoh_session: zenoh.Session,
|
|
112
|
+
config,
|
|
113
|
+
name: str = "rtc_subscriber",
|
|
114
|
+
enable_fps_tracking: bool = True,
|
|
115
|
+
fps_log_interval: int = 100,
|
|
116
|
+
) -> RTCSubscriber | None:
|
|
117
|
+
"""Create a RTC subscriber using configuration object.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
zenoh_session: Active Zenoh session for communication.
|
|
121
|
+
config: Configuration object containing info_key.
|
|
122
|
+
name: Name for logging purposes.
|
|
123
|
+
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
124
|
+
fps_log_interval: Number of frames between FPS calculations.
|
|
125
|
+
Returns:
|
|
126
|
+
RTCSubscriber instance if successful, None otherwise.
|
|
127
|
+
"""
|
|
128
|
+
if "info_key" not in config:
|
|
129
|
+
logger.error("Config subscriber_config missing info_key")
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
if not config["enable"]:
|
|
133
|
+
logger.info(f"Skipping {name} because it is disabled")
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
info_topic = config["info_key"]
|
|
137
|
+
|
|
138
|
+
return create_rtc_subscriber_from_zenoh(
|
|
139
|
+
zenoh_session=zenoh_session,
|
|
140
|
+
info_topic=info_topic,
|
|
141
|
+
name=name,
|
|
142
|
+
enable_fps_tracking=enable_fps_tracking,
|
|
143
|
+
fps_log_interval=fps_log_interval,
|
|
144
|
+
)
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Zenoh subscriber utilities for dexcontrol.
|
|
7
12
|
|
|
@@ -22,6 +27,7 @@ from .generic import GenericZenohSubscriber
|
|
|
22
27
|
from .imu import IMUSubscriber
|
|
23
28
|
from .lidar import LidarSubscriber
|
|
24
29
|
from .protobuf import ProtobufZenohSubscriber
|
|
30
|
+
from .rtc import RTCSubscriber
|
|
25
31
|
|
|
26
32
|
__all__ = [
|
|
27
33
|
"BaseZenohSubscriber",
|
|
@@ -41,4 +47,6 @@ __all__ = [
|
|
|
41
47
|
"LidarSubscriber",
|
|
42
48
|
# IMU subscriber
|
|
43
49
|
"IMUSubscriber",
|
|
50
|
+
# RTC subscriber
|
|
51
|
+
"RTCSubscriber",
|
|
44
52
|
]
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
# Copyright (
|
|
1
|
+
# Copyright (C) 2025 Dexmate Inc.
|
|
2
2
|
#
|
|
3
|
-
#
|
|
4
|
-
#
|
|
3
|
+
# This software is dual-licensed:
|
|
4
|
+
#
|
|
5
|
+
# 1. GNU Affero General Public License v3.0 (AGPL-3.0)
|
|
6
|
+
# See LICENSE-AGPL for details
|
|
7
|
+
#
|
|
8
|
+
# 2. Commercial License
|
|
9
|
+
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
5
10
|
|
|
6
11
|
"""Base Zenoh subscriber utilities.
|
|
7
12
|
|
|
@@ -193,8 +198,24 @@ class BaseZenohSubscriber(ABC):
|
|
|
193
198
|
|
|
194
199
|
def shutdown(self) -> None:
|
|
195
200
|
"""Stop the subscriber and release resources."""
|
|
196
|
-
|
|
197
|
-
|
|
201
|
+
# Mark as inactive first to prevent further data processing
|
|
202
|
+
with self._data_lock:
|
|
203
|
+
self._active = False
|
|
204
|
+
|
|
205
|
+
# Small delay to allow any ongoing data processing to complete
|
|
206
|
+
time.sleep(0.05)
|
|
207
|
+
|
|
208
|
+
try:
|
|
209
|
+
if hasattr(self, "_subscriber") and self._subscriber:
|
|
210
|
+
self._subscriber.undeclare()
|
|
211
|
+
except Exception as e:
|
|
212
|
+
# Don't log "Undeclared subscriber" errors as warnings - they're expected during shutdown
|
|
213
|
+
error_msg = str(e).lower()
|
|
214
|
+
if not ("undeclared" in error_msg or "closed" in error_msg):
|
|
215
|
+
logger.warning(f"Error undeclaring subscriber for {self._topic}: {e}")
|
|
216
|
+
|
|
217
|
+
# Additional delay to allow Zenoh to process the undeclare operation
|
|
218
|
+
time.sleep(0.02)
|
|
198
219
|
|
|
199
220
|
@property
|
|
200
221
|
def topic(self) -> str:
|