kuavo-humanoid-sdk 1.1.2a924__py3-none-any.whl → 1.1.3a1239__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 (45) hide show
  1. kuavo_humanoid_sdk/common/global_config.py +15 -0
  2. kuavo_humanoid_sdk/common/websocket_kuavo_sdk.py +23 -0
  3. kuavo_humanoid_sdk/interfaces/data_types.py +46 -0
  4. kuavo_humanoid_sdk/kuavo/__init__.py +3 -0
  5. kuavo_humanoid_sdk/kuavo/core/audio.py +32 -0
  6. kuavo_humanoid_sdk/kuavo/core/core.py +17 -6
  7. kuavo_humanoid_sdk/kuavo/core/ros/audio.py +93 -0
  8. kuavo_humanoid_sdk/kuavo/core/ros/control.py +645 -19
  9. kuavo_humanoid_sdk/kuavo/core/ros/param.py +142 -4
  10. kuavo_humanoid_sdk/kuavo/core/ros/state.py +329 -15
  11. kuavo_humanoid_sdk/kuavo/core/ros/tools.py +158 -0
  12. kuavo_humanoid_sdk/kuavo/core/ros/vision.py +276 -0
  13. kuavo_humanoid_sdk/kuavo/core/ros_env.py +229 -1
  14. kuavo_humanoid_sdk/kuavo/dexterous_hand.py +6 -2
  15. kuavo_humanoid_sdk/kuavo/leju_claw.py +6 -2
  16. kuavo_humanoid_sdk/kuavo/robot.py +43 -22
  17. kuavo_humanoid_sdk/kuavo/robot_audio.py +39 -0
  18. kuavo_humanoid_sdk/kuavo/robot_info.py +7 -2
  19. kuavo_humanoid_sdk/kuavo/robot_state.py +6 -2
  20. kuavo_humanoid_sdk/kuavo/robot_tool.py +62 -0
  21. kuavo_humanoid_sdk/kuavo/robot_vision.py +90 -0
  22. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetection.py +306 -0
  23. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/_AprilTagDetectionArray.py +437 -0
  24. kuavo_humanoid_sdk/msg/kuavo_msgs/msg/__init__.py +9 -0
  25. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_armHandPose.py +2 -2
  26. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_handPose.py +2 -2
  27. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_ikSolveError.py +13 -13
  28. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_ikSolveParam.py +2 -2
  29. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_robotArmQVVD.py +2 -2
  30. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_twoArmHandPose.py +13 -13
  31. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/msg/_twoArmHandPoseCmd.py +15 -15
  32. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/_SpeechSynthesis.py +270 -0
  33. kuavo_humanoid_sdk/msg/kuavo_msgs/srv/__init__.py +5 -0
  34. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/srv/_changeArmCtrlModeKuavo.py +5 -5
  35. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/srv/_fkSrv.py +13 -13
  36. kuavo_humanoid_sdk/msg/{motion_capture_ik/srv/_changeArmCtrlMode.py → kuavo_msgs/srv/_playmusic.py} +97 -98
  37. kuavo_humanoid_sdk/msg/{motion_capture_ik → kuavo_msgs}/srv/_twoArmHandPoseCmdSrv.py +23 -23
  38. {kuavo_humanoid_sdk-1.1.2a924.dist-info → kuavo_humanoid_sdk-1.1.3a1239.dist-info}/METADATA +2 -1
  39. {kuavo_humanoid_sdk-1.1.2a924.dist-info → kuavo_humanoid_sdk-1.1.3a1239.dist-info}/RECORD +41 -33
  40. kuavo_humanoid_sdk/msg/motion_capture_ik/msg/__init__.py +0 -9
  41. kuavo_humanoid_sdk/msg/motion_capture_ik/msg/_headBodyPose.py +0 -145
  42. kuavo_humanoid_sdk/msg/motion_capture_ik/msg/_robotHandPosition.py +0 -225
  43. kuavo_humanoid_sdk/msg/motion_capture_ik/srv/__init__.py +0 -4
  44. {kuavo_humanoid_sdk-1.1.2a924.dist-info → kuavo_humanoid_sdk-1.1.3a1239.dist-info}/WHEEL +0 -0
  45. {kuavo_humanoid_sdk-1.1.2a924.dist-info → kuavo_humanoid_sdk-1.1.3a1239.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,12 @@
1
- import rospy
1
+ from kuavo_humanoid_sdk.common.global_config import GlobalConfig
2
+ try:
3
+ import rospy
4
+ except ImportError:
5
+ pass
2
6
  import json
3
7
  import xml.etree.ElementTree as ET
4
8
  from kuavo_humanoid_sdk.common.logger import SDKLogger
5
-
9
+ from kuavo_humanoid_sdk.common.websocket_kuavo_sdk import WebSocketKuavoSDK
6
10
  # End effector types
7
11
  class EndEffectorType:
8
12
  QIANGNAO = "qiangnao"
@@ -64,7 +68,126 @@ class RosParameter:
64
68
  return None
65
69
  return rospy.get_param('/initial_state')
66
70
 
67
- kuavo_ros_param = RosParameter()
71
+ import roslibpy
72
+ class RosParamWebsocket:
73
+ def __init__(self):
74
+ self.websocket = WebSocketKuavoSDK()
75
+ if not self.websocket.client.is_connected:
76
+ SDKLogger.error("Failed to connect to WebSocket server")
77
+ raise ConnectionError("Failed to connect to WebSocket server")
78
+
79
+ def robot_version(self)->str:
80
+ try:
81
+ param_service = roslibpy.Param(self.websocket.client, 'robot_version')
82
+ param = param_service.get()
83
+ if param is None:
84
+ SDKLogger.error("robot_version parameter not found")
85
+ return None
86
+ return param
87
+ except Exception as e:
88
+ SDKLogger.error(f"Failed to get robot_version: {e}")
89
+ return None
90
+
91
+ def arm_dof(self)->int:
92
+ try:
93
+ param_service = roslibpy.Param(self.websocket.client, 'armRealDof')
94
+ param = param_service.get()
95
+ if param is None:
96
+ SDKLogger.error("armRealDof parameter not found")
97
+ return None
98
+ return param
99
+ except Exception as e:
100
+ SDKLogger.error(f"Failed to get armRealDof: {e}")
101
+ return None
102
+
103
+ def head_dof(self)->int:
104
+ try:
105
+ param_service = roslibpy.Param(self.websocket.client, 'headRealDof')
106
+ param = param_service.get()
107
+ if param is None:
108
+ SDKLogger.error("headRealDof parameter not found")
109
+ return None
110
+ return param
111
+ except Exception as e:
112
+ SDKLogger.error(f"Failed to get headRealDof: {e}")
113
+ return None
114
+
115
+ def leg_dof(self)->int:
116
+ try:
117
+ param_service = roslibpy.Param(self.websocket.client, 'legRealDof')
118
+ param = param_service.get()
119
+ if param is None:
120
+ SDKLogger.error("legRealDof parameter not found")
121
+ return None
122
+ return param
123
+ except Exception as e:
124
+ SDKLogger.error(f"Failed to get legRealDof: {e}")
125
+ return None
126
+
127
+ def end_effector_type(self)->str:
128
+ try:
129
+ param_service = roslibpy.Param(self.websocket.client, 'end_effector_type')
130
+ param = param_service.get()
131
+ if param is None:
132
+ SDKLogger.error("end_effector_type parameter not found")
133
+ return None
134
+ return param
135
+ except Exception as e:
136
+ SDKLogger.error(f"Failed to get end_effector_type: {e}")
137
+ return None
138
+
139
+ def humanoid_description(self)->str:
140
+ try:
141
+ param_service = roslibpy.Param(self.websocket.client, 'humanoid_description')
142
+ param = param_service.get()
143
+ if param is None:
144
+ SDKLogger.error("humanoid_description parameter not found")
145
+ return None
146
+ return param
147
+ except Exception as e:
148
+ SDKLogger.error(f"Failed to get humanoid_description: {e}")
149
+ return None
150
+
151
+ def model_path(self)->str:
152
+ try:
153
+ param_service = roslibpy.Param(self.websocket.client, 'modelPath')
154
+ param = param_service.get()
155
+ if param is None:
156
+ SDKLogger.error("modelPath parameter not found")
157
+ return None
158
+ return param
159
+ except Exception as e:
160
+ SDKLogger.error(f"Failed to get modelPath: {e}")
161
+ return None
162
+
163
+ def kuavo_config(self)->str:
164
+ try:
165
+ param_service = roslibpy.Param(self.websocket.client, 'kuavo_configuration')
166
+ param = param_service.get()
167
+ if param is None:
168
+ SDKLogger.error("kuavo_configuration parameter not found")
169
+ return None
170
+ return param
171
+ except Exception as e:
172
+ SDKLogger.error(f"Failed to get kuavo_configuration: {e}")
173
+ return None
174
+
175
+ def initial_state(self)->str:
176
+ try:
177
+ param_service = roslibpy.Param(self.websocket.client, 'initial_state')
178
+ param = param_service.get()
179
+ if param is None:
180
+ SDKLogger.error("initial_state parameter not found")
181
+ return None
182
+ return param
183
+ except Exception as e:
184
+ SDKLogger.error(f"Failed to get initial_state: {e}")
185
+ return None
186
+
187
+ # if GlobalConfig.use_websocket:
188
+ # kuavo_ros_param = RosParamWebsocket()
189
+ # else:
190
+ # kuavo_ros_param = RosParameter()
68
191
 
69
192
  def joint_names()->dict:
70
193
  leg_link_names = [
@@ -78,6 +201,11 @@ def joint_names()->dict:
78
201
  head_link_names = [
79
202
  'zhead_1_link', 'zhead_2_link'
80
203
  ]
204
+ if GlobalConfig.use_websocket:
205
+ kuavo_ros_param = RosParamWebsocket()
206
+ else:
207
+ kuavo_ros_param = RosParameter()
208
+
81
209
  robot_desc = kuavo_ros_param.humanoid_description()
82
210
  if robot_desc is None:
83
211
  return None
@@ -135,6 +263,11 @@ kuavo_ros_info = None
135
263
 
136
264
  def end_frames_names()->dict:
137
265
  default = ["torso", "zarm_l7_link", "zarm_r7_link", "zarm_l4_link", "zarm_r4_link"]
266
+ if GlobalConfig.use_websocket:
267
+ kuavo_ros_param = RosParamWebsocket()
268
+ else:
269
+ kuavo_ros_param = RosParameter()
270
+
138
271
  kuavo_json = kuavo_ros_param.kuavo_config()
139
272
  if kuavo_json is None:
140
273
  return default
@@ -154,6 +287,11 @@ def make_robot_param()->dict:
154
287
  if kuavo_ros_info is not None:
155
288
  return kuavo_ros_info
156
289
 
290
+ if GlobalConfig.use_websocket:
291
+ kuavo_ros_param = RosParamWebsocket()
292
+ else:
293
+ kuavo_ros_param = RosParameter()
294
+
157
295
  kuavo_ros_info = {
158
296
  'robot_version': kuavo_ros_param.robot_version(),
159
297
  'arm_dof': kuavo_ros_param.arm_dof(),
@@ -174,4 +312,4 @@ def make_robot_param()->dict:
174
312
 
175
313
  if __name__ == "__main__":
176
314
  rospy.init_node("kuavo_ros_param_test")
177
- print(make_robot_param())
315
+ print(make_robot_param())
@@ -1,17 +1,24 @@
1
- import rospy
1
+ import roslibpy
2
2
  import copy
3
3
  import time
4
4
  from typing import Tuple
5
- from std_msgs.msg import Float64
6
- from nav_msgs.msg import Odometry
7
- from sensor_msgs.msg import JointState
8
- from kuavo_humanoid_sdk.msg.kuavo_msgs.msg import sensorsData, lejuClawState, gaitTimeName, dexhandTouchState
9
- from kuavo_msgs.srv import changeArmCtrlMode, changeArmCtrlModeRequest, getCurrentGaitName, getCurrentGaitNameRequest
10
- from kuavo_humanoid_sdk.msg.ocs2_msgs.msg import mpc_observation
5
+
6
+ try:
7
+ import rospy
8
+ from std_msgs.msg import Float64
9
+ from nav_msgs.msg import Odometry
10
+ from sensor_msgs.msg import JointState
11
+ from kuavo_humanoid_sdk.msg.kuavo_msgs.msg import sensorsData, lejuClawState, gaitTimeName, dexhandTouchState
12
+ from kuavo_msgs.srv import changeArmCtrlMode, changeArmCtrlModeRequest, getCurrentGaitName, getCurrentGaitNameRequest
13
+ from kuavo_humanoid_sdk.msg.ocs2_msgs.msg import mpc_observation
14
+ except:
15
+ pass
16
+
11
17
  from kuavo_humanoid_sdk.interfaces.data_types import (KuavoImuData, KuavoJointData, KuavoOdometry,
12
18
  KuavoArmCtrlMode, EndEffectorState, KuavoDexHandTouchState)
13
19
  from kuavo_humanoid_sdk.kuavo.core.ros.param import make_robot_param, EndEffectorType
14
20
  from kuavo_humanoid_sdk.common.logger import SDKLogger
21
+ from kuavo_humanoid_sdk.common.websocket_kuavo_sdk import WebSocketKuavoSDK
15
22
 
16
23
  from collections import deque
17
24
  from typing import Tuple, Optional
@@ -50,6 +57,7 @@ class GaitManager:
50
57
  return "No_Gait"
51
58
  return self._gait_time_names[-1][1]
52
59
 
60
+
53
61
  class KuavoRobotStateCore:
54
62
  _instance = None
55
63
 
@@ -242,10 +250,10 @@ class KuavoRobotStateCore:
242
250
  self._gait_changed_callbacks.append(callback)
243
251
 
244
252
  """ ------------------------------- callback ------------------------------- """
245
- def _terrain_height_callback(self, msg:Float64)->None:
253
+ def _terrain_height_callback(self, msg)->None:
246
254
  self._terrain_height = msg.data
247
255
 
248
- def _sensors_data_raw_callback(self, msg:sensorsData)->None:
256
+ def _sensors_data_raw_callback(self, msg)->None:
249
257
  # update imu data
250
258
  self._imu_data = KuavoImuData(
251
259
  gyro = (msg.imu_data.gyro.x, msg.imu_data.gyro.y, msg.imu_data.gyro.z),
@@ -261,7 +269,7 @@ class KuavoRobotStateCore:
261
269
  acceleration = copy.deepcopy(msg.joint_data.joint_current if hasattr(msg.joint_data, 'joint_current') else msg.joint_data.joint_torque)
262
270
  )
263
271
 
264
- def _odom_callback(self, msg:Odometry)->None:
272
+ def _odom_callback(self, msg)->None:
265
273
  # update odom data
266
274
  self._odom_data = KuavoOdometry(
267
275
  position = (msg.pose.pose.position.x, msg.pose.pose.position.y, msg.pose.pose.position.z),
@@ -270,7 +278,7 @@ class KuavoRobotStateCore:
270
278
  angular = (msg.twist.twist.angular.x, msg.twist.twist.angular.y, msg.twist.twist.angular.z)
271
279
  )
272
280
 
273
- def _lejuclaw_state_callback(self, msg:lejuClawState)->None:
281
+ def _lejuclaw_state_callback(self, msg)->None:
274
282
  self._eef_state = (EndEffectorState(
275
283
  # left claw
276
284
  position = [msg.data.position[0]],
@@ -285,7 +293,7 @@ class KuavoRobotStateCore:
285
293
  state=EndEffectorState.GraspingState(msg.state[1])
286
294
  ))
287
295
 
288
- def _dexterous_hand_state_callback(self, msg:JointState)->None:
296
+ def _dexterous_hand_state_callback(self, msg)->None:
289
297
  self._eef_state = (EndEffectorState(
290
298
  # left hand
291
299
  position = list(msg.position[:len(msg.position)//2]),
@@ -300,7 +308,7 @@ class KuavoRobotStateCore:
300
308
  state=EndEffectorState.GraspingState.UNKNOWN
301
309
  ))
302
310
 
303
- def _dexhand_touch_state_callback(self, msg:dexhandTouchState)->None:
311
+ def _dexhand_touch_state_callback(self, msg)->None:
304
312
  # Update touch state for both hands
305
313
  self._dexhand_touch_state = (
306
314
  KuavoDexHandTouchState(
@@ -323,7 +331,7 @@ class KuavoRobotStateCore:
323
331
  ) # Right hand touch state
324
332
  )
325
333
 
326
- def _humanoid_mpc_gait_changed_callback(self, msg: gaitTimeName):
334
+ def _humanoid_mpc_gait_changed_callback(self, msg):
327
335
  """
328
336
  Callback function for gait change messages.
329
337
  Updates the current gait name when a gait change occurs.
@@ -331,7 +339,7 @@ class KuavoRobotStateCore:
331
339
  SDKLogger.debug(f"[State] Received gait change message: {msg.gait_name} at time {msg.start_time}")
332
340
  self._gait_manager.add(msg.start_time, msg.gait_name)
333
341
 
334
- def _humanoid_mpc_observation_callback(self, msg: mpc_observation) -> None:
342
+ def _humanoid_mpc_observation_callback(self, msg) -> None:
335
343
  """
336
344
  Callback function for MPC observation messages.
337
345
  Updates the current MPC state and input data.
@@ -378,3 +386,309 @@ class KuavoRobotStateCore:
378
386
  except Exception as e:
379
387
  SDKLogger.error(f"Service call failed: {e}")
380
388
  return None
389
+
390
+ class KuavoRobotStateCoreWebsocket:
391
+ _instance = None
392
+
393
+ def __new__(cls, *args, **kwargs):
394
+ if not cls._instance:
395
+ cls._instance = super().__new__(cls)
396
+ return cls._instance
397
+
398
+ def __init__(self):
399
+ if not hasattr(self, '_initialized'):
400
+ try:
401
+ self.websocket = WebSocketKuavoSDK()
402
+ if not self.websocket.client.is_connected:
403
+ SDKLogger.error("Failed to connect to WebSocket server")
404
+ raise ConnectionError("Failed to connect to WebSocket server")
405
+
406
+ # Initialize subscribers
407
+ self._sub_sensors_data = roslibpy.Topic(self.websocket.client, '/sensors_data_raw', 'kuavo_msgs/sensorsData')
408
+ self._sub_odom = roslibpy.Topic(self.websocket.client, '/odom', 'nav_msgs/Odometry')
409
+ self._sub_terrain_height = roslibpy.Topic(self.websocket.client, '/humanoid/mpc/terrainHeight', 'std_msgs/Float64')
410
+ self._sub_gait_time_name = roslibpy.Topic(self.websocket.client, '/humanoid_mpc_gait_time_name', 'kuavo_msgs/gaitTimeName')
411
+ self._sub_mpc_observation = roslibpy.Topic(self.websocket.client, '/humanoid_mpc_observation', 'ocs2_msgs/mpc_observation')
412
+
413
+ # service calls are time-consuming after subscription, place them before subscription
414
+ kuavo_info = make_robot_param()
415
+
416
+ # Subscribe to topics
417
+ self._sub_sensors_data.subscribe(self._sensors_data_raw_callback)
418
+ self._sub_odom.subscribe(self._odom_callback)
419
+ self._sub_terrain_height.subscribe(self._terrain_height_callback)
420
+ self._sub_gait_time_name.subscribe(self._humanoid_mpc_gait_changed_callback)
421
+ self._sub_mpc_observation.subscribe(self._humanoid_mpc_observation_callback)
422
+
423
+ if kuavo_info is None:
424
+ SDKLogger.error("Failed to get robot parameters")
425
+ raise RuntimeError("Failed to get robot parameters")
426
+
427
+ self._ee_type = kuavo_info['end_effector_type']
428
+ if self._ee_type == EndEffectorType.LEJUCLAW:
429
+ self._sub_lejuclaw_state = roslibpy.Topic(self.websocket.client, '/leju_claw_state', 'kuavo_msgs/lejuClawState')
430
+ self._sub_lejuclaw_state.subscribe(self._lejuclaw_state_callback)
431
+ elif self._ee_type == EndEffectorType.QIANGNAO:
432
+ self._sub_dexhand_state = roslibpy.Topic(self.websocket.client, '/dexhand/state', 'sensor_msgs/JointState')
433
+ self._sub_dexhand_state.subscribe(self._dexterous_hand_state_callback)
434
+ elif self._ee_type == EndEffectorType.QIANGNAO_TOUCH:
435
+ self._sub_dexhand_state = roslibpy.Topic(self.websocket.client, '/dexhand/state', 'sensor_msgs/JointState')
436
+ self._sub_dexhand_touch_state = roslibpy.Topic(self.websocket.client, '/dexhand/touch_state', 'kuavo_msgs/dexhandTouchState')
437
+ self._sub_dexhand_state.subscribe(self._dexterous_hand_state_callback)
438
+ self._sub_dexhand_touch_state.subscribe(self._dexhand_touch_state_callback)
439
+ # Initialize touch state for both hands
440
+ self._dexhand_touch_state = (
441
+ KuavoDexHandTouchState(
442
+ data=(KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
443
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
444
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
445
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
446
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
447
+ ),
448
+ KuavoDexHandTouchState(
449
+ data=(KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
450
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
451
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
452
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
453
+ KuavoDexHandTouchState.KuavoTouchState(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0))
454
+ )
455
+ )
456
+
457
+ """ data """
458
+ self._terrain_height = 0.0
459
+ self._imu_data = KuavoImuData(
460
+ gyro = (0.0, 0.0, 0.0),
461
+ acc = (0.0, 0.0, 0.0),
462
+ free_acc = (0.0, 0.0, 0.0),
463
+ quat = (0.0, 0.0, 0.0, 0.0)
464
+ )
465
+ self._joint_data = KuavoJointData(
466
+ position = [0.0] * 28,
467
+ velocity = [0.0] * 28,
468
+ torque = [0.0] * 28,
469
+ acceleration = [0.0] * 28
470
+ )
471
+ self._odom_data = KuavoOdometry(
472
+ position = (0.0, 0.0, 0.0),
473
+ orientation = (0.0, 0.0, 0.0, 0.0),
474
+ linear = (0.0, 0.0, 0.0),
475
+ angular = (0.0, 0.0, 0.0)
476
+ )
477
+ self._eef_state = (EndEffectorState(
478
+ position = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
479
+ velocity = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
480
+ effort = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
481
+ state=EndEffectorState.GraspingState.UNKNOWN
482
+ ), EndEffectorState(
483
+ position = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
484
+ velocity = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
485
+ effort = [0.0] * 12 if kuavo_info['end_effector_type'].startswith('qiangnao') else [0.0] * 2,
486
+ state=EndEffectorState.GraspingState.UNKNOWN
487
+ ))
488
+
489
+ # gait manager
490
+ self._gait_manager = GaitManager()
491
+ self._prev_gait_name = self.gait_name
492
+
493
+ # Wait for first MPC observation data
494
+ self._mpc_observation_data = None
495
+ start_time = time.time()
496
+ while self._mpc_observation_data is None:
497
+ if time.time() - start_time > 1.0: # 1.0s timeout
498
+ SDKLogger.warn("Timeout waiting for MPC observation data")
499
+ break
500
+ SDKLogger.debug("Waiting for first MPC observation data...")
501
+ time.sleep(0.1)
502
+
503
+ if self._mpc_observation_data is not None:
504
+ if self._gait_manager.is_empty:
505
+ self._prev_gait_name = self.gait_name()
506
+ SDKLogger.debug(f"[State] Adding initial gait state: {self._prev_gait_name} at time {self._mpc_observation_data['time']}")
507
+ self._gait_manager.add(self._mpc_observation_data['time'], self._prev_gait_name)
508
+
509
+ # 获取当前手臂控制模式
510
+ self._arm_ctrl_mode = self._srv_get_arm_ctrl_mode()
511
+ self._initialized = True
512
+ except Exception as e:
513
+ SDKLogger.error(f"Failed to initialize KuavoRobotStateCoreWebsocket: {e}")
514
+ raise
515
+
516
+ @property
517
+ def com_height(self)->float:
518
+ return self._odom_data.position[2] - self._terrain_height
519
+
520
+ @property
521
+ def imu_data(self)->KuavoImuData:
522
+ return self._imu_data
523
+
524
+ @property
525
+ def joint_data(self)->KuavoJointData:
526
+ return self._joint_data
527
+
528
+ @property
529
+ def odom_data(self)->KuavoOdometry:
530
+ return self._odom_data
531
+
532
+ @property
533
+ def arm_control_mode(self) -> KuavoArmCtrlMode:
534
+ mode = self._srv_get_arm_ctrl_mode()
535
+ if mode is not None:
536
+ self._arm_ctrl_mode = mode
537
+ return self._arm_ctrl_mode
538
+
539
+ @property
540
+ def eef_state(self)->Tuple[EndEffectorState, EndEffectorState]:
541
+ return self._eef_state
542
+
543
+ @property
544
+ def dexhand_touch_state(self)-> Tuple[KuavoDexHandTouchState, KuavoDexHandTouchState]:
545
+ if self._ee_type != EndEffectorType.QIANGNAO_TOUCH:
546
+ raise Exception("This robot does not support touch state")
547
+ return self._dexhand_touch_state
548
+
549
+ @property
550
+ def current_observation_time(self) -> float:
551
+ if self._mpc_observation_data is None:
552
+ return None
553
+ return self._mpc_observation_data.time
554
+
555
+ @property
556
+ def current_gait_mode(self) -> int:
557
+ if self._mpc_observation_data is None:
558
+ return None
559
+ return self._mpc_observation_data.mode
560
+
561
+ def gait_name(self)->str:
562
+ return self._srv_get_current_gait_name()
563
+
564
+ def is_gait(self, gait_name: str) -> bool:
565
+ return self._gait_manager.get_gait(self._mpc_observation_data['time']) == gait_name
566
+
567
+ def register_gait_changed_callback(self, callback):
568
+ if not hasattr(self, '_gait_changed_callbacks'):
569
+ self._gait_changed_callbacks = []
570
+
571
+ from inspect import signature
572
+ sig = signature(callback)
573
+ if len(sig.parameters) != 2:
574
+ raise ValueError("Callback must accept 2 parameters: current_time (float), gait_name (str)")
575
+ if callback not in self._gait_changed_callbacks:
576
+ self._gait_changed_callbacks.append(callback)
577
+
578
+ def _terrain_height_callback(self, msg)->None:
579
+ self._terrain_height = msg['data']
580
+
581
+ def _sensors_data_raw_callback(self, msg)->None:
582
+ # update imu data
583
+ self._imu_data = KuavoImuData(
584
+ gyro = (msg['imu_data']['gyro']['x'], msg['imu_data']['gyro']['y'], msg['imu_data']['gyro']['z']),
585
+ acc = (msg['imu_data']['acc']['x'], msg['imu_data']['acc']['y'], msg['imu_data']['acc']['z']),
586
+ free_acc = (msg['imu_data']['free_acc']['x'], msg['imu_data']['free_acc']['y'], msg['imu_data']['free_acc']['z']),
587
+ quat = (msg['imu_data']['quat']['x'], msg['imu_data']['quat']['y'], msg['imu_data']['quat']['z'], msg['imu_data']['quat']['w'])
588
+ )
589
+ # update joint data
590
+ self._joint_data = KuavoJointData(
591
+ position = copy.deepcopy(msg['joint_data']['joint_q']),
592
+ velocity = copy.deepcopy(msg['joint_data']['joint_v']),
593
+ torque = copy.deepcopy(msg['joint_data']['joint_vd']),
594
+ acceleration = copy.deepcopy(msg['joint_data'].get('joint_current', msg['joint_data']['joint_torque']))
595
+ )
596
+
597
+ def _odom_callback(self, msg)->None:
598
+ # update odom data
599
+ self._odom_data = KuavoOdometry(
600
+ position = (msg['pose']['pose']['position']['x'], msg['pose']['pose']['position']['y'], msg['pose']['pose']['position']['z']),
601
+ orientation = (msg['pose']['pose']['orientation']['x'], msg['pose']['pose']['orientation']['y'], msg['pose']['pose']['orientation']['z'], msg['pose']['pose']['orientation']['w']),
602
+ linear = (msg['twist']['twist']['linear']['x'], msg['twist']['twist']['linear']['y'], msg['twist']['twist']['linear']['z']),
603
+ angular = (msg['twist']['twist']['angular']['x'], msg['twist']['twist']['angular']['y'], msg['twist']['twist']['angular']['z'])
604
+ )
605
+
606
+ def _lejuclaw_state_callback(self, msg)->None:
607
+ self._eef_state = (EndEffectorState(
608
+ position = [msg['data']['position'][0]],
609
+ velocity = [msg['data']['velocity'][0]],
610
+ effort = [msg['data']['effort'][0]],
611
+ state=EndEffectorState.GraspingState(msg['state'][0])
612
+ ), EndEffectorState(
613
+ position = [msg['data']['position'][1]],
614
+ velocity = [msg['data']['velocity'][1]],
615
+ effort = [msg['data']['effort'][1]],
616
+ state=EndEffectorState.GraspingState(msg['state'][1])
617
+ ))
618
+
619
+ def _dexterous_hand_state_callback(self, msg)->None:
620
+ self._eef_state = (EndEffectorState(
621
+ position = list(msg['position'][:len(msg['position'])//2]),
622
+ velocity = list(msg['velocity'][:len(msg['velocity'])//2]),
623
+ effort = list(msg['effort'][:len(msg['effort'])//2]),
624
+ state=EndEffectorState.GraspingState.UNKNOWN
625
+ ), EndEffectorState(
626
+ position = list(msg['position'][len(msg['position'])//2:]),
627
+ velocity = list(msg['velocity'][len(msg['velocity'])//2:]),
628
+ effort = list(msg['effort'][len(msg['effort'])//2:]),
629
+ state=EndEffectorState.GraspingState.UNKNOWN
630
+ ))
631
+
632
+ def _dexhand_touch_state_callback(self, msg)->None:
633
+ self._dexhand_touch_state = (
634
+ KuavoDexHandTouchState(
635
+ data=tuple(KuavoDexHandTouchState.KuavoTouchState(
636
+ sensor['normal_force1'], sensor['normal_force2'], sensor['normal_force3'],
637
+ sensor['tangential_force1'], sensor['tangential_force2'], sensor['tangential_force3'],
638
+ sensor['tangential_direction1'], sensor['tangential_direction2'], sensor['tangential_direction3'],
639
+ sensor['self_proximity1'], sensor['self_proximity2'], sensor['mutual_proximity'],
640
+ sensor['status']
641
+ ) for sensor in msg['left_hand'])
642
+ ),
643
+ KuavoDexHandTouchState(
644
+ data=tuple(KuavoDexHandTouchState.KuavoTouchState(
645
+ sensor['normal_force1'], sensor['normal_force2'], sensor['normal_force3'],
646
+ sensor['tangential_force1'], sensor['tangential_force2'], sensor['tangential_force3'],
647
+ sensor['tangential_direction1'], sensor['tangential_direction2'], sensor['tangential_direction3'],
648
+ sensor['self_proximity1'], sensor['self_proximity2'], sensor['mutual_proximity'],
649
+ sensor['status']
650
+ ) for sensor in msg['right_hand'])
651
+ )
652
+ )
653
+
654
+ def _humanoid_mpc_gait_changed_callback(self, msg):
655
+ SDKLogger.debug(f"[State] Received gait change message: {msg['gait_name']} at time {msg['start_time']}")
656
+ self._gait_manager.add(msg['start_time'], msg['gait_name'])
657
+
658
+ def _humanoid_mpc_observation_callback(self, msg) -> None:
659
+ try:
660
+ SDKLogger.debug(f"[State] Received MPC observation message: {msg}")
661
+ self._mpc_observation_data = msg
662
+ if hasattr(self, '_initialized'):
663
+ curr_time = self._mpc_observation_data['time']
664
+ current_gait = self._gait_manager.get_gait(curr_time)
665
+ if self._prev_gait_name != current_gait:
666
+ SDKLogger.debug(f"[State] Gait changed to: {current_gait} at time {curr_time}")
667
+ self._prev_gait_name = current_gait
668
+ if hasattr(self, '_gait_changed_callbacks') and self._gait_changed_callbacks is not None:
669
+ for callback in self._gait_changed_callbacks:
670
+ callback(curr_time, current_gait)
671
+ except Exception as e:
672
+ SDKLogger.error(f"Error processing MPC observation: {e}")
673
+
674
+ def _srv_get_arm_ctrl_mode(self)-> KuavoArmCtrlMode:
675
+ try:
676
+ service = roslibpy.Service(self.websocket.client, '/humanoid_get_arm_ctrl_mode', 'kuavo_msgs/changeArmCtrlMode')
677
+ request = {}
678
+ response = service.call(request)
679
+ return KuavoArmCtrlMode(response.get('mode', 0))
680
+ except Exception as e:
681
+ SDKLogger.error(f"Service call failed: {e}")
682
+ return None
683
+
684
+ def _srv_get_current_gait_name(self)->str:
685
+ try:
686
+ service = roslibpy.Service(self.websocket.client, '/humanoid_get_current_gait_name', 'kuavo_msgs/getCurrentGaitName')
687
+ request = {}
688
+ response = service.call(request)
689
+ if response.get('success', False):
690
+ return response['gait_name']
691
+ return None
692
+ except Exception as e:
693
+ SDKLogger.error(f"Service call failed: {e}")
694
+ return None