dexcontrol 0.3.0__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 +16 -7
- 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/sensors/cameras/__init__.py +1 -2
- dexcontrol/config/sensors/cameras/zed_camera.py +2 -2
- dexcontrol/config/sensors/vega_sensors.py +12 -18
- dexcontrol/core/arm.py +29 -25
- dexcontrol/core/chassis.py +3 -12
- dexcontrol/core/component.py +68 -43
- dexcontrol/core/hand.py +50 -52
- dexcontrol/core/head.py +14 -26
- dexcontrol/core/misc.py +188 -166
- dexcontrol/core/robot_query_interface.py +137 -114
- dexcontrol/core/torso.py +0 -4
- dexcontrol/robot.py +15 -37
- 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 +8 -22
- {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.1.dist-info}/METADATA +2 -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 -369
- dexcontrol-0.3.0.dist-info/RECORD +0 -76
- {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.1.dist-info}/WHEEL +0 -0
- {dexcontrol-0.3.0.dist-info → dexcontrol-0.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,329 @@
|
|
|
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
|
+
"""Core subscriber utilities using DexComm's Raw API.
|
|
12
|
+
|
|
13
|
+
Simple factory functions for creating DexComm subscribers with
|
|
14
|
+
DexControl's default configurations.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Any, Callable, Optional
|
|
19
|
+
|
|
20
|
+
from dexcomm import BufferedSubscriber, Subscriber, wait_for_message
|
|
21
|
+
from dexcomm.serialization import deserialize_auto
|
|
22
|
+
from loguru import logger
|
|
23
|
+
|
|
24
|
+
from dexcontrol.utils.comm_helper import get_zenoh_config_path
|
|
25
|
+
from dexcontrol.utils.os_utils import resolve_key_name
|
|
26
|
+
|
|
27
|
+
# Import specialized deserializers
|
|
28
|
+
try:
|
|
29
|
+
from dexcomm.serialization import deserialize_depth, deserialize_image
|
|
30
|
+
|
|
31
|
+
IMAGE_DESERIALIZERS_AVAILABLE = True
|
|
32
|
+
except ImportError:
|
|
33
|
+
logger.debug("DexComm image deserializers not available")
|
|
34
|
+
deserialize_image = None
|
|
35
|
+
deserialize_depth = None
|
|
36
|
+
IMAGE_DESERIALIZERS_AVAILABLE = False
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
from dexcomm.serialization import deserialize_imu
|
|
40
|
+
|
|
41
|
+
IMU_DESERIALIZER_AVAILABLE = True
|
|
42
|
+
except ImportError:
|
|
43
|
+
logger.debug("DexComm IMU deserializer not available")
|
|
44
|
+
deserialize_imu = None
|
|
45
|
+
IMU_DESERIALIZER_AVAILABLE = False
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
from dexcomm.serialization.lidar import deserialize_lidar_2d
|
|
49
|
+
|
|
50
|
+
LIDAR_DESERIALIZER_AVAILABLE = True
|
|
51
|
+
except ImportError:
|
|
52
|
+
logger.debug("DexComm LiDAR deserializer not available")
|
|
53
|
+
deserialize_lidar_2d = None
|
|
54
|
+
LIDAR_DESERIALIZER_AVAILABLE = False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def create_subscriber(
|
|
58
|
+
topic: str,
|
|
59
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
60
|
+
buffer_size: int = 1,
|
|
61
|
+
deserializer: Optional[Callable[[bytes], Any]] = deserialize_auto,
|
|
62
|
+
config: Optional[Path] = None,
|
|
63
|
+
) -> Subscriber:
|
|
64
|
+
"""Create a DexComm Subscriber with DexControl defaults.
|
|
65
|
+
|
|
66
|
+
Simple wrapper that handles topic resolution and default configuration.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
topic: Topic to subscribe to (will be resolved with robot namespace)
|
|
70
|
+
callback: Optional callback function for incoming messages
|
|
71
|
+
buffer_size: Number of messages to buffer (default 1 for latest only)
|
|
72
|
+
deserializer: Deserialization function (default: auto-detect)
|
|
73
|
+
config: Optional config path (default: use robot's config)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
DexComm Subscriber instance
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
```python
|
|
80
|
+
# Simple subscription
|
|
81
|
+
sub = create_subscriber("sensors/imu")
|
|
82
|
+
data = sub.get_latest()
|
|
83
|
+
|
|
84
|
+
# With callback
|
|
85
|
+
sub = create_subscriber(
|
|
86
|
+
"robot/state",
|
|
87
|
+
callback=lambda msg: print(f"State: {msg}")
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
"""
|
|
91
|
+
full_topic = resolve_key_name(topic)
|
|
92
|
+
config_path = config or get_zenoh_config_path()
|
|
93
|
+
|
|
94
|
+
logger.debug(f"Creating subscriber for topic: {full_topic}")
|
|
95
|
+
|
|
96
|
+
return Subscriber(
|
|
97
|
+
topic=full_topic,
|
|
98
|
+
callback=callback,
|
|
99
|
+
deserializer=deserializer,
|
|
100
|
+
buffer_size=buffer_size,
|
|
101
|
+
config=config_path,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def create_buffered_subscriber(
|
|
106
|
+
topic: str,
|
|
107
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
108
|
+
buffer_size: int = 100,
|
|
109
|
+
deserializer: Optional[Callable[[bytes], Any]] = deserialize_auto,
|
|
110
|
+
config: Optional[Path] = None,
|
|
111
|
+
) -> BufferedSubscriber:
|
|
112
|
+
"""Create a DexComm BufferedSubscriber for data collection.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
topic: Topic to subscribe to
|
|
116
|
+
callback: Optional callback function
|
|
117
|
+
buffer_size: Maximum messages to buffer (default 100)
|
|
118
|
+
deserializer: Deserialization function
|
|
119
|
+
config: Optional config path
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
DexComm BufferedSubscriber instance
|
|
123
|
+
|
|
124
|
+
Example:
|
|
125
|
+
```python
|
|
126
|
+
# Collect sensor data
|
|
127
|
+
sub = create_buffered_subscriber("sensors/lidar", buffer_size=500)
|
|
128
|
+
# ... wait for data ...
|
|
129
|
+
all_scans = sub.get_buffer()
|
|
130
|
+
```
|
|
131
|
+
"""
|
|
132
|
+
full_topic = resolve_key_name(topic)
|
|
133
|
+
config_path = config or get_zenoh_config_path()
|
|
134
|
+
|
|
135
|
+
logger.debug(f"Creating buffered subscriber for topic: {full_topic}")
|
|
136
|
+
|
|
137
|
+
return BufferedSubscriber(
|
|
138
|
+
topic=full_topic,
|
|
139
|
+
callback=callback,
|
|
140
|
+
deserializer=deserializer,
|
|
141
|
+
buffer_size=buffer_size,
|
|
142
|
+
config=config_path,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def create_camera_subscriber(
|
|
147
|
+
topic: str,
|
|
148
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
149
|
+
buffer_size: int = 1,
|
|
150
|
+
) -> Subscriber:
|
|
151
|
+
"""Create a subscriber for RGB camera data.
|
|
152
|
+
|
|
153
|
+
Uses DexComm's image deserializer for efficient image decompression.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
topic: Camera topic
|
|
157
|
+
callback: Optional callback for images
|
|
158
|
+
buffer_size: Number of images to buffer
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Subscriber configured for RGB images
|
|
162
|
+
"""
|
|
163
|
+
deserializer = (
|
|
164
|
+
deserialize_image if IMAGE_DESERIALIZERS_AVAILABLE else deserialize_auto
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
return create_subscriber(
|
|
168
|
+
topic=topic,
|
|
169
|
+
callback=callback,
|
|
170
|
+
buffer_size=buffer_size,
|
|
171
|
+
deserializer=deserializer,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def create_depth_subscriber(
|
|
176
|
+
topic: str,
|
|
177
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
178
|
+
buffer_size: int = 1,
|
|
179
|
+
) -> Subscriber:
|
|
180
|
+
"""Create a subscriber for depth camera data.
|
|
181
|
+
|
|
182
|
+
Uses DexComm's depth deserializer for 16-bit depth images.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
topic: Depth camera topic
|
|
186
|
+
callback: Optional callback for depth images
|
|
187
|
+
buffer_size: Number of images to buffer
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
Subscriber configured for depth images
|
|
191
|
+
"""
|
|
192
|
+
deserializer = (
|
|
193
|
+
deserialize_depth if IMAGE_DESERIALIZERS_AVAILABLE else deserialize_auto
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
return create_subscriber(
|
|
197
|
+
topic=topic,
|
|
198
|
+
callback=callback,
|
|
199
|
+
buffer_size=buffer_size,
|
|
200
|
+
deserializer=deserializer,
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def create_imu_subscriber(
|
|
205
|
+
topic: str,
|
|
206
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
207
|
+
buffer_size: int = 1,
|
|
208
|
+
) -> Subscriber:
|
|
209
|
+
"""Create a subscriber for IMU data.
|
|
210
|
+
|
|
211
|
+
Uses DexComm's IMU deserializer for 6-DOF/9-DOF IMU data.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
topic: IMU topic
|
|
215
|
+
callback: Optional callback for IMU data
|
|
216
|
+
buffer_size: Number of samples to buffer
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
Subscriber configured for IMU data
|
|
220
|
+
"""
|
|
221
|
+
deserializer = deserialize_imu if IMU_DESERIALIZER_AVAILABLE else deserialize_auto
|
|
222
|
+
|
|
223
|
+
return create_subscriber(
|
|
224
|
+
topic=topic,
|
|
225
|
+
callback=callback,
|
|
226
|
+
buffer_size=buffer_size,
|
|
227
|
+
deserializer=deserializer,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def create_lidar_subscriber(
|
|
232
|
+
topic: str,
|
|
233
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
234
|
+
buffer_size: int = 1,
|
|
235
|
+
) -> Subscriber:
|
|
236
|
+
"""Create a subscriber for 2D LiDAR data.
|
|
237
|
+
|
|
238
|
+
Uses DexComm's LiDAR deserializer for laser scan data.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
topic: LiDAR topic
|
|
242
|
+
callback: Optional callback for scan data
|
|
243
|
+
buffer_size: Number of scans to buffer
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Subscriber configured for LiDAR data
|
|
247
|
+
"""
|
|
248
|
+
deserializer = (
|
|
249
|
+
deserialize_lidar_2d if LIDAR_DESERIALIZER_AVAILABLE else deserialize_auto
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
return create_subscriber(
|
|
253
|
+
topic=topic,
|
|
254
|
+
callback=callback,
|
|
255
|
+
buffer_size=buffer_size,
|
|
256
|
+
deserializer=deserializer,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def create_generic_subscriber(
|
|
261
|
+
topic: str,
|
|
262
|
+
callback: Optional[Callable[[Any], None]] = None,
|
|
263
|
+
buffer_size: int = 1,
|
|
264
|
+
raw_bytes: bool = False,
|
|
265
|
+
) -> Subscriber:
|
|
266
|
+
"""Create a generic subscriber with auto-detection or raw bytes.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
topic: Topic to subscribe to
|
|
270
|
+
callback: Optional callback
|
|
271
|
+
buffer_size: Number of messages to buffer
|
|
272
|
+
raw_bytes: If True, return raw bytes without deserialization
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
Generic subscriber instance
|
|
276
|
+
"""
|
|
277
|
+
deserializer = None if raw_bytes else deserialize_auto
|
|
278
|
+
|
|
279
|
+
return create_subscriber(
|
|
280
|
+
topic=topic,
|
|
281
|
+
callback=callback,
|
|
282
|
+
buffer_size=buffer_size,
|
|
283
|
+
deserializer=deserializer,
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def quick_subscribe(topic: str, callback: Callable[[Any], None]) -> Subscriber:
|
|
288
|
+
"""Quick one-liner to subscribe with a callback.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
topic: Topic to subscribe to
|
|
292
|
+
callback: Callback function
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Subscriber instance
|
|
296
|
+
|
|
297
|
+
Example:
|
|
298
|
+
```python
|
|
299
|
+
sub = quick_subscribe("robot/state", lambda msg: print(msg))
|
|
300
|
+
```
|
|
301
|
+
"""
|
|
302
|
+
return create_subscriber(topic, callback=callback)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def wait_for_any_message(topic: str, timeout: float = 5.0) -> Optional[Any]:
|
|
306
|
+
"""Wait for a message on a topic.
|
|
307
|
+
|
|
308
|
+
Convenience wrapper around DexComm's wait_for_message.
|
|
309
|
+
|
|
310
|
+
Args:
|
|
311
|
+
topic: Topic to wait on
|
|
312
|
+
timeout: Maximum wait time in seconds
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Received message or None if timeout
|
|
316
|
+
|
|
317
|
+
Example:
|
|
318
|
+
```python
|
|
319
|
+
state = wait_for_any_message("robot/state", timeout=2.0)
|
|
320
|
+
if state:
|
|
321
|
+
print(f"Robot is in state: {state}")
|
|
322
|
+
```
|
|
323
|
+
"""
|
|
324
|
+
full_topic = resolve_key_name(topic)
|
|
325
|
+
return wait_for_message(
|
|
326
|
+
topic=full_topic,
|
|
327
|
+
timeout=timeout,
|
|
328
|
+
config=get_zenoh_config_path(),
|
|
329
|
+
)
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
# 2. Commercial License
|
|
9
9
|
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
10
|
|
|
11
|
-
from .luxonis_camera import LuxonisCameraConfig
|
|
12
11
|
from .rgb_camera import RGBCameraConfig
|
|
13
12
|
from .zed_camera import ZedCameraConfig
|
|
14
13
|
|
|
15
|
-
__all__ = ["RGBCameraConfig", "ZedCameraConfig"
|
|
14
|
+
__all__ = ["RGBCameraConfig", "ZedCameraConfig"]
|
|
@@ -39,12 +39,12 @@ class ZedCameraConfig:
|
|
|
39
39
|
default_factory=lambda: {
|
|
40
40
|
"left_rgb": {
|
|
41
41
|
"enable": True,
|
|
42
|
-
"info_key": "camera/head/
|
|
42
|
+
"info_key": "camera/head/info", # Query the main camera info endpoint
|
|
43
43
|
"topic": "camera/head/left_rgb",
|
|
44
44
|
},
|
|
45
45
|
"right_rgb": {
|
|
46
46
|
"enable": True,
|
|
47
|
-
"info_key": "camera/head/
|
|
47
|
+
"info_key": "camera/head/info", # Query the main camera info endpoint
|
|
48
48
|
"topic": "camera/head/right_rgb",
|
|
49
49
|
},
|
|
50
50
|
"depth": {
|
|
@@ -18,7 +18,7 @@ LiDAR and ultrasonic sensors.
|
|
|
18
18
|
from collections.abc import Callable
|
|
19
19
|
from dataclasses import dataclass, field
|
|
20
20
|
|
|
21
|
-
from .cameras import
|
|
21
|
+
from .cameras import RGBCameraConfig, ZedCameraConfig
|
|
22
22
|
from .imu import ChassisIMUConfig, ZedIMUConfig
|
|
23
23
|
from .lidar import RPLidarConfig
|
|
24
24
|
from .ultrasonic import UltraSonicConfig
|
|
@@ -33,15 +33,17 @@ def _make_rgb_camera(name: str) -> Callable[[], RGBCameraConfig]:
|
|
|
33
33
|
Returns:
|
|
34
34
|
Factory function that creates an RGBCameraConfig
|
|
35
35
|
"""
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
return (
|
|
37
|
+
lambda: RGBCameraConfig(
|
|
38
|
+
subscriber_config=dict(
|
|
39
|
+
rgb=dict(
|
|
40
|
+
enable=True,
|
|
41
|
+
info_key=f"camera/base_{name}/info", # Matches dexsensor: camera/base_{name}/info
|
|
42
|
+
topic=f"camera/base_{name}/rgb",
|
|
43
|
+
)
|
|
44
|
+
),
|
|
45
|
+
name=f"base_{name}_camera",
|
|
46
|
+
)
|
|
45
47
|
)
|
|
46
48
|
|
|
47
49
|
|
|
@@ -58,12 +60,6 @@ class VegaSensorsConfig:
|
|
|
58
60
|
"""
|
|
59
61
|
|
|
60
62
|
head_camera: ZedCameraConfig = field(default_factory=ZedCameraConfig)
|
|
61
|
-
left_wrist_camera: LuxonisCameraConfig = field(
|
|
62
|
-
default_factory=lambda: LuxonisCameraConfig(name="left_wrist_camera")
|
|
63
|
-
)
|
|
64
|
-
right_wrist_camera: LuxonisCameraConfig = field(
|
|
65
|
-
default_factory=lambda: LuxonisCameraConfig(name="right_wrist_camera")
|
|
66
|
-
)
|
|
67
63
|
base_left_camera: RGBCameraConfig = field(default_factory=_make_rgb_camera("left"))
|
|
68
64
|
base_right_camera: RGBCameraConfig = field(
|
|
69
65
|
default_factory=_make_rgb_camera("right")
|
|
@@ -79,8 +75,6 @@ class VegaSensorsConfig:
|
|
|
79
75
|
|
|
80
76
|
def __post_init__(self) -> None:
|
|
81
77
|
self.head_camera.enable = False
|
|
82
|
-
self.left_wrist_camera.enable = False
|
|
83
|
-
self.right_wrist_camera.enable = False
|
|
84
78
|
self.base_left_camera.enable = False
|
|
85
79
|
self.base_right_camera.enable = False
|
|
86
80
|
self.base_front_camera.enable = False
|
dexcontrol/core/arm.py
CHANGED
|
@@ -15,18 +15,20 @@ communication and the ArmWrenchSensor class for reading wrench sensor data.
|
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
17
|
import time
|
|
18
|
-
from typing import Any,
|
|
18
|
+
from typing import Any, Literal
|
|
19
19
|
|
|
20
20
|
import numpy as np
|
|
21
|
-
import
|
|
21
|
+
from dexcomm import Publisher, call_service
|
|
22
|
+
from dexcomm.serialization import serialize_protobuf
|
|
23
|
+
from dexcomm.utils import RateLimiter
|
|
22
24
|
from jaxtyping import Float
|
|
23
25
|
from loguru import logger
|
|
24
26
|
|
|
25
27
|
from dexcontrol.config.core.arm import ArmConfig
|
|
26
28
|
from dexcontrol.core.component import RobotComponent, RobotJointComponent
|
|
27
29
|
from dexcontrol.proto import dexcontrol_msg_pb2, 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
|
-
from dexcontrol.utils.rate_limiter import RateLimiter
|
|
30
32
|
from dexcontrol.utils.trajectory_utils import generate_linear_trajectory
|
|
31
33
|
|
|
32
34
|
|
|
@@ -44,19 +46,16 @@ class Arm(RobotJointComponent):
|
|
|
44
46
|
def __init__(
|
|
45
47
|
self,
|
|
46
48
|
configs: ArmConfig,
|
|
47
|
-
zenoh_session: zenoh.Session,
|
|
48
49
|
) -> None:
|
|
49
50
|
"""Initialize the arm controller.
|
|
50
51
|
|
|
51
52
|
Args:
|
|
52
53
|
configs: Configuration parameters for the arm including communication topics.
|
|
53
|
-
zenoh_session: Active Zenoh communication session for message passing.
|
|
54
54
|
"""
|
|
55
55
|
super().__init__(
|
|
56
56
|
state_sub_topic=configs.state_sub_topic,
|
|
57
57
|
control_pub_topic=configs.control_pub_topic,
|
|
58
58
|
state_message_type=dexcontrol_msg_pb2.MotorStateWithCurrent,
|
|
59
|
-
zenoh_session=zenoh_session,
|
|
60
59
|
joint_name=configs.joint_name,
|
|
61
60
|
joint_limit=configs.joint_limit
|
|
62
61
|
if hasattr(configs, "joint_limit")
|
|
@@ -67,22 +66,20 @@ class Arm(RobotJointComponent):
|
|
|
67
66
|
pose_pool=configs.pose_pool,
|
|
68
67
|
)
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
)
|
|
69
|
+
# Note: Mode querier functionality will need to be updated to use DexComm
|
|
70
|
+
# For now, we'll store the query topic for later use
|
|
71
|
+
self._mode_query_topic = resolve_key_name(configs.set_mode_query)
|
|
73
72
|
|
|
74
73
|
# Initialize wrench sensor if configured
|
|
75
74
|
self.wrench_sensor: ArmWrenchSensor | None = None
|
|
76
75
|
if configs.wrench_sub_topic:
|
|
77
|
-
self.wrench_sensor = ArmWrenchSensor(
|
|
78
|
-
configs.wrench_sub_topic, zenoh_session
|
|
79
|
-
)
|
|
76
|
+
self.wrench_sensor = ArmWrenchSensor(configs.wrench_sub_topic)
|
|
80
77
|
|
|
81
|
-
# Initialize end effector pass through publisher
|
|
82
|
-
self._ee_pass_through_publisher
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
)
|
|
78
|
+
# Initialize end effector pass through publisher using DexComm
|
|
79
|
+
self._ee_pass_through_publisher = Publisher(
|
|
80
|
+
topic=resolve_key_name(configs.ee_pass_through_pub_topic),
|
|
81
|
+
serializer=serialize_protobuf,
|
|
82
|
+
config=get_zenoh_config_path(),
|
|
86
83
|
)
|
|
87
84
|
|
|
88
85
|
self._default_control_hz = configs.default_control_hz
|
|
@@ -137,7 +134,17 @@ class Arm(RobotJointComponent):
|
|
|
137
134
|
|
|
138
135
|
converted_modes = [mode_map[mode] for mode in modes]
|
|
139
136
|
query_msg = dexcontrol_query_pb2.SetArmMode(modes=converted_modes)
|
|
140
|
-
|
|
137
|
+
# Use DexComm's call_service for mode setting
|
|
138
|
+
from dexcontrol.utils.comm_helper import get_zenoh_config_path
|
|
139
|
+
|
|
140
|
+
call_service(
|
|
141
|
+
self._mode_query_topic,
|
|
142
|
+
request=query_msg,
|
|
143
|
+
timeout=0.05,
|
|
144
|
+
config=get_zenoh_config_path(),
|
|
145
|
+
request_serializer=lambda x: x.SerializeToString(),
|
|
146
|
+
response_deserializer=None,
|
|
147
|
+
)
|
|
141
148
|
|
|
142
149
|
def _send_position_command(self, joint_pos: np.ndarray) -> None:
|
|
143
150
|
"""Send joint position command.
|
|
@@ -318,8 +325,8 @@ class Arm(RobotJointComponent):
|
|
|
318
325
|
"""Cleans up all Zenoh resources."""
|
|
319
326
|
super().shutdown()
|
|
320
327
|
try:
|
|
321
|
-
|
|
322
|
-
|
|
328
|
+
# Mode querier cleanup no longer needed with DexComm
|
|
329
|
+
pass
|
|
323
330
|
except Exception as e:
|
|
324
331
|
# Don't log "Undeclared querier" errors as warnings - they're expected during shutdown
|
|
325
332
|
error_msg = str(e).lower()
|
|
@@ -338,8 +345,7 @@ class Arm(RobotJointComponent):
|
|
|
338
345
|
message: The message to send to the robot arm.
|
|
339
346
|
"""
|
|
340
347
|
control_msg = dexcontrol_msg_pb2.EndEffectorPassThroughCommand(data=message)
|
|
341
|
-
|
|
342
|
-
self._ee_pass_through_publisher.put(control_data)
|
|
348
|
+
self._ee_pass_through_publisher.publish(control_msg)
|
|
343
349
|
|
|
344
350
|
|
|
345
351
|
class ArmWrenchSensor(RobotComponent):
|
|
@@ -348,16 +354,14 @@ class ArmWrenchSensor(RobotComponent):
|
|
|
348
354
|
This class provides methods to read wrench sensor data through Zenoh communication.
|
|
349
355
|
"""
|
|
350
356
|
|
|
351
|
-
def __init__(self, state_sub_topic: str
|
|
357
|
+
def __init__(self, state_sub_topic: str) -> None:
|
|
352
358
|
"""Initialize the wrench sensor reader.
|
|
353
359
|
|
|
354
360
|
Args:
|
|
355
361
|
state_sub_topic: Topic to subscribe to for wrench sensor data.
|
|
356
|
-
zenoh_session: Active Zenoh communication session for message passing.
|
|
357
362
|
"""
|
|
358
363
|
super().__init__(
|
|
359
364
|
state_sub_topic=state_sub_topic,
|
|
360
|
-
zenoh_session=zenoh_session,
|
|
361
365
|
state_message_type=dexcontrol_msg_pb2.WrenchState,
|
|
362
366
|
)
|
|
363
367
|
|
dexcontrol/core/chassis.py
CHANGED
|
@@ -17,13 +17,12 @@ Zenoh communication. It handles steering and wheel velocity control.
|
|
|
17
17
|
import time
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
20
|
-
import
|
|
20
|
+
from dexcomm.utils import RateLimiter
|
|
21
21
|
from jaxtyping import Float
|
|
22
22
|
|
|
23
23
|
from dexcontrol.config.core import ChassisConfig
|
|
24
24
|
from dexcontrol.core.component import RobotJointComponent
|
|
25
25
|
from dexcontrol.proto import dexcontrol_msg_pb2
|
|
26
|
-
from dexcontrol.utils.rate_limiter import RateLimiter
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
class ChassisSteer(RobotJointComponent):
|
|
@@ -36,20 +35,17 @@ class ChassisSteer(RobotJointComponent):
|
|
|
36
35
|
def __init__(
|
|
37
36
|
self,
|
|
38
37
|
configs: ChassisConfig,
|
|
39
|
-
zenoh_session: zenoh.Session,
|
|
40
38
|
) -> None:
|
|
41
39
|
"""Initialize the hand controller.
|
|
42
40
|
|
|
43
41
|
Args:
|
|
44
42
|
configs: Hand configuration parameters containing communication topics
|
|
45
43
|
and predefined hand positions.
|
|
46
|
-
zenoh_session: Active Zenoh communication session for message passing.
|
|
47
44
|
"""
|
|
48
45
|
super().__init__(
|
|
49
46
|
state_sub_topic=configs.steer_state_sub_topic,
|
|
50
47
|
control_pub_topic=configs.steer_control_pub_topic,
|
|
51
48
|
state_message_type=dexcontrol_msg_pb2.MotorStateWithCurrent,
|
|
52
|
-
zenoh_session=zenoh_session,
|
|
53
49
|
joint_name=configs.steer_joint_name,
|
|
54
50
|
)
|
|
55
51
|
|
|
@@ -77,19 +73,16 @@ class ChassisDrive(RobotJointComponent):
|
|
|
77
73
|
def __init__(
|
|
78
74
|
self,
|
|
79
75
|
configs: ChassisConfig,
|
|
80
|
-
zenoh_session: zenoh.Session,
|
|
81
76
|
) -> None:
|
|
82
77
|
"""Initialize the base controller.
|
|
83
78
|
|
|
84
79
|
Args:
|
|
85
80
|
configs: Base configuration parameters containing communication topics.
|
|
86
|
-
zenoh_session: Active Zenoh communication session for message passing.
|
|
87
81
|
"""
|
|
88
82
|
super().__init__(
|
|
89
83
|
state_sub_topic=configs.drive_state_sub_topic,
|
|
90
84
|
control_pub_topic=configs.drive_control_pub_topic,
|
|
91
85
|
state_message_type=dexcontrol_msg_pb2.MotorStateWithCurrent,
|
|
92
|
-
zenoh_session=zenoh_session,
|
|
93
86
|
joint_name=configs.drive_joint_name,
|
|
94
87
|
)
|
|
95
88
|
|
|
@@ -126,16 +119,14 @@ class Chassis:
|
|
|
126
119
|
def __init__(
|
|
127
120
|
self,
|
|
128
121
|
configs: ChassisConfig,
|
|
129
|
-
zenoh_session: zenoh.Session,
|
|
130
122
|
) -> None:
|
|
131
123
|
"""Initialize the base controller.
|
|
132
124
|
|
|
133
125
|
Args:
|
|
134
126
|
configs: Base configuration parameters containing communication topics.
|
|
135
|
-
zenoh_session: Active Zenoh communication session for message passing.
|
|
136
127
|
"""
|
|
137
|
-
self.chassis_steer = ChassisSteer(configs
|
|
138
|
-
self.chassis_drive = ChassisDrive(configs
|
|
128
|
+
self.chassis_steer = ChassisSteer(configs)
|
|
129
|
+
self.chassis_drive = ChassisDrive(configs)
|
|
139
130
|
|
|
140
131
|
self.max_vel = configs.max_vel
|
|
141
132
|
self._center_to_wheel_axis_dist = configs.center_to_wheel_axis_dist
|