kuavo-humanoid-sdk 1.2.1b3279__20250911210455-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.

Files changed (184) hide show
  1. kuavo_humanoid_sdk/__init__.py +6 -0
  2. kuavo_humanoid_sdk/common/logger.py +45 -0
  3. kuavo_humanoid_sdk/interfaces/__init__.py +4 -0
  4. kuavo_humanoid_sdk/interfaces/data_types.py +288 -0
  5. kuavo_humanoid_sdk/interfaces/end_effector.py +62 -0
  6. kuavo_humanoid_sdk/interfaces/robot.py +22 -0
  7. kuavo_humanoid_sdk/interfaces/robot_info.py +56 -0
  8. kuavo_humanoid_sdk/kuavo/__init__.py +16 -0
  9. kuavo_humanoid_sdk/kuavo/core/audio.py +32 -0
  10. kuavo_humanoid_sdk/kuavo/core/core.py +666 -0
  11. kuavo_humanoid_sdk/kuavo/core/dex_hand_control.py +114 -0
  12. kuavo_humanoid_sdk/kuavo/core/leju_claw_control.py +67 -0
  13. kuavo_humanoid_sdk/kuavo/core/llm_doubao.py +608 -0
  14. kuavo_humanoid_sdk/kuavo/core/microphone.py +192 -0
  15. kuavo_humanoid_sdk/kuavo/core/navigation.py +70 -0
  16. kuavo_humanoid_sdk/kuavo/core/ros/audio.py +110 -0
  17. kuavo_humanoid_sdk/kuavo/core/ros/control.py +1524 -0
  18. kuavo_humanoid_sdk/kuavo/core/ros/microphone.py +38 -0
  19. kuavo_humanoid_sdk/kuavo/core/ros/navigation.py +217 -0
  20. kuavo_humanoid_sdk/kuavo/core/ros/observation.py +94 -0
  21. kuavo_humanoid_sdk/kuavo/core/ros/param.py +201 -0
  22. kuavo_humanoid_sdk/kuavo/core/ros/sat_utils.py +103 -0
  23. kuavo_humanoid_sdk/kuavo/core/ros/state.py +652 -0
  24. kuavo_humanoid_sdk/kuavo/core/ros/tools.py +220 -0
  25. kuavo_humanoid_sdk/kuavo/core/ros/vision.py +234 -0
  26. kuavo_humanoid_sdk/kuavo/core/ros_env.py +238 -0
  27. kuavo_humanoid_sdk/kuavo/demo_climbstair.py +249 -0
  28. kuavo_humanoid_sdk/kuavo/dexterous_hand.py +238 -0
  29. kuavo_humanoid_sdk/kuavo/leju_claw.py +235 -0
  30. kuavo_humanoid_sdk/kuavo/logger_client.py +80 -0
  31. kuavo_humanoid_sdk/kuavo/robot.py +561 -0
  32. kuavo_humanoid_sdk/kuavo/robot_arm.py +299 -0
  33. kuavo_humanoid_sdk/kuavo/robot_audio.py +39 -0
  34. kuavo_humanoid_sdk/kuavo/robot_blockly.py +1162 -0
  35. kuavo_humanoid_sdk/kuavo/robot_climbstair.py +1607 -0
  36. kuavo_humanoid_sdk/kuavo/robot_head.py +74 -0
  37. kuavo_humanoid_sdk/kuavo/robot_info.py +134 -0
  38. kuavo_humanoid_sdk/kuavo/robot_microphone.py +19 -0
  39. kuavo_humanoid_sdk/kuavo/robot_navigation.py +135 -0
  40. kuavo_humanoid_sdk/kuavo/robot_observation.py +64 -0
  41. kuavo_humanoid_sdk/kuavo/robot_speech.py +24 -0
  42. kuavo_humanoid_sdk/kuavo/robot_state.py +310 -0
  43. kuavo_humanoid_sdk/kuavo/robot_tool.py +109 -0
  44. kuavo_humanoid_sdk/kuavo/robot_vision.py +81 -0
  45. kuavo_humanoid_sdk/kuavo_strategy/__init__.py +2 -0
  46. kuavo_humanoid_sdk/kuavo_strategy/grasp_box/grasp_box_strategy.py +1325 -0
  47. kuavo_humanoid_sdk/kuavo_strategy/kuavo_strategy.py +106 -0
  48. kuavo_humanoid_sdk/kuavo_strategy_v2/common/data_type.py +340 -0
  49. kuavo_humanoid_sdk/kuavo_strategy_v2/common/events/base_event.py +215 -0
  50. kuavo_humanoid_sdk/kuavo_strategy_v2/common/robot_sdk.py +25 -0
  51. kuavo_humanoid_sdk/kuavo_strategy_v2/pick_place_box/case.py +331 -0
  52. kuavo_humanoid_sdk/kuavo_strategy_v2/pick_place_box/strategy.py +504 -0
  53. kuavo_humanoid_sdk/kuavo_strategy_v2/utils/logger_setup.py +40 -0
  54. kuavo_humanoid_sdk/kuavo_strategy_v2/utils/utils.py +88 -0
  55. kuavo_humanoid_sdk/msg/__init__.py +4 -0
  56. kuavo_humanoid_sdk/msg/kuavo_msgs/__init__.py +7 -0
  57. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetection.py +306 -0
  58. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetectionArray.py +437 -0
  59. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AudioReceiverData.py +122 -0
  60. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_FTsensorData.py +260 -0
  61. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_JoySticks.py +191 -0
  62. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_Metadata.py +199 -0
  63. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_MmDetectionMsg.py +264 -0
  64. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_RobotActionState.py +112 -0
  65. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_TFArray.py +323 -0
  66. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_TaskPoint.py +175 -0
  67. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/__init__.py +62 -0
  68. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armCollisionCheckInfo.py +160 -0
  69. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armHandPose.py +161 -0
  70. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armHandPoseFree.py +171 -0
  71. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armPoseWithTimeStamp.py +168 -0
  72. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armTargetPoses.py +171 -0
  73. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_bezierCurveCubicPoint.py +178 -0
  74. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_dexhandCommand.py +229 -0
  75. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_dexhandTouchState.py +256 -0
  76. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_endEffectorData.py +227 -0
  77. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose.py +123 -0
  78. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose6D.py +123 -0
  79. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose6DTargetTrajectories.py +320 -0
  80. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseTargetTrajectories.py +301 -0
  81. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseWithVision.py +136 -0
  82. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseWithVisionArray.py +231 -0
  83. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoses.py +149 -0
  84. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoses6D.py +149 -0
  85. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_fullBodyTargetTrajectories.py +258 -0
  86. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gaitTimeName.py +147 -0
  87. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gestureInfo.py +218 -0
  88. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gestureTask.py +149 -0
  89. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_handPose.py +136 -0
  90. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_headBodyPose.py +145 -0
  91. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_ikSolveError.py +171 -0
  92. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_ikSolveParam.py +140 -0
  93. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_imuData.py +165 -0
  94. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointBezierTrajectory.py +201 -0
  95. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointCmd.py +390 -0
  96. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointData.py +205 -0
  97. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_kuavoModeSchedule.py +224 -0
  98. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_lejuClawCommand.py +320 -0
  99. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_lejuClawState.py +341 -0
  100. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_motorParam.py +122 -0
  101. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_picoPoseInfo.py +143 -0
  102. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_picoPoseInfoList.py +220 -0
  103. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_planArmState.py +120 -0
  104. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_qv.py +121 -0
  105. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotArmQVVD.py +177 -0
  106. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotBodyMatrices.py +332 -0
  107. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotHandPosition.py +225 -0
  108. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotHeadMotionData.py +128 -0
  109. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotState.py +222 -0
  110. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_sensorsData.py +655 -0
  111. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_switchGaitByName.py +200 -0
  112. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_tagDataArray.py +216 -0
  113. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_touchSensorStatus.py +162 -0
  114. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPose.py +273 -0
  115. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseCmd.py +316 -0
  116. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseCmdFree.py +338 -0
  117. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseFree.py +299 -0
  118. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_yoloDetection.py +251 -0
  119. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_yoloOutputData.py +168 -0
  120. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_CreatePath.py +581 -0
  121. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_ExecuteArmAction.py +281 -0
  122. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetAllMaps.py +241 -0
  123. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetCurrentMap.py +225 -0
  124. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetTargetPartPoseInCamera.py +298 -0
  125. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_InitialPoseWithTaskPoint.py +281 -0
  126. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_LoadMap.py +281 -0
  127. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_NavigateToTaskPoint.py +281 -0
  128. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_RepublishTFs.py +373 -0
  129. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetInitialPose.py +394 -0
  130. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetJoyTopic.py +282 -0
  131. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetLEDMode.py +468 -0
  132. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetLEDMode_free.py +289 -0
  133. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SpeechSynthesis.py +270 -0
  134. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_TaskPointOperation.py +536 -0
  135. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/__init__.py +43 -0
  136. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_adjustZeroPoint.py +277 -0
  137. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeArmCtrlMode.py +275 -0
  138. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeArmCtrlModeKuavo.py +236 -0
  139. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeMotorParam.py +299 -0
  140. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeTorsoCtrlMode.py +274 -0
  141. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_controlLejuClaw.py +408 -0
  142. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_enableHandTouchSensor.py +304 -0
  143. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_fkSrv.py +395 -0
  144. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_footPose6DTargetTrajectoriesSrv.py +426 -0
  145. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_footPoseTargetTrajectoriesSrv.py +409 -0
  146. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureExecute.py +339 -0
  147. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureExecuteState.py +257 -0
  148. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureList.py +418 -0
  149. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getCurrentGaitName.py +253 -0
  150. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getMotorParam.py +299 -0
  151. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getMotorZeroPoints.py +286 -0
  152. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_handForceLevel.py +330 -0
  153. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_jointMoveTo.py +302 -0
  154. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_planArmTrajectoryBezierCurve.py +422 -0
  155. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_planArmTrajectoryCubicSpline.py +490 -0
  156. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_playmusic.py +268 -0
  157. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setHwIntialState.py +304 -0
  158. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setMmCtrlFrame.py +273 -0
  159. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setMotorEncoderRoundService.py +283 -0
  160. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setTagId.py +275 -0
  161. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_singleStepControl.py +444 -0
  162. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_twoArmHandPoseCmdFreeSrv.py +716 -0
  163. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_twoArmHandPoseCmdSrv.py +664 -0
  164. kuavo_humanoid_sdk/msg/motion_capture_ik/__init__.py +7 -0
  165. kuavo_humanoid_sdk/msg/ocs2_msgs/__init__.py +7 -0
  166. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/__init__.py +12 -0
  167. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_constraint.py +142 -0
  168. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_controller_data.py +121 -0
  169. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_lagrangian_metrics.py +148 -0
  170. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mode_schedule.py +150 -0
  171. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_flattened_controller.py +666 -0
  172. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_input.py +122 -0
  173. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_observation.py +209 -0
  174. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_performance_indices.py +140 -0
  175. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_solver_data.py +886 -0
  176. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_state.py +122 -0
  177. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_target_trajectories.py +239 -0
  178. kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_multiplier.py +148 -0
  179. kuavo_humanoid_sdk/msg/ocs2_msgs/srv/__init__.py +1 -0
  180. kuavo_humanoid_sdk/msg/ocs2_msgs/srv/_reset.py +376 -0
  181. kuavo_humanoid_sdk-1.2.1b3279.dist-info/METADATA +296 -0
  182. kuavo_humanoid_sdk-1.2.1b3279.dist-info/RECORD +184 -0
  183. kuavo_humanoid_sdk-1.2.1b3279.dist-info/WHEEL +6 -0
  184. kuavo_humanoid_sdk-1.2.1b3279.dist-info/top_level.txt +1 -0
