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.

@@ -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
- raise Exception(f"ROS master is not running. Please start roscore first or check ROS_MASTER_URI, {e}")
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
- self.launch_ik_node()
82
- self.launch_gait_switch_node()
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) != len(self.joint_names):
33
- raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {len(self.joint_names)}")
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) != (len(self.joint_names)/2):
58
- raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {len(self.joint_names)/2}.")
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) != (len(self.joint_names)/2):
82
- raise ValueError(f"Target positions must have the same length as joint names {len(target_positions)} != {len(self.joint_names)/2}.")
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]*len(self.joint_names)
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
- self.dex_hand_control.make_gestures(gesture)
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[float, float]:
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[float, float]: The position of the dexterous hand.
149
+ Tuple[list, list]: The position of the dexterous hand.
151
150
  """
152
- return self._rb_state.eef_state.position
151
+ state = self._rb_state.eef_state
152
+ return (state[0].position, state[1].position)
153
153
 
154
- def get_velocity(self)->Tuple[float, float]:
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[float, float]: The velocity of the dexterous hand.
158
+ Tuple[list, list]: The velocity of the dexterous hand.
160
159
  """
161
- return self._rb_state.eef_state.velocity
160
+ state = self._rb_state.eef_state
161
+ return (state[0].velocity, state[1].velocity)
162
162
 
163
- def get_effort(self)->Tuple[float, float]:
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[float, float]: The effort of the dexterous hand.
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
- return self._rb_state.eef_state.effort
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
- return self._rb_state.eef_state.state
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 2.
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 1
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) != 1:
73
- raise ValueError("Target positions must be a list of length 1.")
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 1
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) != 1:
112
- raise ValueError("Target positions must be a list of length 1.")
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[float, float]:
170
+ def get_position(self)->Tuple[list, list]:
167
171
  """Get the position of the claws.
168
172
 
169
173
  Returns:
170
- Tuple[float, float]: The position of the claws, range [0.0, 100.0].
174
+ Tuple[list, list]: The position of the claws, range [0.0, 100.0].
171
175
  """
172
- return self._rb_state.eef_state.position
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[float, float]:
179
+ def get_velocity(self)->Tuple[list, list]:
175
180
  """Get the velocity of the claws.
176
181
 
177
182
  Returns:
178
- Tuple[float, float]: The velocity of the claws.
183
+ Tuple[list, list]: The velocity of the claws.
179
184
  """
180
- return self._rb_state.eef_state.velocity
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[float, float]:
188
+ def get_effort(self)->Tuple[list, list]:
183
189
  """Get the effort of the claws.
184
190
 
185
191
  Returns:
186
- Tuple[float, float]: The effort of the claws.
192
+ Tuple[list, list]: The effort of the claws.
187
193
  """
188
- return self._rb_state.eef_state.effort
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() to wait until the robot enters stance mode.
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() to wait until the robot enters trot mode.
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() to wait until the robot enters walk mode.
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.3, 0.0],Negative values indicate squatting down.
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
- limited_height = min(0.0, max(-0.3, height))
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 > 0.0 or height < -0.3:
150
- SDKLogger.warn(f"[Robot] height {height} exceeds limit [-0.3, 0.0], will be limited")
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(0.4, max(-0.4, pitch))
169
+ limited_pitch = min(MAX_PITCH, max(MIN_PITCH, pitch))
153
170
 
154
171
  # Check if pitch exceeds limits
155
- if abs(pitch) > 0.4:
156
- SDKLogger.warn(f"[Robot] pitch {pitch} exceeds limit [-0.4, 0.4], will be limited")
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
- Note:
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)))
@@ -37,6 +37,7 @@ class KuavoRobotInfo(RobotInfoBase):
37
37
  str: The end effector type, where:
38
38
  - "qiangnao" means "dexteroushand"
39
39
  - "lejuclaw" means "lejuclaw"
40
+ - "qiangnao_touch" means "touchdexteroushand"
40
41
  - ...
41
42
  """
42
43
  return self._end_effector_type