dexcontrol 0.2.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 +45 -0
- dexcontrol/apps/dualsense_teleop_base.py +371 -0
- dexcontrol/config/__init__.py +14 -0
- dexcontrol/config/core/__init__.py +22 -0
- dexcontrol/config/core/arm.py +32 -0
- dexcontrol/config/core/chassis.py +22 -0
- dexcontrol/config/core/hand.py +42 -0
- dexcontrol/config/core/head.py +33 -0
- dexcontrol/config/core/misc.py +37 -0
- dexcontrol/config/core/torso.py +36 -0
- dexcontrol/config/sensors/__init__.py +4 -0
- dexcontrol/config/sensors/cameras/__init__.py +7 -0
- dexcontrol/config/sensors/cameras/gemini_camera.py +16 -0
- dexcontrol/config/sensors/cameras/rgb_camera.py +15 -0
- dexcontrol/config/sensors/imu/__init__.py +6 -0
- dexcontrol/config/sensors/imu/gemini_imu.py +15 -0
- dexcontrol/config/sensors/imu/nine_axis_imu.py +15 -0
- dexcontrol/config/sensors/lidar/__init__.py +6 -0
- dexcontrol/config/sensors/lidar/rplidar.py +15 -0
- dexcontrol/config/sensors/ultrasonic/__init__.py +6 -0
- dexcontrol/config/sensors/ultrasonic/ultrasonic.py +15 -0
- dexcontrol/config/sensors/vega_sensors.py +65 -0
- dexcontrol/config/vega.py +203 -0
- dexcontrol/core/__init__.py +0 -0
- dexcontrol/core/arm.py +324 -0
- dexcontrol/core/chassis.py +628 -0
- dexcontrol/core/component.py +834 -0
- dexcontrol/core/hand.py +170 -0
- dexcontrol/core/head.py +232 -0
- dexcontrol/core/misc.py +514 -0
- dexcontrol/core/torso.py +198 -0
- dexcontrol/proto/dexcontrol_msg_pb2.py +69 -0
- dexcontrol/proto/dexcontrol_msg_pb2.pyi +168 -0
- dexcontrol/proto/dexcontrol_query_pb2.py +73 -0
- dexcontrol/proto/dexcontrol_query_pb2.pyi +134 -0
- dexcontrol/robot.py +1091 -0
- dexcontrol/sensors/__init__.py +40 -0
- dexcontrol/sensors/camera/__init__.py +18 -0
- dexcontrol/sensors/camera/gemini_camera.py +139 -0
- dexcontrol/sensors/camera/rgb_camera.py +98 -0
- dexcontrol/sensors/imu/__init__.py +22 -0
- dexcontrol/sensors/imu/gemini_imu.py +139 -0
- dexcontrol/sensors/imu/nine_axis_imu.py +149 -0
- dexcontrol/sensors/lidar/__init__.py +3 -0
- dexcontrol/sensors/lidar/rplidar.py +164 -0
- dexcontrol/sensors/manager.py +185 -0
- dexcontrol/sensors/ultrasonic.py +110 -0
- dexcontrol/utils/__init__.py +15 -0
- dexcontrol/utils/constants.py +12 -0
- dexcontrol/utils/io_utils.py +26 -0
- dexcontrol/utils/motion_utils.py +194 -0
- dexcontrol/utils/os_utils.py +39 -0
- dexcontrol/utils/pb_utils.py +103 -0
- dexcontrol/utils/rate_limiter.py +167 -0
- dexcontrol/utils/reset_orbbec_camera_usb.py +98 -0
- dexcontrol/utils/subscribers/__init__.py +44 -0
- dexcontrol/utils/subscribers/base.py +260 -0
- dexcontrol/utils/subscribers/camera.py +328 -0
- dexcontrol/utils/subscribers/decoders.py +83 -0
- dexcontrol/utils/subscribers/generic.py +105 -0
- dexcontrol/utils/subscribers/imu.py +170 -0
- dexcontrol/utils/subscribers/lidar.py +195 -0
- dexcontrol/utils/subscribers/protobuf.py +106 -0
- dexcontrol/utils/timer.py +136 -0
- dexcontrol/utils/trajectory_utils.py +40 -0
- dexcontrol/utils/viz_utils.py +86 -0
- dexcontrol-0.2.1.dist-info/METADATA +369 -0
- dexcontrol-0.2.1.dist-info/RECORD +72 -0
- dexcontrol-0.2.1.dist-info/WHEEL +5 -0
- dexcontrol-0.2.1.dist-info/licenses/LICENSE +188 -0
- dexcontrol-0.2.1.dist-info/licenses/NOTICE +13 -0
- dexcontrol-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""Sensor implementations for dexcontrol.
|
|
7
|
+
|
|
8
|
+
This module provides sensor classes for various robotic sensors
|
|
9
|
+
using Zenoh subscribers for data communication.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
# Import camera sensors
|
|
13
|
+
from .camera import GeminiCameraSensor, RGBCameraSensor
|
|
14
|
+
|
|
15
|
+
# Import IMU sensors
|
|
16
|
+
from .imu import GeminiIMUSensor, NineAxisIMUSensor
|
|
17
|
+
|
|
18
|
+
# Import other sensors
|
|
19
|
+
from .lidar import RPLidarSensor
|
|
20
|
+
|
|
21
|
+
# Import sensor manager
|
|
22
|
+
from .manager import Sensors
|
|
23
|
+
from .ultrasonic import UltrasonicSensor
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# Camera sensors
|
|
27
|
+
"RGBCameraSensor",
|
|
28
|
+
"GeminiCameraSensor",
|
|
29
|
+
|
|
30
|
+
# IMU sensors
|
|
31
|
+
"NineAxisIMUSensor",
|
|
32
|
+
"GeminiIMUSensor",
|
|
33
|
+
|
|
34
|
+
# Other sensors
|
|
35
|
+
"RPLidarSensor",
|
|
36
|
+
"UltrasonicSensor",
|
|
37
|
+
|
|
38
|
+
# Sensor manager
|
|
39
|
+
"Sensors",
|
|
40
|
+
]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""Camera sensor implementations using Zenoh subscribers.
|
|
7
|
+
|
|
8
|
+
This module provides camera sensor classes that use the specialized camera
|
|
9
|
+
subscribers for RGB and RGBD Gemini camera data, matching the dexsensor structure.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from .gemini_camera import GeminiCameraSensor
|
|
13
|
+
from .rgb_camera import RGBCameraSensor
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"RGBCameraSensor",
|
|
17
|
+
"GeminiCameraSensor",
|
|
18
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""Gemini RGBD camera sensor implementation using Zenoh subscriber."""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import zenoh
|
|
10
|
+
|
|
11
|
+
from dexcontrol.utils.subscribers.camera import RGBDCameraSubscriber
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GeminiCameraSensor:
|
|
15
|
+
"""Gemini RGBD camera sensor using Zenoh subscriber.
|
|
16
|
+
|
|
17
|
+
This sensor provides both RGB and depth image data from an Orbbec Gemini camera
|
|
18
|
+
using the RGBDCameraSubscriber for efficient data handling with lazy decoding.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
configs,
|
|
24
|
+
zenoh_session: zenoh.Session,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Initialize the Gemini RGBD camera sensor.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
rgb_topic: Zenoh topic for RGB data.
|
|
30
|
+
depth_topic: Zenoh topic for depth data.
|
|
31
|
+
zenoh_session: Active Zenoh session for communication.
|
|
32
|
+
name: Name for the sensor instance.
|
|
33
|
+
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
34
|
+
fps_log_interval: Number of frames between FPS calculations.
|
|
35
|
+
"""
|
|
36
|
+
self._name = configs.name
|
|
37
|
+
|
|
38
|
+
# Create the RGBD camera subscriber
|
|
39
|
+
self._subscriber = RGBDCameraSubscriber(
|
|
40
|
+
rgb_topic=configs.rgb_topic,
|
|
41
|
+
depth_topic=configs.depth_topic,
|
|
42
|
+
zenoh_session=zenoh_session,
|
|
43
|
+
name=f"{self._name}_subscriber",
|
|
44
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
45
|
+
fps_log_interval=configs.fps_log_interval,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def shutdown(self) -> None:
|
|
49
|
+
"""Shutdown the camera sensor."""
|
|
50
|
+
self._subscriber.shutdown()
|
|
51
|
+
|
|
52
|
+
def is_active(self) -> bool:
|
|
53
|
+
"""Check if the camera sensor is actively receiving data.
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
True if both RGB and depth are receiving data, False otherwise.
|
|
57
|
+
"""
|
|
58
|
+
return self._subscriber.is_active()
|
|
59
|
+
|
|
60
|
+
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
61
|
+
"""Wait for the camera sensor to start receiving data.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
timeout: Maximum time to wait in seconds.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
True if both RGB and depth become active, False if timeout is reached.
|
|
68
|
+
"""
|
|
69
|
+
return self._subscriber.wait_for_active(timeout)
|
|
70
|
+
|
|
71
|
+
def get_obs(self, obs_keys: list[str] | None = None) -> dict[str, np.ndarray] | None:
|
|
72
|
+
"""Get the latest RGBD data.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
Tuple of (rgb, depth) if both available, None otherwise.
|
|
76
|
+
"""
|
|
77
|
+
if obs_keys is None:
|
|
78
|
+
obs_keys = ["rgb", "depth"]
|
|
79
|
+
obs_out = {}
|
|
80
|
+
for key in obs_keys:
|
|
81
|
+
if key == "rgb":
|
|
82
|
+
obs_out[key] = self._subscriber.get_latest_rgb()
|
|
83
|
+
elif key == "depth":
|
|
84
|
+
obs_out[key] = self._subscriber.get_latest_depth()
|
|
85
|
+
else:
|
|
86
|
+
raise ValueError(f"Invalid observation key: {key}")
|
|
87
|
+
return obs_out
|
|
88
|
+
|
|
89
|
+
def get_rgb_image(self) -> np.ndarray | None:
|
|
90
|
+
"""Get the latest RGB image.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Latest RGB image as numpy array (HxWxC) if available, None otherwise.
|
|
94
|
+
"""
|
|
95
|
+
return self._subscriber.get_latest_rgb()
|
|
96
|
+
|
|
97
|
+
def get_depth_image(self) -> np.ndarray | None:
|
|
98
|
+
"""Get the latest depth image.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Latest depth image as numpy array (HxW) with values in meters if available, None otherwise.
|
|
102
|
+
"""
|
|
103
|
+
return self._subscriber.get_latest_depth()
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def rgb_fps(self) -> float:
|
|
107
|
+
"""Get the RGB stream FPS measurement.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Current RGB frames per second measurement.
|
|
111
|
+
"""
|
|
112
|
+
return self._subscriber.rgb_fps
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def depth_fps(self) -> float:
|
|
116
|
+
"""Get the depth stream FPS measurement.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Current depth frames per second measurement.
|
|
120
|
+
"""
|
|
121
|
+
return self._subscriber.depth_fps
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def fps(self) -> float:
|
|
125
|
+
"""Get the combined FPS measurement (minimum of RGB and depth).
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Current frames per second measurement.
|
|
129
|
+
"""
|
|
130
|
+
return min(self.rgb_fps, self.depth_fps)
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def name(self) -> str:
|
|
134
|
+
"""Get the sensor name.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Sensor name string.
|
|
138
|
+
"""
|
|
139
|
+
return self._name
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""RGB camera sensor implementation using Zenoh subscriber."""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import zenoh
|
|
10
|
+
|
|
11
|
+
from dexcontrol.utils.subscribers.camera import RGBCameraSubscriber
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class RGBCameraSensor:
|
|
15
|
+
"""RGB camera sensor using Zenoh subscriber.
|
|
16
|
+
|
|
17
|
+
This sensor provides RGB image data from a camera using the
|
|
18
|
+
RGBCameraSubscriber for efficient data handling with lazy decoding.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
configs,
|
|
24
|
+
zenoh_session: zenoh.Session,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Initialize the RGB camera sensor.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
configs: Configuration for the RGB camera sensor.
|
|
30
|
+
zenoh_session: Active Zenoh session for communication.
|
|
31
|
+
"""
|
|
32
|
+
self._name = configs.name
|
|
33
|
+
|
|
34
|
+
# Create the RGB camera subscriber
|
|
35
|
+
self._subscriber = RGBCameraSubscriber(
|
|
36
|
+
topic=configs.topic,
|
|
37
|
+
zenoh_session=zenoh_session,
|
|
38
|
+
name=f"{self._name}_subscriber",
|
|
39
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
40
|
+
fps_log_interval=configs.fps_log_interval,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def shutdown(self) -> None:
|
|
44
|
+
"""Shutdown the camera sensor."""
|
|
45
|
+
self._subscriber.shutdown()
|
|
46
|
+
|
|
47
|
+
def is_active(self) -> bool:
|
|
48
|
+
"""Check if the camera sensor is actively receiving data.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
True if receiving data, False otherwise.
|
|
52
|
+
"""
|
|
53
|
+
return self._subscriber.is_active()
|
|
54
|
+
|
|
55
|
+
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
56
|
+
"""Wait for the camera sensor to start receiving data.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
timeout: Maximum time to wait in seconds.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
True if sensor becomes active, False if timeout is reached.
|
|
63
|
+
"""
|
|
64
|
+
return self._subscriber.wait_for_active(timeout)
|
|
65
|
+
|
|
66
|
+
def get_obs(self) -> np.ndarray | None:
|
|
67
|
+
"""Get the latest RGB image data.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Latest RGB image as numpy array (HxWxC) if available, None otherwise.
|
|
71
|
+
"""
|
|
72
|
+
return self._subscriber.get_latest_data()
|
|
73
|
+
|
|
74
|
+
def get_rgb_image(self) -> np.ndarray | None:
|
|
75
|
+
"""Get the latest RGB image.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Latest RGB image as numpy array (HxWxC) if available, None otherwise.
|
|
79
|
+
"""
|
|
80
|
+
return self._subscriber.get_latest_image()
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def fps(self) -> float:
|
|
84
|
+
"""Get the current FPS measurement.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Current frames per second measurement.
|
|
88
|
+
"""
|
|
89
|
+
return self._subscriber.fps
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def name(self) -> str:
|
|
93
|
+
"""Get the sensor name.
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Sensor name string.
|
|
97
|
+
"""
|
|
98
|
+
return self._name
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""IMU sensors package for dexcontrol.
|
|
7
|
+
|
|
8
|
+
This package provides sensor classes for various IMU (Inertial Measurement Unit)
|
|
9
|
+
sensors using Zenoh subscribers for data communication.
|
|
10
|
+
|
|
11
|
+
Available sensors:
|
|
12
|
+
- NineAxisIMUSensor: Standard 9-axis IMU with accelerometer, gyroscope, and magnetometer
|
|
13
|
+
- GeminiIMUSensor: IMU specific to Gemini hardware (6-axis: accelerometer + gyroscope)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from .gemini_imu import GeminiIMUSensor
|
|
17
|
+
from .nine_axis_imu import NineAxisIMUSensor
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"NineAxisIMUSensor",
|
|
21
|
+
"GeminiIMUSensor",
|
|
22
|
+
]
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""Gemini IMU sensor implementation using Zenoh subscriber."""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import zenoh
|
|
10
|
+
|
|
11
|
+
from dexcontrol.utils.subscribers.imu import IMUSubscriber
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class GeminiIMUSensor:
|
|
15
|
+
"""Gemini IMU sensor using Zenoh subscriber.
|
|
16
|
+
|
|
17
|
+
This sensor provides 6-axis IMU data (accelerometer + gyroscope) from Gemini hardware
|
|
18
|
+
using the IMUSubscriber for efficient data handling. Note: Gemini IMU does not include
|
|
19
|
+
magnetometer data.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self,
|
|
24
|
+
configs,
|
|
25
|
+
zenoh_session: zenoh.Session,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Initialize the Gemini IMU sensor.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
topic: Zenoh topic to subscribe to for IMU data.
|
|
31
|
+
zenoh_session: Active Zenoh session for communication.
|
|
32
|
+
name: Name for the sensor instance.
|
|
33
|
+
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
34
|
+
"""
|
|
35
|
+
self._name = configs.name
|
|
36
|
+
|
|
37
|
+
# Create the IMU subscriber
|
|
38
|
+
self._subscriber = IMUSubscriber(
|
|
39
|
+
topic=configs.topic,
|
|
40
|
+
zenoh_session=zenoh_session,
|
|
41
|
+
name=f"{self._name}_subscriber",
|
|
42
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
43
|
+
fps_log_interval=configs.fps_log_interval,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def shutdown(self) -> None:
|
|
48
|
+
"""Shutdown the IMU sensor."""
|
|
49
|
+
self._subscriber.shutdown()
|
|
50
|
+
|
|
51
|
+
def is_active(self) -> bool:
|
|
52
|
+
"""Check if the IMU sensor is actively receiving data.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
True if receiving data, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
return self._subscriber.is_active()
|
|
58
|
+
|
|
59
|
+
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
60
|
+
"""Wait for the IMU sensor to start receiving data.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
timeout: Maximum time to wait in seconds.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
True if sensor becomes active, False if timeout is reached.
|
|
67
|
+
"""
|
|
68
|
+
return self._subscriber.wait_for_active(timeout)
|
|
69
|
+
|
|
70
|
+
def get_obs(self, obs_keys: list[str] | None = None) -> dict[str, np.ndarray] | None:
|
|
71
|
+
"""Get observation data for the Gemini IMU sensor.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
obs_keys: List of observation keys to retrieve. If None, returns available data.
|
|
75
|
+
Valid keys: ['ang_vel', 'acc', 'quat'] (no magnetometer for Gemini)
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Dictionary with observation data including IMU measurements.
|
|
79
|
+
Note: Magnetometer data is not available for Gemini IMU.
|
|
80
|
+
"""
|
|
81
|
+
if obs_keys is None:
|
|
82
|
+
obs_keys = ['ang_vel', 'acc', 'quat']
|
|
83
|
+
|
|
84
|
+
obs_out = {}
|
|
85
|
+
data = self._subscriber.get_latest_data()
|
|
86
|
+
|
|
87
|
+
for key in obs_keys:
|
|
88
|
+
if key == 'ang_vel':
|
|
89
|
+
obs_out[key] = data['ang_vel']
|
|
90
|
+
elif key == 'acc':
|
|
91
|
+
obs_out[key] = data['acc']
|
|
92
|
+
elif key == 'quat':
|
|
93
|
+
obs_out[key] = data['quat']
|
|
94
|
+
else:
|
|
95
|
+
raise ValueError(f"Invalid observation key: {key}")
|
|
96
|
+
|
|
97
|
+
return obs_out
|
|
98
|
+
|
|
99
|
+
def get_acceleration(self) -> np.ndarray | None:
|
|
100
|
+
"""Get the latest linear acceleration.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Linear acceleration [x, y, z] in m/s² if available, None otherwise.
|
|
104
|
+
"""
|
|
105
|
+
return self._subscriber.get_acceleration()
|
|
106
|
+
|
|
107
|
+
def get_angular_velocity(self) -> np.ndarray | None:
|
|
108
|
+
"""Get the latest angular velocity.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Angular velocity [x, y, z] in rad/s if available, None otherwise.
|
|
112
|
+
"""
|
|
113
|
+
return self._subscriber.get_angular_velocity()
|
|
114
|
+
|
|
115
|
+
def get_orientation(self) -> np.ndarray | None:
|
|
116
|
+
"""Get the latest orientation quaternion.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Orientation quaternion [x, y, z, w] if available, None otherwise.
|
|
120
|
+
"""
|
|
121
|
+
return self._subscriber.get_orientation()
|
|
122
|
+
|
|
123
|
+
@property
|
|
124
|
+
def fps(self) -> float:
|
|
125
|
+
"""Get the current FPS measurement.
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Current frames per second measurement.
|
|
129
|
+
"""
|
|
130
|
+
return self._subscriber.fps
|
|
131
|
+
|
|
132
|
+
@property
|
|
133
|
+
def name(self) -> str:
|
|
134
|
+
"""Get the IMU name.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
IMU name string.
|
|
138
|
+
"""
|
|
139
|
+
return self._name
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Copyright (c) 2025 Dexmate CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 with Commons Clause License
|
|
4
|
+
# Condition v1.0 [see LICENSE for details].
|
|
5
|
+
|
|
6
|
+
"""Nine-Axis IMU sensor implementation using Zenoh subscriber."""
|
|
7
|
+
|
|
8
|
+
import numpy as np
|
|
9
|
+
import zenoh
|
|
10
|
+
|
|
11
|
+
from dexcontrol.utils.subscribers.imu import IMUSubscriber
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class NineAxisIMUSensor:
|
|
15
|
+
"""Nine-Axis IMU sensor using Zenoh subscriber.
|
|
16
|
+
|
|
17
|
+
This sensor provides 9-axis IMU data including acceleration, angular velocity,
|
|
18
|
+
orientation, and magnetometer data using the IMUSubscriber for efficient data handling.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
configs,
|
|
24
|
+
zenoh_session: zenoh.Session,
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Initialize the Nine-Axis IMU sensor.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
topic: Zenoh topic to subscribe to for IMU data.
|
|
30
|
+
zenoh_session: Active Zenoh session for communication.
|
|
31
|
+
name: Name for the sensor instance.
|
|
32
|
+
enable_fps_tracking: Whether to track and log FPS metrics.
|
|
33
|
+
"""
|
|
34
|
+
self._name = configs.name
|
|
35
|
+
|
|
36
|
+
# Create the IMU subscriber
|
|
37
|
+
self._subscriber = IMUSubscriber(
|
|
38
|
+
topic=configs.topic,
|
|
39
|
+
zenoh_session=zenoh_session,
|
|
40
|
+
name=f"{self._name}_subscriber",
|
|
41
|
+
enable_fps_tracking=configs.enable_fps_tracking,
|
|
42
|
+
fps_log_interval=configs.fps_log_interval,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def shutdown(self) -> None:
|
|
47
|
+
"""Shutdown the IMU sensor."""
|
|
48
|
+
self._subscriber.shutdown()
|
|
49
|
+
|
|
50
|
+
def is_active(self) -> bool:
|
|
51
|
+
"""Check if the IMU sensor is actively receiving data.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
True if receiving data, False otherwise.
|
|
55
|
+
"""
|
|
56
|
+
return self._subscriber.is_active()
|
|
57
|
+
|
|
58
|
+
def wait_for_active(self, timeout: float = 5.0) -> bool:
|
|
59
|
+
"""Wait for the IMU sensor to start receiving data.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
timeout: Maximum time to wait in seconds.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
True if sensor becomes active, False if timeout is reached.
|
|
66
|
+
"""
|
|
67
|
+
return self._subscriber.wait_for_active(timeout)
|
|
68
|
+
|
|
69
|
+
def get_obs(self, obs_keys: list[str] | None = None) -> dict[str, np.ndarray] | None:
|
|
70
|
+
"""Get observation data for the Nine-Axis IMU sensor.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
obs_keys: List of observation keys to retrieve. If None, returns all available data.
|
|
74
|
+
Valid keys: ['ang_vel', 'acc', 'mag', 'quat']
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
Dictionary with observation data including all IMU measurements.
|
|
78
|
+
"""
|
|
79
|
+
if obs_keys is None:
|
|
80
|
+
obs_keys = ['ang_vel', 'acc', 'mag', 'quat']
|
|
81
|
+
|
|
82
|
+
data = self._subscriber.get_latest_data()
|
|
83
|
+
if data is None:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
obs_out = {}
|
|
87
|
+
for key in obs_keys:
|
|
88
|
+
if key == 'ang_vel':
|
|
89
|
+
obs_out[key] = data['angular_velocity']
|
|
90
|
+
elif key == 'acc':
|
|
91
|
+
obs_out[key] = data['acceleration']
|
|
92
|
+
elif key == 'mag':
|
|
93
|
+
obs_out[key] = data['magnetometer']
|
|
94
|
+
elif key == 'quat':
|
|
95
|
+
obs_out[key] = data['orientation']
|
|
96
|
+
else:
|
|
97
|
+
raise ValueError(f"Invalid observation key: {key}")
|
|
98
|
+
|
|
99
|
+
return obs_out
|
|
100
|
+
|
|
101
|
+
def get_acceleration(self) -> np.ndarray | None:
|
|
102
|
+
"""Get the latest linear acceleration.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Linear acceleration [x, y, z] in m/s² if available, None otherwise.
|
|
106
|
+
"""
|
|
107
|
+
return self._subscriber.get_acceleration()
|
|
108
|
+
|
|
109
|
+
def get_angular_velocity(self) -> np.ndarray | None:
|
|
110
|
+
"""Get the latest angular velocity.
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Angular velocity [x, y, z] in rad/s if available, None otherwise.
|
|
114
|
+
"""
|
|
115
|
+
return self._subscriber.get_angular_velocity()
|
|
116
|
+
|
|
117
|
+
def get_orientation(self) -> np.ndarray | None:
|
|
118
|
+
"""Get the latest orientation quaternion.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Orientation quaternion [x, y, z, w] if available, None otherwise.
|
|
122
|
+
"""
|
|
123
|
+
return self._subscriber.get_orientation()
|
|
124
|
+
|
|
125
|
+
def get_magnetometer(self) -> np.ndarray | None:
|
|
126
|
+
"""Get the latest magnetometer reading.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
Magnetic field [x, y, z] in µT if available, None otherwise.
|
|
130
|
+
"""
|
|
131
|
+
return self._subscriber.get_magnetometer()
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def fps(self) -> float:
|
|
135
|
+
"""Get the current FPS measurement.
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Current frames per second measurement.
|
|
139
|
+
"""
|
|
140
|
+
return self._subscriber.fps
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def name(self) -> str:
|
|
144
|
+
"""Get the IMU name.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
IMU name string.
|
|
148
|
+
"""
|
|
149
|
+
return self._name
|