dexcontrol 0.2.12__py3-none-any.whl → 0.3.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 dexcontrol might be problematic. Click here for more details.
- dexcontrol/__init__.py +18 -8
- dexcontrol/apps/dualsense_teleop_base.py +1 -1
- dexcontrol/comm/__init__.py +51 -0
- dexcontrol/comm/base.py +421 -0
- dexcontrol/comm/rtc.py +400 -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 +61 -37
- dexcontrol/core/chassis.py +141 -119
- dexcontrol/core/component.py +110 -59
- dexcontrol/core/hand.py +118 -85
- dexcontrol/core/head.py +18 -29
- dexcontrol/core/misc.py +327 -155
- dexcontrol/core/robot_query_interface.py +463 -0
- dexcontrol/core/torso.py +4 -8
- dexcontrol/proto/dexcontrol_msg_pb2.py +27 -39
- dexcontrol/proto/dexcontrol_msg_pb2.pyi +75 -118
- dexcontrol/proto/dexcontrol_query_pb2.py +39 -39
- dexcontrol/proto/dexcontrol_query_pb2.pyi +17 -4
- dexcontrol/robot.py +245 -574
- dexcontrol/sensors/__init__.py +1 -2
- dexcontrol/sensors/camera/__init__.py +0 -2
- dexcontrol/sensors/camera/base_camera.py +144 -0
- dexcontrol/sensors/camera/rgb_camera.py +67 -63
- dexcontrol/sensors/camera/zed_camera.py +89 -147
- dexcontrol/sensors/imu/chassis_imu.py +76 -56
- dexcontrol/sensors/imu/zed_imu.py +54 -43
- dexcontrol/sensors/lidar/rplidar.py +16 -20
- dexcontrol/sensors/manager.py +4 -11
- dexcontrol/sensors/ultrasonic.py +14 -27
- dexcontrol/utils/__init__.py +0 -11
- dexcontrol/utils/comm_helper.py +111 -0
- dexcontrol/utils/constants.py +1 -1
- dexcontrol/utils/os_utils.py +169 -1
- dexcontrol/utils/pb_utils.py +0 -22
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.1.dist-info}/METADATA +13 -1
- dexcontrol-0.3.1.dist-info/RECORD +68 -0
- dexcontrol/config/sensors/cameras/luxonis_camera.py +0 -51
- dexcontrol/sensors/camera/luxonis_camera.py +0 -169
- 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.1.dist-info}/WHEEL +0 -0
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.1.dist-info}/licenses/LICENSE +0 -0
dexcontrol/utils/os_utils.py
CHANGED
|
@@ -12,8 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
|
-
from typing import Final
|
|
15
|
+
from typing import Any, Final
|
|
16
16
|
|
|
17
|
+
from loguru import logger
|
|
18
|
+
|
|
19
|
+
import dexcontrol
|
|
17
20
|
from dexcontrol.utils.constants import ROBOT_NAME_ENV_VAR
|
|
18
21
|
|
|
19
22
|
|
|
@@ -56,3 +59,168 @@ def get_robot_model() -> str:
|
|
|
56
59
|
raise ValueError(f"Unknown robot model: {robot_model_abb}")
|
|
57
60
|
model = robot_model_abb_mapping[robot_model_abb] + "-" + robot_name.split("-")[-1]
|
|
58
61
|
return model
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def check_version_compatibility(
|
|
65
|
+
version_info: dict[str, Any], show_warnings: bool = True
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Check version compatibility between client and server.
|
|
68
|
+
|
|
69
|
+
This function uses the new JSON-based version interface to:
|
|
70
|
+
1. Compare client library version with server's minimum required version
|
|
71
|
+
2. Check server component versions for compatibility
|
|
72
|
+
3. Provide clear guidance for version mismatches
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
version_info: Dictionary containing version information from get_version_info()
|
|
76
|
+
show_warnings: Whether to show warning messages (default: True)
|
|
77
|
+
"""
|
|
78
|
+
validate_client_version(version_info)
|
|
79
|
+
validate_server_version(version_info)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def validate_client_version(version_info: dict[str, Any]) -> None:
|
|
83
|
+
"""Validate client library version against server requirements.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
version_info: Dictionary containing server and client version information.
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
client_info = version_info.get("client", {})
|
|
90
|
+
min_required_version = client_info.get("minimal_version")
|
|
91
|
+
|
|
92
|
+
if not min_required_version:
|
|
93
|
+
logger.debug("No minimum version requirement from server")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
# Get current library version
|
|
97
|
+
current_version = getattr(dexcontrol, "__version__", "unknown")
|
|
98
|
+
|
|
99
|
+
if current_version == "unknown":
|
|
100
|
+
logger.warning("Could not determine current library version")
|
|
101
|
+
return
|
|
102
|
+
|
|
103
|
+
# Compare versions
|
|
104
|
+
comparison = compare_versions(current_version, min_required_version)
|
|
105
|
+
|
|
106
|
+
if comparison < 0:
|
|
107
|
+
show_version_upgrade_warning(current_version, min_required_version)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def show_version_upgrade_warning(current: str, required: str) -> None:
|
|
111
|
+
"""Display version upgrade warning to user.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
current: Current library version
|
|
115
|
+
required: Required minimum version
|
|
116
|
+
"""
|
|
117
|
+
logger.error(
|
|
118
|
+
f"🚨 CLIENT VERSION TOO OLD! 🚨\n"
|
|
119
|
+
f"Current library version: {current}\n"
|
|
120
|
+
f"Minimum required version: {required}\n"
|
|
121
|
+
f"\n"
|
|
122
|
+
f"⚠️ Your dexcontrol library is outdated and may not work correctly!\n"
|
|
123
|
+
f"📦 Please update your library using: pip install --upgrade dexcontrol\n"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def validate_server_version(version_info: dict[str, Any]) -> None:
|
|
128
|
+
"""Validate server software versions against minimum requirements.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
version_info: Dictionary containing server and client version information.
|
|
132
|
+
"""
|
|
133
|
+
server_info = version_info.get("server", {})
|
|
134
|
+
|
|
135
|
+
if not server_info:
|
|
136
|
+
logger.debug("No server version information available")
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
# Check each component's software version
|
|
140
|
+
soc_info = server_info.get("soc", {})
|
|
141
|
+
software_version = soc_info.get("software_version", {})
|
|
142
|
+
if software_version is not None:
|
|
143
|
+
software_version_int = int(software_version)
|
|
144
|
+
if software_version_int < dexcontrol.MIN_SOC_SOFTWARE_VERSION:
|
|
145
|
+
show_server_version_warning(
|
|
146
|
+
[("soc", software_version_int)], dexcontrol.MIN_SOC_SOFTWARE_VERSION
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def show_server_version_warning(
|
|
151
|
+
components: list[tuple[str, int]], min_version: int
|
|
152
|
+
) -> None:
|
|
153
|
+
"""Display server version warning to user.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
components: List of (component_name, version) tuples for components below minimum.
|
|
157
|
+
min_version: Minimum required server software version.
|
|
158
|
+
"""
|
|
159
|
+
components_str = "\n".join(
|
|
160
|
+
f" - {name}: version {version}" for name, version in components
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
logger.error(
|
|
164
|
+
f"🚨 SERVER VERSION TOO OLD! 🚨\n"
|
|
165
|
+
f"The following server components are below minimum version {min_version}:\n"
|
|
166
|
+
f"{components_str}\n"
|
|
167
|
+
f"\n"
|
|
168
|
+
f"⚠️ Your robot's firmware may be outdated and some features may not work correctly!\n"
|
|
169
|
+
f"📦 Please contact your robot admin or check https://github.com/dexmate-ai/vega-firmware.\n"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def compare_versions(version1: str, version2: str) -> int:
|
|
174
|
+
"""Compare two semantic version strings.
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
version1: First version string (e.g., "1.2.3")
|
|
178
|
+
version2: Second version string (e.g., "1.1.0")
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
-1 if version1 < version2
|
|
182
|
+
0 if version1 == version2
|
|
183
|
+
1 if version1 > version2
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
# Clean version strings (remove 'v' prefix, handle pre-release suffixes)
|
|
187
|
+
def clean_version(v: str) -> list[int]:
|
|
188
|
+
v = v.strip().lower()
|
|
189
|
+
if v.startswith("v"):
|
|
190
|
+
v = v[1:]
|
|
191
|
+
# Split by dots and take only numeric parts
|
|
192
|
+
parts = v.split(".")
|
|
193
|
+
numeric_parts = []
|
|
194
|
+
for part in parts:
|
|
195
|
+
# Remove any non-numeric suffixes (like -alpha, -rc1, etc.)
|
|
196
|
+
numeric_part = ""
|
|
197
|
+
for char in part:
|
|
198
|
+
if char.isdigit():
|
|
199
|
+
numeric_part += char
|
|
200
|
+
else:
|
|
201
|
+
break
|
|
202
|
+
if numeric_part:
|
|
203
|
+
numeric_parts.append(int(numeric_part))
|
|
204
|
+
return numeric_parts
|
|
205
|
+
|
|
206
|
+
parts1 = clean_version(version1)
|
|
207
|
+
parts2 = clean_version(version2)
|
|
208
|
+
|
|
209
|
+
# Pad shorter version with zeros
|
|
210
|
+
max_len = max(len(parts1), len(parts2))
|
|
211
|
+
parts1.extend([0] * (max_len - len(parts1)))
|
|
212
|
+
parts2.extend([0] * (max_len - len(parts2)))
|
|
213
|
+
|
|
214
|
+
# Compare part by part
|
|
215
|
+
for p1, p2 in zip(parts1, parts2):
|
|
216
|
+
if p1 < p2:
|
|
217
|
+
return -1
|
|
218
|
+
elif p1 > p2:
|
|
219
|
+
return 1
|
|
220
|
+
|
|
221
|
+
return 0
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
logger.debug(f"Version comparison error: {e}")
|
|
225
|
+
# Fallback to string comparison
|
|
226
|
+
return -1 if version1 < version2 else (1 if version1 > version2 else 0)
|
dexcontrol/utils/pb_utils.py
CHANGED
|
@@ -20,28 +20,6 @@ TYPE_SOFTWARE_VERSION = dict[
|
|
|
20
20
|
]
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
def software_version_to_dict(
|
|
24
|
-
version_msg: dexcontrol_query_pb2.SoftwareVersion,
|
|
25
|
-
) -> dict[str, TYPE_SOFTWARE_VERSION]:
|
|
26
|
-
"""Convert a SoftwareVersion protobuf message to a dictionary.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
version_msg: SoftwareVersion protobuf message.
|
|
30
|
-
|
|
31
|
-
Returns:
|
|
32
|
-
Dictionary containing version information with component names as keys.
|
|
33
|
-
"""
|
|
34
|
-
return {
|
|
35
|
-
key: {
|
|
36
|
-
"hardware_version": value.hardware_version,
|
|
37
|
-
"software_version": value.software_version,
|
|
38
|
-
"main_hash": value.main_hash,
|
|
39
|
-
"compile_time": value.compile_time,
|
|
40
|
-
}
|
|
41
|
-
for key, value in version_msg.firmware_version.items()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
23
|
class ComponentStatus(Enum):
|
|
46
24
|
"""Enum representing the status of a component."""
|
|
47
25
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dexcontrol
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: A Python library of Sensing and Control for Dexmate's Robot
|
|
5
5
|
Project-URL: Repository, https://github.com/dexmate-ai/dexcontrol
|
|
6
6
|
Author-email: Dexmate <contact@dexmate.ai>
|
|
@@ -202,6 +202,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
202
202
|
Classifier: Typing :: Typed
|
|
203
203
|
Requires-Python: <3.14,>=3.10
|
|
204
204
|
Requires-Dist: aiortc
|
|
205
|
+
Requires-Dist: dexcomm>=0.1.2
|
|
205
206
|
Requires-Dist: eclipse-zenoh>=1.2.0
|
|
206
207
|
Requires-Dist: hydra-core==1.3.2
|
|
207
208
|
Requires-Dist: jaxtyping>=0.2.38
|
|
@@ -253,6 +254,17 @@ To run the examples in this repo, you can try:
|
|
|
253
254
|
pip install dexcontrol[example]
|
|
254
255
|
```
|
|
255
256
|
|
|
257
|
+
## ⚠️ Version Compatibility
|
|
258
|
+
|
|
259
|
+
**Important:** `dexcontrol >= 0.3.0` requires robot firmware with SoC version `286` or higher.
|
|
260
|
+
|
|
261
|
+
**Before upgrading, check your current firmware version:**
|
|
262
|
+
```shell
|
|
263
|
+
dextop firmware info
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
If your firmware is outdated, please update it before installing the new version to ensure full compatibility. Please contact the Dexmate team if you do not know how to do it.
|
|
267
|
+
|
|
256
268
|
## 📄 Licensing
|
|
257
269
|
|
|
258
270
|
This project is **dual-licensed**:
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
dexcontrol/__init__.py,sha256=IjeenMFvYyasRDb58qzVHdftwTH_nRgKDVLSmhH7g-k,2009
|
|
2
|
+
dexcontrol/robot.py,sha256=mXezG10UMVBYynE-WD5_XBOjvnjVBhYZjarP_W09TPs,40471
|
|
3
|
+
dexcontrol/apps/dualsense_teleop_base.py,sha256=HJ5aB9zaXdEUQtkd8Vrd0dQqYZrKH1nEjcSGSlDpYD4,12686
|
|
4
|
+
dexcontrol/comm/__init__.py,sha256=YV8Sfv15vuUUqR2lg_H97RCHWUaOblxbI5NgigP4kzc,1272
|
|
5
|
+
dexcontrol/comm/base.py,sha256=PjrEiK7fhGvJ6snaWziSeoNHiRe5I9JDiOs_ptLQC1s,12241
|
|
6
|
+
dexcontrol/comm/rtc.py,sha256=kGFppq3ewXIY4OQ92cExf7AohoSrLImYyb_zBFpUswI,13118
|
|
7
|
+
dexcontrol/comm/subscribers.py,sha256=eg_rdgEUDU8dMPpNsLYJhupxB52fKs7MJmfX9ung66Q,8946
|
|
8
|
+
dexcontrol/config/__init__.py,sha256=UVLNpzGD14e8g68rUZFXTh0B7FRx6uS0Eg_MecjinYM,520
|
|
9
|
+
dexcontrol/config/vega.py,sha256=Uh14vBOZoMAmFXQgjh-IK9x_W0j8YvqgUsoDNHx1ZsE,8443
|
|
10
|
+
dexcontrol/config/core/__init__.py,sha256=Ym2R1hr1iMKQuXcg16BpZfQtTb0hQ5Q7smUIMlwKfho,637
|
|
11
|
+
dexcontrol/config/core/arm.py,sha256=5hN1dQMe2H6oufaqgtZqx9vuB969DxM26leJqPsKEiA,1471
|
|
12
|
+
dexcontrol/config/core/chassis.py,sha256=2FjyFujg2q7aw8J9BklNM7eeLxscY9BSTlBvz-tLwOM,1092
|
|
13
|
+
dexcontrol/config/core/hand.py,sha256=r6XVyGCuwv7MFmaMLn7l3iPZUH376NZSmtsfLnznAgw,1033
|
|
14
|
+
dexcontrol/config/core/head.py,sha256=SLwZE-lYEOk8XAmW-Ex7VkLF2w5HLItwsA3Dc7n5FtE,1061
|
|
15
|
+
dexcontrol/config/core/misc.py,sha256=zHkJ144b6kbmMFE63wy_fgfo_6V-4XmM19hr6BUtQ0Y,1567
|
|
16
|
+
dexcontrol/config/core/torso.py,sha256=DCTFgN1_Gn4THkKy23sIHOedACQtQ7cET3g4AmkVPco,1460
|
|
17
|
+
dexcontrol/config/sensors/__init__.py,sha256=bYPMLxbbn5QeuPyA6OPGDS2JTYpnVvaZJT8PeILFjQY,252
|
|
18
|
+
dexcontrol/config/sensors/vega_sensors.py,sha256=m_Z5ywKxGBvP7pUr6dxLNnGEPI2jWt2hDtxln4uFnY8,2675
|
|
19
|
+
dexcontrol/config/sensors/cameras/__init__.py,sha256=GmwRW9ovZ_JcpD2QmzTO_in_LRBoRDorjMVGY6XgGI8,383
|
|
20
|
+
dexcontrol/config/sensors/cameras/rgb_camera.py,sha256=MN4SjyZlfbrQ3JKDDkT8HhC0Aiyc0bWfDLt4ik0Xcvs,1448
|
|
21
|
+
dexcontrol/config/sensors/cameras/zed_camera.py,sha256=UGAjXlOu5E0M3vjMSIOqdvNjK8mhx52OIDgs5RksqUc,1895
|
|
22
|
+
dexcontrol/config/sensors/imu/__init__.py,sha256=fW-DlevCvf_W8HV_fvLe9yIe-XL5op2mggoTKh-6fGQ,328
|
|
23
|
+
dexcontrol/config/sensors/imu/chassis_imu.py,sha256=3OlTTBH6k1QGM5c5bcg8NL3XUXzYA8gCLM8lpCq2KFM,559
|
|
24
|
+
dexcontrol/config/sensors/imu/zed_imu.py,sha256=y-dPI-XS6Kyq0WOf0wwuc2BgVnMN2hwCMxb0Vmwt4O4,550
|
|
25
|
+
dexcontrol/config/sensors/lidar/__init__.py,sha256=j8vFkF675Z7zKtCztJcyG7oSA_XqrD8OeQLEK0GACug,288
|
|
26
|
+
dexcontrol/config/sensors/lidar/rplidar.py,sha256=ybuT_f1ADWF3oGH1gi6D2F80TbJEm4vbm68Fe108OAA,541
|
|
27
|
+
dexcontrol/config/sensors/ultrasonic/__init__.py,sha256=-q83RhIMZJGVFVPYaA4hOugoG6wZw8EL6wJg7-HTSxU,294
|
|
28
|
+
dexcontrol/config/sensors/ultrasonic/ultrasonic.py,sha256=7b4dm1QOhy5_5RFVpY-frXZyDzqok0K1u7ed9gf3PL0,552
|
|
29
|
+
dexcontrol/core/__init__.py,sha256=bYPMLxbbn5QeuPyA6OPGDS2JTYpnVvaZJT8PeILFjQY,252
|
|
30
|
+
dexcontrol/core/arm.py,sha256=j7uffgdQ_6gaavRE8WOVs56n5e3f_-6CrhRkSiNriRE,16231
|
|
31
|
+
dexcontrol/core/chassis.py,sha256=cxiZv86v9vWSGoWJLZZFn-vEo4OCDlh5S-J9Z0iED0c,24189
|
|
32
|
+
dexcontrol/core/component.py,sha256=J7TscC7u4io38gS9SNaRvnBBH48LgKF6dRbSeHzl2Vk,36346
|
|
33
|
+
dexcontrol/core/hand.py,sha256=yRimScGR0wdL6nPaoEO2M1okc7tYBXMIwriLjpOs35g,9596
|
|
34
|
+
dexcontrol/core/head.py,sha256=Dd-Wk4-BiUM8q9LnTU017fijCwDT9QhFsU4v9Hqeo-E,9932
|
|
35
|
+
dexcontrol/core/misc.py,sha256=nCY-rOslBZyx1w7QUfu6M7VZvMeiTfWg2eFTjYAbI-M,30809
|
|
36
|
+
dexcontrol/core/robot_query_interface.py,sha256=fccYDYK1zd6AqftEU46gbpsIZginRj_RDmvKa3DoSC8,17506
|
|
37
|
+
dexcontrol/core/torso.py,sha256=xOLfhivwNM-JR8nw6Lg1FdctXlmX-qvjD2nUgHm5iKw,8762
|
|
38
|
+
dexcontrol/proto/dexcontrol_msg_pb2.py,sha256=pIbSlweIVSU3hN13kZxPycySU_OYXPoHRuDh7NIBXho,5289
|
|
39
|
+
dexcontrol/proto/dexcontrol_msg_pb2.pyi,sha256=1KpTJnLq6KG2eijZ_vlFq1JsTPeCeLz1IdmoUhW6Tec,8722
|
|
40
|
+
dexcontrol/proto/dexcontrol_query_pb2.py,sha256=KXPCAaKb2n55MBxmhIaFvAoqbS9XsKD6RdztGNe0occ,6230
|
|
41
|
+
dexcontrol/proto/dexcontrol_query_pb2.pyi,sha256=CGwwceF5Ibt9U3R8x2e7GjVNv2GyuD5WM_nyN4YF2wQ,7822
|
|
42
|
+
dexcontrol/sensors/__init__.py,sha256=Dp06cuO_3xC6i4u5rHqfK5NqlIC5kaCue_bAtTC6JEE,960
|
|
43
|
+
dexcontrol/sensors/manager.py,sha256=bvoKH6NIHEjfWsnffoUaNU14CDPkgOV06NL7NwY6s2U,6762
|
|
44
|
+
dexcontrol/sensors/ultrasonic.py,sha256=1rXzA_5w5Lf6TgLvPLnNyiIAMS_cuTuJusLWEBkc_fc,2882
|
|
45
|
+
dexcontrol/sensors/camera/__init__.py,sha256=Vwe98I4Lvdv3F2UslOzKkeUkt5Rl2jSqbKlU6gIBeF0,609
|
|
46
|
+
dexcontrol/sensors/camera/base_camera.py,sha256=JqVb1b82CxMH1FleGobr5dxN18sbDaqotE8RuivGAJo,4741
|
|
47
|
+
dexcontrol/sensors/camera/rgb_camera.py,sha256=w_SLJuJvbQgsq_h3DHNrTk8ofn8XEoX18GPCc-19g1g,6344
|
|
48
|
+
dexcontrol/sensors/camera/zed_camera.py,sha256=zOLwszwk3lvlyajEvfPgEhrKrHhQeVBxyLH1ECOD9Gc,13590
|
|
49
|
+
dexcontrol/sensors/imu/__init__.py,sha256=bBC7_NSLJ5qLMvUYu2-9yXKO2bRpQLC0HyywBwnbM0A,768
|
|
50
|
+
dexcontrol/sensors/imu/chassis_imu.py,sha256=kRP7jAeDDim2C0gIV7xG31VSBO4dnXUZDMfh0JOzNDs,5611
|
|
51
|
+
dexcontrol/sensors/imu/zed_imu.py,sha256=rGT0S2TIj0JGdlJMQKNldtCclOYunCmS0xdzvjAhq9A,5628
|
|
52
|
+
dexcontrol/sensors/lidar/__init__.py,sha256=frF16HmeQnfbvH0dVJ4pPjD4TySF13wCk-O9L3Memeg,317
|
|
53
|
+
dexcontrol/sensors/lidar/rplidar.py,sha256=MrmxP99-YsfoRlYUZTCcmvNbF-YCOLynhoe2tCMpDnE,4191
|
|
54
|
+
dexcontrol/utils/__init__.py,sha256=bYPMLxbbn5QeuPyA6OPGDS2JTYpnVvaZJT8PeILFjQY,252
|
|
55
|
+
dexcontrol/utils/comm_helper.py,sha256=5KZPCc_HA3tnptlYlyYLjbi7H17BXqR_bwt9Fy_FjlQ,3572
|
|
56
|
+
dexcontrol/utils/constants.py,sha256=uzR6AW5LNU3Otmf0G8M4yg-0iaHLhKtJ5e-OPuqxr9g,783
|
|
57
|
+
dexcontrol/utils/error_code.py,sha256=iy840qnWn9wv_nVqyEDP8-l2CuXlPH2xXXW0k-5cHKk,7321
|
|
58
|
+
dexcontrol/utils/io_utils.py,sha256=4TYV33ufECo8fuQivrZR9vtSdwWYUiPvpAUSneEzOOs,850
|
|
59
|
+
dexcontrol/utils/motion_utils.py,sha256=p4kXQm_YorISDC2crrpY0gwCVw_yQCPv-acxPUSfh8w,7172
|
|
60
|
+
dexcontrol/utils/os_utils.py,sha256=F9cyAfI-h4JXT3HOlms2nxe6WI1CZuOA3QHUYQA7pk4,7560
|
|
61
|
+
dexcontrol/utils/pb_utils.py,sha256=zN4pMS9GV9OTj1TmhcWTaDmfmgttyIDFJEuOE5tbCS0,2508
|
|
62
|
+
dexcontrol/utils/timer.py,sha256=1sOYYEapbZ5aBqJwknClsxgjDx0FDRQuGEdcTGnYTCI,3948
|
|
63
|
+
dexcontrol/utils/trajectory_utils.py,sha256=TURFb0DeDey0416z4L7AXiWcKJYsgg_bB5AE_JPSpXY,1879
|
|
64
|
+
dexcontrol/utils/viz_utils.py,sha256=rKtZfu32-9D9CS4cSiil-oLub_MiKTJV6hURvJbKd0s,6295
|
|
65
|
+
dexcontrol-0.3.1.dist-info/METADATA,sha256=-otrrBbT0NmHoVdhTJBMgfH34R93nxQpgtkhuFHOwRc,37269
|
|
66
|
+
dexcontrol-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
67
|
+
dexcontrol-0.3.1.dist-info/licenses/LICENSE,sha256=0J2KCMNNnW5WZPK5x8xUiCxApBf7h83693ggSJYiue0,31745
|
|
68
|
+
dexcontrol-0.3.1.dist-info/RECORD,,
|
|
@@ -1,51 +0,0 @@
|
|
|
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
|
-
from dataclasses import dataclass, field
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@dataclass
|
|
15
|
-
class LuxonisCameraConfig:
|
|
16
|
-
"""Configuration for LuxonisCameraSensor (wrist camera).
|
|
17
|
-
|
|
18
|
-
Attributes:
|
|
19
|
-
_target_: Target sensor class for Hydra instantiation.
|
|
20
|
-
name: Logical name of the camera.
|
|
21
|
-
enable: Whether this sensor is enabled.
|
|
22
|
-
enable_fps_tracking: Enable FPS tracking logs.
|
|
23
|
-
fps_log_interval: Frames between FPS logs.
|
|
24
|
-
subscriber_config: Topics for RGB and Depth Zenoh subscribers.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
_target_: str = "dexcontrol.sensors.camera.luxonis_camera.LuxonisCameraSensor"
|
|
28
|
-
name: str = "wrist_camera"
|
|
29
|
-
enable: bool = False
|
|
30
|
-
enable_fps_tracking: bool = False
|
|
31
|
-
fps_log_interval: int = 30
|
|
32
|
-
|
|
33
|
-
# Note: Resolution is set by the publisher (dexsensor). The publisher now defaults
|
|
34
|
-
# to 720p to match common Luxonis sensor capabilities (e.g., OV9782 supports 720p/800p).
|
|
35
|
-
# These subscribers only define topics to consume.
|
|
36
|
-
subscriber_config: dict = field(
|
|
37
|
-
default_factory=lambda: {
|
|
38
|
-
"left_rgb": {
|
|
39
|
-
"enable": True,
|
|
40
|
-
"topic": "camera/wrist/left_rgb",
|
|
41
|
-
},
|
|
42
|
-
"right_rgb": {
|
|
43
|
-
"enable": True,
|
|
44
|
-
"topic": "camera/wrist/right_rgb",
|
|
45
|
-
},
|
|
46
|
-
"depth": {
|
|
47
|
-
"enable": False,
|
|
48
|
-
"topic": "camera/wrist/depth",
|
|
49
|
-
},
|
|
50
|
-
}
|
|
51
|
-
)
|
|
@@ -1,169 +0,0 @@
|
|
|
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
|
-
"""Luxonis wrist camera sensor using RGB and depth Zenoh subscribers.
|
|
12
|
-
|
|
13
|
-
This sensor mirrors the high-level API of other camera sensors. It subscribes to
|
|
14
|
-
RGB (JPEG over Zenoh) and depth streams published by dexsensor's Luxonis camera
|
|
15
|
-
pipeline and exposes a simple interface for getting images.
|
|
16
|
-
"""
|
|
17
|
-
|
|
18
|
-
from __future__ import annotations
|
|
19
|
-
|
|
20
|
-
import logging
|
|
21
|
-
from typing import Any, Dict, Optional, Union
|
|
22
|
-
|
|
23
|
-
import numpy as np
|
|
24
|
-
import zenoh
|
|
25
|
-
|
|
26
|
-
from dexcontrol.config.sensors.cameras.luxonis_camera import LuxonisCameraConfig
|
|
27
|
-
from dexcontrol.utils.subscribers.camera import (
|
|
28
|
-
DepthCameraSubscriber,
|
|
29
|
-
RGBCameraSubscriber,
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
logger = logging.getLogger(__name__)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class LuxonisCameraSensor:
|
|
36
|
-
"""RGBD camera sensor wrapper for Luxonis/OAK wrist camera.
|
|
37
|
-
|
|
38
|
-
Provides access to RGB and depth frames via dedicated Zenoh subscribers.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
def __init__(
|
|
42
|
-
self,
|
|
43
|
-
configs: LuxonisCameraConfig,
|
|
44
|
-
zenoh_session: zenoh.Session,
|
|
45
|
-
) -> None:
|
|
46
|
-
self._name = configs.name
|
|
47
|
-
self._configs = configs
|
|
48
|
-
self._zenoh_session = zenoh_session
|
|
49
|
-
|
|
50
|
-
# Support left and right RGB streams + depth, mirroring ZED structure
|
|
51
|
-
self._subscribers: Dict[str, Optional[Union[RGBCameraSubscriber, DepthCameraSubscriber]]] = {}
|
|
52
|
-
|
|
53
|
-
subscriber_config = configs.subscriber_config
|
|
54
|
-
|
|
55
|
-
try:
|
|
56
|
-
# Create subscribers for left_rgb, right_rgb, depth (configurable enables)
|
|
57
|
-
stream_defs: Dict[str, Dict[str, Any]] = {
|
|
58
|
-
"left_rgb": subscriber_config.get("left_rgb", {}),
|
|
59
|
-
"right_rgb": subscriber_config.get("right_rgb", {}),
|
|
60
|
-
"depth": subscriber_config.get("depth", {}),
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
for stream_name, cfg in stream_defs.items():
|
|
64
|
-
if not cfg or not cfg.get("enable", False):
|
|
65
|
-
self._subscribers[stream_name] = None
|
|
66
|
-
continue
|
|
67
|
-
topic = cfg.get("topic")
|
|
68
|
-
if not topic:
|
|
69
|
-
logger.warning(f"'{self._name}': No topic configured for '{stream_name}'")
|
|
70
|
-
self._subscribers[stream_name] = None
|
|
71
|
-
continue
|
|
72
|
-
|
|
73
|
-
if stream_name == "depth":
|
|
74
|
-
self._subscribers[stream_name] = DepthCameraSubscriber(
|
|
75
|
-
topic=topic,
|
|
76
|
-
zenoh_session=self._zenoh_session,
|
|
77
|
-
name=f"{self._name}_{stream_name}_subscriber",
|
|
78
|
-
enable_fps_tracking=configs.enable_fps_tracking,
|
|
79
|
-
fps_log_interval=configs.fps_log_interval,
|
|
80
|
-
)
|
|
81
|
-
else:
|
|
82
|
-
self._subscribers[stream_name] = RGBCameraSubscriber(
|
|
83
|
-
topic=topic,
|
|
84
|
-
zenoh_session=self._zenoh_session,
|
|
85
|
-
name=f"{self._name}_{stream_name}_subscriber",
|
|
86
|
-
enable_fps_tracking=configs.enable_fps_tracking,
|
|
87
|
-
fps_log_interval=configs.fps_log_interval,
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
except Exception as e:
|
|
91
|
-
logger.error(f"Error creating Luxonis wrist camera subscribers: {e}")
|
|
92
|
-
|
|
93
|
-
# Lifecycle
|
|
94
|
-
def shutdown(self) -> None:
|
|
95
|
-
for sub in self._subscribers.values():
|
|
96
|
-
if sub:
|
|
97
|
-
sub.shutdown()
|
|
98
|
-
logger.info(f"'{self._name}' sensor shut down.")
|
|
99
|
-
|
|
100
|
-
# Status
|
|
101
|
-
def is_active(self) -> bool:
|
|
102
|
-
return any(sub.is_active() for sub in self._subscribers.values() if sub is not None)
|
|
103
|
-
|
|
104
|
-
def wait_for_active(self, timeout: float = 5.0, require_both: bool = False) -> bool:
|
|
105
|
-
subs = [s for s in self._subscribers.values() if s is not None]
|
|
106
|
-
if not subs:
|
|
107
|
-
return False
|
|
108
|
-
if require_both:
|
|
109
|
-
return all(s.wait_for_active(timeout) for s in subs)
|
|
110
|
-
return any(s.wait_for_active(timeout) for s in subs)
|
|
111
|
-
|
|
112
|
-
# Data access
|
|
113
|
-
def get_obs(
|
|
114
|
-
self, obs_keys: Optional[list[str]] = None, include_timestamp: bool = False
|
|
115
|
-
) -> Dict[str, Optional[np.ndarray]]:
|
|
116
|
-
"""Get latest images.
|
|
117
|
-
|
|
118
|
-
obs_keys can include any of: ["left_rgb", "right_rgb", "depth"]. If None, returns
|
|
119
|
-
all available. If include_timestamp is True and the underlying subscriber returns
|
|
120
|
-
(image, timestamp), that tuple is forwarded; otherwise only the image is returned.
|
|
121
|
-
"""
|
|
122
|
-
keys_to_fetch = obs_keys or self.available_streams
|
|
123
|
-
|
|
124
|
-
out: Dict[str, Optional[np.ndarray]] = {}
|
|
125
|
-
for key in keys_to_fetch:
|
|
126
|
-
sub = self._subscribers.get(key)
|
|
127
|
-
data = sub.get_latest_data() if sub else None
|
|
128
|
-
is_tuple_or_list = isinstance(data, (tuple, list))
|
|
129
|
-
if include_timestamp:
|
|
130
|
-
if not is_tuple_or_list and data is not None:
|
|
131
|
-
logger.warning(f"Timestamp is not available yet for {key} stream.")
|
|
132
|
-
out[key] = data
|
|
133
|
-
else:
|
|
134
|
-
out[key] = data[0] if is_tuple_or_list else data
|
|
135
|
-
return out
|
|
136
|
-
|
|
137
|
-
def get_rgb(self) -> Optional[np.ndarray]:
|
|
138
|
-
# Backward-compat: return left_rgb if available else right_rgb
|
|
139
|
-
for key in ("left_rgb", "right_rgb"):
|
|
140
|
-
sub = self._subscribers.get(key)
|
|
141
|
-
if sub:
|
|
142
|
-
data = sub.get_latest_data()
|
|
143
|
-
return data[0] if isinstance(data, (tuple, list)) else data
|
|
144
|
-
return None
|
|
145
|
-
|
|
146
|
-
def get_depth(self) -> Optional[np.ndarray]:
|
|
147
|
-
sub = self._subscribers.get("depth")
|
|
148
|
-
if not sub:
|
|
149
|
-
return None
|
|
150
|
-
data = sub.get_latest_data()
|
|
151
|
-
return data[0] if isinstance(data, (tuple, list)) else data
|
|
152
|
-
|
|
153
|
-
# Properties
|
|
154
|
-
@property
|
|
155
|
-
def fps(self) -> Dict[str, float]:
|
|
156
|
-
return {name: sub.fps for name, sub in self._subscribers.items() if sub is not None}
|
|
157
|
-
|
|
158
|
-
@property
|
|
159
|
-
def name(self) -> str:
|
|
160
|
-
return self._name
|
|
161
|
-
|
|
162
|
-
@property
|
|
163
|
-
def available_streams(self) -> list:
|
|
164
|
-
return [name for name, sub in self._subscribers.items() if sub is not None]
|
|
165
|
-
|
|
166
|
-
@property
|
|
167
|
-
def active_streams(self) -> list:
|
|
168
|
-
return [name for name, sub in self._subscribers.items() if sub and sub.is_active()]
|
|
169
|
-
|