dexbot-utils 0.4.0__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.
- dexbot_utils/__init__.py +49 -0
- dexbot_utils/_helpers.py +49 -0
- dexbot_utils/cfg_modifier.py +99 -0
- dexbot_utils/cli.py +97 -0
- dexbot_utils/configs/__init__.py +31 -0
- dexbot_utils/configs/components/__init__.py +8 -0
- dexbot_utils/configs/components/base.py +28 -0
- dexbot_utils/configs/components/sensors/__init__.py +20 -0
- dexbot_utils/configs/components/sensors/cameras/__init__.py +20 -0
- dexbot_utils/configs/components/sensors/cameras/camera.py +38 -0
- dexbot_utils/configs/components/sensors/cameras/zed_camera.py +102 -0
- dexbot_utils/configs/components/sensors/imu/__init__.py +17 -0
- dexbot_utils/configs/components/sensors/imu/chassis_imu.py +20 -0
- dexbot_utils/configs/components/sensors/imu/zed_imu.py +20 -0
- dexbot_utils/configs/components/sensors/lidar/__init__.py +14 -0
- dexbot_utils/configs/components/sensors/lidar/lidar_3d.py +22 -0
- dexbot_utils/configs/components/sensors/lidar/rplidar.py +20 -0
- dexbot_utils/configs/components/sensors/ultrasonic/__init__.py +13 -0
- dexbot_utils/configs/components/sensors/ultrasonic/ultrasonic.py +20 -0
- dexbot_utils/configs/components/vega_1/__init__.py +30 -0
- dexbot_utils/configs/components/vega_1/arm.py +81 -0
- dexbot_utils/configs/components/vega_1/chassis.py +47 -0
- dexbot_utils/configs/components/vega_1/hand.py +113 -0
- dexbot_utils/configs/components/vega_1/head.py +32 -0
- dexbot_utils/configs/components/vega_1/misc.py +46 -0
- dexbot_utils/configs/components/vega_1/torso.py +41 -0
- dexbot_utils/configs/registry.py +112 -0
- dexbot_utils/configs/robots/base.py +24 -0
- dexbot_utils/configs/robots/vega_1.py +127 -0
- dexbot_utils/configs/robots/vega_1p.py +127 -0
- dexbot_utils/configs/robots/vega_1u.py +102 -0
- dexbot_utils/constants.py +19 -0
- dexbot_utils/hand.py +19 -0
- dexbot_utils/robot_info.py +577 -0
- dexbot_utils/urdf_utils.py +354 -0
- dexbot_utils/validators.py +194 -0
- dexbot_utils-0.4.0.dist-info/METADATA +298 -0
- dexbot_utils-0.4.0.dist-info/RECORD +41 -0
- dexbot_utils-0.4.0.dist-info/WHEEL +4 -0
- dexbot_utils-0.4.0.dist-info/entry_points.txt +2 -0
- dexbot_utils-0.4.0.dist-info/licenses/LICENSE +184 -0
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
from dataclasses import dataclass
|
|
12
|
+
|
|
13
|
+
from ...base import BaseComponentConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class UltraSonicConfig(BaseComponentConfig):
|
|
18
|
+
enabled: bool = False
|
|
19
|
+
topic: str = "state/ultrasonic"
|
|
20
|
+
name: str = "ultrasonic"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Vega-specific component configuration dataclasses.
|
|
2
|
+
|
|
3
|
+
This module contains Vega robot family specific component configurations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .arm import Vega1ArmConfig
|
|
7
|
+
from .chassis import Vega1ChassisConfig
|
|
8
|
+
from .hand import (
|
|
9
|
+
DexDGripperConfig,
|
|
10
|
+
DexSGripperConfig,
|
|
11
|
+
F5D6HandV1Config,
|
|
12
|
+
F5D6HandV2Config,
|
|
13
|
+
)
|
|
14
|
+
from .head import Vega1HeadConfig
|
|
15
|
+
from .misc import BatteryConfig, EStopConfig, HeartbeatConfig
|
|
16
|
+
from .torso import Vega1TorsoConfig
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"Vega1ArmConfig",
|
|
20
|
+
"Vega1ChassisConfig",
|
|
21
|
+
"F5D6HandV1Config",
|
|
22
|
+
"F5D6HandV2Config",
|
|
23
|
+
"DexSGripperConfig",
|
|
24
|
+
"DexDGripperConfig",
|
|
25
|
+
"Vega1HeadConfig",
|
|
26
|
+
"Vega1TorsoConfig",
|
|
27
|
+
"BatteryConfig",
|
|
28
|
+
"EStopConfig",
|
|
29
|
+
"HeartbeatConfig",
|
|
30
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Arm component configuration."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from ..base import BaseJointComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Vega1ArmConfig(BaseJointComponentConfig):
|
|
10
|
+
"""Configuration for Vega robot arm component.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
side: Arm side ("left" or "right")
|
|
14
|
+
pv_mode: Position-velocity control mode flag
|
|
15
|
+
default_control_hz: Default control frequency in Hz
|
|
16
|
+
joints: Property returning list of joint names for the arm
|
|
17
|
+
pose_pool: Property returning dictionary of predefined arm poses
|
|
18
|
+
state_sub_topic: Property returning topic for arm state feedback
|
|
19
|
+
wrench_sub_topic: Property returning topic for wrench feedback
|
|
20
|
+
wrist_button_sub_topic: Property returning topic for wrist button state
|
|
21
|
+
control_pub_topic: Property returning topic for arm control commands
|
|
22
|
+
set_mode_query: Property returning service name for setting arm control mode
|
|
23
|
+
ee_pass_through_pub_topic: Property returning topic for end-effector pass-through
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
side: str = "left"
|
|
27
|
+
pv_mode: bool = False
|
|
28
|
+
default_control_hz: int = 100
|
|
29
|
+
enable_ee_pass_through: bool = False
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def joints(self) -> list[str]:
|
|
33
|
+
joint_prefix = "L" if self.side == "left" else "R"
|
|
34
|
+
joint_suffixes = [
|
|
35
|
+
"arm_j1",
|
|
36
|
+
"arm_j2",
|
|
37
|
+
"arm_j3",
|
|
38
|
+
"arm_j4",
|
|
39
|
+
"arm_j5",
|
|
40
|
+
"arm_j6",
|
|
41
|
+
"arm_j7",
|
|
42
|
+
]
|
|
43
|
+
return [f"{joint_prefix}_{suffix}" for suffix in joint_suffixes]
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def pose_pool(self) -> dict[str, list[float]]:
|
|
47
|
+
pool = {
|
|
48
|
+
"folded": [1.57079, 0.0, 0, -3.1, 0, 0, -0.69813],
|
|
49
|
+
"folded_closed_hand": [1.57079, 0.0, 0, -3.1, 0, 0, -0.9],
|
|
50
|
+
"L_shape": [0.064, 0.3, 0.0, -1.556, 1.271, 0.0, 0.0],
|
|
51
|
+
"lift_up": [0.064, 0.3, 0.0, -2.756, 1.271, 0.0, 0.0],
|
|
52
|
+
"zero": [-1.57079, 0.0, 0, 0.0, 0, 0, 0.0],
|
|
53
|
+
}
|
|
54
|
+
if self.side == "right":
|
|
55
|
+
for k, v in pool.items():
|
|
56
|
+
pool[k] = [-v[0], -v[1], -v[2], v[3], -v[4], -v[5], -v[6]]
|
|
57
|
+
return pool
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def state_sub_topic(self) -> str:
|
|
61
|
+
return f"state/arm/{self.side}"
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def wrench_sub_topic(self) -> str:
|
|
65
|
+
return f"state/wrench/{self.side}"
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def wrist_button_sub_topic(self) -> str:
|
|
69
|
+
return f"state/wrist_button/{self.side}"
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def control_pub_topic(self) -> str:
|
|
73
|
+
return f"control/arm/{self.side}"
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def set_mode_query(self) -> str:
|
|
77
|
+
return f"mode/arm/{self.side}"
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def ee_pass_through_pub_topic(self) -> str:
|
|
81
|
+
return f"control/ee_pass_through/{self.side}"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Chassis component configuration."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from ..base import BaseJointComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Vega1ChassisConfig(BaseJointComponentConfig):
|
|
10
|
+
"""Configuration for Vega robot chassis/mobile base component.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
pv_mode: Position-velocity control mode flag
|
|
14
|
+
max_linear_vel: Maximum linear velocity (m/s)
|
|
15
|
+
max_steering_angle: Maximum steering angle (rad)
|
|
16
|
+
center_to_wheel_axis_dist: Distance from base center to wheel axis (m)
|
|
17
|
+
wheels_dist: Distance between two wheels (m)
|
|
18
|
+
steer_joints: List of steering joint names
|
|
19
|
+
drive_joints: List of drive joint names
|
|
20
|
+
steer_control_pub_topic: Topic for steering control commands
|
|
21
|
+
steer_state_sub_topic: Topic for steering state feedback
|
|
22
|
+
drive_control_pub_topic: Topic for drive control commands
|
|
23
|
+
drive_state_sub_topic: Topic for drive state feedback
|
|
24
|
+
joints: Property returning all chassis joints (steer + drive)
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
pv_mode: bool = False
|
|
28
|
+
max_linear_vel: float = 0.8
|
|
29
|
+
max_steering_angle: float = 2.35
|
|
30
|
+
center_to_wheel_axis_dist: float = 0.219
|
|
31
|
+
wheels_dist: float = 0.45
|
|
32
|
+
steer_joints: list[str] = field(
|
|
33
|
+
default_factory=lambda: ["L_wheel_j1", "R_wheel_j1"]
|
|
34
|
+
)
|
|
35
|
+
drive_joints: list[str] = field(
|
|
36
|
+
default_factory=lambda: ["L_wheel_j2", "R_wheel_j2"]
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
steer_control_pub_topic: str = "control/chassis/steer"
|
|
40
|
+
steer_state_sub_topic: str = "state/chassis/steer"
|
|
41
|
+
drive_control_pub_topic: str = "control/chassis/drive"
|
|
42
|
+
drive_state_sub_topic: str = "state/chassis/drive"
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def joints(self) -> list[str]:
|
|
46
|
+
"""Return all chassis joints (steer + drive)."""
|
|
47
|
+
return self.steer_joints + self.drive_joints
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Hand component configuration."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from ..base import BaseJointComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class F5D6HandV1Config(BaseJointComponentConfig):
|
|
10
|
+
"""Configuration for F5D6 V1 hand component.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
pv_mode: Position-velocity control mode flag
|
|
14
|
+
side: Hand side ("left" or "right")
|
|
15
|
+
pose_pool: Dictionary of predefined hand poses with joint positions
|
|
16
|
+
state_sub_topic: Property returning topic for hand state feedback
|
|
17
|
+
control_pub_topic: Property returning topic for hand control commands
|
|
18
|
+
joints: Property returning list of joint names for the hand
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
pv_mode: bool = False
|
|
22
|
+
side: str = "left"
|
|
23
|
+
pose_pool: dict[str, list[float]] = field(
|
|
24
|
+
default_factory=lambda: {
|
|
25
|
+
"open": [0.1834, 0.2891, 0.2801, 0.284, 0.2811, -0.0158],
|
|
26
|
+
"close": [-0.1, -1.0946, -1.0844, -1.0154, -1.0118, 0.84],
|
|
27
|
+
}
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def state_sub_topic(self) -> str:
|
|
32
|
+
return f"state/hand/{self.side}"
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def control_pub_topic(self) -> str:
|
|
36
|
+
return f"control/hand/{self.side}"
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def joints(self) -> list[str]:
|
|
40
|
+
joint_prefix = "L" if self.side == "left" else "R"
|
|
41
|
+
joint_suffixes = [
|
|
42
|
+
"th_j1",
|
|
43
|
+
"ff_j1",
|
|
44
|
+
"mf_j1",
|
|
45
|
+
"rf_j1",
|
|
46
|
+
"lf_j1",
|
|
47
|
+
"th_j0",
|
|
48
|
+
]
|
|
49
|
+
return [f"{joint_prefix}_{suffix}" for suffix in joint_suffixes]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class F5D6HandV2Config(F5D6HandV1Config):
|
|
54
|
+
"""Configuration for F5D6 V2 hand component with touch sensors.
|
|
55
|
+
|
|
56
|
+
Inherits all attributes from F5D6HandV1Config and adds touch sensor support.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
touch_sensor_sub_topic: Property returning topic for touch sensor feedback
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def touch_sensor_sub_topic(self) -> str:
|
|
64
|
+
# Only for V2 hand
|
|
65
|
+
return f"state/hand/{self.side}/touch"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass
|
|
69
|
+
class DexSGripperConfig(BaseJointComponentConfig):
|
|
70
|
+
"""Configuration for DexS single gripper component.
|
|
71
|
+
|
|
72
|
+
Attributes:
|
|
73
|
+
pv_mode: Position-velocity control mode flag
|
|
74
|
+
side: Gripper side ("left" or "right")
|
|
75
|
+
pose_pool: Dictionary of predefined gripper poses with joint positions
|
|
76
|
+
state_sub_topic: Property returning topic for gripper state feedback
|
|
77
|
+
control_pub_topic: Property returning topic for gripper control commands
|
|
78
|
+
joints: Property returning list of joint names for the gripper
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
pv_mode: bool = False
|
|
82
|
+
side: str = "left"
|
|
83
|
+
pose_pool: dict[str, list[float]] = field(
|
|
84
|
+
default_factory=lambda: {
|
|
85
|
+
"open": [0.7854],
|
|
86
|
+
"close": [0.0],
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# TODO: when gripper auto detection is available, we should use the hand namespace instead of gripper namespace
|
|
91
|
+
@property
|
|
92
|
+
def state_sub_topic(self) -> str:
|
|
93
|
+
return f"state/gripper/{self.side}"
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def control_pub_topic(self) -> str:
|
|
97
|
+
return f"control/gripper/{self.side}"
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def joints(self) -> list[str]:
|
|
101
|
+
joint_prefix = "L" if self.side == "left" else "R"
|
|
102
|
+
return [f"{joint_prefix}_gripper_j1"]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class DexDGripperConfig(DexSGripperConfig):
|
|
107
|
+
"""Configuration for DexD double gripper component.
|
|
108
|
+
|
|
109
|
+
Inherits all attributes and behavior from DexSGripperConfig.
|
|
110
|
+
Double and single grippers currently share the same interface.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
pass
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Head component configuration."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from ..base import BaseJointComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Vega1HeadConfig(BaseJointComponentConfig):
|
|
10
|
+
"""Configuration for Vega robot head component.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
pv_mode: Position-velocity control mode flag
|
|
14
|
+
joints: List of joint names for the head
|
|
15
|
+
state_sub_topic: Topic for head state feedback
|
|
16
|
+
control_pub_topic: Topic for head control commands
|
|
17
|
+
set_mode_query: Service name for setting head control mode
|
|
18
|
+
pose_pool: Dictionary of predefined head poses with joint positions
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
pv_mode: bool = True
|
|
22
|
+
joints: list[str] = field(default_factory=lambda: ["head_j1", "head_j2", "head_j3"])
|
|
23
|
+
|
|
24
|
+
state_sub_topic: str = "state/head"
|
|
25
|
+
control_pub_topic: str = "control/head"
|
|
26
|
+
set_mode_query: str = "mode/head"
|
|
27
|
+
pose_pool: dict[str, list[float]] = field(
|
|
28
|
+
default_factory=lambda: {
|
|
29
|
+
"home": [0.0, 0.0, 0.0],
|
|
30
|
+
"tucked": [0.0, 0.0, -1.37],
|
|
31
|
+
}
|
|
32
|
+
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Miscellaneous component configurations (battery, estop, heartbeat)."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from ..base import BaseComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class BatteryConfig(BaseComponentConfig):
|
|
10
|
+
"""Configuration for battery management system.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
state_sub_topic: Topic for battery state feedback
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
state_sub_topic: str = "state/bms"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class EStopConfig(BaseComponentConfig):
|
|
21
|
+
"""Configuration for emergency stop component.
|
|
22
|
+
|
|
23
|
+
Can be disabled via DEXCONTROL_DISABLE_ESTOP_CHECKING=1 environment variable.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
state_sub_topic: Topic for emergency stop state feedback
|
|
27
|
+
estop_query_name: Service name for emergency stop queries
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
state_sub_topic: str = "state/estop"
|
|
31
|
+
estop_query_name: str = "system/estop"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class HeartbeatConfig(BaseComponentConfig):
|
|
36
|
+
"""Configuration for system heartbeat monitoring.
|
|
37
|
+
|
|
38
|
+
Can be disabled via DEXCONTROL_DISABLE_HEARTBEAT=1 environment variable.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
heartbeat_topic: Topic for heartbeat messages
|
|
42
|
+
timeout_seconds: Heartbeat timeout threshold in seconds
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
heartbeat_topic: str = "heartbeat"
|
|
46
|
+
timeout_seconds: float = 1.0
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Torso component configuration."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from ..base import BaseJointComponentConfig
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Vega1TorsoConfig(BaseJointComponentConfig):
|
|
10
|
+
"""Configuration for Vega robot torso component.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
pv_mode: Position-velocity control mode flag
|
|
14
|
+
joints: List of joint names for the torso
|
|
15
|
+
state_sub_topic: Topic for torso state feedback
|
|
16
|
+
control_pub_topic: Topic for torso control commands
|
|
17
|
+
pose_pool: Dictionary of predefined torso poses with joint positions
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
pv_mode: bool = True
|
|
21
|
+
joints: list[str] = field(
|
|
22
|
+
default_factory=lambda: ["torso_j1", "torso_j2", "torso_j3"]
|
|
23
|
+
)
|
|
24
|
+
state_sub_topic: str = "state/torso"
|
|
25
|
+
control_pub_topic: str = "control/torso"
|
|
26
|
+
|
|
27
|
+
pose_pool: dict[str, list[float]] = field(
|
|
28
|
+
default_factory=lambda: {
|
|
29
|
+
"home": [0.0, 0.0, 0.0],
|
|
30
|
+
"folded": [0.0, 0.0, -1.5708],
|
|
31
|
+
"crouch20_low": [0.0, 0.0, -0.35],
|
|
32
|
+
"crouch20_medium": [0.52, 1.05, 0.18],
|
|
33
|
+
"crouch20_high": [0.78, 1.57, 0.44],
|
|
34
|
+
"crouch45_low": [0.0, 0.0, -0.79],
|
|
35
|
+
"crouch45_medium": [0.52, 1.05, -0.26],
|
|
36
|
+
"crouch45_high": [0.78, 1.57, 0],
|
|
37
|
+
"crouch90_low": [0.0, 0.0, -1.57],
|
|
38
|
+
"crouch90_medium": [0.52, 1.05, -1.04],
|
|
39
|
+
"crouch90_high": [0.78, 1.57, -0.78],
|
|
40
|
+
}
|
|
41
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Configuration registry for robot variants."""
|
|
2
|
+
|
|
3
|
+
from typing import Callable, ClassVar, TypeVar
|
|
4
|
+
|
|
5
|
+
from .robots.base import BaseRobotConfig
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T", bound=BaseRobotConfig)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def register_variant(variant: str) -> Callable[[type[T]], type[T]]:
|
|
11
|
+
"""Decorator to register a robot configuration variant.
|
|
12
|
+
|
|
13
|
+
Use this decorator on robot config dataclasses to auto-register them.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
variant: Variant name (e.g., "vega_1", "vega_1u", "vega_1p")
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
Decorator function
|
|
20
|
+
|
|
21
|
+
Example:
|
|
22
|
+
@register_variant("vega_1")
|
|
23
|
+
@dataclass
|
|
24
|
+
class Vega1Config(BaseRobotConfig):
|
|
25
|
+
robot_model: str = "vega_1"
|
|
26
|
+
...
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def decorator(config_class: type[T]) -> type[T]:
|
|
30
|
+
ConfigRegistry.register(variant, config_class)
|
|
31
|
+
return config_class
|
|
32
|
+
|
|
33
|
+
return decorator
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigRegistry:
|
|
37
|
+
"""Registry for robot configuration variants.
|
|
38
|
+
|
|
39
|
+
Provides a central registry for managing robot configuration classes
|
|
40
|
+
and factory methods for retrieving configurations.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
_registry: ClassVar[dict[str, type[BaseRobotConfig]]] = {}
|
|
44
|
+
|
|
45
|
+
@classmethod
|
|
46
|
+
def register(cls, variant: str, config_class: type[BaseRobotConfig]) -> None:
|
|
47
|
+
"""Register a robot configuration variant.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
variant: Variant name (e.g., "vega_1", "vega_1u")
|
|
51
|
+
config_class: Configuration dataclass for the variant
|
|
52
|
+
"""
|
|
53
|
+
cls._registry[variant] = config_class
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def get(cls, variant: str) -> BaseRobotConfig:
|
|
57
|
+
"""Get a robot configuration instance.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
variant: Variant name
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
Configuration instance for the variant
|
|
64
|
+
|
|
65
|
+
Raises:
|
|
66
|
+
ValueError: If variant is not registered
|
|
67
|
+
"""
|
|
68
|
+
if variant not in cls._registry:
|
|
69
|
+
raise ValueError(
|
|
70
|
+
f"Unknown robot variant: '{variant}'. "
|
|
71
|
+
f"Available variants: {list(cls._registry.keys())}"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
config_class = cls._registry[variant]
|
|
75
|
+
return config_class()
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def list_variants(cls) -> list[str]:
|
|
79
|
+
"""List all registered robot variants.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of variant names
|
|
83
|
+
"""
|
|
84
|
+
return list(cls._registry.keys())
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def get_robot_config(variant: str) -> BaseRobotConfig:
|
|
88
|
+
"""Get robot configuration for a specific variant.
|
|
89
|
+
|
|
90
|
+
Convenience function that delegates to ConfigRegistry.get().
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
variant: Robot variant name (e.g., "vega_1")
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Configuration instance
|
|
97
|
+
|
|
98
|
+
Raises:
|
|
99
|
+
ValueError: If variant is not registered
|
|
100
|
+
"""
|
|
101
|
+
return ConfigRegistry.get(variant)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_available_variants() -> list[str]:
|
|
105
|
+
"""Get list of available robot variants.
|
|
106
|
+
|
|
107
|
+
Convenience function that delegates to ConfigRegistry.list_variants().
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
List of registered variant names
|
|
111
|
+
"""
|
|
112
|
+
return ConfigRegistry.list_variants()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
|
|
3
|
+
from ..components.base import BaseComponentConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class BaseRobotConfig:
|
|
8
|
+
"""Base configuration for a robot.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
robot_model: Robot model name (e.g., "vega_1", "vega_1u")
|
|
12
|
+
abbr: Robot abbreviation (e.g., "vg")
|
|
13
|
+
urdf_path: Path to URDF file (relative or absolute)
|
|
14
|
+
components: Dictionary of component configurations
|
|
15
|
+
querables: Dictionary of queryable service names
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
robot_model: str = ""
|
|
19
|
+
abbr: str = ""
|
|
20
|
+
urdf_path: str = ""
|
|
21
|
+
|
|
22
|
+
components: dict[str, BaseComponentConfig] = field(default_factory=dict)
|
|
23
|
+
sensors: dict[str, BaseComponentConfig] = field(default_factory=dict)
|
|
24
|
+
querables: dict[str, str] = field(default_factory=dict)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""Vega-1 robot configurations."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
from dexmate_urdf import robots
|
|
6
|
+
|
|
7
|
+
from ..components.sensors.cameras import ZedXCameraConfig, ZedXOneCameraConfig
|
|
8
|
+
from ..components.sensors.imu import ChassisIMUConfig, ZedIMUConfig
|
|
9
|
+
from ..components.sensors.lidar import Lidar3DConfig
|
|
10
|
+
from ..components.sensors.ultrasonic import UltraSonicConfig
|
|
11
|
+
from ..components.vega_1 import (
|
|
12
|
+
BatteryConfig,
|
|
13
|
+
DexDGripperConfig,
|
|
14
|
+
EStopConfig,
|
|
15
|
+
F5D6HandV2Config,
|
|
16
|
+
HeartbeatConfig,
|
|
17
|
+
Vega1ArmConfig,
|
|
18
|
+
Vega1ChassisConfig,
|
|
19
|
+
Vega1HeadConfig,
|
|
20
|
+
Vega1TorsoConfig,
|
|
21
|
+
)
|
|
22
|
+
from ..registry import register_variant
|
|
23
|
+
from .base import BaseComponentConfig, BaseRobotConfig
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@register_variant("vega_1")
|
|
27
|
+
@dataclass
|
|
28
|
+
class Vega1Config(BaseRobotConfig):
|
|
29
|
+
"""Configuration for Vega-1 robot base (no hands)."""
|
|
30
|
+
|
|
31
|
+
robot_model: str = "vega_1"
|
|
32
|
+
abbr: str = "vg"
|
|
33
|
+
urdf_path: str = str(robots.humanoid.vega_1.vega_1.urdf)
|
|
34
|
+
|
|
35
|
+
components: dict[str, BaseComponentConfig] = field(
|
|
36
|
+
default_factory=lambda: {
|
|
37
|
+
"left_arm": Vega1ArmConfig(side="left"),
|
|
38
|
+
"right_arm": Vega1ArmConfig(side="right"),
|
|
39
|
+
"torso": Vega1TorsoConfig(),
|
|
40
|
+
"chassis": Vega1ChassisConfig(),
|
|
41
|
+
"head": Vega1HeadConfig(),
|
|
42
|
+
"battery": BatteryConfig(),
|
|
43
|
+
"estop": EStopConfig(),
|
|
44
|
+
"heartbeat": HeartbeatConfig(),
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
sensors: dict[str, BaseComponentConfig] = field(
|
|
49
|
+
default_factory=lambda: {
|
|
50
|
+
"head_camera": ZedXCameraConfig(name="head_camera"),
|
|
51
|
+
"chassis_imu": ChassisIMUConfig(name="chassis_imu"),
|
|
52
|
+
"head_imu": ZedIMUConfig(name="head_imu"),
|
|
53
|
+
"front_lidar_3d": Lidar3DConfig(name="lidar_3d_front"),
|
|
54
|
+
"ultrasonic": UltraSonicConfig(name="ultrasonic"),
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
querables: dict[str, str] = field(
|
|
59
|
+
default_factory=lambda: {
|
|
60
|
+
# Querables
|
|
61
|
+
"version_info": "info/versions",
|
|
62
|
+
"status_info": "info/status",
|
|
63
|
+
"hand_info": "info/hand_type",
|
|
64
|
+
"reboot": "system/reboot",
|
|
65
|
+
"clear_error": "system/clear_error",
|
|
66
|
+
"soc_ntp": "time/soc",
|
|
67
|
+
"chassis_led": "system/led",
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@register_variant("vega_1_f5d6")
|
|
73
|
+
@dataclass
|
|
74
|
+
class Vega1F5D6Config(Vega1Config):
|
|
75
|
+
"""Configuration for Vega-1 robot with F5D6 hands."""
|
|
76
|
+
|
|
77
|
+
urdf_path: str = str(robots.humanoid.vega_1.vega_1_f5d6.urdf)
|
|
78
|
+
|
|
79
|
+
components: dict[str, BaseComponentConfig] = field(
|
|
80
|
+
default_factory=lambda: {
|
|
81
|
+
"left_arm": Vega1ArmConfig(side="left"),
|
|
82
|
+
"right_arm": Vega1ArmConfig(side="right"),
|
|
83
|
+
"torso": Vega1TorsoConfig(),
|
|
84
|
+
"chassis": Vega1ChassisConfig(),
|
|
85
|
+
"head": Vega1HeadConfig(),
|
|
86
|
+
"left_hand": F5D6HandV2Config(side="left"),
|
|
87
|
+
"right_hand": F5D6HandV2Config(side="right"),
|
|
88
|
+
"battery": BatteryConfig(),
|
|
89
|
+
"estop": EStopConfig(),
|
|
90
|
+
"heartbeat": HeartbeatConfig(),
|
|
91
|
+
}
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@register_variant("vega_1_gripper")
|
|
96
|
+
@dataclass
|
|
97
|
+
class Vega1DGripperConfig(Vega1Config):
|
|
98
|
+
"""Configuration for Vega-1 robot with D-gripper hands and wrist cameras."""
|
|
99
|
+
|
|
100
|
+
urdf_path: str = str(robots.humanoid.vega_1.vega_1_gripper.urdf)
|
|
101
|
+
|
|
102
|
+
components: dict[str, BaseComponentConfig] = field(
|
|
103
|
+
default_factory=lambda: {
|
|
104
|
+
"left_arm": Vega1ArmConfig(side="left"),
|
|
105
|
+
"right_arm": Vega1ArmConfig(side="right"),
|
|
106
|
+
"torso": Vega1TorsoConfig(),
|
|
107
|
+
"chassis": Vega1ChassisConfig(),
|
|
108
|
+
"head": Vega1HeadConfig(),
|
|
109
|
+
"left_hand": DexDGripperConfig(side="left"),
|
|
110
|
+
"right_hand": DexDGripperConfig(side="right"),
|
|
111
|
+
"battery": BatteryConfig(),
|
|
112
|
+
"estop": EStopConfig(),
|
|
113
|
+
"heartbeat": HeartbeatConfig(),
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
sensors: dict[str, BaseComponentConfig] = field(
|
|
118
|
+
default_factory=lambda: {
|
|
119
|
+
"head_camera": ZedXCameraConfig(name="head_camera"),
|
|
120
|
+
"chassis_imu": ChassisIMUConfig(name="chassis_imu"),
|
|
121
|
+
"head_imu": ZedIMUConfig(name="head_imu"),
|
|
122
|
+
"front_lidar_3d": Lidar3DConfig(name="lidar_3d_front"),
|
|
123
|
+
"ultrasonic": UltraSonicConfig(name="ultrasonic"),
|
|
124
|
+
"left_wrist_camera": ZedXOneCameraConfig(side="left"),
|
|
125
|
+
"right_wrist_camera": ZedXOneCameraConfig(side="right"),
|
|
126
|
+
}
|
|
127
|
+
)
|