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.

Files changed (59) hide show
  1. dexcontrol/__init__.py +18 -8
  2. dexcontrol/apps/dualsense_teleop_base.py +1 -1
  3. dexcontrol/comm/__init__.py +51 -0
  4. dexcontrol/comm/base.py +421 -0
  5. dexcontrol/comm/rtc.py +400 -0
  6. dexcontrol/comm/subscribers.py +329 -0
  7. dexcontrol/config/core/chassis.py +9 -4
  8. dexcontrol/config/core/hand.py +1 -0
  9. dexcontrol/config/sensors/cameras/__init__.py +1 -2
  10. dexcontrol/config/sensors/cameras/zed_camera.py +2 -2
  11. dexcontrol/config/sensors/vega_sensors.py +12 -18
  12. dexcontrol/config/vega.py +4 -1
  13. dexcontrol/core/arm.py +61 -37
  14. dexcontrol/core/chassis.py +141 -119
  15. dexcontrol/core/component.py +110 -59
  16. dexcontrol/core/hand.py +118 -85
  17. dexcontrol/core/head.py +18 -29
  18. dexcontrol/core/misc.py +327 -155
  19. dexcontrol/core/robot_query_interface.py +463 -0
  20. dexcontrol/core/torso.py +4 -8
  21. dexcontrol/proto/dexcontrol_msg_pb2.py +27 -39
  22. dexcontrol/proto/dexcontrol_msg_pb2.pyi +75 -118
  23. dexcontrol/proto/dexcontrol_query_pb2.py +39 -39
  24. dexcontrol/proto/dexcontrol_query_pb2.pyi +17 -4
  25. dexcontrol/robot.py +245 -574
  26. dexcontrol/sensors/__init__.py +1 -2
  27. dexcontrol/sensors/camera/__init__.py +0 -2
  28. dexcontrol/sensors/camera/base_camera.py +144 -0
  29. dexcontrol/sensors/camera/rgb_camera.py +67 -63
  30. dexcontrol/sensors/camera/zed_camera.py +89 -147
  31. dexcontrol/sensors/imu/chassis_imu.py +76 -56
  32. dexcontrol/sensors/imu/zed_imu.py +54 -43
  33. dexcontrol/sensors/lidar/rplidar.py +16 -20
  34. dexcontrol/sensors/manager.py +4 -11
  35. dexcontrol/sensors/ultrasonic.py +14 -27
  36. dexcontrol/utils/__init__.py +0 -11
  37. dexcontrol/utils/comm_helper.py +111 -0
  38. dexcontrol/utils/constants.py +1 -1
  39. dexcontrol/utils/os_utils.py +169 -1
  40. dexcontrol/utils/pb_utils.py +0 -22
  41. {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.1.dist-info}/METADATA +13 -1
  42. dexcontrol-0.3.1.dist-info/RECORD +68 -0
  43. dexcontrol/config/sensors/cameras/luxonis_camera.py +0 -51
  44. dexcontrol/sensors/camera/luxonis_camera.py +0 -169
  45. dexcontrol/utils/rate_limiter.py +0 -172
  46. dexcontrol/utils/rtc_utils.py +0 -144
  47. dexcontrol/utils/subscribers/__init__.py +0 -52
  48. dexcontrol/utils/subscribers/base.py +0 -281
  49. dexcontrol/utils/subscribers/camera.py +0 -332
  50. dexcontrol/utils/subscribers/decoders.py +0 -88
  51. dexcontrol/utils/subscribers/generic.py +0 -110
  52. dexcontrol/utils/subscribers/imu.py +0 -175
  53. dexcontrol/utils/subscribers/lidar.py +0 -172
  54. dexcontrol/utils/subscribers/protobuf.py +0 -111
  55. dexcontrol/utils/subscribers/rtc.py +0 -316
  56. dexcontrol/utils/zenoh_utils.py +0 -122
  57. dexcontrol-0.2.12.dist-info/RECORD +0 -75
  58. {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.1.dist-info}/WHEEL +0 -0
  59. {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.1.dist-info}/licenses/LICENSE +0 -0
@@ -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)
@@ -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.2.12
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
-