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
dexcontrol/core/hand.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
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
|
+
"""Robot hand control module.
|
|
7
|
+
|
|
8
|
+
This module provides the Hand class for controlling a robotic hand through Zenoh
|
|
9
|
+
communication. It handles joint position control and state monitoring.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
import zenoh
|
|
14
|
+
from jaxtyping import Float
|
|
15
|
+
|
|
16
|
+
from dexcontrol.config.core.hand import HandConfig
|
|
17
|
+
from dexcontrol.core.component import RobotJointComponent
|
|
18
|
+
from dexcontrol.proto import dexcontrol_msg_pb2
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Hand(RobotJointComponent):
|
|
22
|
+
"""Robot hand control class.
|
|
23
|
+
|
|
24
|
+
This class provides methods to control a robotic hand by publishing commands and
|
|
25
|
+
receiving state information through Zenoh communication.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
configs: HandConfig,
|
|
31
|
+
zenoh_session: zenoh.Session,
|
|
32
|
+
) -> None:
|
|
33
|
+
"""Initialize the hand controller.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
configs: Hand configuration parameters containing communication topics
|
|
37
|
+
and predefined hand positions.
|
|
38
|
+
zenoh_session: Active Zenoh communication session for message passing.
|
|
39
|
+
"""
|
|
40
|
+
super().__init__(
|
|
41
|
+
state_sub_topic=configs.state_sub_topic,
|
|
42
|
+
control_pub_topic=configs.control_pub_topic,
|
|
43
|
+
state_message_type=dexcontrol_msg_pb2.HandState,
|
|
44
|
+
zenoh_session=zenoh_session,
|
|
45
|
+
joint_name=configs.joint_name,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Store predefined hand positions as private attributes
|
|
49
|
+
self._joint_pos_open = np.array(configs.joint_pos_open, dtype=np.float32)
|
|
50
|
+
self._joint_pos_close = np.array(configs.joint_pos_close, dtype=np.float32)
|
|
51
|
+
|
|
52
|
+
def _send_position_command(
|
|
53
|
+
self, joint_pos: Float[np.ndarray, " N"] | list[float]
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Send joint position control commands to the hand.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
joint_pos: Joint positions as list or numpy array.
|
|
59
|
+
"""
|
|
60
|
+
control_msg = dexcontrol_msg_pb2.HandCommand()
|
|
61
|
+
joint_pos_array = self._convert_joint_cmd_to_array(joint_pos)
|
|
62
|
+
control_msg.joint_pos.extend(joint_pos_array.tolist())
|
|
63
|
+
self._publish_control(control_msg)
|
|
64
|
+
|
|
65
|
+
def set_joint_pos(
|
|
66
|
+
self,
|
|
67
|
+
joint_pos: Float[np.ndarray, " N"] | list[float] | dict[str, float],
|
|
68
|
+
relative: bool = False,
|
|
69
|
+
wait_time: float = 0.0,
|
|
70
|
+
wait_kwargs: dict[str, float] | None = None,
|
|
71
|
+
) -> None:
|
|
72
|
+
"""Send joint position control commands to the hand.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
joint_pos: Joint positions as either:
|
|
76
|
+
- List of joint values [j1, j2, ..., jN]
|
|
77
|
+
- Numpy array with shape (N,)
|
|
78
|
+
- Dictionary mapping joint names to position values
|
|
79
|
+
relative: If True, the joint positions are relative to the current position.
|
|
80
|
+
wait_time: Time to wait after setting the joint positions.
|
|
81
|
+
wait_kwargs: Optional parameters for trajectory generation (not used in Hand).
|
|
82
|
+
|
|
83
|
+
Raises:
|
|
84
|
+
ValueError: If joint_pos dictionary contains invalid joint names.
|
|
85
|
+
"""
|
|
86
|
+
super().set_joint_pos(joint_pos, relative, wait_time, wait_kwargs)
|
|
87
|
+
|
|
88
|
+
def open_hand(self, wait_time: float = 0.0) -> None:
|
|
89
|
+
"""Open the hand to the predefined open position.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
wait_time: Time to wait after opening the hand.
|
|
93
|
+
"""
|
|
94
|
+
self.set_joint_pos(self._joint_pos_open, wait_time=wait_time)
|
|
95
|
+
|
|
96
|
+
def close_hand(self, wait_time: float = 0.0) -> None:
|
|
97
|
+
"""Close the hand to the predefined closed position.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
wait_time: Time to wait after closing the hand.
|
|
101
|
+
"""
|
|
102
|
+
self.set_joint_pos(self._joint_pos_close, wait_time=wait_time)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class HandF5D6(Hand):
|
|
106
|
+
"""Specialized hand class for the F5D6 hand model.
|
|
107
|
+
|
|
108
|
+
Extends the basic Hand class with additional control methods specific to
|
|
109
|
+
the F5D6 hand model.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
def close_hand(self, wait_time: float = 0.0) -> None:
|
|
113
|
+
"""Close the hand fully using a two-step approach for better control."""
|
|
114
|
+
if self.is_joint_pos_reached(self._joint_pos_close, tolerance=0.1):
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
# First step: Move to intermediate position
|
|
118
|
+
intermediate_pos = self._get_intermediate_close_position()
|
|
119
|
+
first_step_wait_time = 0.8
|
|
120
|
+
self.set_joint_pos(intermediate_pos, wait_time=first_step_wait_time)
|
|
121
|
+
|
|
122
|
+
# Second step: Move to final closed position
|
|
123
|
+
remaining_wait_time = max(0.0, wait_time - first_step_wait_time)
|
|
124
|
+
self.set_joint_pos(self._joint_pos_close, wait_time=remaining_wait_time)
|
|
125
|
+
|
|
126
|
+
def open_hand(self, wait_time: float = 0.0) -> None:
|
|
127
|
+
"""Open the hand fully using a two-step approach for better control."""
|
|
128
|
+
if self.is_joint_pos_reached(self._joint_pos_open, tolerance=0.1):
|
|
129
|
+
return
|
|
130
|
+
|
|
131
|
+
# First step: Move to intermediate position
|
|
132
|
+
intermediate_pos = self._get_intermediate_open_position()
|
|
133
|
+
first_step_wait_time = 0.3
|
|
134
|
+
self.set_joint_pos(intermediate_pos, wait_time=first_step_wait_time)
|
|
135
|
+
|
|
136
|
+
# Second step: Move to final open position
|
|
137
|
+
remaining_wait_time = max(0.0, wait_time - first_step_wait_time)
|
|
138
|
+
self.set_joint_pos(self._joint_pos_open, wait_time=remaining_wait_time)
|
|
139
|
+
|
|
140
|
+
def _get_intermediate_close_position(self) -> np.ndarray:
|
|
141
|
+
"""Get intermediate position for closing hand.
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
Intermediate joint positions for smooth closing motion.
|
|
145
|
+
"""
|
|
146
|
+
intermediate_pos = self._joint_pos_close.copy()
|
|
147
|
+
ratio = 0.2
|
|
148
|
+
# Adjust thumb opposition joint (last joint)
|
|
149
|
+
intermediate_pos[-1] = self._joint_pos_close[-1] * ratio + self._joint_pos_open[
|
|
150
|
+
-1
|
|
151
|
+
] * (1 - ratio)
|
|
152
|
+
return intermediate_pos
|
|
153
|
+
|
|
154
|
+
def _get_intermediate_open_position(self) -> np.ndarray:
|
|
155
|
+
"""Get intermediate position for opening hand.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
Intermediate joint positions for smooth opening motion.
|
|
159
|
+
"""
|
|
160
|
+
intermediate_pos = self._joint_pos_close.copy()
|
|
161
|
+
ratio = 0.2
|
|
162
|
+
# Adjust thumb opposition joint (last joint)
|
|
163
|
+
intermediate_pos[-1] = self._joint_pos_close[-1] * ratio + self._joint_pos_open[
|
|
164
|
+
-1
|
|
165
|
+
] * (1 - ratio)
|
|
166
|
+
# Adjust thumb flexion joint (first joint)
|
|
167
|
+
intermediate_pos[0] = self._joint_pos_close[0] * ratio + self._joint_pos_open[
|
|
168
|
+
0
|
|
169
|
+
] * (1 - ratio)
|
|
170
|
+
return intermediate_pos
|
dexcontrol/core/head.py
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
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
|
+
"""Robot head control module.
|
|
7
|
+
|
|
8
|
+
This module provides the Head class for controlling a robot head through Zenoh
|
|
9
|
+
communication. It handles joint position and velocity control, mode setting, and
|
|
10
|
+
state monitoring.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import time
|
|
14
|
+
from typing import Final, Literal
|
|
15
|
+
|
|
16
|
+
import numpy as np
|
|
17
|
+
import zenoh
|
|
18
|
+
from jaxtyping import Float
|
|
19
|
+
from loguru import logger
|
|
20
|
+
|
|
21
|
+
from dexcontrol.config.core import HeadConfig
|
|
22
|
+
from dexcontrol.core.component import RobotJointComponent
|
|
23
|
+
from dexcontrol.proto import dexcontrol_msg_pb2, dexcontrol_query_pb2
|
|
24
|
+
from dexcontrol.utils.os_utils import resolve_key_name
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class Head(RobotJointComponent):
|
|
28
|
+
"""Robot head control class.
|
|
29
|
+
|
|
30
|
+
This class provides methods to control a robot head by publishing commands and
|
|
31
|
+
receiving state information through Zenoh communication.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
mode_querier: Zenoh querier for setting head mode.
|
|
35
|
+
default_vel: Default joint velocities for all joints.
|
|
36
|
+
max_vel: Maximum allowed joint velocities for all joints.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
configs: HeadConfig,
|
|
42
|
+
zenoh_session: zenoh.Session,
|
|
43
|
+
) -> None:
|
|
44
|
+
"""Initialize the head controller.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
configs: Configuration parameters for the head including communication topics.
|
|
48
|
+
zenoh_session: Active Zenoh communication session for message passing.
|
|
49
|
+
"""
|
|
50
|
+
super().__init__(
|
|
51
|
+
state_sub_topic=configs.state_sub_topic,
|
|
52
|
+
control_pub_topic=configs.control_pub_topic,
|
|
53
|
+
state_message_type=dexcontrol_msg_pb2.HeadState,
|
|
54
|
+
zenoh_session=zenoh_session,
|
|
55
|
+
joint_name=configs.joint_name,
|
|
56
|
+
pose_pool=configs.pose_pool,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
self.mode_querier: Final[zenoh.Querier] = zenoh_session.declare_querier(
|
|
60
|
+
resolve_key_name(configs.set_mode_query), timeout=2.0
|
|
61
|
+
)
|
|
62
|
+
self.default_vel: Final[float] = configs.default_vel
|
|
63
|
+
self.max_vel: Final[float] = configs.max_vel
|
|
64
|
+
assert self.max_vel > self.default_vel, (
|
|
65
|
+
"max_vel must be greater than default_vel"
|
|
66
|
+
)
|
|
67
|
+
self._joint_limit: Float[np.ndarray, "3 2"] = np.stack(
|
|
68
|
+
[configs.joint_limit_lower, configs.joint_limit_upper], axis=1
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def set_joint_pos(
|
|
72
|
+
self,
|
|
73
|
+
joint_pos: Float[np.ndarray, "2"] | list[float] | dict[str, float],
|
|
74
|
+
relative: bool = False,
|
|
75
|
+
wait_time: float = 0.0,
|
|
76
|
+
wait_kwargs: dict[str, float] | None = None,
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Send joint position control commands to the head.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
joint_pos: Joint positions as either:
|
|
82
|
+
- List of joint values [j1, j2]
|
|
83
|
+
- Numpy array with shape (2,), in radians
|
|
84
|
+
- Dictionary mapping joint names to position values
|
|
85
|
+
relative: If True, the joint positions are relative to the current position.
|
|
86
|
+
wait_time: Time to wait after sending command in seconds. If 0, returns
|
|
87
|
+
immediately after sending command.
|
|
88
|
+
wait_kwargs: Optional parameters for trajectory generation (not used in Head).
|
|
89
|
+
|
|
90
|
+
Raises:
|
|
91
|
+
ValueError: If joint_pos dictionary contains invalid joint names.
|
|
92
|
+
"""
|
|
93
|
+
self.set_joint_pos_vel(
|
|
94
|
+
joint_pos, joint_vel=None, relative=relative, wait_time=wait_time
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def set_joint_pos_vel(
|
|
98
|
+
self,
|
|
99
|
+
joint_pos: Float[np.ndarray, "2"] | list[float] | dict[str, float],
|
|
100
|
+
joint_vel: Float[np.ndarray, "2"]
|
|
101
|
+
| list[float]
|
|
102
|
+
| dict[str, float]
|
|
103
|
+
| float
|
|
104
|
+
| None = None,
|
|
105
|
+
relative: bool = False,
|
|
106
|
+
wait_time: float = 0.0,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Send control commands to the head.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
joint_pos: Joint positions as either:
|
|
112
|
+
- List of joint values [j1, j2]
|
|
113
|
+
- Numpy array with shape (2,), in radians
|
|
114
|
+
- Dictionary mapping joint names to position values
|
|
115
|
+
joint_vel: Optional joint velocities as either:
|
|
116
|
+
- List of joint values [v1, v2]
|
|
117
|
+
- Numpy array with shape (2,), in rad/s
|
|
118
|
+
- Dictionary mapping joint names to velocity values
|
|
119
|
+
- Single float value to be applied to all joints
|
|
120
|
+
If None, velocities are calculated based on default velocity setting.
|
|
121
|
+
relative: If True, the joint positions are relative to the current position.
|
|
122
|
+
wait_time: Time to wait after sending command in seconds. If 0, returns
|
|
123
|
+
immediately after sending command.
|
|
124
|
+
|
|
125
|
+
Raises:
|
|
126
|
+
ValueError: If wait_time is negative or joint_pos dictionary contains
|
|
127
|
+
invalid joint names.
|
|
128
|
+
"""
|
|
129
|
+
if wait_time < 0.0:
|
|
130
|
+
raise ValueError("wait_time must be greater than or equal to 0")
|
|
131
|
+
|
|
132
|
+
# Handle relative positioning
|
|
133
|
+
if relative:
|
|
134
|
+
joint_pos = self._resolve_relative_joint_cmd(joint_pos)
|
|
135
|
+
|
|
136
|
+
# Convert inputs to numpy arrays
|
|
137
|
+
joint_pos = self._convert_joint_cmd_to_array(joint_pos)
|
|
138
|
+
joint_vel = self._process_joint_velocities(joint_vel, joint_pos)
|
|
139
|
+
|
|
140
|
+
# Create and send control message
|
|
141
|
+
control_msg = dexcontrol_msg_pb2.HeadCommand()
|
|
142
|
+
control_msg.joint_pos.extend(joint_pos.tolist())
|
|
143
|
+
control_msg.joint_vel.extend(joint_vel.tolist())
|
|
144
|
+
self._publish_control(control_msg)
|
|
145
|
+
|
|
146
|
+
# Wait if specified
|
|
147
|
+
if wait_time > 0.0:
|
|
148
|
+
time.sleep(wait_time)
|
|
149
|
+
|
|
150
|
+
def set_mode(self, mode: Literal["enable", "disable"]) -> None:
|
|
151
|
+
"""Set the operating mode of the head.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
mode: Operating mode for the head. Must be either "enable" or "disable".
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ValueError: If an invalid mode is specified.
|
|
158
|
+
"""
|
|
159
|
+
mode_map = {
|
|
160
|
+
"enable": dexcontrol_query_pb2.SetHeadMode.Mode.ENABLE,
|
|
161
|
+
"disable": dexcontrol_query_pb2.SetHeadMode.Mode.DISABLE,
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if mode not in mode_map:
|
|
165
|
+
raise ValueError(
|
|
166
|
+
f"Invalid mode: {mode}. Must be one of {list(mode_map.keys())}"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
query_msg = dexcontrol_query_pb2.SetHeadMode()
|
|
170
|
+
query_msg.mode = mode_map[mode]
|
|
171
|
+
replies = self.mode_querier.get(payload=query_msg.SerializeToString())
|
|
172
|
+
|
|
173
|
+
for reply in replies:
|
|
174
|
+
if reply.ok is not None and reply.ok.payload is not None:
|
|
175
|
+
logger.info(reply.ok.payload.to_string())
|
|
176
|
+
time.sleep(0.5)
|
|
177
|
+
|
|
178
|
+
def get_joint_limit(self) -> Float[np.ndarray, "3 2"]:
|
|
179
|
+
"""Get the joint limits of the head.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
Array of joint limits with shape (3, 2), where the first column contains
|
|
183
|
+
lower limits and the second column contains upper limits.
|
|
184
|
+
"""
|
|
185
|
+
return self._joint_limit
|
|
186
|
+
|
|
187
|
+
def stop(self) -> None:
|
|
188
|
+
"""Stop the head by setting target position to current position with zero velocity."""
|
|
189
|
+
current_pos = self.get_joint_pos()
|
|
190
|
+
zero_vel = np.zeros(3, dtype=np.float32)
|
|
191
|
+
self.set_joint_pos_vel(current_pos, zero_vel, relative=False, wait_time=0.0)
|
|
192
|
+
|
|
193
|
+
def shutdown(self) -> None:
|
|
194
|
+
"""Clean up Zenoh resources for the head component."""
|
|
195
|
+
self.stop()
|
|
196
|
+
super().shutdown()
|
|
197
|
+
|
|
198
|
+
def _process_joint_velocities(
|
|
199
|
+
self,
|
|
200
|
+
joint_vel: Float[np.ndarray, "2"]
|
|
201
|
+
| list[float]
|
|
202
|
+
| dict[str, float]
|
|
203
|
+
| float
|
|
204
|
+
| None,
|
|
205
|
+
joint_pos: np.ndarray,
|
|
206
|
+
) -> np.ndarray:
|
|
207
|
+
"""Process and validate joint velocities.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
joint_vel: Joint velocities in various formats or None.
|
|
211
|
+
joint_pos: Target joint positions for velocity calculation.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Processed joint velocities as numpy array.
|
|
215
|
+
"""
|
|
216
|
+
if joint_vel is None:
|
|
217
|
+
# Calculate velocities based on motion direction and default velocity
|
|
218
|
+
joint_motion = joint_pos - self.get_joint_pos()
|
|
219
|
+
motion_norm = np.linalg.norm(joint_motion)
|
|
220
|
+
|
|
221
|
+
if motion_norm < 1e-6: # Avoid division by zero
|
|
222
|
+
return np.zeros(2, dtype=np.float32)
|
|
223
|
+
|
|
224
|
+
# Scale velocities by default velocity
|
|
225
|
+
return (joint_motion / motion_norm) * self.default_vel
|
|
226
|
+
|
|
227
|
+
if isinstance(joint_vel, (int, float)):
|
|
228
|
+
# Single value - apply to all joints
|
|
229
|
+
return np.full(2, joint_vel, dtype=np.float32)
|
|
230
|
+
|
|
231
|
+
# Convert to array and clip to max velocity
|
|
232
|
+
return self._convert_joint_cmd_to_array(joint_vel, clip_value=self.max_vel)
|