kuavo-humanoid-sdk-ws 1.2.2__20250922181225-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.
- kuavo_humanoid_sdk/__init__.py +6 -0
- kuavo_humanoid_sdk/common/logger.py +45 -0
- kuavo_humanoid_sdk/common/websocket_kuavo_sdk.py +26 -0
- kuavo_humanoid_sdk/interfaces/__init__.py +4 -0
- kuavo_humanoid_sdk/interfaces/data_types.py +293 -0
- kuavo_humanoid_sdk/interfaces/end_effector.py +62 -0
- kuavo_humanoid_sdk/interfaces/robot.py +22 -0
- kuavo_humanoid_sdk/interfaces/robot_info.py +56 -0
- kuavo_humanoid_sdk/kuavo/__init__.py +11 -0
- kuavo_humanoid_sdk/kuavo/core/audio.py +32 -0
- kuavo_humanoid_sdk/kuavo/core/core.py +602 -0
- kuavo_humanoid_sdk/kuavo/core/dex_hand_control.py +114 -0
- kuavo_humanoid_sdk/kuavo/core/leju_claw_control.py +67 -0
- kuavo_humanoid_sdk/kuavo/core/ros/audio.py +86 -0
- kuavo_humanoid_sdk/kuavo/core/ros/control.py +1045 -0
- kuavo_humanoid_sdk/kuavo/core/ros/observation.py +125 -0
- kuavo_humanoid_sdk/kuavo/core/ros/param.py +246 -0
- kuavo_humanoid_sdk/kuavo/core/ros/sat_utils.py +103 -0
- kuavo_humanoid_sdk/kuavo/core/ros/state.py +426 -0
- kuavo_humanoid_sdk/kuavo/core/ros/tools.py +196 -0
- kuavo_humanoid_sdk/kuavo/core/ros/vision.py +280 -0
- kuavo_humanoid_sdk/kuavo/core/ros_env.py +233 -0
- kuavo_humanoid_sdk/kuavo/dexterous_hand.py +198 -0
- kuavo_humanoid_sdk/kuavo/leju_claw.py +232 -0
- kuavo_humanoid_sdk/kuavo/robot.py +462 -0
- kuavo_humanoid_sdk/kuavo/robot_arm.py +192 -0
- kuavo_humanoid_sdk/kuavo/robot_audio.py +39 -0
- kuavo_humanoid_sdk/kuavo/robot_head.py +39 -0
- kuavo_humanoid_sdk/kuavo/robot_info.py +114 -0
- kuavo_humanoid_sdk/kuavo/robot_observation.py +69 -0
- kuavo_humanoid_sdk/kuavo/robot_state.py +303 -0
- kuavo_humanoid_sdk/kuavo/robot_tool.py +58 -0
- kuavo_humanoid_sdk/kuavo/robot_vision.py +82 -0
- kuavo_humanoid_sdk/kuavo_strategy/__init__.py +2 -0
- kuavo_humanoid_sdk/kuavo_strategy/grasp_box/grasp_box_strategy.py +418 -0
- kuavo_humanoid_sdk/kuavo_strategy/kuavo_strategy.py +63 -0
- kuavo_humanoid_sdk/msg/__init__.py +4 -0
- kuavo_humanoid_sdk_ws-1.2.2.dist-info/METADATA +276 -0
- kuavo_humanoid_sdk_ws-1.2.2.dist-info/RECORD +41 -0
- kuavo_humanoid_sdk_ws-1.2.2.dist-info/WHEEL +6 -0
- kuavo_humanoid_sdk_ws-1.2.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
from kuavo_humanoid_sdk.kuavo.core.ros_env import KuavoROSEnvWebsocket
|
|
4
|
+
from kuavo_humanoid_sdk.interfaces.robot import RobotBase
|
|
5
|
+
from kuavo_humanoid_sdk.common.logger import SDKLogger, disable_sdk_logging
|
|
6
|
+
from kuavo_humanoid_sdk.interfaces.data_types import KuavoPose, KuavoIKParams, KuavoManipulationMpcFrame, KuavoManipulationMpcCtrlMode, KuavoManipulationMpcControlFlow
|
|
7
|
+
from kuavo_humanoid_sdk.kuavo.core.core import KuavoRobotCore
|
|
8
|
+
|
|
9
|
+
from typing import Tuple
|
|
10
|
+
from kuavo_humanoid_sdk.kuavo.robot_info import KuavoRobotInfo
|
|
11
|
+
from kuavo_humanoid_sdk.kuavo.robot_arm import KuavoRobotArm
|
|
12
|
+
from kuavo_humanoid_sdk.kuavo.robot_head import KuavoRobotHead
|
|
13
|
+
from kuavo_humanoid_sdk.common.websocket_kuavo_sdk import WebSocketKuavoSDK
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Kuavo SDK - Python Interface for Kuavo Robot Control
|
|
17
|
+
|
|
18
|
+
This module provides the main interface for controlling Kuavo robots through Python.
|
|
19
|
+
It includes two main classes:
|
|
20
|
+
|
|
21
|
+
KuavoSDK:
|
|
22
|
+
A static class providing SDK initialization and configuration functionality.
|
|
23
|
+
Handles core setup, ROS environment initialization, and logging configuration.
|
|
24
|
+
|
|
25
|
+
KuavoRobot:
|
|
26
|
+
The main robot control interface class that provides access to:
|
|
27
|
+
- Robot information and status (via KuavoRobotInfo)
|
|
28
|
+
- Arm control capabilities (via KuavoRobotArm)
|
|
29
|
+
- Head control capabilities (via KuavoRobotHead)
|
|
30
|
+
- Core robot functions (via KuavoRobotCore)
|
|
31
|
+
|
|
32
|
+
The module requires a properly configured ROS environment to function.
|
|
33
|
+
"""
|
|
34
|
+
__all__ = ["KuavoSDK", "KuavoRobot"]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class KuavoSDK:
|
|
38
|
+
class Options:
|
|
39
|
+
Normal = 0x01
|
|
40
|
+
WithIK = 0x02
|
|
41
|
+
|
|
42
|
+
def __init__(self):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
@staticmethod
|
|
46
|
+
def Init(options:int=Options.Normal, log_level: str = "INFO",
|
|
47
|
+
websocket_mode: bool = False, websocket_host='127.0.0.1', websocket_port=9090, websocket_timeout=5)-> bool:
|
|
48
|
+
"""Initialize the SDK.
|
|
49
|
+
|
|
50
|
+
Initializes the Kuavo SDK with specified options and configuration.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
options (int): Configuration options for the SDK. Use KuavoSDK.Options constants.
|
|
54
|
+
Defaults to Options.Normal.
|
|
55
|
+
log_level (str): Logging level to use. Options are "ERROR", "WARN", "INFO", "DEBUG".
|
|
56
|
+
Defaults to "INFO".
|
|
57
|
+
websocket_mode (bool): Whether to use websocket mode for ROS communication.
|
|
58
|
+
If True, connects to ROS through websocket instead of direct connection.
|
|
59
|
+
Defaults to False.
|
|
60
|
+
websocket_host (str): Host address of the rosbridge websocket server.
|
|
61
|
+
Only used when websocket_mode is True. Defaults to "127.0.0.1".
|
|
62
|
+
websocket_port (int): Port number of the rosbridge websocket server.
|
|
63
|
+
Only used when websocket_mode is True. Defaults to 9090.
|
|
64
|
+
websocket_timeout (int): Timeout in seconds for the websocket connection.
|
|
65
|
+
Defaults to 5.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
bool: True if initialization succeeds, False otherwise.
|
|
69
|
+
|
|
70
|
+
Raises:
|
|
71
|
+
RuntimeError: If initialization fails due to missing dependencies or
|
|
72
|
+
connection issues.
|
|
73
|
+
"""
|
|
74
|
+
WebSocketKuavoSDK.use_websocket = websocket_mode
|
|
75
|
+
WebSocketKuavoSDK.websocket_host = websocket_host
|
|
76
|
+
WebSocketKuavoSDK.websocket_port = websocket_port
|
|
77
|
+
WebSocketKuavoSDK.websocket_timeout = websocket_timeout
|
|
78
|
+
SDKLogger.setLevel(log_level.upper())
|
|
79
|
+
SDKLogger.debug(f" ================= Kuavo Humanoid Websocket SDK =================")
|
|
80
|
+
|
|
81
|
+
# Initialize core components, connect ROS Topics...
|
|
82
|
+
kuavo_core = KuavoRobotCore()
|
|
83
|
+
if log_level.upper() == 'DEBUG':
|
|
84
|
+
debug = True
|
|
85
|
+
else:
|
|
86
|
+
debug = False
|
|
87
|
+
# Check if IK option is enabled
|
|
88
|
+
if options & KuavoSDK.Options.WithIK:
|
|
89
|
+
if not KuavoROSEnvWebsocket.check_rosnode_exists('/arms_ik_node'):
|
|
90
|
+
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")
|
|
91
|
+
exit(1)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
if not kuavo_core.initialize(debug=debug):
|
|
96
|
+
SDKLogger.error("[SDK] Failed to initialize core components.")
|
|
97
|
+
return False
|
|
98
|
+
|
|
99
|
+
return True
|
|
100
|
+
|
|
101
|
+
@staticmethod
|
|
102
|
+
def DisableLogging():
|
|
103
|
+
"""Disable all logging output from the SDK."""
|
|
104
|
+
disable_sdk_logging()
|
|
105
|
+
|
|
106
|
+
class KuavoRobot(RobotBase):
|
|
107
|
+
def __init__(self):
|
|
108
|
+
super().__init__(robot_type="kuavo")
|
|
109
|
+
|
|
110
|
+
kuavo_ros_env = KuavoROSEnvWebsocket()
|
|
111
|
+
if not kuavo_ros_env.Init():
|
|
112
|
+
raise RuntimeError("Failed to initialize ROS environment")
|
|
113
|
+
|
|
114
|
+
self._robot_info = KuavoRobotInfo()
|
|
115
|
+
self._robot_arm = KuavoRobotArm()
|
|
116
|
+
self._robot_head = KuavoRobotHead()
|
|
117
|
+
self._kuavo_core = KuavoRobotCore()
|
|
118
|
+
def stance(self)->bool:
|
|
119
|
+
"""Put the robot into 'stance' mode.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
bool: True if the robot is put into stance mode successfully, False otherwise.
|
|
123
|
+
|
|
124
|
+
Note:
|
|
125
|
+
You can call :meth:`KuavoRobotState.wait_for_stance` to wait until the robot enters stance mode.
|
|
126
|
+
"""
|
|
127
|
+
return self._kuavo_core.to_stance()
|
|
128
|
+
|
|
129
|
+
def trot(self)->bool:
|
|
130
|
+
"""Put the robot into 'trot' mode.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
bool: True if the robot is put into trot mode successfully, False otherwise.
|
|
134
|
+
|
|
135
|
+
Note:
|
|
136
|
+
You can call :meth:`KuavoRobotState.wait_for_walk` to wait until the robot enters trot mode.
|
|
137
|
+
"""
|
|
138
|
+
return self._kuavo_core.to_trot()
|
|
139
|
+
|
|
140
|
+
def walk(self, linear_x:float, linear_y:float, angular_z:float)->bool:
|
|
141
|
+
"""Control the robot's motion.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
linear_x (float): The linear velocity along the x-axis in m/s, range [-0.4, 0.4].
|
|
145
|
+
linear_y (float): The linear velocity along the y-axis in m/s, range [-0.2, 0.2].
|
|
146
|
+
angular_z (float): The angular velocity around the z-axis in rad/s, range [-0.4, 0.4].
|
|
147
|
+
|
|
148
|
+
Returns:
|
|
149
|
+
bool: True if the motion is controlled successfully, False otherwise.
|
|
150
|
+
|
|
151
|
+
Note:
|
|
152
|
+
You can call :meth:`KuavoRobotState.wait_for_walk` to wait until the robot enters walk mode.
|
|
153
|
+
"""
|
|
154
|
+
# Limit velocity ranges
|
|
155
|
+
limited_linear_x = min(0.4, max(-0.4, linear_x))
|
|
156
|
+
limited_linear_y = min(0.2, max(-0.2, linear_y))
|
|
157
|
+
limited_angular_z = min(0.4, max(-0.4, angular_z))
|
|
158
|
+
|
|
159
|
+
# Check if any velocity exceeds limits.
|
|
160
|
+
if abs(linear_x) > 0.4:
|
|
161
|
+
SDKLogger.warn(f"[Robot] linear_x velocity {linear_x} exceeds limit [-0.4, 0.4], will be limited")
|
|
162
|
+
if abs(linear_y) > 0.2:
|
|
163
|
+
SDKLogger.warn(f"[Robot] linear_y velocity {linear_y} exceeds limit [-0.2, 0.2], will be limited")
|
|
164
|
+
if abs(angular_z) > 0.4:
|
|
165
|
+
SDKLogger.warn(f"[Robot] angular_z velocity {angular_z} exceeds limit [-0.4, 0.4], will be limited")
|
|
166
|
+
return self._kuavo_core.walk(limited_linear_x, limited_linear_y, limited_angular_z)
|
|
167
|
+
|
|
168
|
+
def jump(self):
|
|
169
|
+
"""Jump the robot."""
|
|
170
|
+
pass
|
|
171
|
+
|
|
172
|
+
def squat(self, height: float, pitch: float=0.0)->bool:
|
|
173
|
+
"""Control the robot's squat height and pitch.
|
|
174
|
+
Args:
|
|
175
|
+
height (float): The height offset from normal standing height in meters, range [-0.35, 0.0],Negative values indicate squatting down.
|
|
176
|
+
pitch (float): The pitch angle of the robot's torso in radians, range [-0.4, 0.4].
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
bool: True if the squat is controlled successfully, False otherwise.
|
|
180
|
+
"""
|
|
181
|
+
# Limit height range
|
|
182
|
+
MAX_HEIGHT = 0.0
|
|
183
|
+
MIN_HEIGHT = -0.35
|
|
184
|
+
MAX_PITCH = 0.4
|
|
185
|
+
MIN_PITCH = -0.4
|
|
186
|
+
|
|
187
|
+
limited_height = min(MAX_HEIGHT, max(MIN_HEIGHT, height))
|
|
188
|
+
|
|
189
|
+
# Check if height exceeds limits
|
|
190
|
+
if height > MAX_HEIGHT or height < MIN_HEIGHT:
|
|
191
|
+
SDKLogger.warn(f"[Robot] height {height} exceeds limit [{MIN_HEIGHT}, {MAX_HEIGHT}], will be limited")
|
|
192
|
+
# Limit pitch range
|
|
193
|
+
limited_pitch = min(MAX_PITCH, max(MIN_PITCH, pitch))
|
|
194
|
+
|
|
195
|
+
# Check if pitch exceeds limits
|
|
196
|
+
if abs(pitch) > MAX_PITCH:
|
|
197
|
+
SDKLogger.warn(f"[Robot] pitch {pitch} exceeds limit [{MIN_PITCH}, {MAX_PITCH}], will be limited")
|
|
198
|
+
|
|
199
|
+
return self._kuavo_core.squat(limited_height, limited_pitch)
|
|
200
|
+
|
|
201
|
+
def step_by_step(self, target_pose:list, dt:float=0.4, is_left_first_default:bool=True, collision_check:bool=True)->bool:
|
|
202
|
+
"""Control the robot's motion by step.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
target_pose (list): The target position of the robot in [x, y, z, yaw] m, rad.
|
|
206
|
+
dt (float): The time interval between each step in seconds. Defaults to 0.4s.
|
|
207
|
+
is_left_first_default (bool): Whether to start with the left foot. Defaults to True.
|
|
208
|
+
collision_check (bool): Whether to check for collisions. Defaults to True.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
bool: True if motion is successful, False otherwise.
|
|
212
|
+
|
|
213
|
+
Raises:
|
|
214
|
+
RuntimeError: If the robot is not in stance state when trying to control step motion.
|
|
215
|
+
ValueError: If target_pose length is not 4.
|
|
216
|
+
|
|
217
|
+
Note:
|
|
218
|
+
You can call :meth:`KuavoRobotState.wait_for_step_control` to wait until the robot enters step-control mode.
|
|
219
|
+
You can call :meth:`KuavoRobotState.wait_for_stance` to wait the step-control finish.
|
|
220
|
+
"""
|
|
221
|
+
if len(target_pose) != 4:
|
|
222
|
+
raise ValueError(f"[Robot] target_pose length must be 4 (x, y, z, yaw), but got {len(target_pose)}")
|
|
223
|
+
|
|
224
|
+
return self._kuavo_core.step_control(target_pose, dt, is_left_first_default, collision_check)
|
|
225
|
+
|
|
226
|
+
def control_command_pose(self, target_pose_x: float, target_pose_y: float, target_pose_z: float, target_pose_yaw: float) -> bool:
|
|
227
|
+
"""Control robot pose in base_link frame.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
target_pose_x (float): The target x position in meters.
|
|
231
|
+
target_pose_y (float): The target y position in meters.
|
|
232
|
+
target_pose_z (float): The target z position in meters.
|
|
233
|
+
target_pose_yaw (float): The target yaw angle in radians.
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
bool: True if the command was sent successfully, False otherwise.
|
|
237
|
+
|
|
238
|
+
Raises:
|
|
239
|
+
RuntimeError: If the robot is not in stance state when trying to control pose.
|
|
240
|
+
|
|
241
|
+
Note:
|
|
242
|
+
This command changes the robot state to 'command_pose'.
|
|
243
|
+
"""
|
|
244
|
+
return self._kuavo_core.control_command_pose(target_pose_x, target_pose_y, target_pose_z, target_pose_yaw)
|
|
245
|
+
|
|
246
|
+
def control_command_pose_world(self, target_pose_x: float, target_pose_y: float, target_pose_z: float, target_pose_yaw: float) -> bool:
|
|
247
|
+
"""Control robot pose in odom (world) frame.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
target_pose_x (float): The target x position in meters.
|
|
251
|
+
target_pose_y (float): The target y position in meters.
|
|
252
|
+
target_pose_z (float): The target z position in meters.
|
|
253
|
+
target_pose_yaw (float): The target yaw angle in radians.
|
|
254
|
+
|
|
255
|
+
Returns:
|
|
256
|
+
bool: True if the command was sent successfully, False otherwise.
|
|
257
|
+
|
|
258
|
+
Raises:
|
|
259
|
+
RuntimeError: If the robot is not in stance state when trying to control pose.
|
|
260
|
+
|
|
261
|
+
Note:
|
|
262
|
+
This command changes the robot state to 'command_pose_world'.
|
|
263
|
+
"""
|
|
264
|
+
return self._kuavo_core.control_command_pose_world(target_pose_x, target_pose_y, target_pose_z, target_pose_yaw)
|
|
265
|
+
|
|
266
|
+
def control_head(self, yaw: float, pitch: float)->bool:
|
|
267
|
+
"""Control the head of the robot.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
yaw (float): The yaw angle of the head in radians, range [-1.396, 1.396] (-80 to 80 degrees).
|
|
271
|
+
pitch (float): The pitch angle of the head in radians, range [-0.436, 0.436] (-25 to 25 degrees).
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
bool: True if the head is controlled successfully, False otherwise.
|
|
275
|
+
"""
|
|
276
|
+
return self._robot_head.control_head(yaw=yaw, pitch=pitch)
|
|
277
|
+
|
|
278
|
+
def enable_head_tracking(self, target_id: int)->bool:
|
|
279
|
+
"""Enable the head tracking.
|
|
280
|
+
"""
|
|
281
|
+
return self._robot_head.enable_head_tracking(target_id)
|
|
282
|
+
|
|
283
|
+
def disable_head_tracking(self)->bool:
|
|
284
|
+
"""Disable the head tracking.
|
|
285
|
+
"""
|
|
286
|
+
return self._robot_head.disable_head_tracking()
|
|
287
|
+
|
|
288
|
+
""" Robot Arm Control """
|
|
289
|
+
def arm_reset(self)->bool:
|
|
290
|
+
"""Reset the robot arm.
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
bool: True if the arm is reset successfully, False otherwise.
|
|
294
|
+
"""
|
|
295
|
+
return self._robot_arm.arm_reset()
|
|
296
|
+
|
|
297
|
+
def manipulation_mpc_reset(self)->bool:
|
|
298
|
+
"""Reset the robot arm.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
bool: True if the arm is reset successfully, False otherwise.
|
|
302
|
+
"""
|
|
303
|
+
return self._robot_arm.manipulation_mpc_reset()
|
|
304
|
+
|
|
305
|
+
def control_arm_joint_positions(self, joint_positions:list)->bool:
|
|
306
|
+
"""Control the position of the arm.
|
|
307
|
+
|
|
308
|
+
Args:
|
|
309
|
+
joint_positions (list): The target position of the arm in radians.
|
|
310
|
+
|
|
311
|
+
Returns:
|
|
312
|
+
bool: True if the arm is controlled successfully, False otherwise.
|
|
313
|
+
|
|
314
|
+
Raises:
|
|
315
|
+
ValueError: If the joint position list is not of the correct length.
|
|
316
|
+
ValueError: If the joint position is outside the range of [-π, π].
|
|
317
|
+
RuntimeError: If the robot is not in stance state when trying to control the arm.
|
|
318
|
+
"""
|
|
319
|
+
if len(joint_positions) != self._robot_info.arm_joint_dof:
|
|
320
|
+
print("The length of the position list must be equal to the number of DOFs of the arm.")
|
|
321
|
+
return False
|
|
322
|
+
|
|
323
|
+
return self._robot_arm.control_arm_joint_positions(joint_positions)
|
|
324
|
+
|
|
325
|
+
def control_arm_joint_trajectory(self, times:list, q_frames:list)->bool:
|
|
326
|
+
"""Control the target poses of the robot arm.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
times (list): List of time intervals in seconds.
|
|
330
|
+
q_frames (list): List of joint positions in radians.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
bool: True if the control was successful, False otherwise.
|
|
334
|
+
|
|
335
|
+
Raises:
|
|
336
|
+
ValueError: If the times list is not of the correct length.
|
|
337
|
+
ValueError: If the joint position list is not of the correct length.
|
|
338
|
+
ValueError: If the joint position is outside the range of [-π, π].
|
|
339
|
+
RuntimeError: If the robot is not in stance state when trying to control the arm.
|
|
340
|
+
|
|
341
|
+
Warning:
|
|
342
|
+
This is an asynchronous interface. The function returns immediately after sending the command.
|
|
343
|
+
Users need to wait for the motion to complete on their own.
|
|
344
|
+
"""
|
|
345
|
+
return self._robot_arm.control_arm_joint_trajectory(times, q_frames)
|
|
346
|
+
|
|
347
|
+
def set_fixed_arm_mode(self) -> bool:
|
|
348
|
+
"""Freezes the robot arm.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
bool: True if the arm is frozen successfully, False otherwise.
|
|
352
|
+
"""
|
|
353
|
+
return self._robot_arm.set_fixed_arm_mode()
|
|
354
|
+
|
|
355
|
+
def set_auto_swing_arm_mode(self) -> bool:
|
|
356
|
+
"""Swing the robot arm.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
bool: True if the arm is swinging successfully, False otherwise.
|
|
360
|
+
"""
|
|
361
|
+
return self._robot_arm.set_auto_swing_arm_mode()
|
|
362
|
+
|
|
363
|
+
def set_external_control_arm_mode(self) -> bool:
|
|
364
|
+
"""External control the robot arm.
|
|
365
|
+
|
|
366
|
+
Returns:
|
|
367
|
+
bool: True if the arm is external controlled successfully, False otherwise.
|
|
368
|
+
"""
|
|
369
|
+
return self._robot_arm.set_external_control_arm_mode()
|
|
370
|
+
|
|
371
|
+
def set_manipulation_mpc_mode(self, ctrl_mode: KuavoManipulationMpcCtrlMode) -> bool:
|
|
372
|
+
"""
|
|
373
|
+
Set the manipulation mpc mode.
|
|
374
|
+
Returns:
|
|
375
|
+
bool: True if the manipulation mpc mode is set successfully, False otherwise.
|
|
376
|
+
"""
|
|
377
|
+
return self._robot_arm.set_manipulation_mpc_mode(ctrl_mode)
|
|
378
|
+
|
|
379
|
+
def set_manipulation_mpc_control_flow(self, control_flow: KuavoManipulationMpcControlFlow) -> bool:
|
|
380
|
+
"""
|
|
381
|
+
Set the manipulation mpc control flow.
|
|
382
|
+
Returns:
|
|
383
|
+
bool: True if the manipulation mpc control flow is set successfully, False otherwise.
|
|
384
|
+
"""
|
|
385
|
+
return self._robot_arm.set_manipulation_mpc_control_flow(control_flow)
|
|
386
|
+
|
|
387
|
+
def set_manipulation_mpc_frame(self, frame: KuavoManipulationMpcFrame) -> bool:
|
|
388
|
+
"""
|
|
389
|
+
Set the manipulation mpc frame.
|
|
390
|
+
Returns:
|
|
391
|
+
bool: True if the manipulation mpc frame is set successfully, False otherwise.
|
|
392
|
+
"""
|
|
393
|
+
return self._robot_arm.set_manipulation_mpc_frame(frame)
|
|
394
|
+
|
|
395
|
+
""" Arm Forward kinematics && Arm Inverse kinematics """
|
|
396
|
+
def arm_ik(self,
|
|
397
|
+
left_pose: KuavoPose,
|
|
398
|
+
right_pose: KuavoPose,
|
|
399
|
+
left_elbow_pos_xyz: list = [0.0, 0.0, 0.0],
|
|
400
|
+
right_elbow_pos_xyz: list = [0.0, 0.0, 0.0],
|
|
401
|
+
arm_q0: list = None,
|
|
402
|
+
params: KuavoIKParams=None) -> list:
|
|
403
|
+
"""Inverse kinematics for the robot arm.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
left_pose (KuavoPose): Pose of the robot left arm, xyz and quat.
|
|
407
|
+
right_pose (KuavoPose): Pose of the robot right arm, xyz and quat.
|
|
408
|
+
left_elbow_pos_xyz (list): Position of the robot left elbow. If [0.0, 0.0, 0.0], will be ignored.
|
|
409
|
+
right_elbow_pos_xyz (list): Position of the robot right elbow. If [0.0, 0.0, 0.0], will be ignored.
|
|
410
|
+
arm_q0 (list, optional): Initial joint positions in radians. If None, will be ignored.
|
|
411
|
+
params (KuavoIKParams, optional): Parameters for the inverse kinematics. If None, will be ignored.
|
|
412
|
+
Contains:
|
|
413
|
+
- major_optimality_tol: Major optimality tolerance
|
|
414
|
+
- major_feasibility_tol: Major feasibility tolerance
|
|
415
|
+
- minor_feasibility_tol: Minor feasibility tolerance
|
|
416
|
+
- major_iterations_limit: Major iterations limit
|
|
417
|
+
- oritation_constraint_tol: Orientation constraint tolerance
|
|
418
|
+
- pos_constraint_tol: Position constraint tolerance, works when pos_cost_weight==0.0
|
|
419
|
+
- pos_cost_weight: Position cost weight. Set to 0.0 for high accuracy
|
|
420
|
+
|
|
421
|
+
Returns:
|
|
422
|
+
list: List of joint positions in radians, or None if inverse kinematics failed.
|
|
423
|
+
|
|
424
|
+
Warning:
|
|
425
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
426
|
+
"""
|
|
427
|
+
return self._robot_arm.arm_ik(left_pose, right_pose, left_elbow_pos_xyz, right_elbow_pos_xyz, arm_q0, params)
|
|
428
|
+
|
|
429
|
+
def arm_fk(self, q: list) -> Tuple[KuavoPose, KuavoPose]:
|
|
430
|
+
"""Forward kinematics for the robot arm.
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
q (list): List of joint positions in radians.
|
|
434
|
+
|
|
435
|
+
Returns:
|
|
436
|
+
Tuple[KuavoPose, KuavoPose]: Tuple of poses for the robot left arm and right arm,
|
|
437
|
+
or (None, None) if forward kinematics failed.
|
|
438
|
+
|
|
439
|
+
Warning:
|
|
440
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
441
|
+
"""
|
|
442
|
+
return self._robot_arm.arm_fk(q)
|
|
443
|
+
|
|
444
|
+
def control_robot_end_effector_pose(self, left_pose: KuavoPose, right_pose: KuavoPose, frame: KuavoManipulationMpcFrame)->bool:
|
|
445
|
+
"""Control the end effector pose of the robot arm.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
left_pose (KuavoPose): Pose of the robot left arm, xyz and quat.
|
|
449
|
+
right_pose (KuavoPose): Pose of the robot right arm, xyz and quat.
|
|
450
|
+
frame (KuavoManipulationMpcFrame): Frame of the robot arm.
|
|
451
|
+
|
|
452
|
+
Returns:
|
|
453
|
+
bool: True if the end effector pose is controlled successfully, False otherwise.
|
|
454
|
+
"""
|
|
455
|
+
return self._robot_arm.control_robot_end_effector_pose(left_pose, right_pose, frame)
|
|
456
|
+
|
|
457
|
+
if __name__ == "__main__":
|
|
458
|
+
robot = KuavoRobot()
|
|
459
|
+
robot.set_manipulation_mpc_mode(KuavoManipulationMpcCtrlMode.ArmOnly)
|
|
460
|
+
robot.set_manipulation_mpc_control_flow(KuavoManipulationMpcControlFlow.DirectToWbc)
|
|
461
|
+
robot.set_manipulation_mpc_frame(KuavoManipulationMpcFrame.WorldFrame)
|
|
462
|
+
robot.control_robot_end_effector_pose(KuavoPose(position=[0.3, 0.4, 0.9], orientation=[0.0, 0.0, 0.0, 1.0]), KuavoPose(position=[0.3, -0.5, 1.0], orientation=[0.0, 0.0, 0.0, 1.0]), KuavoManipulationMpcFrame.WorldFrame)
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
import math
|
|
5
|
+
from typing import Tuple
|
|
6
|
+
from kuavo_humanoid_sdk.interfaces.data_types import KuavoArmCtrlMode, KuavoIKParams, KuavoPose, KuavoManipulationMpcFrame, KuavoManipulationMpcCtrlMode, KuavoManipulationMpcControlFlow
|
|
7
|
+
from kuavo_humanoid_sdk.kuavo.core.core import KuavoRobotCore
|
|
8
|
+
from kuavo_humanoid_sdk.kuavo.robot_info import KuavoRobotInfo
|
|
9
|
+
|
|
10
|
+
class KuavoRobotArm:
|
|
11
|
+
def __init__(self):
|
|
12
|
+
self._kuavo_core = KuavoRobotCore()
|
|
13
|
+
self._robot_info = KuavoRobotInfo(robot_type="kuavo")
|
|
14
|
+
|
|
15
|
+
def arm_reset(self)-> bool:
|
|
16
|
+
return self._kuavo_core.robot_arm_reset()
|
|
17
|
+
|
|
18
|
+
def manipulation_mpc_reset(self)-> bool:
|
|
19
|
+
return self._kuavo_core.robot_manipulation_mpc_reset()
|
|
20
|
+
|
|
21
|
+
def control_arm_joint_positions(self, joint_position:list)->bool:
|
|
22
|
+
"""
|
|
23
|
+
Control the position of the robot arm joint.
|
|
24
|
+
Args:
|
|
25
|
+
joint_position (list): List of joint positions in radians
|
|
26
|
+
Raises:
|
|
27
|
+
ValueError: If the joint position list is not of the correct length.
|
|
28
|
+
ValueError: If the joint position is outside the range of [-π, π].
|
|
29
|
+
RuntimeError: If the robot is not in stance state when trying to control the arm.
|
|
30
|
+
Returns:
|
|
31
|
+
True if the control was successful, False otherwise.
|
|
32
|
+
"""
|
|
33
|
+
if len(joint_position) != self._robot_info.arm_joint_dof:
|
|
34
|
+
raise ValueError("Invalid position length. Expected {}, got {}".format(self._robot_info.arm_joint_dof, len(joint_position)))
|
|
35
|
+
|
|
36
|
+
# Check if joint positions are within ±180 degrees (±π radians)
|
|
37
|
+
for pos in joint_position:
|
|
38
|
+
if abs(pos) > math.pi:
|
|
39
|
+
raise ValueError(f"Joint position {pos} rad exceeds ±π rad (±180 deg) limit")
|
|
40
|
+
|
|
41
|
+
return self._kuavo_core.control_robot_arm_joint_positions(joint_data=joint_position)
|
|
42
|
+
|
|
43
|
+
def control_arm_joint_trajectory(self, times:list, joint_q:list)->bool:
|
|
44
|
+
"""
|
|
45
|
+
Control the target poses of the robot arm.
|
|
46
|
+
Args:
|
|
47
|
+
times (list): List of time intervals in seconds
|
|
48
|
+
joint_q (list): List of joint positions in radians
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If the times list is not of the correct length.
|
|
51
|
+
ValueError: If the joint position list is not of the correct length.
|
|
52
|
+
ValueError: If the joint position is outside the range of [-π, π].
|
|
53
|
+
RuntimeError: If the robot is not in stance state when trying to control the arm.
|
|
54
|
+
Returns:
|
|
55
|
+
bool: True if the control was successful, False otherwise.
|
|
56
|
+
"""
|
|
57
|
+
if len(times) != len(joint_q):
|
|
58
|
+
raise ValueError("Invalid input. times and joint_q must have thesame length.")
|
|
59
|
+
|
|
60
|
+
# Check if joint positions are within ±180 degrees (±π radians)
|
|
61
|
+
q_degs = []
|
|
62
|
+
for q in joint_q:
|
|
63
|
+
if any(abs(pos) > math.pi for pos in q):
|
|
64
|
+
raise ValueError("Joint positions must be within ±π rad (±180 deg)")
|
|
65
|
+
if len(q) != self._robot_info.arm_joint_dof:
|
|
66
|
+
raise ValueError("Invalid position length. Expected {}, got {}".format(self._robot_info.arm_joint_dof, len(q)))
|
|
67
|
+
# Convert joint positions from radians to degrees
|
|
68
|
+
q_degs.append([(p * 180.0 / math.pi) for p in q])
|
|
69
|
+
|
|
70
|
+
return self._kuavo_core.control_robot_arm_joint_trajectory(times=times, joint_q=q_degs)
|
|
71
|
+
|
|
72
|
+
def control_robot_end_effector_pose(self, left_pose: KuavoPose, right_pose: KuavoPose, frame: KuavoManipulationMpcFrame)->bool:
|
|
73
|
+
"""
|
|
74
|
+
Control the end effector pose of the robot arm.
|
|
75
|
+
Args:
|
|
76
|
+
left_pose (KuavoPose): Pose of the robot left arm, xyz and quat.
|
|
77
|
+
right_pose (KuavoPose): Pose of the robot right arm, xyz and quat.
|
|
78
|
+
frame (KuavoManipulationMpcFrame): Frame of the robot end effector pose.
|
|
79
|
+
Returns:
|
|
80
|
+
bool: True if the control was successful, False otherwise.
|
|
81
|
+
"""
|
|
82
|
+
return self._kuavo_core.control_robot_end_effector_pose(left_pose, right_pose, frame)
|
|
83
|
+
|
|
84
|
+
def set_fixed_arm_mode(self) -> bool:
|
|
85
|
+
"""
|
|
86
|
+
Freezes the robot arm.
|
|
87
|
+
Returns:
|
|
88
|
+
bool: True if the arm is frozen successfully, False otherwise.
|
|
89
|
+
"""
|
|
90
|
+
return self._kuavo_core.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ArmFixed)
|
|
91
|
+
|
|
92
|
+
def set_auto_swing_arm_mode(self) -> bool:
|
|
93
|
+
"""
|
|
94
|
+
Swing the robot arm.
|
|
95
|
+
Returns:
|
|
96
|
+
bool: True if the arm is swinging successfully, False otherwise.
|
|
97
|
+
"""
|
|
98
|
+
return self._kuavo_core.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.AutoSwing)
|
|
99
|
+
|
|
100
|
+
def set_external_control_arm_mode(self) -> bool:
|
|
101
|
+
"""
|
|
102
|
+
External control the robot arm.
|
|
103
|
+
Returns:
|
|
104
|
+
bool: True if the arm is external controlled successfully, False otherwise.
|
|
105
|
+
"""
|
|
106
|
+
return self._kuavo_core.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ExternalControl)
|
|
107
|
+
|
|
108
|
+
def set_manipulation_mpc_mode(self, ctrl_mode: KuavoManipulationMpcCtrlMode) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Set the manipulation mpc mode.
|
|
111
|
+
Returns:
|
|
112
|
+
bool: True if the manipulation mpc mode is set successfully, False otherwise.
|
|
113
|
+
"""
|
|
114
|
+
return self._kuavo_core.change_manipulation_mpc_ctrl_mode(ctrl_mode)
|
|
115
|
+
|
|
116
|
+
def set_manipulation_mpc_control_flow(self, control_flow: KuavoManipulationMpcControlFlow) -> bool:
|
|
117
|
+
"""
|
|
118
|
+
Set the manipulation mpc control flow.
|
|
119
|
+
Returns:
|
|
120
|
+
bool: True if the manipulation mpc control flow is set successfully, False otherwise.
|
|
121
|
+
"""
|
|
122
|
+
return self._kuavo_core.change_manipulation_mpc_control_flow(control_flow)
|
|
123
|
+
|
|
124
|
+
def set_manipulation_mpc_frame(self, frame: KuavoManipulationMpcFrame) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Set the manipulation mpc frame.
|
|
127
|
+
Returns:
|
|
128
|
+
bool: True if the manipulation mpc frame is set successfully, False otherwise.
|
|
129
|
+
"""
|
|
130
|
+
return self._kuavo_core.change_manipulation_mpc_frame(frame)
|
|
131
|
+
|
|
132
|
+
""" Arm Forward kinematics && Arm Inverse kinematics """
|
|
133
|
+
def arm_ik(self,
|
|
134
|
+
left_pose: KuavoPose,
|
|
135
|
+
right_pose: KuavoPose,
|
|
136
|
+
left_elbow_pos_xyz: list = [0.0, 0.0, 0.0],
|
|
137
|
+
right_elbow_pos_xyz: list = [0.0, 0.0, 0.0],
|
|
138
|
+
arm_q0: list = None,
|
|
139
|
+
params: KuavoIKParams=None) -> list:
|
|
140
|
+
"""Inverse kinematics for the robot arm.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
left_pose (KuavoPose): Pose of the robot left arm, xyz and quat.
|
|
144
|
+
right_pose (KuavoPose): Pose of the robot right arm, xyz and quat.
|
|
145
|
+
left_elbow_pos_xyz (list): Position of the robot left elbow. If [0.0, 0.0, 0.0], will be ignored.
|
|
146
|
+
right_elbow_pos_xyz (list): Position of the robot right elbow. If [0.0, 0.0, 0.0], will be ignored.
|
|
147
|
+
arm_q0 (list, optional): Initial joint positions in radians. If None, will be ignored.
|
|
148
|
+
params (KuavoIKParams, optional): Parameters for the inverse kinematics. If None, will be ignored.
|
|
149
|
+
Contains:
|
|
150
|
+
- major_optimality_tol: Major optimality tolerance
|
|
151
|
+
- major_feasibility_tol: Major feasibility tolerance
|
|
152
|
+
- minor_feasibility_tol: Minor feasibility tolerance
|
|
153
|
+
- major_iterations_limit: Major iterations limit
|
|
154
|
+
- oritation_constraint_tol: Orientation constraint tolerance
|
|
155
|
+
- pos_constraint_tol: Position constraint tolerance, works when pos_cost_weight==0.0
|
|
156
|
+
- pos_cost_weight: Position cost weight. Set to 0.0 for high accuracy
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
list: List of joint positions in radians, or None if inverse kinematics failed.
|
|
160
|
+
|
|
161
|
+
Warning:
|
|
162
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
163
|
+
"""
|
|
164
|
+
return self._kuavo_core.arm_ik(left_pose, right_pose, left_elbow_pos_xyz, right_elbow_pos_xyz, arm_q0, params)
|
|
165
|
+
|
|
166
|
+
def arm_fk(self, q: list) -> Tuple[KuavoPose, KuavoPose]:
|
|
167
|
+
"""Forward kinematics for the robot arm.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
q (list): List of joint positions in radians.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Tuple[KuavoPose, KuavoPose]: Tuple of poses for the robot left arm and right arm,
|
|
174
|
+
or (None, None) if forward kinematics failed.
|
|
175
|
+
|
|
176
|
+
Warning:
|
|
177
|
+
This function requires initializing the SDK with the :attr:`KuavoSDK.Options.WithIK`.
|
|
178
|
+
"""
|
|
179
|
+
if len(q) != self._robot_info.arm_joint_dof:
|
|
180
|
+
raise ValueError("Invalid position length. Expected {}, got {}".format(self._robot_info.arm_joint_dof, len(q)))
|
|
181
|
+
|
|
182
|
+
result = self._kuavo_core.arm_fk(q)
|
|
183
|
+
if result is None:
|
|
184
|
+
return None, None
|
|
185
|
+
return result
|
|
186
|
+
|
|
187
|
+
# if __name__ == "__main__":
|
|
188
|
+
# arm = KuavoRobotArm()
|
|
189
|
+
# arm.set_manipulation_mpc_mode(KuavoManipulationMpcCtrlMode.ArmOnly)
|
|
190
|
+
# arm.set_manipulation_mpc_control_flow(KuavoManipulationMpcControlFlow.DirectToWbc)
|
|
191
|
+
# arm.set_manipulation_mpc_frame(KuavoManipulationMpcFrame.WorldFrame)
|
|
192
|
+
# arm.control_robot_end_effector_pose(KuavoPose(position=[0.3, 0.4, 0.9], orientation=[0.0, 0.0, 0.0, 1.0]), KuavoPose(position=[0.3, -0.5, 1.0], orientation=[0.0, 0.0, 0.0, 1.0]), KuavoManipulationMpcFrame.WorldFrame)
|