dexcontrol 0.2.1__py3-none-any.whl → 0.2.3__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 (81) hide show
  1. dexcontrol/__init__.py +14 -3
  2. dexcontrol/apps/dualsense_teleop_base.py +16 -11
  3. dexcontrol/config/__init__.py +10 -5
  4. dexcontrol/config/core/__init__.py +8 -3
  5. dexcontrol/config/core/arm.py +8 -3
  6. dexcontrol/config/core/chassis.py +10 -5
  7. dexcontrol/config/core/hand.py +14 -9
  8. dexcontrol/config/core/head.py +8 -3
  9. dexcontrol/config/core/misc.py +8 -3
  10. dexcontrol/config/core/torso.py +8 -3
  11. dexcontrol/config/sensors/__init__.py +8 -3
  12. dexcontrol/config/sensors/cameras/__init__.py +9 -4
  13. dexcontrol/config/sensors/cameras/rgb_camera.py +18 -5
  14. dexcontrol/config/sensors/cameras/zed_camera.py +36 -0
  15. dexcontrol/config/sensors/imu/__init__.py +10 -5
  16. dexcontrol/config/sensors/imu/chassis_imu.py +21 -0
  17. dexcontrol/config/sensors/imu/zed_imu.py +21 -0
  18. dexcontrol/config/sensors/lidar/__init__.py +8 -3
  19. dexcontrol/config/sensors/lidar/rplidar.py +9 -3
  20. dexcontrol/config/sensors/ultrasonic/__init__.py +8 -3
  21. dexcontrol/config/sensors/ultrasonic/ultrasonic.py +9 -3
  22. dexcontrol/config/sensors/vega_sensors.py +34 -21
  23. dexcontrol/config/vega.py +14 -6
  24. dexcontrol/core/__init__.py +9 -0
  25. dexcontrol/core/arm.py +21 -6
  26. dexcontrol/core/chassis.py +8 -3
  27. dexcontrol/core/component.py +26 -6
  28. dexcontrol/core/hand.py +8 -3
  29. dexcontrol/core/head.py +18 -3
  30. dexcontrol/core/misc.py +94 -16
  31. dexcontrol/core/torso.py +8 -3
  32. dexcontrol/proto/dexcontrol_msg_pb2.py +17 -15
  33. dexcontrol/proto/dexcontrol_msg_pb2.pyi +24 -0
  34. dexcontrol/robot.py +82 -28
  35. dexcontrol/sensors/__init__.py +13 -8
  36. dexcontrol/sensors/camera/__init__.py +11 -6
  37. dexcontrol/sensors/camera/rgb_camera.py +33 -24
  38. dexcontrol/sensors/camera/zed_camera.py +364 -0
  39. dexcontrol/sensors/imu/__init__.py +13 -8
  40. dexcontrol/sensors/imu/chassis_imu.py +155 -0
  41. dexcontrol/sensors/imu/{nine_axis_imu.py → zed_imu.py} +41 -26
  42. dexcontrol/sensors/lidar/__init__.py +11 -1
  43. dexcontrol/sensors/lidar/rplidar.py +8 -3
  44. dexcontrol/sensors/manager.py +22 -9
  45. dexcontrol/sensors/ultrasonic.py +8 -3
  46. dexcontrol/utils/__init__.py +8 -3
  47. dexcontrol/utils/constants.py +10 -0
  48. dexcontrol/utils/io_utils.py +8 -3
  49. dexcontrol/utils/motion_utils.py +8 -3
  50. dexcontrol/utils/os_utils.py +23 -4
  51. dexcontrol/utils/pb_utils.py +8 -3
  52. dexcontrol/utils/rate_limiter.py +8 -3
  53. dexcontrol/utils/rtc_utils.py +144 -0
  54. dexcontrol/utils/subscribers/__init__.py +11 -3
  55. dexcontrol/utils/subscribers/base.py +26 -5
  56. dexcontrol/utils/subscribers/camera.py +10 -6
  57. dexcontrol/utils/subscribers/decoders.py +8 -3
  58. dexcontrol/utils/subscribers/generic.py +8 -3
  59. dexcontrol/utils/subscribers/imu.py +8 -3
  60. dexcontrol/utils/subscribers/lidar.py +8 -3
  61. dexcontrol/utils/subscribers/protobuf.py +8 -3
  62. dexcontrol/utils/subscribers/rtc.py +315 -0
  63. dexcontrol/utils/timer.py +8 -3
  64. dexcontrol/utils/trajectory_utils.py +8 -3
  65. dexcontrol/utils/viz_utils.py +8 -3
  66. dexcontrol/utils/zenoh_utils.py +83 -0
  67. dexcontrol-0.2.3.dist-info/METADATA +265 -0
  68. dexcontrol-0.2.3.dist-info/RECORD +72 -0
  69. {dexcontrol-0.2.1.dist-info → dexcontrol-0.2.3.dist-info}/WHEEL +1 -2
  70. dexcontrol-0.2.3.dist-info/licenses/LICENSE +184 -0
  71. dexcontrol/config/sensors/cameras/gemini_camera.py +0 -16
  72. dexcontrol/config/sensors/imu/gemini_imu.py +0 -15
  73. dexcontrol/config/sensors/imu/nine_axis_imu.py +0 -15
  74. dexcontrol/sensors/camera/gemini_camera.py +0 -139
  75. dexcontrol/sensors/imu/gemini_imu.py +0 -139
  76. dexcontrol/utils/reset_orbbec_camera_usb.py +0 -98
  77. dexcontrol-0.2.1.dist-info/METADATA +0 -369
  78. dexcontrol-0.2.1.dist-info/RECORD +0 -72
  79. dexcontrol-0.2.1.dist-info/licenses/LICENSE +0 -188
  80. dexcontrol-0.2.1.dist-info/licenses/NOTICE +0 -13
  81. dexcontrol-0.2.1.dist-info/top_level.txt +0 -1
