dexcontrol 0.3.0__py3-none-any.whl → 0.3.2__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 (51) hide show
  1. dexcontrol/__init__.py +16 -7
  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/sensors/cameras/__init__.py +1 -2
  8. dexcontrol/config/sensors/cameras/zed_camera.py +2 -2
  9. dexcontrol/config/sensors/vega_sensors.py +12 -18
  10. dexcontrol/core/arm.py +29 -25
  11. dexcontrol/core/chassis.py +3 -12
  12. dexcontrol/core/component.py +68 -43
  13. dexcontrol/core/hand.py +50 -52
  14. dexcontrol/core/head.py +14 -26
  15. dexcontrol/core/misc.py +188 -166
  16. dexcontrol/core/robot_query_interface.py +140 -117
  17. dexcontrol/core/torso.py +0 -4
  18. dexcontrol/robot.py +15 -37
  19. dexcontrol/sensors/__init__.py +1 -2
  20. dexcontrol/sensors/camera/__init__.py +0 -2
  21. dexcontrol/sensors/camera/base_camera.py +144 -0
  22. dexcontrol/sensors/camera/rgb_camera.py +67 -63
  23. dexcontrol/sensors/camera/zed_camera.py +89 -147
  24. dexcontrol/sensors/imu/chassis_imu.py +76 -56
  25. dexcontrol/sensors/imu/zed_imu.py +54 -43
  26. dexcontrol/sensors/lidar/rplidar.py +16 -20
  27. dexcontrol/sensors/manager.py +4 -11
  28. dexcontrol/sensors/ultrasonic.py +14 -27
  29. dexcontrol/utils/__init__.py +0 -11
  30. dexcontrol/utils/comm_helper.py +111 -0
  31. dexcontrol/utils/constants.py +1 -1
  32. dexcontrol/utils/os_utils.py +8 -22
  33. {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.2.dist-info}/METADATA +2 -1
  34. dexcontrol-0.3.2.dist-info/RECORD +68 -0
  35. dexcontrol/config/sensors/cameras/luxonis_camera.py +0 -51
  36. dexcontrol/sensors/camera/luxonis_camera.py +0 -169
  37. dexcontrol/utils/rate_limiter.py +0 -172
  38. dexcontrol/utils/rtc_utils.py +0 -144
  39. dexcontrol/utils/subscribers/__init__.py +0 -52
  40. dexcontrol/utils/subscribers/base.py +0 -281
  41. dexcontrol/utils/subscribers/camera.py +0 -332
  42. dexcontrol/utils/subscribers/decoders.py +0 -88
  43. dexcontrol/utils/subscribers/generic.py +0 -110
  44. dexcontrol/utils/subscribers/imu.py +0 -175
  45. dexcontrol/utils/subscribers/lidar.py +0 -172
  46. dexcontrol/utils/subscribers/protobuf.py +0 -111
  47. dexcontrol/utils/subscribers/rtc.py +0 -316
  48. dexcontrol/utils/zenoh_utils.py +0 -369
  49. dexcontrol-0.3.0.dist-info/RECORD +0 -76
  50. {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.2.dist-info}/WHEEL +0 -0
  51. {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -8,30 +8,32 @@
8
8
  # 2. Commercial License
9
9
  # For commercial licensing terms, contact: contact@dexmate.ai
10
10
 
11
- """Zenoh query utilities for robot communication.
11
+ """Query utilities for robot communication using DexComm.
12
12
 
13
- This module provides the ZenohQueryable class that encapsulates all zenoh-based
14
- queries and communication with the robot server. It handles various query types
15
- including hand type detection, version information, status queries, and control operations.
13
+ This module provides the RobotQueryInterface class that encapsulates all communication
14
+ queries with the robot server using DexComm's service pattern. It handles various query
15
+ types including hand type detection, version information, status queries, and control
16
+ operations.
16
17
  """
17
18
 
18
19
  import json
19
20
  import time
20
21
  from typing import TYPE_CHECKING, Any, Literal, cast
21
22
 
22
- import zenoh
23
+ # Use DexComm for all communication
24
+ from dexcomm import call_service
23
25
  from loguru import logger
24
26
 
25
27
  from dexcontrol.config.vega import VegaConfig, get_vega_config
26
28
  from dexcontrol.core.hand import HandType
27
29
  from dexcontrol.proto import dexcontrol_query_pb2
30
+ from dexcontrol.utils.comm_helper import get_zenoh_config_path
28
31
  from dexcontrol.utils.os_utils import resolve_key_name
29
32
  from dexcontrol.utils.pb_utils import (
30
33
  ComponentStatus,
31
34
  status_to_dict,
32
35
  )
33
36
  from dexcontrol.utils.viz_utils import show_component_status
34
- from dexcontrol.utils.zenoh_utils import compute_ntp_stats, create_zenoh_session
35
37
 
36
38
  if TYPE_CHECKING:
37
39
  from dexcontrol.config.vega import VegaConfig
@@ -49,63 +51,47 @@ class RobotQueryInterface:
49
51
  ... version_info = interface.get_version_info()
50
52
  """
51
53
 
52
- def __init__(self, zenoh_session: zenoh.Session, configs: "VegaConfig"):
53
- """Initialize the RobotQueryInterface with session and config.
54
+ def __init__(self, configs: "VegaConfig"):
55
+ """Initialize the RobotQueryInterface.
54
56
 
55
57
  Args:
56
- zenoh_session: Active zenoh session for communication.
57
58
  configs: Robot configuration containing query names.
58
59
  """
59
- self._zenoh_session: zenoh.Session = zenoh_session
60
+ # Session parameter kept for compatibility but not used
60
61
  self._configs = configs
61
62
  self._owns_session = False
62
63
 
63
64
  @classmethod
64
- def create(
65
- cls,
66
- zenoh_config_file: str | None = None,
67
- ) -> "RobotQueryInterface":
68
- """Create a standalone RobotQueryInterface with its own zenoh session.
65
+ def create(cls) -> "RobotQueryInterface":
66
+ """Create a standalone RobotQueryInterface.
69
67
 
70
68
  This class method provides a convenient way to create a RobotQueryInterface
71
- without requiring the full Robot class. The created interface will manage
72
- its own zenoh session and configuration.
73
-
74
- Args:
75
- zenoh_config_file: Path to zenoh configuration file. If None,
76
- uses the default configuration path.
77
- robot_config_path: Path to robot configuration file. If None,
78
- uses default configuration for detected robot model.
69
+ without requiring the full Robot class. DexComm handles all session
70
+ management internally.
79
71
 
80
72
  Returns:
81
73
  RobotQueryInterface instance ready for use.
82
74
 
83
- Raises:
84
- RuntimeError: If zenoh session initialization fails.
85
- ValueError: If robot configuration cannot be loaded.
86
-
87
75
  Example:
88
- >>> query_interface = RobotQueryInterface.create_standalone()
76
+ >>> query_interface = RobotQueryInterface.create()
89
77
  >>> version_info = query_interface.get_version_info()
90
78
  >>> query_interface.close()
91
79
  """
92
-
93
- session = create_zenoh_session(zenoh_config_file)
80
+ # DexComm handles session internally, we just need config
94
81
  config: VegaConfig = get_vega_config()
95
-
96
- instance = cls(session, config)
82
+ instance = cls(config)
97
83
  instance._owns_session = True
98
84
  return instance
99
85
 
100
86
  def close(self) -> None:
101
- """Close the zenoh session if owned by this instance.
87
+ """Close the communication session if owned by this instance.
102
88
 
103
89
  This method should be called when done using a standalone
104
90
  RobotQueryInterface to properly clean up resources.
105
91
  """
106
- if self._owns_session and self._zenoh_session:
107
- logger.debug("Closing zenoh session")
108
- self._zenoh_session.close()
92
+ if self._owns_session:
93
+ # DexComm cleanup is handled automatically
94
+ logger.debug("DexComm session cleanup handled automatically")
109
95
 
110
96
  def __enter__(self) -> "RobotQueryInterface":
111
97
  """Enter context manager."""
@@ -128,37 +114,42 @@ class RobotQueryInterface:
128
114
  RuntimeError: If hand type information cannot be retrieved.
129
115
  """
130
116
  try:
131
- # Sleep more time to avoice zenoh query timeout
132
- time.sleep(2)
133
- replies = self._zenoh_session.get(
134
- resolve_key_name(self._configs.hand_info_query_name), timeout=5.0
117
+ # Query hand type using DexComm service
118
+ full_topic = resolve_key_name(self._configs.hand_info_query_name)
119
+
120
+ response = call_service(
121
+ full_topic,
122
+ timeout=5.0,
123
+ config=get_zenoh_config_path(),
124
+ request_serializer=None,
125
+ response_deserializer=None,
135
126
  )
136
127
 
137
- for reply in replies:
138
- if reply.ok and reply.ok.payload:
139
- # Parse JSON response (not protobuf)
140
- payload_bytes = reply.ok.payload.to_bytes()
141
- payload_str = payload_bytes.decode("utf-8")
142
- hand_info = json.loads(payload_str)
143
-
144
- # Validate the expected format
145
- if isinstance(hand_info, dict):
146
- logger.info(f"End effector hand types: {hand_info}")
147
- return {
148
- "left": HandType(hand_info["left"]),
149
- "right": HandType(hand_info["right"]),
150
- }
151
- else:
152
- logger.warning(
153
- f"Invalid hand type format received: {hand_info}"
154
- )
128
+ if response:
129
+ # Parse JSON response directly
130
+ if isinstance(response, bytes):
131
+ payload_str = response.decode("utf-8")
132
+ else:
133
+ payload_str = response
134
+
135
+ hand_info = json.loads(payload_str)
136
+
137
+ # Validate the expected format
138
+ if isinstance(hand_info, dict):
139
+ logger.info(f"End effector hand types: {hand_info}")
140
+ return {
141
+ "left": HandType(hand_info["left"]),
142
+ "right": HandType(hand_info["right"]),
143
+ }
144
+ else:
145
+ logger.warning(f"Invalid hand type format received: {hand_info}")
155
146
 
156
147
  # If no valid response received, assume v1 for backward compatibility
157
- return {"left": HandType.HandF5D6_V1, "right": HandType.HandF5D6_V1}
148
+ return {"left": HandType.UNKNOWN, "right": HandType.UNKNOWN}
158
149
 
159
150
  except Exception as e:
160
- logger.warning(f"Failed to query hand type: {e}. Assuming v1 hand types.")
161
- return {"left": HandType.HandF5D6_V1, "right": HandType.HandF5D6_V1}
151
+ logger.warning(f"Failed to query hand type: {e}. V1 hand type unknown.")
152
+ return {"left": HandType.UNKNOWN, "right": HandType.UNKNOWN}
162
153
 
163
154
  def query_ntp(
164
155
  self,
@@ -189,23 +180,29 @@ class RobotQueryInterface:
189
180
  time_offset = []
190
181
  time_rtt = []
191
182
 
192
- querier = self._zenoh_session.declare_querier(ntp_key, timeout=timeout)
193
- time.sleep(0.1)
194
-
195
183
  reply_count = 0
196
184
  for i in range(sample_count):
197
185
  request = dexcontrol_query_pb2.NTPRequest()
198
186
  request.client_send_time_ns = time.time_ns()
199
187
  request.sample_count = sample_count
200
188
  request.sample_index = i
201
- replies = querier.get(payload=request.SerializeToString())
202
189
 
203
- for reply in replies:
204
- reply_count += 1
205
- if reply.ok and reply.ok.payload:
190
+ # Use call_service for NTP query
191
+ try:
192
+ response_data = call_service(
193
+ ntp_key,
194
+ request=request,
195
+ timeout=timeout,
196
+ config=get_zenoh_config_path(),
197
+ request_serializer=lambda x: x.SerializeToString(),
198
+ response_deserializer=None,
199
+ )
200
+
201
+ if response_data:
202
+ reply_count += 1
206
203
  client_receive_time_ns = time.time_ns()
207
204
  response = dexcontrol_query_pb2.NTPResponse()
208
- response.ParseFromString(reply.ok.payload.to_bytes())
205
+ response.ParseFromString(response_data)
209
206
  t0 = request.client_send_time_ns
210
207
  t1 = response.server_receive_time_ns
211
208
  t2 = response.server_send_time_ns
@@ -214,16 +211,25 @@ class RobotQueryInterface:
214
211
  rtt = (t3 - t0) / 1e9
215
212
  time_offset.append(offset)
216
213
  time_rtt.append(rtt)
214
+ except Exception as e:
215
+ logger.debug(f"NTP query {i} failed: {e}")
216
+
217
217
  if i < sample_count - 1:
218
218
  time.sleep(0.01)
219
-
220
- querier.undeclare()
221
219
  if reply_count == 0:
222
- return {"success": False, "offset": 0, "rtt": 0}
220
+ return {"success": False, "offset": 0.0, "rtt": 0.0}
221
+
222
+ # Compute simple NTP statistics
223
+ import numpy as np
223
224
 
224
- stats = compute_ntp_stats(time_offset, time_rtt)
225
- offset = stats["offset (mean)"]
226
- rtt = stats["round_trip_time (mean)"]
225
+ stats = {
226
+ "offset (mean)": float(np.mean(time_offset)) if time_offset else 0.0,
227
+ "round_trip_time (mean)": float(np.mean(time_rtt)) if time_rtt else 0.0,
228
+ "offset (std)": float(np.std(time_offset)) if time_offset else 0.0,
229
+ "round_trip_time (std)": float(np.std(time_rtt)) if time_rtt else 0.0,
230
+ }
231
+ offset = float(stats["offset (mean)"])
232
+ rtt = float(stats["round_trip_time (mean)"])
227
233
  if show:
228
234
  from dexcontrol.utils.viz_utils import show_ntp_stats
229
235
 
@@ -263,34 +269,38 @@ class RobotQueryInterface:
263
269
  RuntimeError: If version information cannot be retrieved.
264
270
  """
265
271
  try:
266
- replies = self._zenoh_session.get(
267
- resolve_key_name(self._configs.version_info_name), timeout=5.0
272
+ response = call_service(
273
+ resolve_key_name(self._configs.version_info_name),
274
+ timeout=5.0,
275
+ config=get_zenoh_config_path(),
276
+ request_serializer=None,
277
+ response_deserializer=None,
268
278
  )
269
279
 
270
- for reply in replies:
271
- if reply.ok and reply.ok.payload:
272
- try:
273
- # Parse JSON response
274
- payload_bytes = reply.ok.payload.to_bytes()
275
- payload_str = payload_bytes.decode("utf-8")
276
- version_info = json.loads(payload_str)
277
-
278
- # Validate expected structure
279
- if (
280
- isinstance(version_info, dict)
281
- and "server" in version_info
282
- and "client" in version_info
283
- ):
284
- if show:
285
- self._show_version_info(version_info)
286
- return version_info
287
- else:
288
- logger.warning(
289
- f"Invalid version info format received: {version_info}"
290
- )
291
- except (json.JSONDecodeError, UnicodeDecodeError) as e:
292
- logger.warning(f"Failed to parse version info response: {e}")
293
- continue
280
+ if response:
281
+ try:
282
+ # Parse JSON response directly
283
+ if isinstance(response, bytes):
284
+ payload_str = response.decode("utf-8")
285
+ else:
286
+ payload_str = response
287
+ version_info = json.loads(payload_str)
288
+
289
+ # Validate expected structure
290
+ if (
291
+ isinstance(version_info, dict)
292
+ and "server" in version_info
293
+ and "client" in version_info
294
+ ):
295
+ if show:
296
+ self._show_version_info(version_info)
297
+ return version_info
298
+ else:
299
+ logger.warning(
300
+ f"Invalid version info format received: {version_info}"
301
+ )
302
+ except (json.JSONDecodeError, UnicodeDecodeError) as e:
303
+ logger.warning(f"Failed to parse version info response: {e}")
294
304
 
295
305
  raise RuntimeError("No valid version information received from server")
296
306
 
@@ -312,19 +322,22 @@ class RobotQueryInterface:
312
322
  RuntimeError: If status information cannot be retrieved.
313
323
  """
314
324
  try:
315
- replies = self._zenoh_session.get(
316
- resolve_key_name(self._configs.status_info_name)
325
+ response = call_service(
326
+ resolve_key_name(self._configs.status_info_name),
327
+ timeout=2.0,
328
+ config=get_zenoh_config_path(),
329
+ request_serializer=None,
330
+ response_deserializer=None,
317
331
  )
332
+
318
333
  status_dict = {}
319
- for reply in replies:
320
- if reply.ok and reply.ok.payload:
321
- status_bytes = reply.ok.payload.to_bytes()
322
- status_msg = cast(
323
- dexcontrol_query_pb2.ComponentStates,
324
- dexcontrol_query_pb2.ComponentStates.FromString(status_bytes),
325
- )
326
- status_dict = status_to_dict(status_msg)
327
- break
334
+ if response:
335
+ # Parse protobuf response directly
336
+ status_msg = cast(
337
+ dexcontrol_query_pb2.ComponentStates,
338
+ dexcontrol_query_pb2.ComponentStates.FromString(response),
339
+ )
340
+ status_dict = status_to_dict(status_msg)
328
341
 
329
342
  if show:
330
343
  show_component_status(status_dict)
@@ -358,9 +371,14 @@ class RobotQueryInterface:
358
371
  query_msg = dexcontrol_query_pb2.RebootComponent(
359
372
  component=component_map[part]
360
373
  )
361
- self._zenoh_session.get(
374
+
375
+ call_service(
362
376
  resolve_key_name(self._configs.reboot_query_name),
363
- payload=query_msg.SerializeToString(),
377
+ request=query_msg,
378
+ timeout=2.0,
379
+ config=get_zenoh_config_path(),
380
+ request_serializer=lambda x: x.SerializeToString(),
381
+ response_deserializer=None,
364
382
  )
365
383
  logger.info(f"Rebooting component: {part}")
366
384
  except Exception as e:
@@ -391,11 +409,16 @@ class RobotQueryInterface:
391
409
 
392
410
  try:
393
411
  query_msg = dexcontrol_query_pb2.ClearError(component=component_map[part])
394
- self._zenoh_session.get(
412
+
413
+ call_service(
395
414
  resolve_key_name(self._configs.clear_error_query_name),
396
- handler=lambda reply: logger.info(f"Cleared error of {part}"),
397
- payload=query_msg.SerializeToString(),
415
+ request=query_msg,
416
+ timeout=2.0,
417
+ config=get_zenoh_config_path(),
418
+ request_serializer=lambda x: x.SerializeToString(),
419
+ response_deserializer=None,
398
420
  )
421
+ logger.info(f"Cleared error of {part}")
399
422
  except Exception as e:
400
423
  raise RuntimeError(
401
424
  f"Failed to clear error for component {part}: {e}"
dexcontrol/core/torso.py CHANGED
@@ -15,7 +15,6 @@ communication. It handles joint position and velocity control and state monitori
15
15
  """
16
16
 
17
17
  import numpy as np
18
- import zenoh
19
18
  from jaxtyping import Float
20
19
 
21
20
  from dexcontrol.config.core import TorsoConfig
@@ -37,20 +36,17 @@ class Torso(RobotJointComponent):
37
36
  def __init__(
38
37
  self,
39
38
  configs: TorsoConfig,
40
- zenoh_session: zenoh.Session,
41
39
  ) -> None:
42
40
  """Initialize the torso controller.
43
41
 
44
42
  Args:
45
43
  configs: Torso configuration parameters containing communication topics
46
44
  and default velocity settings.
47
- zenoh_session: Active Zenoh communication session for message passing.
48
45
  """
49
46
  super().__init__(
50
47
  state_sub_topic=configs.state_sub_topic,
51
48
  control_pub_topic=configs.control_pub_topic,
52
49
  state_message_type=dexcontrol_msg_pb2.MotorStateWithCurrent,
53
- zenoh_session=zenoh_session,
54
50
  joint_name=configs.joint_name,
55
51
  joint_limit=configs.joint_limit
56
52
  if hasattr(configs, "joint_limit")
dexcontrol/robot.py CHANGED
@@ -25,7 +25,6 @@ from __future__ import annotations
25
25
  import os
26
26
  import signal
27
27
  import sys
28
- import threading
29
28
  import time
30
29
  import weakref
31
30
  from typing import TYPE_CHECKING, Any, Final, Literal, cast
@@ -33,7 +32,8 @@ from typing import TYPE_CHECKING, Any, Final, Literal, cast
33
32
  import hydra.utils
34
33
  import numpy as np
35
34
  import omegaconf
36
- import zenoh
35
+ from dexcomm import cleanup_session
36
+ from dexcomm.utils import RateLimiter
37
37
  from loguru import logger
38
38
  from rich.console import Console
39
39
  from rich.table import Table
@@ -47,13 +47,7 @@ from dexcontrol.core.robot_query_interface import RobotQueryInterface
47
47
  from dexcontrol.sensors import Sensors
48
48
  from dexcontrol.utils.constants import ROBOT_NAME_ENV_VAR
49
49
  from dexcontrol.utils.os_utils import check_version_compatibility, get_robot_model
50
- from dexcontrol.utils.rate_limiter import RateLimiter
51
50
  from dexcontrol.utils.trajectory_utils import generate_linear_trajectory
52
- from dexcontrol.utils.zenoh_utils import (
53
- close_zenoh_session_with_timeout,
54
- create_zenoh_session,
55
- wait_for_zenoh_cleanup,
56
- )
57
51
 
58
52
  if TYPE_CHECKING:
59
53
  from dexcontrol.core.arm import Arm
@@ -142,7 +136,6 @@ class Robot(RobotQueryInterface):
142
136
  self,
143
137
  robot_model: str | None = None,
144
138
  configs: VegaConfig | None = None,
145
- zenoh_config_file: str | None = None,
146
139
  auto_shutdown: bool = True,
147
140
  ) -> None:
148
141
  """Initializes the Robot with the given configuration.
@@ -153,8 +146,6 @@ class Robot(RobotQueryInterface):
153
146
  Ignored if configs is provided.
154
147
  configs: Configuration parameters for all robot components.
155
148
  If None, will use the configuration specified by robot_model.
156
- zenoh_config_file: Optional path to the zenoh config file.
157
- Defaults to None to use system defaults.
158
149
  auto_shutdown: Whether to automatically register signal handlers for
159
150
  graceful shutdown on program interruption. Default is True.
160
151
 
@@ -168,19 +159,18 @@ class Robot(RobotQueryInterface):
168
159
  robot_model = get_robot_model()
169
160
  self._robot_model: Final[str] = robot_model
170
161
 
171
- # Load configuration and initialize zenoh session
162
+ # Load configuration
172
163
  self._configs: Final[VegaConfig] = configs or get_vega_config(robot_model)
173
- self._zenoh_session: zenoh.Session = create_zenoh_session(zenoh_config_file)
174
164
 
175
- # Initialize ZenohQueryable parent class
176
- super().__init__(self._zenoh_session, self._configs)
165
+ super().__init__(self._configs)
177
166
 
178
167
  self._robot_name: Final[str] = os.getenv(ROBOT_NAME_ENV_VAR, "robot")
179
168
  self._pv_components: list[str] = [
180
169
  "head",
181
170
  "torso",
182
171
  ]
183
- self._log_subscriber = ServerLogSubscriber(self._zenoh_session)
172
+ # Note: zenoh_session no longer needed as DexComm handles sessions
173
+ self._log_subscriber = ServerLogSubscriber()
184
174
  self._hand_types: dict[str, HandType] = {}
185
175
 
186
176
  # Register for automatic shutdown on signals if enabled
@@ -280,7 +270,8 @@ class Robot(RobotQueryInterface):
280
270
 
281
271
  def _initialize_sensors(self) -> None:
282
272
  """Initialize sensors and wait for activation."""
283
- self.sensors = Sensors(self._configs.sensors, self._zenoh_session)
273
+ # Note: zenoh_session no longer needed as DexComm handles sessions
274
+ self.sensors = Sensors(self._configs.sensors)
284
275
  self.sensors.wait_for_all_active()
285
276
 
286
277
  def _initialize_robot_components(self) -> None:
@@ -290,7 +281,6 @@ class Robot(RobotQueryInterface):
290
281
 
291
282
  initialized_components = []
292
283
  failed_components = []
293
-
294
284
  for component_name, component_config in config_dict.items():
295
285
  if component_name == "sensors":
296
286
  continue
@@ -335,9 +325,8 @@ class Robot(RobotQueryInterface):
335
325
  temp_config["hand_type"] = hand_type
336
326
 
337
327
  # Instantiate component with error handling
338
- component_instance = hydra.utils.instantiate(
339
- temp_config, zenoh_session=self._zenoh_session
340
- )
328
+ # Note: zenoh_session no longer needed as DexComm handles sessions
329
+ component_instance = hydra.utils.instantiate(temp_config)
341
330
 
342
331
  # Store component instance both as attribute and in tracking dictionaries
343
332
  setattr(self, str(component_name), component_instance)
@@ -347,7 +336,6 @@ class Robot(RobotQueryInterface):
347
336
  logger.error(f"Failed to initialize component {component_name}: {e}")
348
337
  failed_components.append(component_name)
349
338
  # Continue with other components rather than failing completely
350
-
351
339
  # Report initialization summary
352
340
  if failed_components:
353
341
  logger.warning(
@@ -614,23 +602,13 @@ class Robot(RobotQueryInterface):
614
602
  except Exception as e: # pylint: disable=broad-except
615
603
  logger.debug(f"Error shutting down log subscriber: {e}")
616
604
 
617
- # Close Zenoh session
618
- close_zenoh_session_with_timeout(self._zenoh_session)
619
- lingering_threads = wait_for_zenoh_cleanup()
620
- if lingering_threads:
621
- self._assist_clean_exit_if_needed()
605
+ # Cleanup DexComm shared session
606
+ try:
607
+ cleanup_session()
608
+ except Exception as e:
609
+ logger.debug(f"Session cleanup note: {e}")
622
610
  logger.info("Robot shutdown complete")
623
611
 
624
- def _assist_clean_exit_if_needed(self) -> None:
625
- """Assist with clean exit if this is the last robot and we're exiting."""
626
- if (
627
- not _active_robots # No more active robots
628
- and threading.current_thread() is threading.main_thread() # In main thread
629
- and not hasattr(sys, "ps1") # Not in interactive mode
630
- ):
631
- logger.debug("Assisting clean exit due to lingering Zenoh threads")
632
- sys.exit(0)
633
-
634
612
  def is_shutdown(self) -> bool:
635
613
  """Check if the robot has been shutdown.
636
614
 
@@ -15,7 +15,7 @@ using Zenoh subscribers for data communication.
15
15
  """
16
16
 
17
17
  # Import camera sensors
18
- from .camera import LuxonisCameraSensor, RGBCameraSensor, ZedCameraSensor
18
+ from .camera import RGBCameraSensor, ZedCameraSensor
19
19
 
20
20
  # Import IMU sensors
21
21
  from .imu import ChassisIMUSensor, ZedIMUSensor
@@ -31,7 +31,6 @@ __all__ = [
31
31
  # Camera sensors
32
32
  "RGBCameraSensor",
33
33
  "ZedCameraSensor",
34
- "LuxonisCameraSensor",
35
34
 
36
35
  # IMU sensors
37
36
  "ChassisIMUSensor",
@@ -14,12 +14,10 @@ This module provides camera sensor classes that use the specialized camera
14
14
  subscribers for RGB and RGBD camera data, matching the dexsensor structure.
15
15
  """
16
16
 
17
- from .luxonis_camera import LuxonisCameraSensor
18
17
  from .rgb_camera import RGBCameraSensor
19
18
  from .zed_camera import ZedCameraSensor
20
19
 
21
20
  __all__ = [
22
21
  "RGBCameraSensor",
23
22
  "ZedCameraSensor",
24
- "LuxonisCameraSensor",
25
23
  ]