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.
- kuavo_humanoid_sdk/__init__.py +6 -0
- kuavo_humanoid_sdk/common/logger.py +45 -0
- kuavo_humanoid_sdk/interfaces/__init__.py +4 -0
- kuavo_humanoid_sdk/interfaces/data_types.py +288 -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 +16 -0
- kuavo_humanoid_sdk/kuavo/core/audio.py +32 -0
- kuavo_humanoid_sdk/kuavo/core/core.py +666 -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/llm_doubao.py +608 -0
- kuavo_humanoid_sdk/kuavo/core/microphone.py +192 -0
- kuavo_humanoid_sdk/kuavo/core/navigation.py +70 -0
- kuavo_humanoid_sdk/kuavo/core/ros/audio.py +110 -0
- kuavo_humanoid_sdk/kuavo/core/ros/control.py +1524 -0
- kuavo_humanoid_sdk/kuavo/core/ros/microphone.py +38 -0
- kuavo_humanoid_sdk/kuavo/core/ros/navigation.py +217 -0
- kuavo_humanoid_sdk/kuavo/core/ros/observation.py +94 -0
- kuavo_humanoid_sdk/kuavo/core/ros/param.py +201 -0
- kuavo_humanoid_sdk/kuavo/core/ros/sat_utils.py +103 -0
- kuavo_humanoid_sdk/kuavo/core/ros/state.py +652 -0
- kuavo_humanoid_sdk/kuavo/core/ros/tools.py +220 -0
- kuavo_humanoid_sdk/kuavo/core/ros/vision.py +234 -0
- kuavo_humanoid_sdk/kuavo/core/ros_env.py +238 -0
- kuavo_humanoid_sdk/kuavo/demo_climbstair.py +249 -0
- kuavo_humanoid_sdk/kuavo/dexterous_hand.py +238 -0
- kuavo_humanoid_sdk/kuavo/leju_claw.py +235 -0
- kuavo_humanoid_sdk/kuavo/logger_client.py +80 -0
- kuavo_humanoid_sdk/kuavo/robot.py +561 -0
- kuavo_humanoid_sdk/kuavo/robot_arm.py +299 -0
- kuavo_humanoid_sdk/kuavo/robot_audio.py +39 -0
- kuavo_humanoid_sdk/kuavo/robot_blockly.py +1162 -0
- kuavo_humanoid_sdk/kuavo/robot_climbstair.py +1607 -0
- kuavo_humanoid_sdk/kuavo/robot_head.py +74 -0
- kuavo_humanoid_sdk/kuavo/robot_info.py +134 -0
- kuavo_humanoid_sdk/kuavo/robot_microphone.py +19 -0
- kuavo_humanoid_sdk/kuavo/robot_navigation.py +135 -0
- kuavo_humanoid_sdk/kuavo/robot_observation.py +64 -0
- kuavo_humanoid_sdk/kuavo/robot_speech.py +24 -0
- kuavo_humanoid_sdk/kuavo/robot_state.py +310 -0
- kuavo_humanoid_sdk/kuavo/robot_tool.py +109 -0
- kuavo_humanoid_sdk/kuavo/robot_vision.py +81 -0
- kuavo_humanoid_sdk/kuavo_strategy/__init__.py +2 -0
- kuavo_humanoid_sdk/kuavo_strategy/grasp_box/grasp_box_strategy.py +1325 -0
- kuavo_humanoid_sdk/kuavo_strategy/kuavo_strategy.py +106 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/common/data_type.py +340 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/common/events/base_event.py +215 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/common/robot_sdk.py +25 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/pick_place_box/case.py +331 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/pick_place_box/strategy.py +504 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/utils/logger_setup.py +40 -0
- kuavo_humanoid_sdk/kuavo_strategy_v2/utils/utils.py +88 -0
- kuavo_humanoid_sdk/msg/__init__.py +4 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/__init__.py +7 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetection.py +306 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetectionArray.py +437 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AudioReceiverData.py +122 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_FTsensorData.py +260 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_JoySticks.py +191 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_Metadata.py +199 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_MmDetectionMsg.py +264 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_RobotActionState.py +112 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_TFArray.py +323 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_TaskPoint.py +175 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/__init__.py +62 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armCollisionCheckInfo.py +160 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armHandPose.py +161 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armHandPoseFree.py +171 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armPoseWithTimeStamp.py +168 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_armTargetPoses.py +171 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_bezierCurveCubicPoint.py +178 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_dexhandCommand.py +229 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_dexhandTouchState.py +256 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_endEffectorData.py +227 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose.py +123 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose6D.py +123 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPose6DTargetTrajectories.py +320 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseTargetTrajectories.py +301 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseWithVision.py +136 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoseWithVisionArray.py +231 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoses.py +149 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_footPoses6D.py +149 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_fullBodyTargetTrajectories.py +258 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gaitTimeName.py +147 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gestureInfo.py +218 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_gestureTask.py +149 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_handPose.py +136 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_headBodyPose.py +145 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_ikSolveError.py +171 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_ikSolveParam.py +140 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_imuData.py +165 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointBezierTrajectory.py +201 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointCmd.py +390 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_jointData.py +205 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_kuavoModeSchedule.py +224 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_lejuClawCommand.py +320 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_lejuClawState.py +341 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_motorParam.py +122 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_picoPoseInfo.py +143 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_picoPoseInfoList.py +220 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_planArmState.py +120 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_qv.py +121 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotArmQVVD.py +177 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotBodyMatrices.py +332 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotHandPosition.py +225 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotHeadMotionData.py +128 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_robotState.py +222 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_sensorsData.py +655 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_switchGaitByName.py +200 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_tagDataArray.py +216 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_touchSensorStatus.py +162 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPose.py +273 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseCmd.py +316 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseCmdFree.py +338 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_twoArmHandPoseFree.py +299 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_yoloDetection.py +251 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_yoloOutputData.py +168 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_CreatePath.py +581 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_ExecuteArmAction.py +281 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetAllMaps.py +241 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetCurrentMap.py +225 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_GetTargetPartPoseInCamera.py +298 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_InitialPoseWithTaskPoint.py +281 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_LoadMap.py +281 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_NavigateToTaskPoint.py +281 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_RepublishTFs.py +373 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetInitialPose.py +394 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetJoyTopic.py +282 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetLEDMode.py +468 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SetLEDMode_free.py +289 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SpeechSynthesis.py +270 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_TaskPointOperation.py +536 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/__init__.py +43 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_adjustZeroPoint.py +277 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeArmCtrlMode.py +275 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeArmCtrlModeKuavo.py +236 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeMotorParam.py +299 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_changeTorsoCtrlMode.py +274 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_controlLejuClaw.py +408 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_enableHandTouchSensor.py +304 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_fkSrv.py +395 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_footPose6DTargetTrajectoriesSrv.py +426 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_footPoseTargetTrajectoriesSrv.py +409 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureExecute.py +339 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureExecuteState.py +257 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_gestureList.py +418 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getCurrentGaitName.py +253 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getMotorParam.py +299 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_getMotorZeroPoints.py +286 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_handForceLevel.py +330 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_jointMoveTo.py +302 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_planArmTrajectoryBezierCurve.py +422 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_planArmTrajectoryCubicSpline.py +490 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_playmusic.py +268 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setHwIntialState.py +304 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setMmCtrlFrame.py +273 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setMotorEncoderRoundService.py +283 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_setTagId.py +275 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_singleStepControl.py +444 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_twoArmHandPoseCmdFreeSrv.py +716 -0
- kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_twoArmHandPoseCmdSrv.py +664 -0
- kuavo_humanoid_sdk/msg/motion_capture_ik/__init__.py +7 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/__init__.py +7 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/__init__.py +12 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_constraint.py +142 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_controller_data.py +121 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_lagrangian_metrics.py +148 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mode_schedule.py +150 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_flattened_controller.py +666 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_input.py +122 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_observation.py +209 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_performance_indices.py +140 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_solver_data.py +886 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_state.py +122 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_mpc_target_trajectories.py +239 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/msg/_multiplier.py +148 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/srv/__init__.py +1 -0
- kuavo_humanoid_sdk/msg/ocs2_msgs/srv/_reset.py +376 -0
- kuavo_humanoid_sdk-1.2.1b3279.dist-info/METADATA +296 -0
- kuavo_humanoid_sdk-1.2.1b3279.dist-info/RECORD +184 -0
- kuavo_humanoid_sdk-1.2.1b3279.dist-info/WHEEL +6 -0
- kuavo_humanoid_sdk-1.2.1b3279.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
import copy
|
|
5
|
+
import time
|
|
6
|
+
import numpy as np
|
|
7
|
+
from typing import Tuple, Union
|
|
8
|
+
from kuavo_humanoid_sdk.common.logger import SDKLogger
|
|
9
|
+
import rospy
|
|
10
|
+
from kuavo_humanoid_sdk.msg.kuavo_msgs.srv import RepublishTFs
|
|
11
|
+
from kuavo_humanoid_sdk.msg.kuavo_msgs.msg import TFArray
|
|
12
|
+
import tf.transformations as tf_trans
|
|
13
|
+
from std_msgs.msg import Float64
|
|
14
|
+
from nav_msgs.msg import Odometry
|
|
15
|
+
from sensor_msgs.msg import JointState
|
|
16
|
+
from kuavo_humanoid_sdk.msg.kuavo_msgs.msg import sensorsData, lejuClawState, gaitTimeName, dexhandTouchState
|
|
17
|
+
from geometry_msgs.msg import TransformStamped
|
|
18
|
+
from kuavo_humanoid_sdk.interfaces.data_types import (PoseQuaternion, HomogeneousMatrix)
|
|
19
|
+
from kuavo_humanoid_sdk.kuavo.core.ros.param import make_robot_param, EndEffectorType
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class KuavoRobotToolsCore:
|
|
24
|
+
"""Provides core ROS tools for Kuavo humanoid robot transformations.
|
|
25
|
+
|
|
26
|
+
Attributes:
|
|
27
|
+
tf_service (rospy.ServiceProxy): Service proxy for tf2_web_republisher
|
|
28
|
+
_transform_cache (dict): Cache for storing recent transforms
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
"""Initializes TF2 web republisher service proxy."""
|
|
33
|
+
if not hasattr(self, '_initialized'):
|
|
34
|
+
try:
|
|
35
|
+
# 初始化TF2 web republisher服务
|
|
36
|
+
rospy.wait_for_service('/republish_tfs', timeout=5.0)
|
|
37
|
+
self.tf_service = rospy.ServiceProxy('/republish_tfs', RepublishTFs)
|
|
38
|
+
self._transform_cache = {}
|
|
39
|
+
self._initialized = True
|
|
40
|
+
except Exception as e:
|
|
41
|
+
SDKLogger.error(f"Failed to initialize kuavo_tf2_web_republisher: {str(e)}")
|
|
42
|
+
SDKLogger.error(f"kuavo_tf2_web_republisher 节点未运行")
|
|
43
|
+
SDKLogger.error("请运行 `cd <kuavo_ros_application> && source devel/setup.bash && rosrun kuavo_tf2_web_republisher kuavo_tf2_web_republisher` 启动 kuavo_tf2_web_republisher 节点")
|
|
44
|
+
raise
|
|
45
|
+
|
|
46
|
+
def _get_tf_tree_transform(self, target_frame: str, source_frame: str,
|
|
47
|
+
time=0.0, timeout=5.0,
|
|
48
|
+
return_type: str = "pose_quaternion") -> Union[PoseQuaternion, HomogeneousMatrix, None]:
|
|
49
|
+
"""Gets transform between coordinate frames using tf2_web_republisher.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
target_frame (str): Target coordinate frame name
|
|
53
|
+
source_frame (str): Source coordinate frame name
|
|
54
|
+
time (rospy.Time, optional): Time of transform. Defaults to latest.
|
|
55
|
+
timeout (float, optional): Wait timeout in seconds. Defaults to 5.0.
|
|
56
|
+
return_type (str, optional): Return data format. Options:
|
|
57
|
+
"pose_quaternion", "homogeneous". Defaults to "pose_quaternion".
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Union[PoseQuaternion, HomogeneousMatrix, None]: Requested transform data
|
|
61
|
+
or None if failed
|
|
62
|
+
"""
|
|
63
|
+
try:
|
|
64
|
+
|
|
65
|
+
# 调用服务
|
|
66
|
+
response = self.tf_service(
|
|
67
|
+
source_frames=[source_frame],
|
|
68
|
+
target_frame=target_frame,
|
|
69
|
+
angular_thres=0.01, # 角度阈值
|
|
70
|
+
trans_thres=0.01, # 平移阈值
|
|
71
|
+
rate=10.0, # 更新频率
|
|
72
|
+
timeout=rospy.Duration(timeout)
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if response.status == -1:
|
|
76
|
+
SDKLogger.error(f"{source_frame} or {target_frame} not exist")
|
|
77
|
+
return None
|
|
78
|
+
|
|
79
|
+
# 检查话题是否发布
|
|
80
|
+
published_topics = rospy.get_published_topics()
|
|
81
|
+
if not any(topic_tuple[0] == response.topic_name for topic_tuple in published_topics):
|
|
82
|
+
SDKLogger.error(f"Topic {response.topic_name} not published")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
# 创建订阅者
|
|
86
|
+
transform_received = False
|
|
87
|
+
transform_data = None
|
|
88
|
+
|
|
89
|
+
def transform_callback(msg):
|
|
90
|
+
nonlocal transform_received, transform_data
|
|
91
|
+
transform_received = True
|
|
92
|
+
transform_data = msg
|
|
93
|
+
|
|
94
|
+
sub = rospy.Subscriber(response.topic_name, TFArray, transform_callback)
|
|
95
|
+
|
|
96
|
+
# 等待接收数据
|
|
97
|
+
start_time = rospy.Time.now()
|
|
98
|
+
while not transform_received or (rospy.Time.now() - start_time).to_sec() > timeout:
|
|
99
|
+
rospy.sleep(0.1)
|
|
100
|
+
|
|
101
|
+
# 取消订阅
|
|
102
|
+
sub.unregister()
|
|
103
|
+
|
|
104
|
+
if not transform_received:
|
|
105
|
+
SDKLogger.error("No transform data received")
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
# 从TFArray中获取对应的变换
|
|
109
|
+
for tf_msg in transform_data.transforms:
|
|
110
|
+
if tf_msg.header.frame_id == target_frame and tf_msg.child_frame_id == source_frame:
|
|
111
|
+
return self._parse_transform(tf_msg.transform, return_type)
|
|
112
|
+
|
|
113
|
+
SDKLogger.error(f"No matching transform found in TFArray")
|
|
114
|
+
return None
|
|
115
|
+
|
|
116
|
+
except rospy.ServiceException as e:
|
|
117
|
+
SDKLogger.error(f"Service call failed: {str(e)}")
|
|
118
|
+
return None
|
|
119
|
+
except Exception as e:
|
|
120
|
+
SDKLogger.error(f"Transform error: {str(e)}")
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
def _parse_transform(self, transform, return_type: str) -> Union[PoseQuaternion, HomogeneousMatrix, None]:
|
|
124
|
+
"""Parses transform data to specified format.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
transform (geometry_msgs/Transform): Input transform data
|
|
128
|
+
return_type (str): Output format type. Valid values:
|
|
129
|
+
"pose_quaternion", "homogeneous"
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Union[PoseQuaternion, HomogeneousMatrix, None]: Parsed transform data
|
|
133
|
+
in requested format, or None if invalid input
|
|
134
|
+
|
|
135
|
+
Note:
|
|
136
|
+
Falls back to pose_quaternion format if invalid return_type specified
|
|
137
|
+
"""
|
|
138
|
+
if return_type == "pose_quaternion":
|
|
139
|
+
return PoseQuaternion(
|
|
140
|
+
position=(transform.translation.x,
|
|
141
|
+
transform.translation.y,
|
|
142
|
+
transform.translation.z),
|
|
143
|
+
orientation=(transform.rotation.x,
|
|
144
|
+
transform.rotation.y,
|
|
145
|
+
transform.rotation.z,
|
|
146
|
+
transform.rotation.w)
|
|
147
|
+
)
|
|
148
|
+
elif return_type == "homogeneous":
|
|
149
|
+
return HomogeneousMatrix(
|
|
150
|
+
matrix=self._transform_to_homogeneous(transform)
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
SDKLogger.warn(f"Invalid return_type: {return_type}, using default(pose_quaternion)")
|
|
154
|
+
return self._parse_transform(transform, "pose_quaternion")
|
|
155
|
+
|
|
156
|
+
def _transform_to_homogeneous(self, transform) -> np.ndarray:
|
|
157
|
+
"""Converts geometry_msgs/Transform to homogeneous matrix.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
transform (geometry_msgs/Transform): Input transform message
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
np.ndarray: 4x4 homogeneous transformation matrix (numpy.float32)
|
|
164
|
+
|
|
165
|
+
Example:
|
|
166
|
+
>>> matrix = _transform_to_homogeneous(transform_msg)
|
|
167
|
+
>>> print(matrix.shape)
|
|
168
|
+
(4, 4)
|
|
169
|
+
"""
|
|
170
|
+
# 四元数转旋转矩阵
|
|
171
|
+
rotation = [
|
|
172
|
+
transform.rotation.x,
|
|
173
|
+
transform.rotation.y,
|
|
174
|
+
transform.rotation.z,
|
|
175
|
+
transform.rotation.w
|
|
176
|
+
]
|
|
177
|
+
rot_matrix = tf_trans.quaternion_matrix(rotation)
|
|
178
|
+
|
|
179
|
+
# 设置平移分量
|
|
180
|
+
translation = [
|
|
181
|
+
transform.translation.x,
|
|
182
|
+
transform.translation.y,
|
|
183
|
+
transform.translation.z
|
|
184
|
+
]
|
|
185
|
+
rot_matrix[:3, 3] = translation
|
|
186
|
+
|
|
187
|
+
return rot_matrix.astype(np.float32) # 确保矩阵数据类型一致
|
|
188
|
+
|
|
189
|
+
def get_link_pose(self, link_name: str, target_frame: str = "base_link") -> Union[PoseQuaternion, None]:
|
|
190
|
+
"""获取指定关节链接的位姿(核心实现)
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
link_name (str): 关节链接名称
|
|
194
|
+
target_frame (str): 目标坐标系
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
PoseQuaternion: 包含位置和姿态的四元数表示
|
|
198
|
+
"""
|
|
199
|
+
return self._get_tf_tree_transform(
|
|
200
|
+
target_frame,
|
|
201
|
+
link_name,
|
|
202
|
+
return_type="pose_quaternion"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
# if __name__ == "__main__":
|
|
206
|
+
# robot_tools = KuavoRobotToolsCore()
|
|
207
|
+
# time.sleep(0.1)
|
|
208
|
+
# # 获取位姿信息
|
|
209
|
+
# pose = robot_tools._get_tf_tree_transform("odom", "base_link", return_type="pose_quaternion")
|
|
210
|
+
# print(f"Position: {pose.position}")
|
|
211
|
+
# print(f"Orientation: {pose.orientation}")
|
|
212
|
+
|
|
213
|
+
# # 获取齐次矩阵
|
|
214
|
+
# homogeneous = robot_tools._get_tf_tree_transform("odom", "base_link", return_type="homogeneous")
|
|
215
|
+
# print(f"Transformation matrix:\n{homogeneous.matrix}")
|
|
216
|
+
|
|
217
|
+
# # 矩阵运算示例
|
|
218
|
+
# transform_matrix = homogeneous.matrix
|
|
219
|
+
# inverse_matrix = np.linalg.inv(transform_matrix) # 求逆变换
|
|
220
|
+
# print(f"Inverse matrix:\n{inverse_matrix}")
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
#! /usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
import copy
|
|
5
|
+
import time
|
|
6
|
+
from collections import deque
|
|
7
|
+
from typing import Tuple, Optional
|
|
8
|
+
|
|
9
|
+
from kuavo_humanoid_sdk.kuavo.core.ros.param import make_robot_param, EndEffectorType
|
|
10
|
+
from kuavo_humanoid_sdk.common.logger import SDKLogger
|
|
11
|
+
from kuavo_humanoid_sdk.interfaces.data_types import (AprilTagData, AprilTagDetection)
|
|
12
|
+
|
|
13
|
+
import rospy
|
|
14
|
+
from std_msgs.msg import Float64
|
|
15
|
+
from nav_msgs.msg import Odometry
|
|
16
|
+
from sensor_msgs.msg import JointState
|
|
17
|
+
from apriltag_ros.msg import AprilTagDetectionArray
|
|
18
|
+
from geometry_msgs.msg import TransformStamped, PoseStamped
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class KuavoRobotVisionCore:
|
|
22
|
+
"""Handles vision-related data processing for Kuavo humanoid robot.
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
tf_buffer (tf2_ros.Buffer): TF2 transform buffer
|
|
26
|
+
tf_listener (tf2_ros.TransformListener): TF2 transform listener
|
|
27
|
+
tf_broadcaster (tf2_ros.TransformBroadcaster): TF2 transform broadcaster
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self):
|
|
31
|
+
"""Initializes vision system components including TF and AprilTag subscribers."""
|
|
32
|
+
if not hasattr(self, '_initialized'):
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# FIRST: Initialize data structures before creating subscribers
|
|
36
|
+
""" data """
|
|
37
|
+
self._apriltag_data_from_camera = AprilTagData(
|
|
38
|
+
id = [],
|
|
39
|
+
size = [],
|
|
40
|
+
pose = []
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
self._apriltag_data_from_base = AprilTagData(
|
|
44
|
+
id = [],
|
|
45
|
+
size = [],
|
|
46
|
+
pose = []
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
self._apriltag_data_from_odom = AprilTagData(
|
|
50
|
+
id = [],
|
|
51
|
+
size = [],
|
|
52
|
+
pose = []
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# THEN: Create subscribers after all data structures are initialized
|
|
56
|
+
self._apriltag_data_camera_sub = rospy.Subscriber('/tag_detections', AprilTagDetectionArray, self._apriltag_data_callback_camera)
|
|
57
|
+
self._apriltag_data_base_sub = rospy.Subscriber('/robot_tag_info', AprilTagDetectionArray, self._apriltag_data_callback_base)
|
|
58
|
+
self._apriltag_data_odom_sub = rospy.Subscriber('/robot_tag_info_odom', AprilTagDetectionArray, self._apriltag_data_callback_odom)
|
|
59
|
+
|
|
60
|
+
# Mark as initialized
|
|
61
|
+
self._initialized = True
|
|
62
|
+
|
|
63
|
+
def _apriltag_data_callback_camera(self, data):
|
|
64
|
+
"""Callback for processing AprilTag detections from camera.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
data (AprilTagDetectionArray): Raw detection data from camera
|
|
68
|
+
"""
|
|
69
|
+
# 清空之前的数据
|
|
70
|
+
self._apriltag_data_from_camera.id = []
|
|
71
|
+
self._apriltag_data_from_camera.size = []
|
|
72
|
+
self._apriltag_data_from_camera.pose = []
|
|
73
|
+
|
|
74
|
+
# 处理每个标签检测
|
|
75
|
+
for detection in data.detections:
|
|
76
|
+
# 添加ID
|
|
77
|
+
for id in detection.id:
|
|
78
|
+
self._apriltag_data_from_camera.id.append(id)
|
|
79
|
+
|
|
80
|
+
# 添加尺寸 (从size数组中获取)
|
|
81
|
+
if detection.size and len(detection.size) > 0:
|
|
82
|
+
self._apriltag_data_from_camera.size.append(detection.size[0])
|
|
83
|
+
else:
|
|
84
|
+
self._apriltag_data_from_camera.size.append(0.0)
|
|
85
|
+
|
|
86
|
+
# 添加姿态
|
|
87
|
+
self._apriltag_data_from_camera.pose.append(AprilTagDetection(
|
|
88
|
+
position=detection.pose.pose.pose.position,
|
|
89
|
+
orientation=detection.pose.pose.pose.orientation
|
|
90
|
+
))
|
|
91
|
+
# # debug
|
|
92
|
+
# rospy.loginfo("Apriltag data from camera: %s", self._apriltag_data_from_camera)
|
|
93
|
+
|
|
94
|
+
def _apriltag_data_callback_base(self, data):
|
|
95
|
+
"""Callback for processing AprilTag detections from base link.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
data (AprilTagDetectionArray): Raw detection data from base frame
|
|
99
|
+
"""
|
|
100
|
+
# 清空之前的数据
|
|
101
|
+
self._apriltag_data_from_base.id = []
|
|
102
|
+
self._apriltag_data_from_base.size = []
|
|
103
|
+
self._apriltag_data_from_base.pose = []
|
|
104
|
+
|
|
105
|
+
# 处理每个标签检测
|
|
106
|
+
for detection in data.detections:
|
|
107
|
+
# 添加ID
|
|
108
|
+
for id in detection.id:
|
|
109
|
+
self._apriltag_data_from_base.id.append(id)
|
|
110
|
+
|
|
111
|
+
# 添加尺寸 (从size数组中获取)
|
|
112
|
+
if detection.size and len(detection.size) > 0:
|
|
113
|
+
self._apriltag_data_from_base.size.append(detection.size[0])
|
|
114
|
+
else:
|
|
115
|
+
self._apriltag_data_from_base.size.append(0.0)
|
|
116
|
+
|
|
117
|
+
# 添加姿态
|
|
118
|
+
self._apriltag_data_from_base.pose.append(AprilTagDetection(
|
|
119
|
+
position=detection.pose.pose.pose.position,
|
|
120
|
+
orientation=detection.pose.pose.pose.orientation
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
# # debug
|
|
124
|
+
# rospy.loginfo("Apriltag data from base: %s", self._apriltag_data_from_base)
|
|
125
|
+
|
|
126
|
+
def _apriltag_data_callback_odom(self, data):
|
|
127
|
+
"""Callback for processing AprilTag detections from odom.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
data (AprilTagDetectionArray): Raw detection data from odom frame
|
|
131
|
+
"""
|
|
132
|
+
# 清空之前的数据
|
|
133
|
+
self._apriltag_data_from_odom.id = []
|
|
134
|
+
self._apriltag_data_from_odom.size = []
|
|
135
|
+
self._apriltag_data_from_odom.pose = []
|
|
136
|
+
|
|
137
|
+
# 处理每个标签检测
|
|
138
|
+
for detection in data.detections:
|
|
139
|
+
# 添加ID
|
|
140
|
+
for id in detection.id:
|
|
141
|
+
self._apriltag_data_from_odom.id.append(id)
|
|
142
|
+
|
|
143
|
+
# 添加尺寸 (从size数组中获取)
|
|
144
|
+
if detection.size and len(detection.size) > 0:
|
|
145
|
+
self._apriltag_data_from_odom.size.append(detection.size[0])
|
|
146
|
+
else:
|
|
147
|
+
self._apriltag_data_from_odom.size.append(0.0)
|
|
148
|
+
|
|
149
|
+
# 添加姿态
|
|
150
|
+
self._apriltag_data_from_odom.pose.append(AprilTagDetection(
|
|
151
|
+
position=detection.pose.pose.pose.position,
|
|
152
|
+
orientation=detection.pose.pose.pose.orientation
|
|
153
|
+
))
|
|
154
|
+
|
|
155
|
+
# # debug
|
|
156
|
+
# rospy.loginfo("Apriltag data from odom: %s", self._apriltag_data_from_odom)
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def apriltag_data_from_camera(self) -> AprilTagData:
|
|
160
|
+
"""AprilTag detection data in camera coordinate frame.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
AprilTagData: Contains lists of tag IDs, sizes and poses
|
|
164
|
+
"""
|
|
165
|
+
return self._apriltag_data_from_camera
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
def apriltag_data_from_base(self) -> AprilTagData:
|
|
169
|
+
"""AprilTag detection data in base_link coordinate frame.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
AprilTagData: Contains lists of tag IDs, sizes and poses
|
|
173
|
+
"""
|
|
174
|
+
return self._apriltag_data_from_base
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def apriltag_data_from_odom(self) -> AprilTagData:
|
|
178
|
+
"""AprilTag detection data in odom coordinate frame.
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
AprilTagData: Contains lists of tag IDs, sizes and transformed poses
|
|
182
|
+
"""
|
|
183
|
+
return self._apriltag_data_from_odom
|
|
184
|
+
|
|
185
|
+
def _get_data_by_id(self, target_id: int, data_source: str = "base") -> Optional[dict]:
|
|
186
|
+
"""Retrieves AprilTag data by specific ID from selected source.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
target_id (int): AprilTag ID to search for
|
|
190
|
+
data_source (str): Data source selector, valid options:
|
|
191
|
+
"camera", "base", "odom"
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Optional[dict]: Dictionary containing 'sizes' and 'poses' lists if found,
|
|
195
|
+
None if no matching data
|
|
196
|
+
|
|
197
|
+
Raises:
|
|
198
|
+
ValueError: If invalid data_source is specified
|
|
199
|
+
"""
|
|
200
|
+
data_map = {
|
|
201
|
+
"camera": self._apriltag_data_from_camera,
|
|
202
|
+
"base": self._apriltag_data_from_base,
|
|
203
|
+
"odom": self._apriltag_data_from_odom
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if data_source not in data_map:
|
|
207
|
+
# SDKLogger.error(f"Invalid data source: {data_source}, must be one of {list(data_map.keys())}")
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
data = data_map[data_source]
|
|
211
|
+
|
|
212
|
+
# 查找所有匹配的索引
|
|
213
|
+
indices = [i for i, tag_id in enumerate(data.id) if tag_id == target_id]
|
|
214
|
+
|
|
215
|
+
if not indices:
|
|
216
|
+
# SDKLogger.debug(f"No data found for tag ID {target_id} in {data_source} source")
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
"sizes": [data.size[i] for i in indices],
|
|
221
|
+
"poses": [data.pose[i] for i in indices]
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
# if __name__ == "__main__":
|
|
225
|
+
|
|
226
|
+
# kuavo_robot_vision_core = KuavoRobotVisionCore()
|
|
227
|
+
# time.sleep(5)
|
|
228
|
+
# print("apriltag_data_from_camera:")
|
|
229
|
+
# print(kuavo_robot_vision_core.apriltag_data_from_camera)
|
|
230
|
+
# print("apriltag_data_from_base:")
|
|
231
|
+
# print(kuavo_robot_vision_core.apriltag_data_from_base)
|
|
232
|
+
# print("apriltag_data_from_odom:")
|
|
233
|
+
# print(kuavo_robot_vision_core.apriltag_data_from_odom)
|
|
234
|
+
# rospy.spin()
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# coding: utf-8
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
try:
|
|
6
|
+
import rospy
|
|
7
|
+
except ImportError:
|
|
8
|
+
pass
|
|
9
|
+
import subprocess
|
|
10
|
+
import atexit
|
|
11
|
+
from kuavo_humanoid_sdk.common.logger import SDKLogger
|
|
12
|
+
|
|
13
|
+
class KuavoROSEnv:
|
|
14
|
+
_instance = None
|
|
15
|
+
_processes = [] # Store all subprocess instances
|
|
16
|
+
|
|
17
|
+
def __new__(cls):
|
|
18
|
+
if cls._instance is None:
|
|
19
|
+
cls._instance = super(KuavoROSEnv, cls).__new__(cls)
|
|
20
|
+
cls._instance._initialized = False
|
|
21
|
+
cls._instance._init_success = False # Add flag to track successful Init() call
|
|
22
|
+
# Register cleanup handler
|
|
23
|
+
atexit.register(cls._cleanup_processes)
|
|
24
|
+
return cls._instance
|
|
25
|
+
|
|
26
|
+
def __init__(self):
|
|
27
|
+
if not self._initialized:
|
|
28
|
+
self._initialized = True
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def _cleanup_processes(cls):
|
|
32
|
+
"""Cleanup all registered processes on exit"""
|
|
33
|
+
for process in cls._processes:
|
|
34
|
+
if process.poll() is None: # Process is still running
|
|
35
|
+
process.terminate()
|
|
36
|
+
try:
|
|
37
|
+
process.wait(timeout=3) # Wait for process to terminate
|
|
38
|
+
except subprocess.TimeoutExpired:
|
|
39
|
+
process.kill() # Force kill if not terminated
|
|
40
|
+
cls._processes.clear()
|
|
41
|
+
|
|
42
|
+
def _get_kuavo_ws_root(self) -> str:
|
|
43
|
+
if not rospy.has_param('/modelPath'):
|
|
44
|
+
raise Exception("modelPath parameter not found")
|
|
45
|
+
model_path = rospy.get_param('/modelPath')
|
|
46
|
+
|
|
47
|
+
if not os.path.exists(model_path):
|
|
48
|
+
raise Exception(f"modelPath f{model_path} not found")
|
|
49
|
+
# ws
|
|
50
|
+
return model_path.replace('/src/kuavo_assets/models', '')
|
|
51
|
+
|
|
52
|
+
def Init(self) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
Initialize the ROS environment.
|
|
55
|
+
Raises:
|
|
56
|
+
Exception: If the modelPath parameter is not found or the modelPath parameter is not a valid path.
|
|
57
|
+
"""
|
|
58
|
+
# if generate docs, skip init.
|
|
59
|
+
if 'GEN_KUAVO_HUMANOID_SDK_DOCS' in os.environ:
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
# Return directly if already initialized successfully
|
|
63
|
+
if self._init_success:
|
|
64
|
+
return True
|
|
65
|
+
|
|
66
|
+
# Check if ROS master is running
|
|
67
|
+
try:
|
|
68
|
+
rospy.get_master().getPid()
|
|
69
|
+
except Exception as e:
|
|
70
|
+
print(f"\033[31m\nError:Can't connect to ros master, Please start roscore first or check ROS_MASTER_URI.\nException:{e}\033[0m"
|
|
71
|
+
"\nMaybe manually launch the app first?"
|
|
72
|
+
"\n - for example(sim): roslaunch humanoid_controller load_kuavo_mujoco_sim.launch, "
|
|
73
|
+
"\n - for example(real): roslaunch humanoid_controller load_kuavo_real.launch\n")
|
|
74
|
+
exit(1)
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
# NOTE: We add kuavo_msgs/motion_capture_ik package in SDK integration.
|
|
78
|
+
kuavo_ws_root = self._get_kuavo_ws_root()
|
|
79
|
+
# Add kuavo_msgs package to Python path
|
|
80
|
+
dist_path = os.path.join(kuavo_ws_root, 'devel/lib/python3/dist-packages')
|
|
81
|
+
if not os.path.exists(dist_path):
|
|
82
|
+
dist_path = os.path.join(kuavo_ws_root, 'install/lib/python3/dist-packages')
|
|
83
|
+
if not os.path.exists(dist_path):
|
|
84
|
+
raise Exception(f"{dist_path} package not found in Python path")
|
|
85
|
+
if dist_path not in os.sys.path:
|
|
86
|
+
os.sys.path.append(dist_path)
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Initialize the ROS node
|
|
90
|
+
if not rospy.get_node_uri():
|
|
91
|
+
rospy.init_node(f'kuavo_sdk_node', anonymous=True, disable_signals=True)
|
|
92
|
+
|
|
93
|
+
# Only check nodes exist when Init SDK, if not, tips user manually launch nodes.
|
|
94
|
+
# self.launch_ik_node()
|
|
95
|
+
# self.launch_gait_switch_node()
|
|
96
|
+
deps_nodes = ['/humanoid_gait_switch_by_name']
|
|
97
|
+
for node in deps_nodes:
|
|
98
|
+
if not KuavoROSEnv.check_rosnode_exists(node):
|
|
99
|
+
print(f"\033[31m\nError:Node {node} not found. Please launch it manuallly.\033[0m")
|
|
100
|
+
exit(1)
|
|
101
|
+
|
|
102
|
+
self._init_success = True # Set flag after successful initialization
|
|
103
|
+
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
def _launch_ros_node(self, node_name, launch_cmd, log_name):
|
|
107
|
+
"""Launch a ROS node with the given command and log the output.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
node_name (str): Name of the node to launch
|
|
111
|
+
launch_cmd (str): Full launch command including source and roslaunch
|
|
112
|
+
log_name (str): Name for the log file
|
|
113
|
+
|
|
114
|
+
Raises:
|
|
115
|
+
Exception: If node launch fails
|
|
116
|
+
"""
|
|
117
|
+
# Launch in background and check if successful
|
|
118
|
+
try:
|
|
119
|
+
os.makedirs('/var/log/kuavo_humanoid_sdk', exist_ok=True)
|
|
120
|
+
log_path = f'/var/log/kuavo_humanoid_sdk/{log_name}.log'
|
|
121
|
+
except PermissionError:
|
|
122
|
+
os.makedirs('log/kuavo_humanoid_sdk', exist_ok=True)
|
|
123
|
+
log_path = f'log/kuavo_humanoid_sdk/{log_name}.log'
|
|
124
|
+
|
|
125
|
+
with open(log_path, 'w') as log_file:
|
|
126
|
+
process = subprocess.Popen(launch_cmd, shell=True, executable='/bin/bash', stdout=log_file, stderr=log_file)
|
|
127
|
+
self._processes.append(process) # Add process to tracking list
|
|
128
|
+
|
|
129
|
+
if process.returncode is not None and process.returncode != 0:
|
|
130
|
+
raise Exception(f"Failed to launch {node_name}, return code: {process.returncode}")
|
|
131
|
+
|
|
132
|
+
SDKLogger.info(f"{node_name} launched successfully")
|
|
133
|
+
|
|
134
|
+
def _get_setup_file(self, ws_root=None):
|
|
135
|
+
"""Get the appropriate ROS setup file path based on shell type.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
ws_root (str, optional): ROS workspace root path. If None, uses ROS_WORKSPACE.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
str: Path to the setup file
|
|
142
|
+
|
|
143
|
+
Raises:
|
|
144
|
+
Exception: If setup file not found
|
|
145
|
+
"""
|
|
146
|
+
is_zsh = 'zsh' in os.environ.get('SHELL', '')
|
|
147
|
+
|
|
148
|
+
if ws_root is None:
|
|
149
|
+
ws_root = os.environ['ROS_WORKSPACE']
|
|
150
|
+
|
|
151
|
+
setup_files = {
|
|
152
|
+
'zsh': os.path.join(ws_root, 'devel/setup.zsh'),
|
|
153
|
+
'bash': os.path.join(ws_root, 'devel/setup.bash')
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
setup_file = setup_files['zsh'] if is_zsh else setup_files['bash']
|
|
157
|
+
if not os.path.exists(setup_file):
|
|
158
|
+
setup_file = setup_file.replace('devel', 'install')
|
|
159
|
+
if not os.path.exists(setup_file):
|
|
160
|
+
raise Exception(f"Setup file not found in either devel or install: {setup_file}")
|
|
161
|
+
|
|
162
|
+
return setup_file
|
|
163
|
+
|
|
164
|
+
def launch_ik_node(self):
|
|
165
|
+
# nodes: /arms_ik_node
|
|
166
|
+
# services: /ik/two_arm_hand_pose_cmd_srv, /ik/fk_srv
|
|
167
|
+
try:
|
|
168
|
+
rospy.get_master().getPid()
|
|
169
|
+
except Exception as e:
|
|
170
|
+
raise Exception(f"ROS master is not running. Please start roscore first or check ROS_MASTER_URI, {e}")
|
|
171
|
+
|
|
172
|
+
# Check if IK node and services exist
|
|
173
|
+
try:
|
|
174
|
+
# Check if arms_ik_node is running
|
|
175
|
+
nodes = subprocess.check_output(['rosnode', 'list']).decode('utf-8').split('\n')
|
|
176
|
+
if '/arms_ik_node' not in nodes:
|
|
177
|
+
# Launch IK node if not running
|
|
178
|
+
kuavo_ws_root = self._get_kuavo_ws_root()
|
|
179
|
+
setup_file = self._get_setup_file(kuavo_ws_root)
|
|
180
|
+
source_cmd = f"source {setup_file}"
|
|
181
|
+
|
|
182
|
+
# Get robot version from rosparam
|
|
183
|
+
robot_version = rospy.get_param('/robot_version', None)
|
|
184
|
+
if robot_version is None:
|
|
185
|
+
raise Exception("Failed to get robot_version from rosparam")
|
|
186
|
+
|
|
187
|
+
# Launch IK node
|
|
188
|
+
launch_cmd = f"roslaunch motion_capture_ik ik_node.launch robot_version:={robot_version}"
|
|
189
|
+
full_cmd = f"{source_cmd} && {launch_cmd}"
|
|
190
|
+
|
|
191
|
+
self._launch_ros_node('IK node', full_cmd, 'launch_ik')
|
|
192
|
+
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
except Exception as e:
|
|
196
|
+
raise Exception(f"Failed to verify IK node and services: {e}")
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def launch_gait_switch_node(self)-> bool:
|
|
200
|
+
"""Verify that the gait switch node is running, launch if not."""
|
|
201
|
+
try:
|
|
202
|
+
# Check if node exists
|
|
203
|
+
nodes = subprocess.check_output(['rosnode', 'list']).decode('utf-8').split('\n')
|
|
204
|
+
if '/humanoid_gait_switch_by_name' not in nodes:
|
|
205
|
+
kuavo_ws_root = self._get_kuavo_ws_root()
|
|
206
|
+
setup_file = self._get_setup_file(kuavo_ws_root)
|
|
207
|
+
source_cmd = f"source {setup_file}"
|
|
208
|
+
|
|
209
|
+
# Launch gait switch node
|
|
210
|
+
launch_cmd = "roslaunch humanoid_interface_ros humanoid_switch_gait.launch"
|
|
211
|
+
full_cmd = f"{source_cmd} && {launch_cmd}"
|
|
212
|
+
|
|
213
|
+
self._launch_ros_node('Gait switch node', full_cmd, 'launch_gait_switch')
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
raise Exception(f"Failed to launch gait_switch node: {e}")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
@staticmethod
|
|
220
|
+
def check_rosnode_exists(node_name):
|
|
221
|
+
"""Check if a ROS node is running.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
node_name (str): Name of the node to check
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
bool: True if node is running, False otherwise
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
nodes = subprocess.check_output(['rosnode', 'list']).decode('utf-8').split('\n')
|
|
231
|
+
# print(f"Debug: nodes: {nodes}")
|
|
232
|
+
return node_name in nodes
|
|
233
|
+
except subprocess.CalledProcessError as e:
|
|
234
|
+
SDKLogger.error(f"Error checking if node {node_name} exists: {e}")
|
|
235
|
+
return False
|
|
236
|
+
except Exception as e:
|
|
237
|
+
SDKLogger.error(f"Error checking if node {node_name} exists: {e}")
|
|
238
|
+
return False
|