@@ -1,15 +0,0 @@
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
- from dataclasses import dataclass
7
-
8
-
9
- @dataclass
10
- class GeminiIMUConfig:
11
- _target_: str = "dexcontrol.sensors.imu.gemini_imu.GeminiIMUSensor"
12
- topic: str = "/imu/gemini"
13
- name: str = "gemini_imu"
14
- enable_fps_tracking: bool = False
15
- fps_log_interval: int = 30
@@ -1,15 +0,0 @@
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
- from dataclasses import dataclass
7
-
8
-
9
- @dataclass
10
- class NineAxisIMUConfig:
11
- _target_: str = "dexcontrol.sensors.imu.nine_axis_imu.NineAxisIMUSensor"
12
- topic: str = "/imu/nine_axis"
13
- name: str = "nine_axis_imu"
14
- enable_fps_tracking: bool = False
15
- fps_log_interval: int = 30
@@ -1,139 +0,0 @@
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
@@ -1,139 +0,0 @@
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
@@ -1,98 +0,0 @@
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
- """Utility for resetting Orbbec camera USB connection."""
7
-
8
- import os
9
- import sys
10
- import time
11
-
12
- import pyudev
13
- from loguru import logger
14
-
15
- # Orbbec vendor ID constant
16
- _ORBBEC_VENDOR_ID = "2bc5"
17
-
18
-
19
- def check_root() -> None:
20
- """Verify the script is running with root privileges.
21
-
22
- Exits the program if not running as root.
23
- """
24
- if os.geteuid() != 0:
25
- logger.error("This script must be run as root (sudo). Exiting...")
26
- logger.info("Run with: sudo $(which python) reset_orbbec_camera_usb.py")
27
- sys.exit(1)
28
-
29
-
30
- def reset_orbbec() -> bool:
31
- """Reset the USB connection for an Orbbec camera.
32
-
33
- Simulates unplugging and replugging the device by toggling the USB
34
- authorization state.
35
-
36
- Returns:
37
- bool: True if camera was found and reset successfully, False otherwise.
38
- """
39
- # Check for root privileges first
40
- check_root()
41
-
42
- # Initialize pyudev
43
- context = pyudev.Context()
44
-
45
- # Find Orbbec device
46
- for device in context.list_devices(subsystem="usb"):
47
- if device.properties.get("ID_VENDOR_ID") == _ORBBEC_VENDOR_ID:
48
- return _reset_device(device)
49
-
50
- logger.warning("Orbbec camera not found")
51
- return False
52
-
53
-
54
- def _reset_device(device: pyudev.Device) -> bool:
55
- """Reset a specific USB device.
56
-
57
- Args:
58
- device: pyudev Device object to reset
59
-
60
- Returns:
61
- bool: True if reset was successful, False otherwise
62
- """
63
- # Get the parent device (USB port)
64
- port = device.find_parent("usb", "usb_device")
65
-
66
- if port is None:
67
- logger.warning("Could not find parent USB device")
68
- return False
69
-
70
- # Construct path to authorized file
71
- path = os.path.join(port.sys_path, "authorized")
72
-
73
- if not os.path.exists(path):
74
- logger.warning(f"Authorize file not found at: {path}")
75
- return False
76
-
77
- try:
78
- # Simulate unplug
79
- with open(path, "w") as f:
80
- f.write("0")
81
- logger.info("USB device deauthorized")
82
-
83
- # Wait a moment for the system to process
84
- time.sleep(1)
85
-
86
- # Simulate plug
87
- with open(path, "w") as f:
88
- f.write("1")
89
-
90
- logger.info(f"Orbbec camera reset successfully. Path: {path}")
91
- return True
92
- except IOError as e:
93
- logger.error(f"Failed to reset device: {e}")
94
- return False
95
-
96
-
97
- if __name__ == "__main__":
98
- reset_orbbec()