@@ -0,0 +1,1162 @@
1
+ import time
2
+ import signal
3
+ from kuavo_humanoid_sdk import KuavoPose
4
+ from kuavo_humanoid_sdk.kuavo.robot import KuavoRobot
5
+ from kuavo_humanoid_sdk.kuavo.robot_state import KuavoRobotState
6
+ from kuavo_humanoid_sdk.kuavo.robot_audio import KuavoRobotAudio
7
+ from kuavo_humanoid_sdk.kuavo.robot_climbstair import KuavoRobotClimbStair, set_pitch_limit
8
+ from kuavo_humanoid_sdk.interfaces.data_types import KuavoPose
9
+ from kuavo_msgs.srv import planArmTrajectoryBezierCurve, planArmTrajectoryBezierCurveRequest
10
+ from kuavo_msgs.msg import planArmState, jointBezierTrajectory, bezierCurveCubicPoint, robotHandPosition, robotHeadMotionData, sensorsData
11
+ from geometry_msgs.msg import Twist
12
+ import asyncio
13
+ import math
14
+ import json
15
+ import os
16
+ import rospkg
17
+ import rospy
18
+ import threading
19
+ import tf
20
+ import time
21
+ import pwd
22
+ import numpy as np
23
+ from sensor_msgs.msg import JointState
24
+ from trajectory_msgs.msg import JointTrajectory
25
+
26
+ from geometry_msgs.msg import Pose, Point, Quaternion
27
+
28
+ from std_msgs.msg import Float64MultiArray
29
+
30
+ import inspect
31
+ from model_utils import *
32
+ import model_utils as model_utils
33
+
34
+
35
+ from enum import IntEnum
36
+ # Global flag for handling Ctrl+C
37
+ running = False
38
+ robot_version = int(os.environ.get("ROBOT_VERSION", "45"))
39
+ ocs2_joint_state = JointState()
40
+ ocs2_hand_state = robotHandPosition()
41
+ ocs2_head_state = robotHeadMotionData()
42
+ if robot_version >= 40:
43
+ INIT_ARM_POS = [20, 0, 0, -30, 0, 0, 0, 20, 0, 0, -30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
44
+ else:
45
+ INIT_ARM_POS = [22.91831, 0, 0, -45.83662, 22.91831, 0, 0, -45.83662] # task.info: shoudler_center: 0.4rad, elbow_center: -0.8rad
46
+
47
+
48
+
49
+ global ACTION_FILE_FOLDER, package_path, THRESHOLD_HEAD_CONTROL_COUNT, FLOAT_MAX
50
+ THRESHOLD_HEAD_CONTROL_COUNT = 30
51
+ FLOAT_MAX = float('inf')
52
+
53
+ package_name = 'planarmwebsocketservice'
54
+ package_path = rospkg.RosPack().get_path(package_name)
55
+ sudo_user = os.environ.get("SUDO_USER")
56
+ if sudo_user:
57
+ user_info = pwd.getpwnam(sudo_user)
58
+ home_path = user_info.pw_dir
59
+ else:
60
+ home_path = os.path.expanduser("~")
61
+ ACTION_FILE_FOLDER = os.path.join(home_path, '.config', 'lejuconfig', 'action_files')
62
+ plan_arm_state_progress = 0
63
+ plan_arm_state_status = False
64
+
65
+
66
+ def signal_handler(sig, frame):
67
+ """Signal handler for catching Ctrl+C and safely stopping the robot.
68
+
69
+ Args:
70
+ sig: Signal number.
71
+ frame: Current stack frame.
72
+ """
73
+ global running
74
+ print('\nCtrl+C pressed. Stopping robot...')
75
+ running = False
76
+
77
+ def frames_to_custom_action_data(file_path: str):
78
+ """Parse action file and convert frames to custom action data.
79
+
80
+ Args:
81
+ file_path (str): Path to the action file.
82
+
83
+ Returns:
84
+ dict: Action data for each joint.
85
+ """
86
+ with open(file_path, "r") as f:
87
+ data = json.load(f)
88
+ frames = data["frames"]
89
+ action_data = {}
90
+ for frame in frames:
91
+ servos, keyframe, attribute = frame["servos"], frame["keyframe"], frame["attribute"]
92
+ for index, value in enumerate(servos):
93
+ key = index + 1
94
+ if key not in action_data:
95
+ action_data[key] = []
96
+ if keyframe != 0 and len(action_data[key]) == 0:
97
+ if key <= len(INIT_ARM_POS):
98
+ action_data[key].append([
99
+ [0, INIT_ARM_POS[key-1]],
100
+ [0, 0],
101
+ [0, INIT_ARM_POS[key-1]],
102
+ ])
103
+ if value is not None:
104
+ CP = attribute[str(key)]["CP"]
105
+ left_CP, right_CP = CP
106
+ action_data[key].append([
107
+ [round(keyframe/100, 1), int(value)],
108
+ [round((keyframe+left_CP[0])/100, 1), int(value+left_CP[1])],
109
+ [round((keyframe+right_CP[0])/100, 1), int(value+right_CP[1])],
110
+ ])
111
+ return action_data
112
+
113
+ def get_start_end_frame_time(file_path: str):
114
+ """Get the start and end frame time from the action file.
115
+
116
+ Args:
117
+ file_path (str): Path to the action file.
118
+
119
+ Returns:
120
+ tuple: (start_frame_time, end_frame_time)
121
+ """
122
+ with open(file_path, "r") as f:
123
+ data = json.load(f)
124
+ start_frame_time = data["first"] / 100
125
+ end_frame_time = data["finish"] / 100
126
+ return start_frame_time, end_frame_time
127
+
128
+ def frames_to_custom_action_data_ocs2(file_path: str, start_frame_time: float, current_arm_joint_state: list):
129
+ """Parse action file and convert frames to custom action data for OCS2.
130
+
131
+ Args:
132
+ file_path (str): Path to the action file.
133
+ start_frame_time (float): Start frame time.
134
+ current_arm_joint_state (list): Current arm joint state.
135
+
136
+ Returns:
137
+ dict: Filtered action data for each joint.
138
+ """
139
+ def filter_data(action_data, start_frame_time, current_arm_joint_state):
140
+ """Filter and adjust action data for OCS2.
141
+
142
+ Args:
143
+ action_data (dict): Raw action data.
144
+ start_frame_time (float): Start frame time.
145
+ current_arm_joint_state (list): Current arm joint state.
146
+
147
+ Returns:
148
+ dict: Filtered action data.
149
+ """
150
+ filtered_action_data = {}
151
+ x_shift = start_frame_time - 1
152
+ for key, frames in action_data.items():
153
+ filtered_frames = []
154
+ found_start = False
155
+ skip_next = False
156
+ for i in range(-1, len(frames)):
157
+ frame = frames[i]
158
+ if i == len(frames) - 1:
159
+ next_frame = frame
160
+ else:
161
+ next_frame = frames[i+1]
162
+ end_time = next_frame[0][0]
163
+
164
+ if not found_start and end_time >= start_frame_time:
165
+ found_start = True
166
+
167
+ p0 = np.array([0, current_arm_joint_state[key-1]])
168
+ p3 = np.array([next_frame[0][0] - x_shift, next_frame[0][1]])
169
+
170
+ # Calculate control points for smooth transition
171
+ curve_length = np.linalg.norm(p3 - p0)
172
+ p1 = p0 + curve_length * 0.25 * np.array([1, 0]) # Move 1/4 curve length to the right
173
+ p2 = p3 - curve_length * 0.25 * np.array([1, 0]) # Move 1/4 curve length to the left
174
+
175
+ # Create new frame
176
+ frame1 = [
177
+ p0.tolist(),
178
+ p0.tolist(),
179
+ p1.tolist()
180
+ ]
181
+
182
+ # Modify next_frame's left control point
183
+ next_frame[1] = p2.tolist()
184
+
185
+ filtered_frames.append(frame1)
186
+ skip_next = True
187
+
188
+ if found_start:
189
+ if skip_next:
190
+ skip_next = False
191
+ continue
192
+ end_point = [round(frame[0][0] - x_shift, 1), round(frame[0][1], 1)]
193
+ left_control_point = [round(frame[1][0] - x_shift, 1), round(frame[1][1], 1)]
194
+ right_control_point = [round(frame[2][0] - x_shift, 1), round(frame[2][1], 1)]
195
+ filtered_frames.append([end_point, left_control_point, right_control_point])
196
+
197
+ filtered_action_data[key] = filtered_frames
198
+ return filtered_action_data
199
+
200
+ with open(file_path, "r") as f:
201
+ data = json.load(f)
202
+ frames = data["frames"]
203
+ action_data = {}
204
+ for frame in frames:
205
+ servos, keyframe, attribute = frame["servos"], frame["keyframe"], frame["attribute"]
206
+ for index, value in enumerate(servos):
207
+ key = index + 1
208
+ if key not in action_data:
209
+ action_data[key] = []
210
+ if keyframe != 0 and len(action_data[key]) == 0:
211
+ if key <= len(INIT_ARM_POS):
212
+ action_data[key].append([
213
+ [0, math.radians(INIT_ARM_POS[key-1])],
214
+ [0, math.radians(INIT_ARM_POS[key-1])],
215
+ [0, math.radians(INIT_ARM_POS[key-1])],
216
+ ])
217
+ if value is not None:
218
+ CP = attribute[str(key)]["CP"]
219
+ left_CP, right_CP = CP
220
+ action_data[key].append([
221
+ [round(keyframe/100, 1), math.radians(value)],
222
+ [round((keyframe+left_CP[0])/100, 1), math.radians(value+left_CP[1])],
223
+ [round((keyframe+right_CP[0])/100, 1), math.radians(value+right_CP[1])],
224
+ ])
225
+ return filter_data(action_data, start_frame_time, current_arm_joint_state)
226
+
227
+
228
+ class RobotArmControlMode(IntEnum):
229
+ """Enum for robot arm control modes.
230
+
231
+ Attributes:
232
+ Fixed: Fixed mode, arm remains stationary.
233
+ AutoSwing: Auto swing mode, arm swings automatically.
234
+ ExternalControl: External control mode, arm is controlled by external signals.
235
+ """
236
+ Fixed = 0 # Fixed mode, arm remains stationary
237
+ AutoSwing = 1 # Auto swing mode, arm swings automatically
238
+ ExternalControl = 2 # External control mode, arm is controlled externally
239
+
240
+ if robot_version >= 40:
241
+ joint_names = [
242
+ "l_arm_pitch",
243
+ "l_arm_roll",
244
+ "l_arm_yaw",
245
+ "l_forearm_pitch",
246
+ "l_hand_yaw",
247
+ "l_hand_pitch",
248
+ "l_hand_roll",
249
+ "r_arm_pitch",
250
+ "r_arm_roll",
251
+ "r_arm_yaw",
252
+ "r_forearm_pitch",
253
+ "r_hand_yaw",
254
+ "r_hand_pitch",
255
+ "r_hand_roll",
256
+ ]
257
+ else:
258
+ joint_names = [
259
+ "zarm_l1_link", "zarm_l2_link", "zarm_l3_link", "zarm_l4_link",
260
+ "zarm_r1_link", "zarm_r2_link", "zarm_r3_link", "zarm_r4_link",
261
+ ]
262
+
263
+
264
+ robot_settings = {
265
+ "kuavo": {
266
+ "plan_arm_trajectory_bezier_service_name": "/plan_arm_trajectory_bezier_curve",
267
+ "stop_plan_arm_trajectory_service_name": "/stop_plan_arm_trajectory",
268
+ "arm_traj_state_topic_name": "/robot_plan_arm_state",
269
+ },
270
+ "ocs2": {
271
+ "plan_arm_trajectory_bezier_service_name": "/bezier/plan_arm_trajectory",
272
+ "stop_plan_arm_trajectory_service_name": "/bezier/stop_plan_arm_trajectory",
273
+ "arm_traj_state_topic_name": "/bezier/arm_traj_state",
274
+ }
275
+ }
276
+
277
+ class RobotControlBlockly:
278
+ def __init__(self):
279
+ """Initialize the robot control class, create robot objects, register signal handlers, and initialize ROS publishers and subscribers."""
280
+ self.robot = KuavoRobot()
281
+ self.robot_state = KuavoRobotState()
282
+ self.robot_audio = KuavoRobotAudio()
283
+ self.climb_stair = KuavoRobotClimbStair()
284
+ # Register signal handler
285
+ signal.signal(signal.SIGINT, signal_handler)
286
+ global g_robot_type
287
+ g_robot_type = "ocs2"
288
+ self.loop = None
289
+ self.init_ros_publishers()
290
+ self.to_stance()
291
+
292
+ def set_action_file_path(self, proj_name: str):
293
+ """Set the action file path for a specific project.
294
+
295
+ Args:
296
+ proj_name (str): Project name.
297
+ """
298
+ global ACTION_FILE_FOLDER
299
+ ACTION_FILE_FOLDER = package_path + "/upload_files/" + proj_name + "/action_files"
300
+
301
+ def init_ros_publishers(self):
302
+ """Initialize ROS publishers and subscribers for arm, hand, head control topics, sensor data, and trajectory state."""
303
+ global kuavo_arm_traj_pub, control_hand_pub, control_head_pub, g_robot_type, control_waist_pub
304
+ kuavo_arm_traj_pub = rospy.Publisher('/kuavo_arm_traj', JointState, queue_size=1, tcp_nodelay=True)
305
+ control_hand_pub = rospy.Publisher('/control_robot_hand_position', robotHandPosition, queue_size=1, tcp_nodelay=True)
306
+ control_head_pub = rospy.Publisher('/robot_head_motion_data', robotHeadMotionData, queue_size=1, tcp_nodelay=True)
307
+
308
+ control_waist_pub = rospy.Publisher('/robot_waist_motion_data', Float64MultiArray, queue_size=1, tcp_nodelay=True)
309
+
310
+ rospy.Subscriber('/sensors_data_raw', sensorsData, self.sensors_data_callback, queue_size=1, tcp_nodelay=True)
311
+ rospy.Timer(rospy.Duration(0.01), self.timer_callback)
312
+ rospy.Subscriber(robot_settings[g_robot_type]["arm_traj_state_topic_name"], planArmState, self.plan_arm_state_callback)
313
+ rospy.Subscriber('/bezier/arm_traj', JointTrajectory, self.traj_callback, queue_size=1, tcp_nodelay=True)
314
+
315
+ def timer_callback(self, event):
316
+ """Timer callback for periodically publishing arm, hand, and head state to ROS topics.
317
+
318
+ Only publishes when robot type is 'ocs2' and trajectory is not finished.
319
+ """
320
+ global kuavo_arm_traj_pub, control_hand_pub, control_head_pub, g_robot_type
321
+ if g_robot_type == "ocs2" and len(ocs2_joint_state.position) > 0 and plan_arm_state_status is False:
322
+ kuavo_arm_traj_pub.publish(ocs2_joint_state)
323
+ control_hand_pub.publish(ocs2_hand_state)
324
+ # control_head_pub.publish(ocs2_head_state)
325
+
326
+ def sensors_data_callback(self, msg):
327
+ """Callback for sensor data, extracts current arm joint state and saves to global variables.
328
+
329
+ Args:
330
+ msg (sensorsData): Sensor data message.
331
+ """
332
+ global current_arm_joint_state, robot_version, current_head_joint_state
333
+ if robot_version >= 40:
334
+ current_arm_joint_state = msg.joint_data.joint_q[12:26]
335
+ current_arm_joint_state = [round(pos, 2) for pos in current_arm_joint_state]
336
+ current_arm_joint_state.extend([0] * 14)
337
+ current_head_joint_state = msg.joint_data.joint_q[-2:]
338
+ current_head_joint_state = [round(pos, 4) for pos in current_head_joint_state]
339
+ elif robot_version >= 10 and robot_version < 30:
340
+ current_arm_joint_state = msg.joint_data.joint_q[13:21]
341
+ current_arm_joint_state = [round(pos, 5) for pos in current_arm_joint_state]
342
+ current_arm_joint_state.extend([0] * 14)
343
+ current_head_joint_state = msg.joint_data.joint_q[-2:]
344
+ current_head_joint_state = [round(pos, 4) for pos in current_head_joint_state]
345
+
346
+ def plan_arm_state_callback(self, msg: planArmState):
347
+ """Callback for arm trajectory state, updates global variables for progress and completion.
348
+
349
+ Args:
350
+ msg (planArmState): Arm trajectory state message.
351
+ """
352
+ global plan_arm_state_progress, plan_arm_state_status
353
+ plan_arm_state_progress = msg.progress
354
+ plan_arm_state_status = msg.is_finished
355
+
356
+ def traj_callback(self, msg):
357
+ """Callback for trajectory messages, parses trajectory points and updates global arm, hand, and head state variables.
358
+
359
+ Args:
360
+ msg (JointTrajectory): Trajectory message.
361
+ """
362
+ global ocs2_joint_state
363
+ global robot_version
364
+ if len(msg.points) == 0:
365
+ return
366
+ point = msg.points[0]
367
+
368
+ if robot_version >= 40:
369
+ ocs2_joint_state.name = joint_names
370
+ ocs2_joint_state.position = [math.degrees(pos) for pos in point.positions[:14]]
371
+ ocs2_joint_state.velocity = [math.degrees(vel) for vel in point.velocities[:14]]
372
+ ocs2_joint_state.effort = [0] * 14
373
+ ocs2_hand_state.left_hand_position = [int(math.degrees(pos)) for pos in point.positions[14:20]]
374
+ ocs2_hand_state.right_hand_position = [int(math.degrees(pos)) for pos in point.positions[20:26]]
375
+ ocs2_head_state.joint_data = [math.degrees(pos) for pos in point.positions[26:]]
376
+
377
+ elif robot_version >= 10 and robot_version < 30:
378
+ ocs2_joint_state.name = joint_names
379
+ ocs2_joint_state.position = [math.degrees(pos) for pos in point.positions[:8]]
380
+ ocs2_joint_state.velocity = [math.degrees(vel) for vel in point.velocities[:8]]
381
+ ocs2_joint_state.effort = [0] * 8
382
+
383
+ def arm_reset(self):
384
+ """Reset the arm position to the initial state."""
385
+ try:
386
+ self.robot.arm_reset()
387
+ time.sleep(1)
388
+ except Exception as e:
389
+ print(f"Arm reset failed: {str(e)}")
390
+
391
+ def set_auto_swing_arm_mode(self):
392
+ """Set the arm to auto swing mode."""
393
+ try:
394
+ self.robot.set_auto_swing_arm_mode()
395
+ time.sleep(1)
396
+ except Exception as e:
397
+ print(f"Set auto swing arm mode failed: {str(e)}")
398
+
399
+ def set_fixed_arm_mode(self):
400
+ """Set the arm to fixed mode."""
401
+ try:
402
+ self.robot.set_fixed_arm_mode()
403
+ time.sleep(1)
404
+ except Exception as e:
405
+ print(f"Set fixed arm mode failed: {str(e)}")
406
+
407
+ def set_external_control_arm_mode(self):
408
+ """Set the arm to external control mode."""
409
+ try:
410
+ if self.robot.set_external_control_arm_mode():
411
+ print("Set external control mode succeeded")
412
+ else:
413
+ print("Set external control mode failed")
414
+ time.sleep(1)
415
+ except Exception as e:
416
+ print(f"Set external control arm mode failed: {str(e)}")
417
+
418
+ def set_arm_control_mode(self, mode: int):
419
+ """Set the arm control mode.
420
+
421
+ Args:
422
+ mode (int): 0 - Fixed, 1 - AutoSwing, 2 - ExternalControl
423
+ """
424
+ try:
425
+ if mode == RobotArmControlMode.Fixed:
426
+ self.set_fixed_arm_mode()
427
+ elif mode == RobotArmControlMode.AutoSwing:
428
+ self.set_auto_swing_arm_mode()
429
+ elif mode == RobotArmControlMode.ExternalControl:
430
+ self.set_external_control_arm_mode()
431
+ except Exception as e:
432
+ print(f"Set arm control mode failed: {str(e)}")
433
+
434
+ def _start_loop_in_thread(self):
435
+ """Start a new asyncio event loop in a separate thread."""
436
+ self.loop = asyncio.new_event_loop()
437
+ def run_loop():
438
+ asyncio.set_event_loop(self.loop)
439
+ self.loop.run_forever()
440
+ t = threading.Thread(target=run_loop, daemon=True)
441
+ t.start()
442
+
443
+ def walk(self, linear_x: float, linear_y: float, angular_z: float):
444
+ """Start walking with specified velocities.
445
+
446
+ Args:
447
+ linear_x (float): Linear velocity in x direction.
448
+ linear_y (float): Linear velocity in y direction.
449
+ angular_z (float): Angular velocity around z axis.
450
+ """
451
+ if self.loop is None:
452
+ self._start_loop_in_thread()
453
+ global running
454
+ # If already walking, stop first
455
+ global global_linear_x, global_linear_y, global_angular_z
456
+ global_linear_x = linear_x
457
+ global_linear_y = linear_y
458
+ global_angular_z = angular_z
459
+ if running:
460
+ self.robot.walk(global_linear_x, global_linear_y, global_angular_z)
461
+ else:
462
+ self.control_robot_height("up", 0.4, 0.0)
463
+ running = True
464
+ async def robot_walk():
465
+ try:
466
+ while running:
467
+ self.robot.walk(global_linear_x, global_linear_y, global_angular_z)
468
+ await asyncio.sleep(0.1)
469
+ self.robot.walk(0.0, 0.0, 0.0)
470
+ await asyncio.sleep(0.5)
471
+ self.robot.stance()
472
+ except asyncio.CancelledError:
473
+ pass
474
+ asyncio.run_coroutine_threadsafe(robot_walk(), self.loop)
475
+
476
+ def walk_angle(self, linear_x: float, linear_y: float, angular_z: float):
477
+ """Start walking with specified velocities.
478
+
479
+ Args:
480
+ linear_x (float): Linear velocity in x direction.
481
+ linear_y (float): Linear velocity in y direction.
482
+ angular_z (float): Angular velocity around z axis.
483
+ """
484
+ # 角度转弧度
485
+ angular_z = math.radians(angular_z)
486
+ self.walk(linear_x, linear_y, angular_z)
487
+
488
+ def start(self):
489
+ """Start robot movement and Begin trot."""
490
+ self.robot.trot()
491
+
492
+ def stop(self):
493
+ """Stop robot movement and set the running flag to False."""
494
+ global running
495
+ running = False
496
+ while True:
497
+ time.sleep(0.01)
498
+ if self.robot_state.is_stance():
499
+ break
500
+ # Wait for gait switch to complete
501
+ time.sleep(0.5)
502
+
503
+ def control_arm_position(self, joint_positions: list):
504
+ """Move the arm to the specified joint angles using interpolation for smooth transition.
505
+
506
+ Args:
507
+ joint_positions (list): Target joint angles in degrees.
508
+ """
509
+ try:
510
+ self.robot.arm_reset()
511
+ q_list = []
512
+ q0 = [0.0] * 8
513
+ num = 90
514
+ for i in range(num):
515
+ q_tmp = [0.0] * 8
516
+ for j in range(8):
517
+ q_tmp[j] = q0[j] + i / float(num) * (joint_positions[j] - q0[j])
518
+ q_list.append(q_tmp)
519
+ for q in q_list:
520
+ # Convert degrees to radians
521
+ q = [math.radians(angle) for angle in q]
522
+ self.robot.control_arm_position(q)
523
+ time.sleep(0.02)
524
+ except Exception as e:
525
+ print(f"Arm position control failed: {str(e)}")
526
+
527
+ def stance(self):
528
+ """Set the robot to standing posture."""
529
+ try:
530
+ self.robot.stance()
531
+ while True:
532
+ time.sleep(0.01)
533
+ if self.robot_state.is_stance():
534
+ break
535
+ except Exception as e:
536
+ print(f"Stance adjustment failed: {str(e)}")
537
+
538
+ def create_bezier_request(self, action_data, start_frame_time, end_frame_time):
539
+ """Construct a Bezier curve trajectory request message for arm trajectory planning service.
540
+
541
+ Args:
542
+ action_data (dict): Joint action data.
543
+ start_frame_time (float): Start frame time.
544
+ end_frame_time (float): End frame time.
545
+
546
+ Returns:
547
+ planArmTrajectoryBezierCurveRequest: Bezier curve trajectory request object.
548
+ """
549
+ global robot_version
550
+ req = planArmTrajectoryBezierCurveRequest()
551
+ for key, value in action_data.items():
552
+ msg = jointBezierTrajectory()
553
+ for frame in value:
554
+ point = bezierCurveCubicPoint()
555
+ point.end_point, point.left_control_point, point.right_control_point = frame
556
+ msg.bezier_curve_points.append(point)
557
+ req.multi_joint_bezier_trajectory.append(msg)
558
+ req.start_frame_time = start_frame_time
559
+ req.end_frame_time = end_frame_time
560
+ if robot_version >= 40:
561
+ req.joint_names = [
562
+ "l_arm_pitch", "l_arm_roll", "l_arm_yaw", "l_forearm_pitch", "l_hand_yaw", "l_hand_pitch", "l_hand_roll",
563
+ "r_arm_pitch", "r_arm_roll", "r_arm_yaw", "r_forearm_pitch", "r_hand_yaw", "r_hand_pitch", "r_hand_roll"
564
+ ]
565
+ else:
566
+ req.joint_names = [
567
+ "zarm_l1_link", "zarm_l2_link", "zarm_l3_link", "zarm_l4_link",
568
+ "zarm_r1_link", "zarm_r2_link", "zarm_r3_link", "zarm_r4_link"
569
+ ]
570
+ return req
571
+
572
+ def plan_arm_trajectory_bezier_curve_client(self, req):
573
+ """Call the arm Bezier curve trajectory planning service and send the trajectory request.
574
+
575
+ Args:
576
+ req: Bezier trajectory request object.
577
+
578
+ Returns:
579
+ bool: True if service call succeeded, False otherwise.
580
+ """
581
+ service_name = robot_settings[g_robot_type]["plan_arm_trajectory_bezier_service_name"]
582
+ rospy.wait_for_service(service_name)
583
+ try:
584
+ plan_service = rospy.ServiceProxy(service_name, planArmTrajectoryBezierCurve)
585
+ res = plan_service(req)
586
+ return res.success
587
+ except rospy.ServiceException as e:
588
+ rospy.logerr(f"Service call failed: {e}")
589
+ return False
590
+
591
+ def excute_action_file(self, action_file: str, proj_name: str = None, music_file: str = None):
592
+ """Execute an action file, parse action frames, and send Bezier trajectory request to the robot.
593
+
594
+ Args:
595
+ action_file (str): Action file name.
596
+ proj_name (str, optional): Project name.
597
+ """
598
+ global g_robot_type
599
+ g_robot_type = "ocs2"
600
+ global plan_arm_state_status
601
+ plan_arm_state_status = False
602
+
603
+ action_filename = action_file + ".tact"
604
+ if proj_name is None:
605
+ action_file_path = os.path.expanduser(f"{ACTION_FILE_FOLDER}/{action_filename}")
606
+ else:
607
+ action_file_path = package_path + "/upload_files/" + proj_name + "/action_files/" + action_filename
608
+ if not os.path.exists(action_file_path):
609
+ print(f"Action file not found: {action_file_path}")
610
+ return
611
+
612
+ start_frame_time, end_frame_time = get_start_end_frame_time(action_file_path)
613
+
614
+ if g_robot_type == "ocs2":
615
+ action_frames = frames_to_custom_action_data_ocs2(action_file_path, start_frame_time, current_arm_joint_state)
616
+ end_frame_time += 1
617
+ self.set_arm_control_mode(2)
618
+ else:
619
+ action_frames = frames_to_custom_action_data(action_file_path)
620
+
621
+ req = self.create_bezier_request(action_frames, start_frame_time, end_frame_time)
622
+ if music_file is not None:
623
+ self.play_music(music_file)
624
+ self.plan_arm_trajectory_bezier_curve_client(req)
625
+ time.sleep(0.5)
626
+ while plan_arm_state_status is False:
627
+ time.sleep(0.01)
628
+ print("action file executed")
629
+
630
+ def control_robot_head(self, yaw: float, pitch: float):
631
+ """Control the robot head rotation.
632
+
633
+ Args:
634
+ yaw (float): Yaw angle in degrees.
635
+ pitch (float): Pitch angle in degrees.
636
+ """
637
+ try:
638
+ global THRESHOLD_HEAD_CONTROL_COUNT, FLOAT_MAX
639
+ self.robot.stance()
640
+ yaw = math.radians(yaw)
641
+ pitch = math.radians(pitch)
642
+ self.robot.control_head(yaw, pitch)
643
+ diff_yaw_min = FLOAT_MAX
644
+ diff_pitch_min = FLOAT_MAX
645
+ times_count = 0
646
+ while True:
647
+ time.sleep(0.01)
648
+ diff_yaw = abs(current_head_joint_state[0] - yaw)
649
+ diff_pitch = abs(current_head_joint_state[1] - pitch)
650
+
651
+ if diff_yaw < diff_yaw_min:
652
+ times_count = 0
653
+ diff_yaw_min = diff_yaw
654
+ continue
655
+ if diff_pitch < diff_pitch_min:
656
+ times_count = 0
657
+ diff_pitch_min = diff_pitch
658
+ continue
659
+ times_count += 1
660
+ if times_count > THRESHOLD_HEAD_CONTROL_COUNT:
661
+ break
662
+
663
+ except Exception as e:
664
+ print(f"Robot head control failed: {str(e)}")
665
+
666
+ def control_robot_head_only_yaw(self, yaw: float):
667
+ """Control the robot head yaw only.
668
+
669
+ Args:
670
+ yaw (float): Yaw angle in degrees.
671
+ """
672
+ try:
673
+ global THRESHOLD_HEAD_CONTROL_COUNT, FLOAT_MAX
674
+ self.robot.stance()
675
+ yaw = math.radians(yaw)
676
+ self.robot.control_head(yaw, current_head_joint_state[1])
677
+ times_count = 0
678
+ diff_yaw_min = FLOAT_MAX
679
+ while True:
680
+ time.sleep(0.01)
681
+ diff_yaw = abs(current_head_joint_state[0] - yaw)
682
+ if diff_yaw < diff_yaw_min:
683
+ times_count = 0
684
+ diff_yaw_min = diff_yaw
685
+ continue
686
+ times_count += 1
687
+ if times_count > THRESHOLD_HEAD_CONTROL_COUNT:
688
+ break
689
+ except Exception as e:
690
+ print(f"Robot head control failed: {str(e)}")
691
+
692
+ def control_robot_head_only_pitch(self, pitch: float):
693
+ """Control the robot head pitch only.
694
+
695
+ Args:
696
+ pitch (float): Pitch angle in degrees.
697
+ """
698
+ try:
699
+ global THRESHOLD_HEAD_CONTROL_COUNT, FLOAT_MAX
700
+ self.robot.stance()
701
+ pitch = math.radians(pitch)
702
+ self.robot.control_head(current_head_joint_state[0], pitch)
703
+ times_count = 0
704
+ diff_pitch_min = FLOAT_MAX
705
+ while True:
706
+ time.sleep(0.01)
707
+ diff_pitch = abs(current_head_joint_state[1] - pitch)
708
+ if diff_pitch < diff_pitch_min:
709
+ times_count = 0
710
+ diff_pitch_min = diff_pitch
711
+ continue
712
+ times_count += 1
713
+ if times_count > THRESHOLD_HEAD_CONTROL_COUNT:
714
+ break
715
+ except Exception as e:
716
+ print(f"Robot head control failed: {str(e)}")
717
+
718
+ def control_robot_height(self, is_up: str, height: float, pitch: float = 0.0):
719
+ """Control the robot's height and body pitch.
720
+
721
+ Args:
722
+ is_up (str): "up" to increase height, "down" to decrease height.
723
+ height (float): Target height.
724
+ pitch (float, optional): Body pitch angle. Defaults to 0.0.
725
+ """
726
+ try:
727
+ global com_height
728
+
729
+ while not self.robot_state.is_stance():
730
+ self.robot.stance()
731
+ time.sleep(0.01)
732
+ now_height = self.robot_state.com_height
733
+ mass_height = now_height - com_height
734
+ if is_up == "up":
735
+ height = mass_height + height
736
+ elif is_up == "down":
737
+ height = mass_height - height
738
+ else:
739
+ raise ValueError("is_up must be 'up' or 'down'")
740
+
741
+ if height >= 0.0:
742
+ height = -0.0000000001
743
+ self.robot.squat(height, pitch)
744
+ while True:
745
+ # print(self.robot_state.com_height - com_height - height)
746
+ if abs(self.robot_state.com_height - com_height - height) < 0.1:
747
+ break
748
+ except Exception as e:
749
+ print(f"Robot height control failed: {str(e)}")
750
+
751
+ def to_stance(self):
752
+ """Return the robot to the standard standing posture, including arm reset and head centering."""
753
+ try:
754
+ self.arm_reset()
755
+ self.control_robot_head(0, 0)
756
+ self.robot._kuavo_core._control.robot_stance()
757
+ global com_height
758
+ while True:
759
+ time.sleep(0.01)
760
+ if self.robot_state.is_stance():
761
+ break
762
+ time.sleep(0.5)
763
+ com_height = self.robot_state.com_height
764
+ except Exception as e:
765
+ print(f"Stance adjustment failed: {str(e)}")
766
+
767
+ def slove_move_time(self, current_pos, target_pos):
768
+ """Calculate the time required for the arm to move to the target position.
769
+
770
+ Args:
771
+ current_pos (list): Current position.
772
+ target_pos (list): Target position.
773
+
774
+ Returns:
775
+ float: Move time in seconds.
776
+ """
777
+ left_dist = np.linalg.norm(np.array(current_pos) - np.array(target_pos))
778
+ right_dist = np.linalg.norm(np.array(current_pos) - np.array(target_pos))
779
+ max_dist = max(left_dist, right_dist)
780
+ min_time = 1.0
781
+ max_time = 10.0
782
+ # Linear interpolation: 1m -> 10s, 0m -> 1s
783
+ move_time = min_time + (max_time - min_time) * min(max_dist, 1.0)
784
+ return move_time
785
+
786
+ def control_arm_target_pose(self, x1, y1, z1, roll1, pitch1, yaw1, x2, y2, z2, roll2, pitch2, yaw2) -> bool:
787
+ """Calculate target joint angles using IK and move the arm to the target pose.
788
+
789
+ Args:
790
+ x1, y1, z1, roll1, pitch1, yaw1: Left arm end-effector position and orientation.
791
+ x2, y2, z2, roll2, pitch2, yaw2: Right arm end-effector position and orientation.
792
+
793
+ Returns:
794
+ bool: True if successful, False otherwise.
795
+ """
796
+ # Get the transform from zarm_r7_end_effector to base_link using tf
797
+ listener = tf.TransformListener()
798
+ try:
799
+ listener.waitForTransform("base_link", "zarm_r7_end_effector", rospy.Time(0), rospy.Duration(2.0))
800
+ listener.waitForTransform("base_link", "zarm_l7_end_effector", rospy.Time(0), rospy.Duration(2.0))
801
+ (trans_r, rot_r) = listener.lookupTransform("base_link", "zarm_r7_end_effector", rospy.Time(0))
802
+ (trans_l, rot_l) = listener.lookupTransform("base_link", "zarm_l7_end_effector", rospy.Time(0))
803
+ except (tf.Exception, tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException) as e:
804
+ print("Failed to get tf from zarm_r7_end_effector to base_link: ", str(e))
805
+ return False
806
+ global current_arm_joint_state
807
+ front_left = [x1, y1, z1] # Left arm target position
808
+ front_right = [x2, y2, z2] # Right arm target position
809
+ # Convert Euler angles to quaternion
810
+ l_front_orientation = tf.transformations.quaternion_from_euler(yaw1, pitch1, roll1)
811
+ r_front_orientation = tf.transformations.quaternion_from_euler(yaw2, pitch2, roll2)
812
+
813
+ res = self.robot.arm_ik_free(
814
+ KuavoPose(position=front_left, orientation=l_front_orientation),
815
+ KuavoPose(position=front_right, orientation=r_front_orientation)
816
+ )
817
+
818
+ if res:
819
+ # Calculate the spatial distance between current and target end-effector positions
820
+ current_left_pos = trans_l
821
+ current_right_pos = trans_r
822
+ target_left_pos = front_left
823
+ target_right_pos = front_right
824
+ move_time_left = self.slove_move_time(current_left_pos, target_left_pos)
825
+ move_time_right = self.slove_move_time(current_right_pos, target_right_pos)
826
+ move_time = max(move_time_left, move_time_right)
827
+ times = [1.0, move_time]
828
+ q_frames = [current_arm_joint_state[0:8], res]
829
+ self.robot.control_arm_joint_trajectory(times, q_frames)
830
+ time.sleep(move_time + 1.0)
831
+ # self.set_auto_swing_arm_mode()
832
+ # time.sleep(1.0)
833
+ return True
834
+ else:
835
+ return False
836
+
837
+ def control_arm_target_pose_by_single(self, hand_type, x, y, z, roll, pitch, yaw) -> bool:
838
+ """Move a single arm to the specified spatial position and orientation.
839
+
840
+ Args:
841
+ hand_type (str): "left" or "right".
842
+ x, y, z (float): Target position.
843
+ roll, pitch, yaw (float): Target orientation.
844
+
845
+ Returns:
846
+ bool: True if successful, False otherwise.
847
+ """
848
+ # Get the transform from zarm_r7_end_effector to base_link using tf
849
+ listener = tf.TransformListener()
850
+ try:
851
+ listener.waitForTransform("base_link", "zarm_r7_end_effector", rospy.Time(0), rospy.Duration(2.0))
852
+ listener.waitForTransform("base_link", "zarm_l7_end_effector", rospy.Time(0), rospy.Duration(2.0))
853
+ (trans_r, rot_r) = listener.lookupTransform("base_link", "zarm_r7_end_effector", rospy.Time(0))
854
+ (trans_l, rot_l) = listener.lookupTransform("base_link", "zarm_l7_end_effector", rospy.Time(0))
855
+ except (tf.Exception, tf.LookupException, tf.ConnectivityException, tf.ExtrapolationException) as e:
856
+ print("Failed to get tf from zarm_r7_end_effector to base_link: ", str(e))
857
+ return False
858
+
859
+ global current_arm_joint_state
860
+ if hand_type == "left":
861
+ front_left = [x, y, z] # Left arm target position
862
+ front_right = trans_r # Right arm current position
863
+ # Convert left arm Euler angles to quaternion
864
+ l_front_orientation = tf.transformations.quaternion_from_euler(yaw, pitch, roll)
865
+ r_front_orientation = rot_r
866
+ res = self.robot.arm_ik_free(
867
+ KuavoPose(position=front_left, orientation=l_front_orientation),
868
+ KuavoPose(position=front_right, orientation=r_front_orientation)
869
+ )
870
+
871
+ if res:
872
+ # Calculate the spatial distance between current and target end-effector positions
873
+ current_left_pos = trans_l
874
+ current_right_pos = trans_r
875
+ target_left_pos = front_left
876
+ target_right_pos = front_right
877
+ move_time = self.slove_move_time(current_left_pos, target_left_pos)
878
+ times = [1.0, move_time]
879
+ res = list(res)
880
+ res[4:8] = current_arm_joint_state[4:8]
881
+ q_frames = [current_arm_joint_state[0:8], res]
882
+ self.robot.control_arm_joint_trajectory(times, q_frames)
883
+ time.sleep(move_time + 1.0)
884
+ # self.set_auto_swing_arm_mode()
885
+ # time.sleep(1.0)
886
+ elif hand_type == "right":
887
+ front_left = trans_l # Left arm current position
888
+ front_right = [x, y, z] # Right arm target position
889
+ # Convert right arm Euler angles to quaternion
890
+ r_front_orientation = tf.transformations.quaternion_from_euler(yaw, pitch, roll)
891
+ l_front_orientation = rot_l
892
+ res = self.robot.arm_ik_free(
893
+ KuavoPose(position=front_left, orientation=l_front_orientation),
894
+ KuavoPose(position=front_right, orientation=r_front_orientation)
895
+ )
896
+
897
+ if res:
898
+ # Calculate the spatial distance between current and target end-effector positions
899
+ current_left_pos = trans_l
900
+ current_right_pos = trans_r
901
+ target_left_pos = front_left
902
+ target_right_pos = front_right
903
+ move_time = self.slove_move_time(current_right_pos, target_right_pos)
904
+ times = [1.0, move_time]
905
+ res = list(res)
906
+ res[0:4] = current_arm_joint_state[0:4]
907
+ q_frames = [current_arm_joint_state[0:8], res]
908
+ self.robot.control_arm_joint_trajectory(times, q_frames)
909
+ time.sleep(move_time + 1.0)
910
+ return True
911
+
912
+ def play_music(self, music_number: str, volume: int = 100, speed: float = 1.0) -> bool:
913
+ """Play a music file.
914
+
915
+ Args:
916
+ music_number (str): The number of the music file to play.
917
+ volume (int, optional): The volume of the music. Defaults to 100.
918
+ speed (float, optional): The speed of the music. Defaults to 1.0.
919
+
920
+ Returns:
921
+ bool: True if the music was played successfully, False otherwise.
922
+ """
923
+ return self.robot_audio.play_audio(music_number, volume, speed)
924
+
925
+ def stop_music(self) -> bool:
926
+ """Stop the currently playing music.
927
+
928
+ Returns:
929
+ bool: True if the music was stopped successfully, False otherwise.
930
+ """
931
+ return self.robot_audio.stop_music()
932
+
933
+ # ===== Stair Climbing Methods =====
934
+
935
+ def set_stair_parameters(
936
+ self,
937
+ step_height: float = 0.13,
938
+ step_length: float = 0.28,
939
+ foot_width: float = 0.10,
940
+ stand_height: float = 0.0,
941
+ dt: float = 0.6,
942
+ ss_time: float = 0.5,
943
+ ) -> bool:
944
+ """Set stair climbing parameters with version-specific defaults.
945
+
946
+ Args:
947
+ step_height (float): Step height in meters. Defaults to 0.13.
948
+ step_length (float): Step length in meters. Defaults to 0.28.
949
+ foot_width (float): Foot width in meters. Defaults to 0.10.
950
+ stand_height (float): Standing height offset in meters. Defaults to 0.0.
951
+ dt (float): Gait cycle time in seconds. Defaults to 0.6.
952
+ ss_time (float): Single support time ratio. Defaults to 0.5.
953
+
954
+ Returns:
955
+ bool: True if parameters were set successfully, False otherwise.
956
+ """
957
+ try:
958
+ # Set default parameters based on robot version
959
+ if robot_version >= 40:
960
+ step_height = step_height or 0.13
961
+ step_length = step_length or 0.28
962
+ foot_width = foot_width or 0.10
963
+ stand_height = stand_height or 0.0
964
+ dt = dt or 0.6
965
+ ss_time = ss_time or 0.5
966
+ else:
967
+ step_height = step_height or 0.08
968
+ step_length = step_length or 0.25
969
+ foot_width = foot_width or 0.10
970
+ stand_height = stand_height or -0.02
971
+ dt = dt or 1.0
972
+ ss_time = ss_time or 0.6
973
+
974
+ return self.climb_stair.set_stair_parameters(
975
+ step_height=step_height,
976
+ step_length=step_length,
977
+ foot_width=foot_width,
978
+ stand_height=stand_height,
979
+ dt=dt,
980
+ ss_time=ss_time,
981
+ )
982
+ except Exception as e:
983
+ print(f"Set stair parameters failed: {str(e)}")
984
+ return False
985
+
986
+ def climb_up_stairs(self, num_steps: int = 4, stair_offset: float = 0.03) -> bool:
987
+ """Plan and add up stairs trajectory to accumulated trajectory.
988
+
989
+ Args:
990
+ num_steps (int): Number of steps to climb up. Defaults to 4.
991
+
992
+ Returns:
993
+ bool: True if planning was successful, False otherwise.
994
+ """
995
+ try:
996
+ return self.climb_stair.climb_up_stairs(num_steps, stair_offset)
997
+ except Exception as e:
998
+ print(f"Climb up stairs failed: {str(e)}")
999
+ return False
1000
+
1001
+ def climb_down_stairs(self, num_steps: int = 5) -> bool:
1002
+ """Plan and add down stairs trajectory to accumulated trajectory.
1003
+
1004
+ Note: This functionality is currently disabled.
1005
+
1006
+ Args:
1007
+ num_steps (int): Number of steps to climb down. Defaults to 5.
1008
+
1009
+ Returns:
1010
+ bool: False (functionality disabled).
1011
+ """
1012
+ print("⚠ Down stairs functionality is currently disabled (under development)")
1013
+ return self.climb_stair.climb_down_stairs(num_steps)
1014
+
1015
+ def stair_move_to_position(
1016
+ self,
1017
+ dx: float = 0.2,
1018
+ dy: float = 0.0,
1019
+ dyaw: float = 0.0,
1020
+ max_step_x: float = 0.28,
1021
+ max_step_y: float = 0.15,
1022
+ max_step_yaw: float = 30.0,
1023
+ ) -> bool:
1024
+ """Plan stair climbing move to position trajectory and add to accumulated trajectory.
1025
+
1026
+ Args:
1027
+ dx (float): X direction displacement in meters. Defaults to 0.2.
1028
+ dy (float): Y direction displacement in meters. Defaults to 0.0.
1029
+ dyaw (float): Yaw angle displacement in degrees. Defaults to 0.0.
1030
+ max_step_x (float): Maximum step size in X direction. Defaults to 0.28.
1031
+ max_step_y (float): Maximum step size in Y direction. Defaults to 0.15.
1032
+ max_step_yaw (float): Maximum yaw step size in degrees. Defaults to 30.0.
1033
+
1034
+ Returns:
1035
+ bool: True if planning was successful, False otherwise.
1036
+ """
1037
+ try:
1038
+ return self.climb_stair.move_to_position(
1039
+ dx=dx, dy=dy, dyaw=dyaw, max_step_x=max_step_x, max_step_y=max_step_y, max_step_yaw=max_step_yaw
1040
+ )
1041
+ except Exception as e:
1042
+ print(f"Stair move to position failed: {str(e)}")
1043
+ return False
1044
+
1045
+ def execute_stair_trajectory(self) -> bool:
1046
+ """Execute the complete accumulated stair climbing trajectory.
1047
+
1048
+ Returns:
1049
+ bool: True if execution was successful, False otherwise.
1050
+ """
1051
+ try:
1052
+ return self.climb_stair.execute_trajectory()
1053
+ except Exception as e:
1054
+ print(f"Execute stair trajectory failed: {str(e)}")
1055
+ return False
1056
+
1057
+ def control_waist_rotation(self, degree: float = 0):
1058
+ """Control the robot's waist rotation.
1059
+
1060
+ Args:
1061
+ degree (float): Rotation angle in degrees.
1062
+ """
1063
+ try:
1064
+ global control_waist_pub
1065
+ msg = Float64MultiArray()
1066
+ msg.data = [degree]
1067
+ control_waist_pub.publish(msg)
1068
+
1069
+ except Exception as e:
1070
+ print(f"Robot waist control failed: {str(e)}")
1071
+
1072
+ def alignment_target(self, class_name: str, x: float, y: float, z: float):
1073
+ """
1074
+ 使机器人对准指定类别到对象,并移动到目标前
1075
+ :param class_name: yolo 检测中需要检测的对象名称,和训练模型中的类别名称一致
1076
+ :param x: 图像 x 方向的偏移值,物体中心的 x 小于该参数 -x 时,机器人左移,大于该参数 x 时,机器人右移
1077
+ :param y: 图像 y 方向的偏移值,物体中心的 y 小于该参数 y 时,机器人前进,否则停止移动
1078
+ :param z: 机器人上下蹲的高度控制
1079
+ """
1080
+ try:
1081
+ # 加载 yolo 模型
1082
+ yolo_detection = model_utils.yolo_detection
1083
+ model = model_utils.model
1084
+ is_yolo_init = model_utils.is_yolo_init
1085
+
1086
+ if not is_yolo_init:
1087
+ yolo_detection = YOLO_detection()
1088
+ yolo_detection.init_ros_node()
1089
+ caller_file_path = inspect.stack()[1].filename
1090
+ model_path = os.path.join(os.path.dirname(caller_file_path), 'best.pt')
1091
+ # print(f"函数被文件调用: {model_path}")
1092
+ model = yolo_detection.load_model(model_path)
1093
+
1094
+
1095
+ IMAGE_CENTER_X = yolo_detection.cv_image_shape[1] / 2.0
1096
+ IMAGE_CENTER_Y = yolo_detection.cv_image_shape[0] / 2.0
1097
+ # rospy.loginfo(f"IMAGE_CENTER_X: {IMAGE_CENTER_X}")
1098
+ # rospy.loginfo(f"IMAGE_CENTER_Y: {IMAGE_CENTER_Y}")
1099
+ MOVE_SPEED_Y = 0.05 # Y方向移动速度
1100
+ MOVE_SPEED_X = 0.10 # X方向移动速度
1101
+
1102
+ while not rospy.is_shutdown():
1103
+
1104
+ results = yolo_detection.get_detections("head", model)
1105
+
1106
+ if not results:
1107
+ continue
1108
+
1109
+ # 存储所有符合条件的目标信息
1110
+ targets = []
1111
+ for result in results:
1112
+
1113
+ boxes = result.boxes.cpu().numpy() # 边界框数据转NumPy数组
1114
+ xywh = boxes.xywh # [中心x, 中心y, 宽, 高]
1115
+ class_names = [result.names[cls.item()] for cls in result.boxes.cls.int()] # 类别名称列表
1116
+
1117
+ # 遍历每个检测框,提取目标信息
1118
+ for i in range(len(xywh)):
1119
+ if class_names[i] == class_name: # 筛选目标类别
1120
+ targets.append({
1121
+ "class": class_names[i],
1122
+ "x": xywh[i][0], # 目标中心x坐标
1123
+ "y": xywh[i][1], # 目标中心y坐标
1124
+ "area": xywh[i][2] * xywh[i][3] # 目标面积(宽×高)
1125
+ })
1126
+
1127
+ if not targets:
1128
+ continue # 无目标时继续循环
1129
+
1130
+ # 选择面积最大的目标
1131
+ best_target = max(targets, key=lambda t: t["area"])
1132
+
1133
+ # 计算相对于图像中心的偏移量
1134
+ offset_x = best_target["x"] - IMAGE_CENTER_X
1135
+ offset_y = best_target["y"] - IMAGE_CENTER_Y
1136
+
1137
+ # rospy.loginfo(f"目标中心偏移: x={offset_x:.2f}, y={offset_y:.2f}")
1138
+
1139
+ # 根据偏移量控制机器人移动
1140
+ if abs(offset_x) > x:
1141
+ # X方向偏移过大,左右移动调整
1142
+ speed_y = -MOVE_SPEED_Y if offset_x > x else MOVE_SPEED_Y
1143
+ self.walk_angle(0.00, speed_y, 0.00)
1144
+ elif offset_y < y:
1145
+ # Y方向偏移过大,向前移动调整
1146
+ self.walk_angle(MOVE_SPEED_X, 0.00, 0.00)
1147
+ else:
1148
+ # 偏移量在可接受范围内,停止移动
1149
+ self.stop()
1150
+
1151
+ # 控制机器人高度
1152
+ if z >= 0:
1153
+ self.control_robot_height("up", z)
1154
+ else:
1155
+ self.control_robot_height("down", -z)
1156
+
1157
+ break
1158
+
1159
+ time.sleep(0.1)
1160
+
1161
+ except Exception as e:
1162
+ print(f"Robot alignment to target failed: {str(e)}")