dexcontrol 0.2.12__py3-none-any.whl → 0.3.4__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.
- dexcontrol/__init__.py +17 -8
- dexcontrol/apps/dualsense_teleop_base.py +1 -1
- dexcontrol/comm/__init__.py +51 -0
- dexcontrol/comm/rtc.py +401 -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 +66 -42
- dexcontrol/core/chassis.py +142 -120
- dexcontrol/core/component.py +107 -58
- dexcontrol/core/hand.py +119 -86
- dexcontrol/core/head.py +22 -33
- dexcontrol/core/misc.py +331 -158
- dexcontrol/core/robot_query_interface.py +467 -0
- dexcontrol/core/torso.py +5 -9
- dexcontrol/robot.py +245 -574
- dexcontrol/sensors/__init__.py +1 -2
- dexcontrol/sensors/camera/__init__.py +0 -2
- dexcontrol/sensors/camera/base_camera.py +150 -0
- dexcontrol/sensors/camera/rgb_camera.py +68 -64
- dexcontrol/sensors/camera/zed_camera.py +140 -164
- dexcontrol/sensors/imu/chassis_imu.py +81 -62
- dexcontrol/sensors/imu/zed_imu.py +54 -43
- dexcontrol/sensors/lidar/rplidar.py +16 -20
- dexcontrol/sensors/manager.py +4 -14
- dexcontrol/sensors/ultrasonic.py +15 -28
- dexcontrol/utils/__init__.py +0 -11
- dexcontrol/utils/comm_helper.py +110 -0
- dexcontrol/utils/constants.py +1 -1
- dexcontrol/utils/error_code.py +2 -4
- dexcontrol/utils/os_utils.py +172 -4
- dexcontrol/utils/pb_utils.py +6 -28
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.4.dist-info}/METADATA +16 -3
- dexcontrol-0.3.4.dist-info/RECORD +62 -0
- {dexcontrol-0.2.12.dist-info → dexcontrol-0.3.4.dist-info}/WHEEL +1 -1
- dexcontrol/config/sensors/cameras/luxonis_camera.py +0 -51
- dexcontrol/proto/dexcontrol_msg_pb2.py +0 -73
- dexcontrol/proto/dexcontrol_msg_pb2.pyi +0 -220
- dexcontrol/proto/dexcontrol_query_pb2.py +0 -77
- dexcontrol/proto/dexcontrol_query_pb2.pyi +0 -162
- dexcontrol/sensors/camera/luxonis_camera.py +0 -169
- dexcontrol/utils/motion_utils.py +0 -199
- 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.4.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,23 +8,19 @@
|
|
|
8
8
|
# 2. Commercial License
|
|
9
9
|
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
10
|
|
|
11
|
-
"""
|
|
11
|
+
"""Chassis IMU sensor implementation using DexComm subscribers.
|
|
12
12
|
|
|
13
|
-
This module provides
|
|
14
|
-
|
|
13
|
+
This module provides IMU sensor class for chassis IMU data
|
|
14
|
+
using DexComm's Raw API.
|
|
15
15
|
"""
|
|
16
16
|
|
|
17
|
-
from typing import Literal, cast
|
|
18
|
-
|
|
19
17
|
import numpy as np
|
|
20
|
-
import zenoh
|
|
21
18
|
|
|
22
|
-
from dexcontrol.
|
|
23
|
-
from dexcontrol.utils.subscribers import ProtobufZenohSubscriber
|
|
19
|
+
from dexcontrol.comm import create_imu_subscriber
|
|
24
20
|
|
|
25
21
|
|
|
26
22
|
class ChassisIMUSensor:
|
|
27
|
-
"""Chassis IMU sensor using
|
|
23
|
+
"""Chassis IMU sensor using DexComm subscriber.
|
|
28
24
|
|
|
29
25
|
This sensor provides IMU data from the chassis
|
|
30
26
|
"""
|
|
@@ -32,41 +28,34 @@ class ChassisIMUSensor:
|
|
|
32
28
|
def __init__(
|
|
33
29
|
self,
|
|
34
30
|
configs,
|
|
35
|
-
zenoh_session: zenoh.Session,
|
|
36
31
|
) -> None:
|
|
37
|
-
"""Initialize the
|
|
32
|
+
"""Initialize the chassis IMU sensor.
|
|
38
33
|
|
|
39
34
|
Args:
|
|
40
|
-
configs: Configuration for the
|
|
41
|
-
zenoh_session: Active Zenoh session for communication.
|
|
35
|
+
configs: Configuration for the chassis IMU sensor.
|
|
42
36
|
"""
|
|
43
37
|
self._name = configs.name
|
|
44
38
|
|
|
45
|
-
# Create
|
|
46
|
-
self._subscriber =
|
|
39
|
+
# Create IMU subscriber using DexComm integration
|
|
40
|
+
self._subscriber = create_imu_subscriber(
|
|
47
41
|
topic=configs.topic,
|
|
48
|
-
zenoh_session=zenoh_session,
|
|
49
|
-
message_type=dexcontrol_msg_pb2.IMUState,
|
|
50
|
-
name=f"{self._name}_subscriber",
|
|
51
|
-
enable_fps_tracking=configs.enable_fps_tracking,
|
|
52
|
-
fps_log_interval=configs.fps_log_interval,
|
|
53
42
|
)
|
|
54
43
|
|
|
55
|
-
|
|
56
44
|
def shutdown(self) -> None:
|
|
57
|
-
"""Shutdown the
|
|
45
|
+
"""Shutdown the chassis IMU sensor."""
|
|
58
46
|
self._subscriber.shutdown()
|
|
59
47
|
|
|
60
48
|
def is_active(self) -> bool:
|
|
61
|
-
"""Check if the
|
|
49
|
+
"""Check if the chassis IMU sensor is actively receiving data.
|
|
62
50
|
|
|
63
51
|
Returns:
|
|
64
52
|
True if receiving data, False otherwise.
|
|
65
53
|
"""
|
|
66
|
-
|
|
54
|
+
data = self._subscriber.get_latest()
|
|
55
|
+
return data is not None
|
|
67
56
|
|
|
68
57
|
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
69
|
-
"""Wait for the
|
|
58
|
+
"""Wait for the chassis IMU sensor to start receiving data.
|
|
70
59
|
|
|
71
60
|
Args:
|
|
72
61
|
timeout: Maximum time to wait in seconds.
|
|
@@ -74,80 +63,110 @@ class ChassisIMUSensor:
|
|
|
74
63
|
Returns:
|
|
75
64
|
True if sensor becomes active, False if timeout is reached.
|
|
76
65
|
"""
|
|
77
|
-
|
|
66
|
+
msg = self._subscriber.wait_for_message(timeout)
|
|
67
|
+
return msg is not None
|
|
78
68
|
|
|
79
|
-
def get_obs(
|
|
80
|
-
|
|
69
|
+
def get_obs(
|
|
70
|
+
self, obs_keys: list[str] | None = None
|
|
71
|
+
) -> dict[str, np.ndarray] | None:
|
|
72
|
+
"""Get observation data for the chassis IMU sensor.
|
|
81
73
|
|
|
82
74
|
Args:
|
|
83
75
|
obs_keys: List of observation keys to retrieve. If None, returns all available data.
|
|
84
|
-
Valid keys: ['ang_vel', 'acc', 'quat']
|
|
76
|
+
Valid keys: ['ang_vel', 'acc', 'quat', 'mag', 'timestamp']
|
|
85
77
|
|
|
86
78
|
Returns:
|
|
87
79
|
Dictionary with observation data including all IMU measurements.
|
|
88
80
|
Keys are mapped as follows:
|
|
89
|
-
- 'ang_vel': Angular velocity from '
|
|
90
|
-
- 'acc': Linear acceleration from '
|
|
91
|
-
- 'quat': Orientation quaternion from '
|
|
81
|
+
- 'ang_vel': Angular velocity from 'gyro'
|
|
82
|
+
- 'acc': Linear acceleration from 'acc'
|
|
83
|
+
- 'quat': Orientation quaternion from 'quat' [w, x, y, z]
|
|
84
|
+
- 'mag': Magnetometer from 'mag' (if available)
|
|
92
85
|
- 'timestamp_ns': Timestamp in nanoseconds
|
|
93
86
|
"""
|
|
94
87
|
if obs_keys is None:
|
|
95
|
-
obs_keys = [
|
|
88
|
+
obs_keys = ["ang_vel", "acc", "quat"]
|
|
96
89
|
|
|
97
|
-
data = self._subscriber.
|
|
98
|
-
data = cast(dexcontrol_msg_pb2.IMUState, data)
|
|
90
|
+
data = self._subscriber.get_latest()
|
|
99
91
|
if data is None:
|
|
100
|
-
|
|
92
|
+
return None
|
|
101
93
|
|
|
102
94
|
obs_out = {}
|
|
103
95
|
|
|
96
|
+
# Add timestamp if available
|
|
97
|
+
if "timestamp" in data:
|
|
98
|
+
obs_out["timestamp_ns"] = data["timestamp"]
|
|
99
|
+
|
|
104
100
|
for key in obs_keys:
|
|
105
|
-
if key ==
|
|
106
|
-
obs_out[key] =
|
|
107
|
-
elif key ==
|
|
108
|
-
obs_out[key] =
|
|
109
|
-
elif key ==
|
|
110
|
-
obs_out[key] = np.array([
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
obs_out['timestamp_ns'] = data.timestamp_ns
|
|
101
|
+
if key == "ang_vel":
|
|
102
|
+
obs_out[key] = data.get("gyro", np.zeros(3))
|
|
103
|
+
elif key == "acc":
|
|
104
|
+
obs_out[key] = data.get("acc", np.zeros(3))
|
|
105
|
+
elif key == "quat":
|
|
106
|
+
obs_out[key] = data.get("quat", np.array([1.0, 0.0, 0.0, 0.0]))
|
|
107
|
+
elif key == "mag" and "mag" in data:
|
|
108
|
+
obs_out[key] = data["mag"]
|
|
109
|
+
elif key == "timestamp" and "timestamp" in data:
|
|
110
|
+
obs_out["timestamp_ns"] = data["timestamp"]
|
|
116
111
|
|
|
117
112
|
return obs_out
|
|
118
113
|
|
|
119
|
-
def
|
|
120
|
-
"""Get the latest linear acceleration from
|
|
114
|
+
def get_acc(self) -> np.ndarray | None:
|
|
115
|
+
"""Get the latest linear acceleration from chassis IMU.
|
|
121
116
|
|
|
122
117
|
Returns:
|
|
123
118
|
Linear acceleration [x, y, z] in m/s² if available, None otherwise.
|
|
124
119
|
"""
|
|
125
|
-
|
|
120
|
+
data = self._subscriber.get_latest()
|
|
121
|
+
return data.get("acc") if data else None
|
|
126
122
|
|
|
127
|
-
def
|
|
128
|
-
"""Get the latest angular velocity from
|
|
123
|
+
def get_gyro(self) -> np.ndarray | None:
|
|
124
|
+
"""Get the latest angular velocity from chassis IMU.
|
|
129
125
|
|
|
130
126
|
Returns:
|
|
131
127
|
Angular velocity [x, y, z] in rad/s if available, None otherwise.
|
|
132
128
|
"""
|
|
133
|
-
|
|
129
|
+
data = self._subscriber.get_latest()
|
|
130
|
+
return data.get("gyro") if data else None
|
|
134
131
|
|
|
135
|
-
def
|
|
136
|
-
"""Get the latest orientation quaternion from
|
|
132
|
+
def get_quat(self) -> np.ndarray | None:
|
|
133
|
+
"""Get the latest orientation quaternion from chassis IMU.
|
|
137
134
|
|
|
138
135
|
Returns:
|
|
139
|
-
Orientation quaternion [x, y, z
|
|
136
|
+
Orientation quaternion [w, x, y, z] if available, None otherwise.
|
|
137
|
+
Note: dexcomm uses [w, x, y, z] quaternion format.
|
|
140
138
|
"""
|
|
141
|
-
|
|
139
|
+
data = self._subscriber.get_latest()
|
|
140
|
+
return data.get("quat") if data else None
|
|
142
141
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
def get_mag(self) -> np.ndarray | None:
|
|
143
|
+
"""Get the latest magnetometer reading from chassis IMU.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Magnetic field [x, y, z] in µT if available, None otherwise.
|
|
147
|
+
"""
|
|
148
|
+
data = self._subscriber.get_latest()
|
|
149
|
+
if not data or not isinstance(data, dict):
|
|
150
|
+
return None
|
|
151
|
+
return data.get("mag", None)
|
|
152
|
+
|
|
153
|
+
def has_mag(self) -> bool:
|
|
154
|
+
"""Check if the chassis IMU has magnetometer data available.
|
|
146
155
|
|
|
147
156
|
Returns:
|
|
148
|
-
|
|
157
|
+
True if magnetometer data is available, False otherwise.
|
|
149
158
|
"""
|
|
150
|
-
|
|
159
|
+
data = self._subscriber.get_latest()
|
|
160
|
+
if not data or not isinstance(data, dict):
|
|
161
|
+
return False
|
|
162
|
+
return "mag" in data and data["mag"] is not None
|
|
163
|
+
|
|
164
|
+
# Backward compatibility aliases
|
|
165
|
+
get_acceleration = get_acc
|
|
166
|
+
get_angular_velocity = get_gyro
|
|
167
|
+
get_orientation = get_quat
|
|
168
|
+
get_magnetometer = get_mag
|
|
169
|
+
has_magnetometer = has_mag
|
|
151
170
|
|
|
152
171
|
@property
|
|
153
172
|
def name(self) -> str:
|
|
@@ -11,9 +11,8 @@
|
|
|
11
11
|
"""ZED IMU sensor implementation using Zenoh subscriber."""
|
|
12
12
|
|
|
13
13
|
import numpy as np
|
|
14
|
-
import zenoh
|
|
15
14
|
|
|
16
|
-
from dexcontrol.
|
|
15
|
+
from dexcontrol.comm import create_imu_subscriber
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class ZedIMUSensor:
|
|
@@ -28,23 +27,17 @@ class ZedIMUSensor:
|
|
|
28
27
|
def __init__(
|
|
29
28
|
self,
|
|
30
29
|
configs,
|
|
31
|
-
zenoh_session: zenoh.Session,
|
|
32
30
|
) -> None:
|
|
33
31
|
"""Initialize the ZED IMU sensor.
|
|
34
32
|
|
|
35
33
|
Args:
|
|
36
34
|
configs: Configuration object containing topic, name, and other settings.
|
|
37
|
-
zenoh_session: Active Zenoh session for communication.
|
|
38
35
|
"""
|
|
39
36
|
self._name = configs.name
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
self._subscriber =
|
|
38
|
+
|
|
39
|
+
self._subscriber = create_imu_subscriber(
|
|
43
40
|
topic=configs.topic,
|
|
44
|
-
zenoh_session=zenoh_session,
|
|
45
|
-
name=f"{self._name}_subscriber",
|
|
46
|
-
enable_fps_tracking=configs.enable_fps_tracking,
|
|
47
|
-
fps_log_interval=configs.fps_log_interval,
|
|
48
41
|
)
|
|
49
42
|
|
|
50
43
|
def shutdown(self) -> None:
|
|
@@ -57,7 +50,8 @@ class ZedIMUSensor:
|
|
|
57
50
|
Returns:
|
|
58
51
|
True if receiving data, False otherwise.
|
|
59
52
|
"""
|
|
60
|
-
|
|
53
|
+
data = self._subscriber.get_latest()
|
|
54
|
+
return data is not None
|
|
61
55
|
|
|
62
56
|
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
63
57
|
"""Wait for the ZED IMU sensor to start receiving data.
|
|
@@ -68,92 +62,109 @@ class ZedIMUSensor:
|
|
|
68
62
|
Returns:
|
|
69
63
|
True if sensor becomes active, False if timeout is reached.
|
|
70
64
|
"""
|
|
71
|
-
|
|
65
|
+
msg = self._subscriber.wait_for_message(timeout)
|
|
66
|
+
return msg is not None
|
|
72
67
|
|
|
73
68
|
def get_obs(self, obs_keys: list[str] | None = None) -> dict[str, np.ndarray] | None:
|
|
74
69
|
"""Get observation data for the ZED IMU sensor.
|
|
75
70
|
|
|
76
71
|
Args:
|
|
77
72
|
obs_keys: List of observation keys to retrieve. If None, returns all available data.
|
|
78
|
-
Valid keys: ['ang_vel', 'acc', 'quat', 'timestamp']
|
|
73
|
+
Valid keys: ['ang_vel', 'acc', 'quat', 'mag', 'timestamp']
|
|
79
74
|
|
|
80
75
|
Returns:
|
|
81
76
|
Dictionary with observation data including all IMU measurements.
|
|
82
77
|
Keys are mapped as follows:
|
|
83
|
-
- 'ang_vel': Angular velocity from '
|
|
84
|
-
- 'acc': Linear acceleration from '
|
|
85
|
-
- 'quat': Orientation quaternion from '
|
|
78
|
+
- 'ang_vel': Angular velocity from 'gyro'
|
|
79
|
+
- 'acc': Linear acceleration from 'acc'
|
|
80
|
+
- 'quat': Orientation quaternion from 'quat'
|
|
81
|
+
- 'mag': Magnetometer from 'mag' (if available)
|
|
86
82
|
- 'timestamp_ns': Timestamp from 'timestamp'
|
|
87
83
|
"""
|
|
88
84
|
if obs_keys is None:
|
|
89
85
|
obs_keys = ['ang_vel', 'acc', 'quat']
|
|
90
86
|
|
|
91
|
-
data = self._subscriber.
|
|
87
|
+
data = self._subscriber.get_latest()
|
|
92
88
|
if data is None:
|
|
93
89
|
return None
|
|
94
90
|
|
|
95
|
-
obs_out = {
|
|
91
|
+
obs_out = {}
|
|
92
|
+
|
|
93
|
+
# Add timestamp if available
|
|
94
|
+
if 'timestamp' in data:
|
|
95
|
+
obs_out['timestamp_ns'] = data['timestamp']
|
|
96
96
|
|
|
97
97
|
for key in obs_keys:
|
|
98
98
|
if key == 'ang_vel':
|
|
99
|
-
obs_out[key] = data
|
|
99
|
+
obs_out[key] = data.get('gyro', np.zeros(3))
|
|
100
100
|
elif key == 'acc':
|
|
101
|
-
obs_out[key] = data
|
|
101
|
+
obs_out[key] = data.get('acc', np.zeros(3))
|
|
102
102
|
elif key == 'quat':
|
|
103
|
-
obs_out[key] = data
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
obs_out[key] = data.get('quat', np.array([1.0, 0.0, 0.0, 0.0]))
|
|
104
|
+
elif key == 'mag' and 'mag' in data:
|
|
105
|
+
obs_out[key] = data['mag']
|
|
106
|
+
elif key == 'timestamp' and 'timestamp' in data:
|
|
107
|
+
obs_out['timestamp_ns'] = data['timestamp']
|
|
106
108
|
|
|
107
109
|
return obs_out
|
|
108
110
|
|
|
109
|
-
def
|
|
111
|
+
def get_acc(self) -> np.ndarray | None:
|
|
110
112
|
"""Get the latest linear acceleration from ZED IMU.
|
|
111
113
|
|
|
112
114
|
Returns:
|
|
113
115
|
Linear acceleration [x, y, z] in m/s² if available, None otherwise.
|
|
114
116
|
"""
|
|
115
|
-
|
|
117
|
+
data = self._subscriber.get_latest()
|
|
118
|
+
return data.get('acc') if data else None
|
|
116
119
|
|
|
117
|
-
def
|
|
120
|
+
def get_gyro(self) -> np.ndarray | None:
|
|
118
121
|
"""Get the latest angular velocity from ZED IMU.
|
|
119
122
|
|
|
120
123
|
Returns:
|
|
121
124
|
Angular velocity [x, y, z] in rad/s if available, None otherwise.
|
|
122
125
|
"""
|
|
123
|
-
|
|
126
|
+
data = self._subscriber.get_latest()
|
|
127
|
+
return data.get('gyro') if data else None
|
|
124
128
|
|
|
125
|
-
def
|
|
129
|
+
def get_quat(self) -> np.ndarray | None:
|
|
126
130
|
"""Get the latest orientation quaternion from ZED IMU.
|
|
127
131
|
|
|
128
132
|
Returns:
|
|
129
|
-
Orientation quaternion [x, y, z
|
|
133
|
+
Orientation quaternion [w, x, y, z] if available, None otherwise.
|
|
134
|
+
Note: dexcomm uses [w, x, y, z] quaternion format.
|
|
130
135
|
"""
|
|
131
|
-
|
|
136
|
+
data = self._subscriber.get_latest()
|
|
137
|
+
return data.get('quat') if data else None
|
|
132
138
|
|
|
133
|
-
def
|
|
139
|
+
def get_mag(self) -> np.ndarray | None:
|
|
134
140
|
"""Get the latest magnetometer reading from ZED IMU.
|
|
135
141
|
|
|
136
142
|
Returns:
|
|
137
143
|
Magnetic field [x, y, z] in µT if available, None otherwise.
|
|
138
144
|
"""
|
|
139
|
-
|
|
145
|
+
data = self._subscriber.get_latest()
|
|
146
|
+
if not data or not isinstance(data, dict):
|
|
147
|
+
return None
|
|
148
|
+
return data.get('mag', None)
|
|
140
149
|
|
|
141
|
-
def
|
|
150
|
+
def has_mag(self) -> bool:
|
|
142
151
|
"""Check if the ZED IMU has magnetometer data available.
|
|
143
152
|
|
|
144
153
|
Returns:
|
|
145
154
|
True if magnetometer data is available, False otherwise.
|
|
146
155
|
"""
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
156
|
+
data = self._subscriber.get_latest()
|
|
157
|
+
if not data or not isinstance(data, dict):
|
|
158
|
+
return False
|
|
159
|
+
return 'mag' in data and data['mag'] is not None
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# Backward compatibility aliases
|
|
163
|
+
get_acceleration = get_acc
|
|
164
|
+
get_angular_velocity = get_gyro
|
|
165
|
+
get_orientation = get_quat
|
|
166
|
+
get_magnetometer = get_mag
|
|
167
|
+
has_magnetometer = has_mag
|
|
157
168
|
|
|
158
169
|
@property
|
|
159
170
|
def name(self) -> str:
|
|
@@ -17,9 +17,8 @@ subscriber for scan data.
|
|
|
17
17
|
from typing import Any
|
|
18
18
|
|
|
19
19
|
import numpy as np
|
|
20
|
-
import zenoh
|
|
21
20
|
|
|
22
|
-
from dexcontrol.
|
|
21
|
+
from dexcontrol.comm import create_lidar_subscriber
|
|
23
22
|
|
|
24
23
|
|
|
25
24
|
class RPLidarSensor:
|
|
@@ -32,26 +31,17 @@ class RPLidarSensor:
|
|
|
32
31
|
def __init__(
|
|
33
32
|
self,
|
|
34
33
|
configs,
|
|
35
|
-
zenoh_session: zenoh.Session,
|
|
36
34
|
) -> None:
|
|
37
35
|
"""Initialize the LIDAR sensor.
|
|
38
36
|
|
|
39
37
|
Args:
|
|
40
|
-
|
|
41
|
-
zenoh_session: Active Zenoh session for communication.
|
|
42
|
-
name: Name for the sensor instance.
|
|
43
|
-
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
44
|
-
fps_log_interval: Number of frames between FPS calculations.
|
|
38
|
+
configs: Configuration for the LIDAR sensor.
|
|
45
39
|
"""
|
|
46
40
|
self._name = configs.name
|
|
47
41
|
|
|
48
42
|
# Create the LIDAR subscriber
|
|
49
|
-
self._subscriber =
|
|
50
|
-
topic=configs.topic
|
|
51
|
-
zenoh_session=zenoh_session,
|
|
52
|
-
name=f"{self._name}_subscriber",
|
|
53
|
-
enable_fps_tracking=configs.enable_fps_tracking,
|
|
54
|
-
fps_log_interval=configs.fps_log_interval,
|
|
43
|
+
self._subscriber = create_lidar_subscriber(
|
|
44
|
+
topic=configs.topic
|
|
55
45
|
)
|
|
56
46
|
|
|
57
47
|
|
|
@@ -65,7 +55,8 @@ class RPLidarSensor:
|
|
|
65
55
|
Returns:
|
|
66
56
|
True if receiving data, False otherwise.
|
|
67
57
|
"""
|
|
68
|
-
|
|
58
|
+
data = self._subscriber.get_latest()
|
|
59
|
+
return data is not None
|
|
69
60
|
|
|
70
61
|
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
71
62
|
"""Wait for the LIDAR sensor to start receiving data.
|
|
@@ -76,7 +67,8 @@ class RPLidarSensor:
|
|
|
76
67
|
Returns:
|
|
77
68
|
True if sensor becomes active, False if timeout is reached.
|
|
78
69
|
"""
|
|
79
|
-
|
|
70
|
+
msg = self._subscriber.wait_for_message(timeout)
|
|
71
|
+
return msg is not None
|
|
80
72
|
|
|
81
73
|
def get_obs(self) -> dict[str, Any] | None:
|
|
82
74
|
"""Get the latest LIDAR scan data.
|
|
@@ -89,7 +81,8 @@ class RPLidarSensor:
|
|
|
89
81
|
- qualities: Array of quality values (0-255) if available, None otherwise
|
|
90
82
|
- timestamp: Timestamp in nanoseconds (int)
|
|
91
83
|
"""
|
|
92
|
-
|
|
84
|
+
data = self._subscriber.get_latest()
|
|
85
|
+
return data
|
|
93
86
|
|
|
94
87
|
def get_ranges(self) -> np.ndarray | None:
|
|
95
88
|
"""Get the latest range measurements.
|
|
@@ -97,7 +90,8 @@ class RPLidarSensor:
|
|
|
97
90
|
Returns:
|
|
98
91
|
Array of range measurements in meters if available, None otherwise.
|
|
99
92
|
"""
|
|
100
|
-
|
|
93
|
+
data = self._subscriber.get_latest()
|
|
94
|
+
return data.ranges if data else None
|
|
101
95
|
|
|
102
96
|
def get_angles(self) -> np.ndarray | None:
|
|
103
97
|
"""Get the latest angle measurements.
|
|
@@ -105,7 +99,8 @@ class RPLidarSensor:
|
|
|
105
99
|
Returns:
|
|
106
100
|
Array of angle measurements in radians if available, None otherwise.
|
|
107
101
|
"""
|
|
108
|
-
|
|
102
|
+
data = self._subscriber.get_latest()
|
|
103
|
+
return data.angles if data else None
|
|
109
104
|
|
|
110
105
|
def get_qualities(self) -> np.ndarray | None:
|
|
111
106
|
"""Get the latest quality measurements.
|
|
@@ -113,7 +108,8 @@ class RPLidarSensor:
|
|
|
113
108
|
Returns:
|
|
114
109
|
Array of quality values (0-255) if available, None otherwise.
|
|
115
110
|
"""
|
|
116
|
-
|
|
111
|
+
data = self._subscriber.get_latest()
|
|
112
|
+
return data.qualities if data else None
|
|
117
113
|
|
|
118
114
|
def get_point_count(self) -> int:
|
|
119
115
|
"""Get the number of points in the latest scan.
|
dexcontrol/sensors/manager.py
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
# 2. Commercial License
|
|
9
9
|
# For commercial licensing terms, contact: contact@dexmate.ai
|
|
10
10
|
|
|
11
|
-
"""Sensor manager for managing robot sensors
|
|
11
|
+
"""Sensor manager for managing robot sensors.
|
|
12
12
|
|
|
13
13
|
This module provides the Sensors class that manages multiple sensor instances
|
|
14
14
|
based on configuration and provides unified access to them.
|
|
@@ -19,14 +19,12 @@ import traceback
|
|
|
19
19
|
from typing import TYPE_CHECKING, Any
|
|
20
20
|
|
|
21
21
|
import hydra.utils as hydra_utils
|
|
22
|
-
import zenoh
|
|
23
22
|
from loguru import logger
|
|
24
23
|
from omegaconf import DictConfig, OmegaConf
|
|
25
24
|
|
|
26
25
|
from dexcontrol.config.sensors.vega_sensors import VegaSensorsConfig
|
|
27
26
|
|
|
28
27
|
if TYPE_CHECKING:
|
|
29
|
-
from dexcontrol.sensors.camera.luxonis_camera import LuxonisCameraSensor
|
|
30
28
|
from dexcontrol.sensors.camera.rgb_camera import RGBCameraSensor
|
|
31
29
|
from dexcontrol.sensors.camera.zed_camera import ZedCameraSensor
|
|
32
30
|
from dexcontrol.sensors.imu.chassis_imu import ChassisIMUSensor
|
|
@@ -44,14 +42,11 @@ class Sensors:
|
|
|
44
42
|
|
|
45
43
|
Attributes:
|
|
46
44
|
_sensors: List of instantiated sensor objects.
|
|
47
|
-
_zenoh_session: Active Zenoh session for communication.
|
|
48
45
|
"""
|
|
49
46
|
|
|
50
47
|
if TYPE_CHECKING:
|
|
51
48
|
# Type annotations for dynamically created sensor attributes
|
|
52
49
|
head_camera: ZedCameraSensor
|
|
53
|
-
left_wrist_camera: LuxonisCameraSensor
|
|
54
|
-
right_wrist_camera: LuxonisCameraSensor
|
|
55
50
|
base_left_camera: RGBCameraSensor
|
|
56
51
|
base_right_camera: RGBCameraSensor
|
|
57
52
|
base_front_camera: RGBCameraSensor
|
|
@@ -61,16 +56,13 @@ class Sensors:
|
|
|
61
56
|
lidar: RPLidarSensor
|
|
62
57
|
ultrasonic: UltrasonicSensor
|
|
63
58
|
|
|
64
|
-
def __init__(self, configs: DictConfig | VegaSensorsConfig
|
|
65
|
-
zenoh_session: zenoh.Session) -> None:
|
|
59
|
+
def __init__(self, configs: DictConfig | VegaSensorsConfig) -> None:
|
|
66
60
|
"""Initialize sensors from configuration.
|
|
67
61
|
|
|
68
62
|
Args:
|
|
69
63
|
configs: Configuration for all sensors (VegaSensorsConfig or DictConfig).
|
|
70
|
-
zenoh_session: Active Zenoh session for communication.
|
|
71
64
|
"""
|
|
72
65
|
self._sensors: list[Any] = []
|
|
73
|
-
self._zenoh_session = zenoh_session
|
|
74
66
|
|
|
75
67
|
dict_configs = (OmegaConf.structured(configs) if isinstance(configs, VegaSensorsConfig)
|
|
76
68
|
else configs)
|
|
@@ -108,10 +100,8 @@ class Sensors:
|
|
|
108
100
|
'configs': {k: v for k, v in config.items() if k != '_target_'}
|
|
109
101
|
})
|
|
110
102
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
zenoh_session=self._zenoh_session,
|
|
114
|
-
)
|
|
103
|
+
# Note: zenoh_session no longer needed as DexComm handles sessions
|
|
104
|
+
sensor = hydra_utils.instantiate(temp_config)
|
|
115
105
|
|
|
116
106
|
if hasattr(sensor, "start"):
|
|
117
107
|
sensor.start()
|