kuavo-humanoid-sdk 0.1.1__py3-none-any.whl → 1.1.2a692__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 kuavo-humanoid-sdk might be problematic. Click here for more details.
- kuavo_humanoid_sdk/__init__.py +1 -0
- kuavo_humanoid_sdk/interfaces/data_types.py +30 -7
- kuavo_humanoid_sdk/interfaces/end_effector.py +23 -4
- kuavo_humanoid_sdk/kuavo/__init__.py +1 -1
- kuavo_humanoid_sdk/kuavo/core/core.py +21 -13
- kuavo_humanoid_sdk/kuavo/core/dex_hand_control.py +44 -15
- kuavo_humanoid_sdk/kuavo/core/ros/control.py +72 -16
- kuavo_humanoid_sdk/kuavo/core/ros/param.py +6 -0
- kuavo_humanoid_sdk/kuavo/core/ros/state.py +88 -33
- kuavo_humanoid_sdk/kuavo/core/ros_env.py +48 -10
- kuavo_humanoid_sdk/kuavo/dexterous_hand.py +40 -25
- kuavo_humanoid_sdk/kuavo/leju_claw.py +24 -17
- kuavo_humanoid_sdk/kuavo/robot.py +42 -15
- kuavo_humanoid_sdk/kuavo/robot_arm.py +6 -0
- kuavo_humanoid_sdk/kuavo/robot_info.py +1 -0
- {kuavo_humanoid_sdk-0.1.1.dist-info → kuavo_humanoid_sdk-1.1.2a692.dist-info}/METADATA +74 -18
- kuavo_humanoid_sdk-1.1.2a692.dist-info/RECORD +27 -0
- {kuavo_humanoid_sdk-0.1.1.dist-info → kuavo_humanoid_sdk-1.1.2a692.dist-info}/WHEEL +1 -1
- kuavo_humanoid_sdk-0.1.1.dist-info/RECORD +0 -27
- {kuavo_humanoid_sdk-0.1.1.dist-info → kuavo_humanoid_sdk-1.1.2a692.dist-info}/top_level.txt +0 -0
|
@@ -52,37 +52,54 @@ class KuavoROSEnv:
|
|
|
52
52
|
Raises:
|
|
53
53
|
Exception: If the modelPath parameter is not found or the modelPath parameter is not a valid path.
|
|
54
54
|
"""
|
|
55
|
+
# if generate docs, skip init.
|
|
56
|
+
if 'GEN_KUAVO_HUMANOID_SDK_DOCS' in os.environ:
|
|
57
|
+
return True
|
|
58
|
+
|
|
55
59
|
# Return directly if already initialized successfully
|
|
56
60
|
if self._init_success:
|
|
57
61
|
return True
|
|
58
|
-
|
|
62
|
+
|
|
59
63
|
# Check if ROS master is running
|
|
60
64
|
try:
|
|
61
65
|
rospy.get_master().getPid()
|
|
62
66
|
except Exception as e:
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
print(f"\033[31m\nError:Can't connect to ros master, Please start roscore first or check ROS_MASTER_URI.\nException:{e}\033[0m"
|
|
68
|
+
"\nMaybe manually launch the app first?"
|
|
69
|
+
"\n - for example(sim): roslaunch humanoid_controller load_kuavo_mujoco_sim.launch, "
|
|
70
|
+
"\n - for example(real): roslaunch humanoid_controller load_kuavo_real.launch\n")
|
|
71
|
+
exit(1)
|
|
72
|
+
|
|
73
|
+
"""
|
|
74
|
+
# NOTE: We add kuavo_msgs/motion_capture_ik package in SDK integration.
|
|
65
75
|
kuavo_ws_root = self._get_kuavo_ws_root()
|
|
66
|
-
|
|
67
76
|
# Add kuavo_msgs package to Python path
|
|
68
77
|
dist_path = os.path.join(kuavo_ws_root, 'devel/lib/python3/dist-packages')
|
|
69
78
|
if not os.path.exists(dist_path):
|
|
70
79
|
dist_path = os.path.join(kuavo_ws_root, 'install/lib/python3/dist-packages')
|
|
71
80
|
if not os.path.exists(dist_path):
|
|
72
81
|
raise Exception(f"{dist_path} package not found in Python path")
|
|
73
|
-
|
|
74
82
|
if dist_path not in os.sys.path:
|
|
75
83
|
os.sys.path.append(dist_path)
|
|
76
|
-
|
|
84
|
+
"""
|
|
85
|
+
|
|
77
86
|
# Initialize the ROS node
|
|
78
87
|
if not rospy.get_node_uri():
|
|
79
88
|
rospy.init_node(f'kuavo_sdk_node', anonymous=True, disable_signals=True)
|
|
80
89
|
|
|
81
|
-
|
|
82
|
-
self.
|
|
83
|
-
|
|
90
|
+
# Only check nodes exist when Init SDK, if not, tips user manually launch nodes.
|
|
91
|
+
# self.launch_ik_node()
|
|
92
|
+
# self.launch_gait_switch_node()
|
|
93
|
+
deps_nodes = ['/humanoid_gait_switch_by_name']
|
|
94
|
+
for node in deps_nodes:
|
|
95
|
+
if not KuavoROSEnv.check_rosnode_exists(node):
|
|
96
|
+
print(f"\033[31m\nError:Node {node} not found. Please launch it manuallly.\033[0m")
|
|
97
|
+
exit(1)
|
|
98
|
+
|
|
84
99
|
self._init_success = True # Set flag after successful initialization
|
|
100
|
+
|
|
85
101
|
return True
|
|
102
|
+
|
|
86
103
|
def _launch_ros_node(self, node_name, launch_cmd, log_name):
|
|
87
104
|
"""Launch a ROS node with the given command and log the output.
|
|
88
105
|
|
|
@@ -193,4 +210,25 @@ class KuavoROSEnv:
|
|
|
193
210
|
self._launch_ros_node('Gait switch node', full_cmd, 'launch_gait_switch')
|
|
194
211
|
|
|
195
212
|
except Exception as e:
|
|
196
|
-
raise Exception(f"Failed to launch gait_switch node: {e}")
|
|
213
|
+
raise Exception(f"Failed to launch gait_switch node: {e}")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@staticmethod
|
|
217
|
+
def check_rosnode_exists(node_name):
|
|
218
|
+
"""Check if a ROS node is running.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
node_name (str): Name of the node to check
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
bool: True if node is running, False otherwise
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
nodes = subprocess.check_output(['rosnode', 'list']).decode('utf-8').split('\n')
|
|
228
|
+
return node_name in nodes
|
|
229
|
+
except subprocess.CalledProcessError as e:
|
|
230
|
+
SDKLogger.error(f"Error checking if node {node_name} exists: {e}")
|
|
231
|
+
return False
|
|
232
|
+
except Exception as e:
|
|
233
|
+
SDKLogger.error(f"Error checking if node {node_name} exists: {e}")
|
|
234
|
+
return False
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# coding: utf-8
|
|
3
3
|
from typing import Tuple
|
|
4
4
|
from kuavo_humanoid_sdk.interfaces.end_effector import EndEffector
|
|
5
|
-
from kuavo_humanoid_sdk.interfaces.data_types import EndEffectorSide, EndEffectorState
|
|
5
|
+
from kuavo_humanoid_sdk.interfaces.data_types import EndEffectorSide, EndEffectorState, KuavoDexHandTouchState
|
|
6
6
|
from kuavo_humanoid_sdk.kuavo.core.dex_hand_control import DexHandControl
|
|
7
7
|
from kuavo_humanoid_sdk.kuavo.core.ros.state import KuavoRobotStateCore
|
|
8
8
|
|
|
@@ -29,8 +29,8 @@ class DexterousHand(EndEffector):
|
|
|
29
29
|
Note:
|
|
30
30
|
target_velocities and target_torques are not supported.
|
|
31
31
|
"""
|
|
32
|
-
if len(target_positions) !=
|
|
33
|
-
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {
|
|
32
|
+
if len(target_positions) != self.joint_count():
|
|
33
|
+
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {self.joint_count()}")
|
|
34
34
|
|
|
35
35
|
q = [max(0, min(100, pos if isinstance(pos, int) else int(pos))) for pos in target_positions]
|
|
36
36
|
|
|
@@ -54,8 +54,8 @@ class DexterousHand(EndEffector):
|
|
|
54
54
|
Note:
|
|
55
55
|
target_velocities and target_torques are not supported.
|
|
56
56
|
"""
|
|
57
|
-
if len(target_positions) != (
|
|
58
|
-
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {
|
|
57
|
+
if len(target_positions) != (self.joint_count()/2):
|
|
58
|
+
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {self.joint_count()/2}.")
|
|
59
59
|
|
|
60
60
|
q = [max(0, min(100, pos if isinstance(pos, int) else int(pos))) for pos in target_positions]
|
|
61
61
|
|
|
@@ -78,8 +78,8 @@ class DexterousHand(EndEffector):
|
|
|
78
78
|
Note:
|
|
79
79
|
target_velocities and target_torques are not supported.
|
|
80
80
|
"""
|
|
81
|
-
if len(target_positions) != (
|
|
82
|
-
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {
|
|
81
|
+
if len(target_positions) != (self.joint_count()/2):
|
|
82
|
+
raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {self.joint_count()/2}.")
|
|
83
83
|
|
|
84
84
|
q = [max(0, min(100, pos if isinstance(pos, int) else int(pos))) for pos in target_positions]
|
|
85
85
|
|
|
@@ -95,7 +95,7 @@ class DexterousHand(EndEffector):
|
|
|
95
95
|
Returns:
|
|
96
96
|
bool: True if open command sent successfully, False otherwise.
|
|
97
97
|
"""
|
|
98
|
-
zero_pos = [0]*
|
|
98
|
+
zero_pos = [0]*self.joint_count()
|
|
99
99
|
if side == EndEffectorSide.LEFT:
|
|
100
100
|
return self.dex_hand_control.control(target_positions=zero_pos[:len(zero_pos)//2], side=EndEffectorSide.LEFT)
|
|
101
101
|
elif side == EndEffectorSide.RIGHT:
|
|
@@ -119,10 +119,11 @@ class DexterousHand(EndEffector):
|
|
|
119
119
|
gesture = []
|
|
120
120
|
if l_gesture_name is not None:
|
|
121
121
|
gesture.append({'gesture_name':l_gesture_name, 'hand_side':EndEffectorSide.LEFT})
|
|
122
|
+
self.dex_hand_control.make_gestures(gesture)
|
|
122
123
|
if r_gesture_name is not None:
|
|
123
124
|
gesture.append({'gesture_name':r_gesture_name, 'hand_side':EndEffectorSide.RIGHT})
|
|
124
|
-
|
|
125
|
-
|
|
125
|
+
self.dex_hand_control.make_gestures(gesture)
|
|
126
|
+
return True
|
|
126
127
|
def get_gesture_names(self)->list:
|
|
127
128
|
"""Get the names of all gestures.
|
|
128
129
|
|
|
@@ -134,7 +135,6 @@ class DexterousHand(EndEffector):
|
|
|
134
135
|
return self.dex_hand_control.get_gesture_names()
|
|
135
136
|
|
|
136
137
|
def get_state(self)->Tuple[EndEffectorState, EndEffectorState]:
|
|
137
|
-
# TODO(kuavo): Not implemented yet
|
|
138
138
|
"""Get the state of the dexterous hand.
|
|
139
139
|
|
|
140
140
|
Returns:
|
|
@@ -142,42 +142,57 @@ class DexterousHand(EndEffector):
|
|
|
142
142
|
"""
|
|
143
143
|
return self._rb_state.eef_state
|
|
144
144
|
|
|
145
|
-
def get_position(self)->Tuple[
|
|
146
|
-
# TODO(kuavo): Not implemented yet
|
|
145
|
+
def get_position(self)->Tuple[list, list]:
|
|
147
146
|
"""Get the position of the dexterous hand.
|
|
148
147
|
|
|
149
148
|
Returns:
|
|
150
|
-
Tuple[
|
|
149
|
+
Tuple[list, list]: The position of the dexterous hand.
|
|
151
150
|
"""
|
|
152
|
-
|
|
151
|
+
state = self._rb_state.eef_state
|
|
152
|
+
return (state[0].position, state[1].position)
|
|
153
153
|
|
|
154
|
-
def get_velocity(self)->Tuple[
|
|
155
|
-
# TODO(kuavo): Not implemented yet
|
|
154
|
+
def get_velocity(self)->Tuple[list, list]:
|
|
156
155
|
"""Get the velocity of the dexterous hand.
|
|
157
156
|
|
|
158
157
|
Returns:
|
|
159
|
-
Tuple[
|
|
158
|
+
Tuple[list, list]: The velocity of the dexterous hand.
|
|
160
159
|
"""
|
|
161
|
-
|
|
160
|
+
state = self._rb_state.eef_state
|
|
161
|
+
return (state[0].velocity, state[1].velocity)
|
|
162
162
|
|
|
163
|
-
def get_effort(self)->Tuple[
|
|
164
|
-
# TODO(kuavo): Not implemented yet
|
|
163
|
+
def get_effort(self)->Tuple[list, list]:
|
|
165
164
|
"""Get the effort of the dexterous hand.
|
|
166
165
|
|
|
167
166
|
Returns:
|
|
168
|
-
Tuple[
|
|
167
|
+
Tuple[list, list]: The effort of the dexterous hand.
|
|
169
168
|
|
|
170
169
|
Note:
|
|
171
170
|
0 ~ 100 for each finger. Fraction of max motor current, absolute number.
|
|
172
171
|
The max motor current is 600mA, in a word, 100.
|
|
173
172
|
"""
|
|
174
|
-
|
|
173
|
+
state = self._rb_state.eef_state
|
|
174
|
+
return (state[0].effort, state[1].effort)
|
|
175
175
|
|
|
176
176
|
def get_grasping_state(self)->Tuple[EndEffectorState.GraspingState, EndEffectorState.GraspingState]:
|
|
177
|
-
# TODO(kuavo): Not implemented yet
|
|
178
177
|
"""Get the grasping state of the dexterous hand.
|
|
179
178
|
|
|
179
|
+
Note:
|
|
180
|
+
The grasping state is not implemented yet.
|
|
181
|
+
|
|
180
182
|
Returns:
|
|
181
183
|
Tuple[EndEffectorState.GraspingState, EndEffectorState.GraspingState]: The grasping state of the dexterous hand.
|
|
182
184
|
"""
|
|
183
|
-
|
|
185
|
+
raise NotImplementedError("This function is not implemented yet")
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class TouchDexterousHand(DexterousHand):
|
|
189
|
+
def __init__(self):
|
|
190
|
+
super().__init__()
|
|
191
|
+
|
|
192
|
+
def get_touch_state(self)-> Tuple[KuavoDexHandTouchState, KuavoDexHandTouchState]:
|
|
193
|
+
"""Get the touch state of the dexterous hand.
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Tuple[KuavoDexHandTouchState, KuavoDexHandTouchState]
|
|
197
|
+
"""
|
|
198
|
+
return self._rb_state.dexhand_touch_state
|
|
@@ -22,7 +22,7 @@ class LejuClaw(EndEffector):
|
|
|
22
22
|
target_torques (list, optional): The target torques of the claws. If None, default value [1.0, 1.0] will be used.
|
|
23
23
|
|
|
24
24
|
Note:
|
|
25
|
-
The target_positions, target_velocities, target_torques must be a list of length
|
|
25
|
+
The target_positions, target_velocities, target_torques must be a list of length `self.joint_count()`.
|
|
26
26
|
After calling this function, you can call wait_for_finish() to wait until the claws reach the target position.
|
|
27
27
|
|
|
28
28
|
Warning:
|
|
@@ -31,18 +31,22 @@ class LejuClaw(EndEffector):
|
|
|
31
31
|
Returns:
|
|
32
32
|
bool: True if the claws are successfully gripped, False otherwise
|
|
33
33
|
"""
|
|
34
|
-
if target_positions is None:
|
|
34
|
+
if target_positions is None or len(target_positions) != self.joint_count():
|
|
35
35
|
raise ValueError("Target positions must be provided.")
|
|
36
36
|
|
|
37
37
|
target_positions = [max(0.0, min(100.0, pos)) for pos in target_positions]
|
|
38
38
|
if target_velocities is None:
|
|
39
39
|
target_velocities = [90, 90]
|
|
40
40
|
else:
|
|
41
|
+
if len(target_velocities) != self.joint_count():
|
|
42
|
+
raise ValueError("Target velocities must be a list of length 2.")
|
|
41
43
|
target_velocities = [max(0.0, min(100.0, vel)) for vel in target_velocities]
|
|
42
44
|
|
|
43
45
|
if target_torques is None:
|
|
44
46
|
target_torques = [1.0, 1.0]
|
|
45
47
|
else:
|
|
48
|
+
if len(target_torques) != self.joint_count():
|
|
49
|
+
raise ValueError("Target torques must be a list of length 2.")
|
|
46
50
|
target_torques = [max(0.0, min(10.0, torque)) for torque in target_torques]
|
|
47
51
|
|
|
48
52
|
return self.leju_claw_control.control(target_positions, target_velocities, target_torques, EndEffectorSide.BOTH)
|
|
@@ -56,7 +60,7 @@ class LejuClaw(EndEffector):
|
|
|
56
60
|
target_torques (list, optional): The target torque of the left claw. If None, default value 1.0 will be used.
|
|
57
61
|
|
|
58
62
|
Note:
|
|
59
|
-
The target_positions, target_velocities, target_torques must be a list of length
|
|
63
|
+
The target_positions, target_velocities, target_torques must be a list of length `self.joint_count()/2`.
|
|
60
64
|
After calling this function, you can call wait_for_finish() to wait until the claws reach the target position.
|
|
61
65
|
|
|
62
66
|
Warning:
|
|
@@ -69,8 +73,8 @@ class LejuClaw(EndEffector):
|
|
|
69
73
|
raise ValueError("Target positions must be provided.")
|
|
70
74
|
|
|
71
75
|
for data in [target_positions, target_velocities, target_torques]:
|
|
72
|
-
if data is not None and len(data) !=
|
|
73
|
-
raise ValueError("Target
|
|
76
|
+
if data is not None and len(data) != self.joint_count()/2:
|
|
77
|
+
raise ValueError(f"Target data must be a list of length {self.joint_count()/2}.")
|
|
74
78
|
|
|
75
79
|
q = max(0.0, min(100.0, target_positions[0]))
|
|
76
80
|
|
|
@@ -98,7 +102,7 @@ class LejuClaw(EndEffector):
|
|
|
98
102
|
bool: True if the claw is successfully gripped, False otherwise.
|
|
99
103
|
|
|
100
104
|
Note:
|
|
101
|
-
The target_positions, target_velocities, target_torques must be a list of length
|
|
105
|
+
The target_positions, target_velocities, target_torques must be a list of length `self.joint_count()/2`.
|
|
102
106
|
After calling this function, you can call wait_for_finish() to wait until the claws reach the target position.
|
|
103
107
|
|
|
104
108
|
Warning:
|
|
@@ -108,8 +112,8 @@ class LejuClaw(EndEffector):
|
|
|
108
112
|
raise ValueError("Target positions must be provided.")
|
|
109
113
|
|
|
110
114
|
for data in [target_positions, target_velocities, target_torques]:
|
|
111
|
-
if data is not None and len(data) !=
|
|
112
|
-
raise ValueError("Target
|
|
115
|
+
if data is not None and len(data) != self.joint_count()/2:
|
|
116
|
+
raise ValueError(f"Target data must be a list of length {self.joint_count()/2}.")
|
|
113
117
|
|
|
114
118
|
q = max(0.0, min(100.0, target_positions[0]))
|
|
115
119
|
|
|
@@ -163,29 +167,32 @@ class LejuClaw(EndEffector):
|
|
|
163
167
|
"""
|
|
164
168
|
return self._rb_state.eef_state.state
|
|
165
169
|
|
|
166
|
-
def get_position(self)->Tuple[
|
|
170
|
+
def get_position(self)->Tuple[list, list]:
|
|
167
171
|
"""Get the position of the claws.
|
|
168
172
|
|
|
169
173
|
Returns:
|
|
170
|
-
Tuple[
|
|
174
|
+
Tuple[list, list]: The position of the claws, range [0.0, 100.0].
|
|
171
175
|
"""
|
|
172
|
-
|
|
176
|
+
claw_state = self._rb_state.eef_state
|
|
177
|
+
return (claw_state[0].position, claw_state[1].position)
|
|
173
178
|
|
|
174
|
-
def get_velocity(self)->Tuple[
|
|
179
|
+
def get_velocity(self)->Tuple[list, list]:
|
|
175
180
|
"""Get the velocity of the claws.
|
|
176
181
|
|
|
177
182
|
Returns:
|
|
178
|
-
Tuple[
|
|
183
|
+
Tuple[list, list]: The velocity of the claws.
|
|
179
184
|
"""
|
|
180
|
-
|
|
185
|
+
claw_state = self._rb_state.eef_state
|
|
186
|
+
return (claw_state[0].velocity, claw_state[1].velocity)
|
|
181
187
|
|
|
182
|
-
def get_effort(self)->Tuple[
|
|
188
|
+
def get_effort(self)->Tuple[list, list]:
|
|
183
189
|
"""Get the effort of the claws.
|
|
184
190
|
|
|
185
191
|
Returns:
|
|
186
|
-
Tuple[
|
|
192
|
+
Tuple[list, list]: The effort of the claws.
|
|
187
193
|
"""
|
|
188
|
-
|
|
194
|
+
claw_state = self._rb_state.eef_state
|
|
195
|
+
return (claw_state[0].effort, claw_state[1].effort)
|
|
189
196
|
|
|
190
197
|
def get_state(self)->Tuple[EndEffectorState, EndEffectorState]:
|
|
191
198
|
"""Get the state of the claws.
|
|
@@ -5,7 +5,6 @@ kuavo_ros_env = KuavoROSEnv()
|
|
|
5
5
|
if not kuavo_ros_env.Init():
|
|
6
6
|
raise RuntimeError("Failed to initialize ROS environment")
|
|
7
7
|
else:
|
|
8
|
-
import time
|
|
9
8
|
from typing import Tuple
|
|
10
9
|
from kuavo_humanoid_sdk.kuavo.robot_info import KuavoRobotInfo
|
|
11
10
|
from kuavo_humanoid_sdk.kuavo.robot_arm import KuavoRobotArm
|
|
@@ -14,6 +13,7 @@ else:
|
|
|
14
13
|
from kuavo_humanoid_sdk.interfaces.robot import RobotBase
|
|
15
14
|
from kuavo_humanoid_sdk.interfaces.data_types import KuavoPose, KuavoIKParams
|
|
16
15
|
from kuavo_humanoid_sdk.common.logger import SDKLogger, disable_sdk_logging
|
|
16
|
+
|
|
17
17
|
"""
|
|
18
18
|
Kuavo SDK - Python Interface for Kuavo Robot Control
|
|
19
19
|
|
|
@@ -35,12 +35,17 @@ The module requires a properly configured ROS environment to function.
|
|
|
35
35
|
"""
|
|
36
36
|
__all__ = ["KuavoSDK", "KuavoRobot"]
|
|
37
37
|
|
|
38
|
+
|
|
38
39
|
class KuavoSDK:
|
|
40
|
+
class Options:
|
|
41
|
+
Normal = 0x01
|
|
42
|
+
WithIK = 0x02
|
|
43
|
+
|
|
39
44
|
def __init__(self):
|
|
40
45
|
pass
|
|
41
46
|
|
|
42
47
|
@staticmethod
|
|
43
|
-
def Init(log_level: str = "INFO")-> bool:
|
|
48
|
+
def Init(options:int=Options.Normal, log_level: str = "INFO")-> bool:
|
|
44
49
|
"""Initialize the SDK.
|
|
45
50
|
|
|
46
51
|
Args:
|
|
@@ -59,7 +64,14 @@ class KuavoSDK:
|
|
|
59
64
|
if log_level.upper() == 'DEBUG':
|
|
60
65
|
debug = True
|
|
61
66
|
else:
|
|
62
|
-
debug = False
|
|
67
|
+
debug = False
|
|
68
|
+
|
|
69
|
+
# Check if IK option is enabled
|
|
70
|
+
if options & KuavoSDK.Options.WithIK:
|
|
71
|
+
if not KuavoROSEnv.check_rosnode_exists('/arms_ik_node'):
|
|
72
|
+
print("\033[31m\nError:WithIK option is enabled but ik_node is not running, please run `roslaunch motion_capture_ik ik_node.launch`\033[0m")
|
|
73
|
+
exit(1)
|
|
74
|
+
|
|
63
75
|
if not kuavo_core.initialize(debug=debug):
|
|
64
76
|
SDKLogger.error("[SDK] Failed to initialize core components.")
|
|
65
77
|
return False
|
|
@@ -86,7 +98,7 @@ class KuavoRobot(RobotBase):
|
|
|
86
98
|
bool: True if the robot is put into stance mode successfully, False otherwise.
|
|
87
99
|
|
|
88
100
|
Note:
|
|
89
|
-
You can call KuavoRobotState.wait_for_stance
|
|
101
|
+
You can call :meth:`KuavoRobotState.wait_for_stance` to wait until the robot enters stance mode.
|
|
90
102
|
"""
|
|
91
103
|
return self._kuavo_core.to_stance()
|
|
92
104
|
|
|
@@ -97,7 +109,7 @@ class KuavoRobot(RobotBase):
|
|
|
97
109
|
bool: True if the robot is put into trot mode successfully, False otherwise.
|
|
98
110
|
|
|
99
111
|
Note:
|
|
100
|
-
You can call KuavoRobotState.wait_for_walk
|
|
112
|
+
You can call :meth:`KuavoRobotState.wait_for_walk` to wait until the robot enters trot mode.
|
|
101
113
|
"""
|
|
102
114
|
return self._kuavo_core.to_trot()
|
|
103
115
|
|
|
@@ -113,7 +125,7 @@ class KuavoRobot(RobotBase):
|
|
|
113
125
|
bool: True if the motion is controlled successfully, False otherwise.
|
|
114
126
|
|
|
115
127
|
Note:
|
|
116
|
-
You can call KuavoRobotState.wait_for_walk
|
|
128
|
+
You can call :meth:`KuavoRobotState.wait_for_walk` to wait until the robot enters walk mode.
|
|
117
129
|
"""
|
|
118
130
|
# Limit velocity ranges
|
|
119
131
|
limited_linear_x = min(0.4, max(-0.4, linear_x))
|
|
@@ -136,24 +148,29 @@ class KuavoRobot(RobotBase):
|
|
|
136
148
|
def squat(self, height: float, pitch: float=0.0)->bool:
|
|
137
149
|
"""Control the robot's squat height and pitch.
|
|
138
150
|
Args:
|
|
139
|
-
height (float): The height offset from normal standing height in meters, range [-0.
|
|
151
|
+
height (float): The height offset from normal standing height in meters, range [-0.35, 0.0],Negative values indicate squatting down.
|
|
140
152
|
pitch (float): The pitch angle of the robot's torso in radians, range [-0.4, 0.4].
|
|
141
153
|
|
|
142
154
|
Returns:
|
|
143
155
|
bool: True if the squat is controlled successfully, False otherwise.
|
|
144
156
|
"""
|
|
145
157
|
# Limit height range
|
|
146
|
-
|
|
158
|
+
MAX_HEIGHT = 0.0
|
|
159
|
+
MIN_HEIGHT = -0.35
|
|
160
|
+
MAX_PITCH = 0.4
|
|
161
|
+
MIN_PITCH = -0.4
|
|
162
|
+
|
|
163
|
+
limited_height = min(MAX_HEIGHT, max(MIN_HEIGHT, height))
|
|
147
164
|
|
|
148
165
|
# Check if height exceeds limits
|
|
149
|
-
if height >
|
|
150
|
-
SDKLogger.warn(f"[Robot] height {height} exceeds limit [
|
|
166
|
+
if height > MAX_HEIGHT or height < MIN_HEIGHT:
|
|
167
|
+
SDKLogger.warn(f"[Robot] height {height} exceeds limit [{MIN_HEIGHT}, {MAX_HEIGHT}], will be limited")
|
|
151
168
|
# Limit pitch range
|
|
152
|
-
limited_pitch = min(
|
|
169
|
+
limited_pitch = min(MAX_PITCH, max(MIN_PITCH, pitch))
|
|
153
170
|
|
|
154
171
|
# Check if pitch exceeds limits
|
|
155
|
-
if abs(pitch) >
|
|
156
|
-
SDKLogger.warn(f"[Robot] pitch {pitch} exceeds limit [
|
|
172
|
+
if abs(pitch) > MAX_PITCH:
|
|
173
|
+
SDKLogger.warn(f"[Robot] pitch {pitch} exceeds limit [{MIN_PITCH}, {MAX_PITCH}], will be limited")
|
|
157
174
|
|
|
158
175
|
return self._kuavo_core.squat(limited_height, limited_pitch)
|
|
159
176
|
|
|
@@ -172,7 +189,11 @@ class KuavoRobot(RobotBase):
|
|
|
172
189
|
Raises:
|
|
173
190
|
RuntimeError: If the robot is not in stance state when trying to control step motion.
|
|
174
191
|
ValueError: If target_pose length is not 4.
|
|
175
|
-
|
|
192
|
+
|
|
193
|
+
Note:
|
|
194
|
+
You can call :meth:`KuavoRobotState.wait_for_step_control` to wait until the robot enters step-control mode.
|
|
195
|
+
You can call :meth:`KuavoRobotState.wait_for_stance` to wait the step-control finish.
|
|
196
|
+
"""
|
|
176
197
|
if len(target_pose) != 4:
|
|
177
198
|
raise ValueError(f"[Robot] target_pose length must be 4 (x, y, z, yaw), but got {len(target_pose)}")
|
|
178
199
|
|
|
@@ -235,7 +256,7 @@ class KuavoRobot(RobotBase):
|
|
|
235
256
|
ValueError: If the joint position is outside the range of [-π, π].
|
|
236
257
|
RuntimeError: If the robot is not in stance state when trying to control the arm.
|
|
237
258
|
|
|
238
|
-
|
|
259
|
+
Warning:
|
|
239
260
|
This is an asynchronous interface. The function returns immediately after sending the command.
|
|
240
261
|
Users need to wait for the motion to complete on their own.
|
|
241
262
|
"""
|
|
@@ -293,6 +314,9 @@ class KuavoRobot(RobotBase):
|
|
|
293
314
|
|
|
294
315
|
Returns:
|
|
295
316
|
list: List of joint positions in radians, or None if inverse kinematics failed.
|
|
317
|
+
|
|
318
|
+
Warning:
|
|
319
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
296
320
|
"""
|
|
297
321
|
return self._robot_arm.arm_ik(left_pose, right_pose, left_elbow_pos_xyz, right_elbow_pos_xyz, arm_q0, params)
|
|
298
322
|
|
|
@@ -305,5 +329,8 @@ class KuavoRobot(RobotBase):
|
|
|
305
329
|
Returns:
|
|
306
330
|
Tuple[KuavoPose, KuavoPose]: Tuple of poses for the robot left arm and right arm,
|
|
307
331
|
or (None, None) if forward kinematics failed.
|
|
332
|
+
|
|
333
|
+
Warning:
|
|
334
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
308
335
|
"""
|
|
309
336
|
return self._robot_arm.arm_fk(q)
|
|
@@ -118,6 +118,9 @@ class KuavoRobotArm:
|
|
|
118
118
|
|
|
119
119
|
Returns:
|
|
120
120
|
list: List of joint positions in radians, or None if inverse kinematics failed.
|
|
121
|
+
|
|
122
|
+
Warning:
|
|
123
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
121
124
|
"""
|
|
122
125
|
return self._kuavo_core.arm_ik(left_pose, right_pose, left_elbow_pos_xyz, right_elbow_pos_xyz, arm_q0, params)
|
|
123
126
|
|
|
@@ -130,6 +133,9 @@ class KuavoRobotArm:
|
|
|
130
133
|
Returns:
|
|
131
134
|
Tuple[KuavoPose, KuavoPose]: Tuple of poses for the robot left arm and right arm,
|
|
132
135
|
or (None, None) if forward kinematics failed.
|
|
136
|
+
|
|
137
|
+
Warning:
|
|
138
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
133
139
|
"""
|
|
134
140
|
if len(q) != self._robot_info.arm_joint_dof:
|
|
135
141
|
raise ValueError("Invalid position length. Expected {}, got {}".format(self._robot_info.arm_joint_dof, len(q)))
|