kuavo-humanoid-sdk 1.1.3a1240__py3-none-any.whl → 1.1.5__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.

@@ -1,4 +1,6 @@
1
1
  from .msg import *
2
2
  from .interfaces import *
3
3
  from .kuavo import *
4
+ from .kuavo_strategy import *
5
+ from .kuavo_strategy.grasp_box import *
4
6
  from kuavo_humanoid_sdk.common.logger import SDKLogger, disable_sdk_logging
@@ -6,6 +6,7 @@ class GlobalConfig:
6
6
  use_websocket = False
7
7
  websocket_host = '127.0.0.1'
8
8
  websocket_port = 9090
9
+ websocket_timeout = 5.0
9
10
 
10
11
  def __new__(cls, *args, **kwargs):
11
12
  if cls._instance is None:
@@ -60,6 +60,50 @@ class KuavoArmCtrlMode(Enum):
60
60
  AutoSwing = 1
61
61
  ExternalControl = 2
62
62
 
63
+ class KuavoManipulationMpcFrame(Enum):
64
+ """Enum class representing the manipulation mpc frame for the Kuavo robot end effector.
65
+
66
+ Attributes:
67
+ KeepCurrentFrame: Keep the current frame (value: 0)
68
+ WorldFrame: World frame (value: 1)
69
+ LocalFrame: Local frame (value: 2)
70
+ VRFrame: VR frame (value: 3)
71
+ ManipulationWorldFrame: Manipulation world frame (value: 4)
72
+ """
73
+ KeepCurrentFrame = 0
74
+ WorldFrame = 1
75
+ LocalFrame = 2
76
+ VRFrame = 3
77
+ ManipulationWorldFrame = 4
78
+ ERROR = -1
79
+
80
+ class KuavoManipulationMpcCtrlMode(Enum):
81
+ """Enum class representing the control mode for the Kuavo robot manipulation MPC.
82
+
83
+ Attributes:
84
+ NoControl: No control (value: 0)
85
+ ArmOnly: Only control the arm (value: 1)
86
+ BaseOnly: Only control the base (value: 2)
87
+ BaseArm: Control both the base and the arm (value: 3)
88
+ ERROR: Error state (value: -1)
89
+ """
90
+ NoControl = 0
91
+ ArmOnly = 1
92
+ BaseOnly = 2
93
+ BaseArm = 3
94
+ ERROR = -1
95
+
96
+ class KuavoManipulationMpcControlFlow(Enum):
97
+ """Enum class representing the control data flow for the Kuavo robot manipulation.
98
+
99
+ Attributes:
100
+ ThroughFullBodyMpc: Control data flows through full-body MPC before entering WBC (value: 0)
101
+ DirectToWbc: Control data flows directly to WBC without full-body MPC (value: 1)
102
+ Error: Invalid control path (value: -1)
103
+ """
104
+ ThroughFullBodyMpc = 0
105
+ DirectToWbc = 1
106
+ Error = -1
63
107
 
64
108
  @dataclass
65
109
  class EndEffectorState:
@@ -6,11 +6,15 @@ from typing import Tuple
6
6
  from transitions import Machine, State
7
7
 
8
8
  from kuavo_humanoid_sdk.common.logger import SDKLogger
9
- from kuavo_humanoid_sdk.kuavo.core.ros.audio import Audio
9
+ from kuavo_humanoid_sdk.common.global_config import GlobalConfig
10
+ from kuavo_humanoid_sdk.kuavo.core.ros.audio import Audio, AudioWebsocket
10
11
 
11
12
  class KuavoRobotAudioCore:
12
13
  def __init__(self):
13
- self.robot_audio = Audio()
14
+ if GlobalConfig.use_websocket:
15
+ self.robot_audio = AudioWebsocket()
16
+ else:
17
+ self.robot_audio = Audio()
14
18
 
15
19
  def play_audio(self, music_number: str, volume: float = 0.5, speed: float = 1.0) -> bool:
16
20
  """
@@ -24,7 +24,7 @@ import numpy as np
24
24
  from typing import Tuple
25
25
  from transitions import Machine, State
26
26
 
27
- from kuavo_humanoid_sdk.interfaces.data_types import KuavoArmCtrlMode, KuavoIKParams, KuavoPose
27
+ from kuavo_humanoid_sdk.interfaces.data_types import KuavoArmCtrlMode, KuavoIKParams, KuavoPose, KuavoManipulationMpcFrame, KuavoManipulationMpcCtrlMode, KuavoManipulationMpcControlFlow
28
28
  from kuavo_humanoid_sdk.kuavo.core.ros.control import KuavoRobotControl, KuavoRobotControlWebsocket
29
29
  from kuavo_humanoid_sdk.kuavo.core.ros.vision import KuavoRobotVisionCore
30
30
  from kuavo_humanoid_sdk.kuavo.core.ros.tools import KuavoRobotToolsCore
@@ -37,15 +37,19 @@ ROBOT_STATES = [
37
37
  State(name='stance', on_enter=['_on_enter_stance']),
38
38
  State(name='walk', on_enter=['_on_enter_walk']),
39
39
  State(name='trot', on_enter=['_on_enter_trot']),
40
- State(name='custom_gait', on_enter=['_on_enter_custom_gait'])
40
+ State(name='custom_gait', on_enter=['_on_enter_custom_gait']),
41
+ State(name='command_pose_world', on_enter=['_on_enter_command_pose_world']),
42
+ State(name='command_pose', on_enter=['_on_enter_command_pose']),
41
43
  ]
42
44
 
43
45
  # Define state transitions
44
46
  ROBOT_TRANSITIONS = [
45
- {'trigger': 'to_stance', 'source': ['walk', 'trot', 'custom_gait'], 'dest': 'stance'},
47
+ {'trigger': 'to_stance', 'source': ['walk', 'trot', 'custom_gait', 'command_pose_world', 'command_pose'], 'dest': 'stance'},
46
48
  {'trigger': 'to_walk', 'source': ['stance', 'trot', 'custom_gait'], 'dest': 'walk'},
47
49
  {'trigger': 'to_trot', 'source': ['stance', 'walk', 'custom_gait'], 'dest': 'trot'},
48
50
  {'trigger': 'to_custom_gait', 'source': ['stance', 'custom_gait'], 'dest': 'custom_gait'},
51
+ {'trigger': 'to_command_pose_world', 'source': ['stance', 'command_pose_world'], 'dest': 'command_pose_world'},
52
+ {'trigger': 'to_command_pose', 'source': ['stance', 'command_pose'], 'dest': 'command_pose'},
49
53
  ]
50
54
 
51
55
  class KuavoRobotCore:
@@ -72,10 +76,16 @@ class KuavoRobotCore:
72
76
  else:
73
77
  self._control = KuavoRobotControl()
74
78
  self._rb_state = KuavoRobotStateCore()
75
- # robot vision
76
- self._robot_vision = KuavoRobotVisionCore()
77
- # robot ros tf
78
- self._robot_tf_tool = KuavoRobotToolsCore()
79
+
80
+ # manipulation mpc
81
+ self._manipulation_mpc_frame = KuavoManipulationMpcFrame.KeepCurrentFrame
82
+ self._manipulation_mpc_ctrl_mode = KuavoManipulationMpcCtrlMode.NoControl
83
+ self._manipulation_mpc_control_flow = KuavoManipulationMpcControlFlow.ThroughFullBodyMpc
84
+
85
+ # robot vision
86
+ self._robot_vision = KuavoRobotVisionCore()
87
+ # robot ros tf
88
+ self._robot_tf_tool = KuavoRobotToolsCore()
79
89
 
80
90
  self._arm_ctrl_mode = KuavoArmCtrlMode.AutoSwing
81
91
 
@@ -104,6 +114,21 @@ class KuavoRobotCore:
104
114
  if arm_ctrl_mode is not None:
105
115
  self._arm_ctrl_mode = arm_ctrl_mode
106
116
  SDKLogger.debug(f"[Core] initialize arm control mode: {arm_ctrl_mode}")
117
+
118
+ # init manipulation mpc
119
+ manipulation_mpc_frame = self._rb_state.manipulation_mpc_frame
120
+ if manipulation_mpc_frame is not None:
121
+ self._manipulation_mpc_frame = manipulation_mpc_frame
122
+ SDKLogger.debug(f"[Core] initialize manipulation mpc frame: {manipulation_mpc_frame}")
123
+ manipulation_mpc_ctrl_mode = self._rb_state.manipulation_mpc_ctrl_mode
124
+ if manipulation_mpc_ctrl_mode is not None:
125
+ self._manipulation_mpc_ctrl_mode = manipulation_mpc_ctrl_mode
126
+ SDKLogger.debug(f"[Core] initialize manipulation mpc ctrl mode: {manipulation_mpc_ctrl_mode}")
127
+ manipulation_mpc_control_flow = self._rb_state.manipulation_mpc_control_flow
128
+ if manipulation_mpc_control_flow is not None:
129
+ self._manipulation_mpc_control_flow = manipulation_mpc_control_flow
130
+ SDKLogger.debug(f"[Core] initialize manipulation mpc control flow: {manipulation_mpc_control_flow}")
131
+
107
132
  except Exception as e:
108
133
  raise RuntimeError(f"[Core] initialize failed: \n"
109
134
  f"{e}, please check the robot is launched, "
@@ -165,6 +190,21 @@ class KuavoRobotCore:
165
190
  SDKLogger.debug(f"[Core] [StateMachine] State unchanged: already in custom_gait state")
166
191
  return
167
192
  SDKLogger.debug(f"[Core] [StateMachine] Entering custom_gait state, from {previous_state}")
193
+
194
+ def _on_enter_command_pose_world(self, event):
195
+ previous_state = event.transition.source
196
+ if self.state == previous_state:
197
+ SDKLogger.debug(f"[Core] [StateMachine] State unchanged: already in command_pose_world state")
198
+ return
199
+ SDKLogger.debug(f"[Core] [StateMachine] Entering command_pose_world state, from {previous_state}")
200
+
201
+ def _on_enter_command_pose(self, event):
202
+ previous_state = event.transition.source
203
+ if self.state == previous_state:
204
+ SDKLogger.debug(f"[Core] [StateMachine] State unchanged: already in command_pose state")
205
+ return
206
+ SDKLogger.debug(f"[Core] [StateMachine] Entering command_pose state, from {previous_state}")
207
+
168
208
  """ -------------------------------------------------------------"""
169
209
 
170
210
  """ --------------------------- Control -------------------------"""
@@ -300,6 +340,54 @@ class KuavoRobotCore:
300
340
 
301
341
  return True
302
342
 
343
+ def control_command_pose(self, target_pose_x:float, target_pose_y:float, target_pose_z:float, target_pose_yaw:float)->bool:
344
+ """
345
+ Control robot pose in base_link frame
346
+
347
+ Arguments:
348
+ - target_pose_x: x position (meters)
349
+ - target_pose_y: y position (meters)
350
+ - target_pose_z: z position (meters)
351
+ - target_pose_yaw: yaw angle (radians)
352
+
353
+ Returns:
354
+ bool: True if command was sent successfully, False otherwise
355
+
356
+ Raises:
357
+ RuntimeError: If robot is not in stance state
358
+ """
359
+ if self.state != 'stance':
360
+ raise RuntimeError(f"[Core] control_command_pose failed: robot must be in stance state, current state: {self.state}")
361
+
362
+ # Add any parameter validation if needed
363
+ # e.g., limit ranges for safety
364
+ self.to_command_pose()
365
+ return self._control.control_command_pose(target_pose_x, target_pose_y, target_pose_z, target_pose_yaw)
366
+
367
+ def control_command_pose_world(self, target_pose_x:float, target_pose_y:float, target_pose_z:float, target_pose_yaw:float)->bool:
368
+ """
369
+ Control robot pose in odom (world) frame
370
+
371
+ Arguments:
372
+ - target_pose_x: x position (meters)
373
+ - target_pose_y: y position (meters)
374
+ - target_pose_z: z position (meters)
375
+ - target_pose_yaw: yaw angle (radians)
376
+
377
+ Returns:
378
+ bool: True if command was sent successfully, False otherwise
379
+
380
+ Raises:
381
+ RuntimeError: If robot is not in stance state
382
+ """
383
+ # if self.state != 'stance':
384
+ # raise RuntimeError(f"[Core] control_command_pose_world failed: robot must be in stance state, current state: {self.state}")
385
+
386
+ # Add any parameter validation if needed
387
+ # e.g., limit ranges for safety
388
+ self.to_command_pose_world()
389
+ return self._control.control_command_pose_world(target_pose_x, target_pose_y, target_pose_z, target_pose_yaw)
390
+
303
391
  def execute_gesture(self, gestures:list)->bool:
304
392
  return self._control.execute_gesture(gestures)
305
393
 
@@ -322,30 +410,111 @@ class KuavoRobotCore:
322
410
  pitch_deg = pitch * 180 / math.pi
323
411
  return self._control.control_robot_head(yaw_deg, pitch_deg)
324
412
 
325
- def control_robot_arm_traj(self, joint_data:list)->bool:
413
+ def enable_head_tracking(self, target_id: int)->bool:
414
+ return self._control.enable_head_tracking(target_id)
415
+
416
+ def disable_head_tracking(self)->bool:
417
+ return self._control.disable_head_tracking()
418
+
419
+ def control_robot_arm_joint_positions(self, joint_data:list)->bool:
326
420
  if self.state != 'stance':
327
- raise RuntimeError(f"[Core] control_robot_arm_traj failed: robot must be in stance state, current state: {self.state}")
421
+ raise RuntimeError(f"[Core] control_robot_arm_joint_positions failed: robot must be in stance state, current state: {self.state}")
328
422
 
329
423
  # change to external control mode
330
424
  if self._arm_ctrl_mode != KuavoArmCtrlMode.ExternalControl:
331
- SDKLogger.debug("[Core] control_robot_arm_traj, current arm mode != ExternalControl, change it.")
425
+ SDKLogger.debug("[Core] control_robot_arm_joint_positions, current arm mode != ExternalControl, change it.")
332
426
  if not self.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ExternalControl):
333
- SDKLogger.warn("[Core] control_robot_arm_traj failed, change robot arm ctrl mode failed!")
427
+ SDKLogger.warn("[Core] control_robot_arm_joint_positions failed, change robot arm ctrl mode failed!")
334
428
  return False
335
- return self._control.control_robot_arm_traj(joint_data)
429
+ return self._control.control_robot_arm_joint_positions(joint_data)
336
430
 
337
- def control_robot_arm_target_poses(self, times:list, joint_q:list)->bool:
431
+ def control_robot_arm_joint_trajectory(self, times:list, joint_q:list)->bool:
338
432
  if self.state != 'stance':
339
- raise RuntimeError("[Core] control_robot_arm_target_poses failed: robot must be in stance state")
433
+ raise RuntimeError("[Core] control_robot_arm_joint_trajectory failed: robot must be in stance state")
340
434
 
341
435
  if self._arm_ctrl_mode != KuavoArmCtrlMode.ExternalControl:
342
- SDKLogger.debug("[Core] control_robot_arm_target_poses, current arm mode != ExternalControl, change it.")
436
+ SDKLogger.debug("[Core] control_robot_arm_joint_trajectory, current arm mode != ExternalControl, change it.")
343
437
  if not self.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ExternalControl):
344
- SDKLogger.warn("[Core] control_robot_arm_target_poses failed, change robot arm ctrl mode failed!")
438
+ SDKLogger.warn("[Core] control_robot_arm_joint_trajectory failed, change robot arm ctrl mode failed!")
345
439
  return False
346
440
 
347
- return self._control.control_robot_arm_target_poses(times, joint_q)
441
+ return self._control.control_robot_arm_joint_trajectory(times, joint_q)
442
+
443
+ def control_robot_end_effector_pose(self, left_pose: KuavoPose, right_pose: KuavoPose, frame: KuavoManipulationMpcFrame)->bool:
444
+ if self._arm_ctrl_mode != KuavoArmCtrlMode.ExternalControl:
445
+ SDKLogger.debug("[Core] control_robot_end_effector_pose, current arm mode != ExternalControl, change it.")
446
+ if not self.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ExternalControl):
447
+ SDKLogger.warn("[Core] control_robot_end_effector_pose failed, change robot arm ctrl mode failed!")
448
+ return False
449
+
450
+ if self._manipulation_mpc_ctrl_mode == KuavoManipulationMpcCtrlMode.NoControl:
451
+ SDKLogger.debug("[Core] control_robot_end_effector_pose, manipulation mpc ctrl mode is NoControl, change it.")
452
+ if not self.change_manipulation_mpc_ctrl_mode(KuavoManipulationMpcCtrlMode.ArmOnly):
453
+ SDKLogger.warn("[Core] control_robot_end_effector_pose failed, change manipulation mpc ctrl mode failed!")
454
+ return False
455
+
456
+ return self._control.control_robot_end_effector_pose(left_pose, right_pose, frame)
348
457
 
458
+ def change_manipulation_mpc_frame(self, frame: KuavoManipulationMpcFrame)->bool:
459
+ timeout = 1.0
460
+ count = 0
461
+ while self._rb_state.manipulation_mpc_frame != frame:
462
+ SDKLogger.debug(f"[Core] Change manipulation mpc frame from {self._rb_state.manipulation_mpc_frame} to {frame}, retry: {count}")
463
+ self._control.change_manipulation_mpc_frame(frame)
464
+ if self._rb_state.manipulation_mpc_frame == frame:
465
+ break
466
+ if timeout <= 0:
467
+ SDKLogger.warn("[Core] Change manipulation mpc frame timeout!")
468
+ return False
469
+ timeout -= 0.1
470
+ time.sleep(0.1)
471
+ count += 1
472
+ if not hasattr(self, '_manipulation_mpc_frame_lock'):
473
+ self._manipulation_mpc_frame_lock = threading.Lock()
474
+ with self._manipulation_mpc_frame_lock:
475
+ self._manipulation_mpc_frame = frame
476
+ return True
477
+
478
+ def change_manipulation_mpc_ctrl_mode(self, control_mode: KuavoManipulationMpcCtrlMode)->bool:
479
+ timeout = 1.0
480
+ count = 0
481
+ while self._rb_state.manipulation_mpc_ctrl_mode != control_mode:
482
+ SDKLogger.debug(f"[Core] Change manipulation mpc ctrl mode from {self._rb_state.manipulation_mpc_ctrl_mode} to {control_mode}, retry: {count}")
483
+ self._control.change_manipulation_mpc_ctrl_mode(control_mode)
484
+ if self._rb_state.manipulation_mpc_ctrl_mode == control_mode:
485
+ break
486
+ if timeout <= 0:
487
+ SDKLogger.warn("[Core] Change manipulation mpc ctrl mode timeout!")
488
+ return False
489
+ timeout -= 0.1
490
+ time.sleep(0.1)
491
+ count += 1
492
+ if not hasattr(self, '_manipulation_mpc_ctrl_mode_lock'):
493
+ self._manipulation_mpc_ctrl_mode_lock = threading.Lock()
494
+ with self._manipulation_mpc_ctrl_mode_lock:
495
+ self._manipulation_mpc_ctrl_mode = control_mode
496
+ return True
497
+
498
+ def change_manipulation_mpc_control_flow(self, control_flow: KuavoManipulationMpcControlFlow)->bool:
499
+ timeout = 1.0
500
+ count = 0
501
+ while self._rb_state.manipulation_mpc_control_flow != control_flow:
502
+ SDKLogger.debug(f"[Core] Change manipulation mpc control flow from {self._rb_state.manipulation_mpc_control_flow} to {control_flow}, retry: {count}")
503
+ self._control.change_manipulation_mpc_control_flow(control_flow)
504
+ if self._rb_state.manipulation_mpc_control_flow == control_flow:
505
+ break
506
+ if timeout <= 0:
507
+ SDKLogger.warn("[Core] Change manipulation mpc control flow timeout!")
508
+ return False
509
+ timeout -= 0.1
510
+ time.sleep(0.1)
511
+ count += 1
512
+ if not hasattr(self, '_manipulation_mpc_control_flow_lock'):
513
+ self._manipulation_mpc_control_flow_lock = threading.Lock()
514
+ with self._manipulation_mpc_control_flow_lock:
515
+ self._manipulation_mpc_control_flow = control_flow
516
+ return True
517
+
349
518
  def change_robot_arm_ctrl_mode(self, mode:KuavoArmCtrlMode)->bool:
350
519
  timeout = 1.0
351
520
  count = 0
@@ -375,12 +544,25 @@ class KuavoRobotCore:
375
544
  return
376
545
 
377
546
  # init_pos = [0.0]*14
378
- # if not self.control_robot_arm_target_poses([1.5], [init_pos]):
547
+ # if not self.control_robot_arm_joint_trajectory([1.5], [init_pos]):
379
548
  # SDKLogger.warn("[Core] robot arm reset failed, control robot arm traj failed!")
380
549
  # return False
381
550
 
382
551
  return self.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.AutoSwing)
383
552
 
553
+ def robot_manipulation_mpc_reset(self)->bool:
554
+ if self._manipulation_mpc_ctrl_mode != KuavoManipulationMpcCtrlMode.NoControl:
555
+ SDKLogger.debug("[Core] robot manipulation mpc reset, current manipulation mpc ctrl mode != NoControl, change it.")
556
+ if not self.change_manipulation_mpc_ctrl_mode(KuavoManipulationMpcCtrlMode.NoControl):
557
+ SDKLogger.warn("[Core] robot manipulation mpc reset failed, change manipulation mpc ctrl mode failed!")
558
+ return False
559
+ if self._manipulation_mpc_control_flow != KuavoManipulationMpcControlFlow.ThroughFullBodyMpc:
560
+ SDKLogger.debug("[Core] robot manipulation mpc reset, current manipulation mpc control flow != ThroughFullBodyMpc, change it.")
561
+ if not self.change_manipulation_mpc_control_flow(KuavoManipulationMpcControlFlow.ThroughFullBodyMpc):
562
+ SDKLogger.warn("[Core] robot manipulation mpc reset failed, change manipulation mpc control flow failed!")
563
+ return False
564
+ return True
565
+
384
566
  """ ------------------------------------------------------------------------"""
385
567
  """ Arm Forward kinematics && Arm Inverse kinematics """
386
568
  def arm_ik(self,
@@ -405,3 +587,28 @@ class KuavoRobotCore:
405
587
  SDKLogger.debug(f"[Core] Received gait change notification: {gait_name} at time {current_time}")
406
588
  # Call the transition method if it exists
407
589
  getattr(self, to_method)()
590
+
591
+
592
+ if __name__ == "__main__":
593
+ DEBUG_MODE = 0
594
+ core = KuavoRobotCore()
595
+ core.initialize()
596
+
597
+ if DEBUG_MODE == 0:
598
+ time.sleep(1.0)
599
+ core.change_robot_arm_ctrl_mode(KuavoArmCtrlMode.ExternalControl)
600
+ core.change_manipulation_mpc_frame(KuavoManipulationMpcFrame.VRFrame)
601
+ core.change_manipulation_mpc_ctrl_mode(KuavoManipulationMpcCtrlMode.ArmOnly)
602
+ core.change_manipulation_mpc_control_flow(KuavoManipulationMpcControlFlow.DirectToWbc)
603
+ core.robot_manipulation_mpc_reset()
604
+ elif DEBUG_MODE == 1:
605
+ core.to_stance()
606
+ print("state now is to_stance:", core.state)
607
+ core.control_command_pose_world(0.0, 1.0, 0.0, 1.57)
608
+ print("state now is control_command_pose_world:", core.state)
609
+ elif DEBUG_MODE == 2:
610
+ core.to_trot()
611
+ print("state now is to_trot:", core.state)
612
+ time.sleep(3.0)
613
+ core.to_stance()
614
+ print("state now is to_stance:", core.state)
@@ -1,15 +1,16 @@
1
1
  #!/usr/bin/env python3
2
2
  # coding: utf-8
3
-
4
3
  from kuavo_humanoid_sdk.common.logger import SDKLogger
5
4
  from kuavo_humanoid_sdk.kuavo.core.core import KuavoRobotCore
5
+ from kuavo_humanoid_sdk.common.websocket_kuavo_sdk import WebSocketKuavoSDK
6
+ import roslibpy
6
7
 
7
- try:
8
+ try:
8
9
  import rospy
9
10
  from std_msgs.msg import Bool
10
11
  from kuavo_humanoid_sdk.msg.kuavo_msgs.srv import playmusic, playmusicRequest
11
12
  from kuavo_humanoid_sdk.msg.kuavo_msgs.srv import SpeechSynthesis, SpeechSynthesisRequest
12
- except ImportError:
13
+ except:
13
14
  pass
14
15
 
15
16
  class Audio:
@@ -91,3 +92,85 @@ class Audio:
91
92
  except Exception as e:
92
93
  SDKLogger.error(f"[Robot Audio] Failed to play audio text: {str(e)}")
93
94
  return False
95
+
96
+ class AudioWebsocket:
97
+ """WebSocket-based audio system interface for controlling audio playback functionality of Kuavo humanoid robot.
98
+
99
+ Provides functionality to play music files through WebSocket connection.
100
+ """
101
+
102
+ def __init__(self):
103
+ """Initialize the WebSocket audio system."""
104
+ websocket = WebSocketKuavoSDK()
105
+ self._audio_stop_publisher = roslibpy.Topic(websocket.client, 'stop_music', 'std_msgs/Bool')
106
+ self._audio_stop_publisher.advertise()
107
+
108
+ def play_audio(self, file_name: str, volume: float = 0.5, speed: float = 1.0) -> bool:
109
+ """Play the specified audio file through WebSocket.
110
+
111
+ Args:
112
+ file_name (str): Name of the audio file to play
113
+ volume (float): Volume level (0.0 to 1.0)
114
+ speed (float): Playback speed
115
+
116
+ Returns:
117
+ bool: True if the play request was successfully sent, False otherwise
118
+ """
119
+ try:
120
+ websocket = WebSocketKuavoSDK()
121
+ service = roslibpy.Service(websocket.client, 'play_music', 'kuavo_msgs/playmusic')
122
+
123
+ volume = min(max(volume, 0), 1.0)
124
+ request = {
125
+ "music_number": file_name,
126
+ "volume": volume,
127
+ "speed": speed
128
+ }
129
+
130
+ response = service.call(request)
131
+ SDKLogger.info(f"[Robot Audio] Requested to play audio file: {file_name}")
132
+ return True
133
+ except Exception as e:
134
+ SDKLogger.error(f"[Robot Audio] Failed to play audio file: {str(e)}")
135
+ return False
136
+
137
+ def stop_audio(self) -> bool:
138
+ """Stop the currently playing audio through WebSocket."""
139
+ try:
140
+ msg = {
141
+ "data": True
142
+ }
143
+ self._audio_stop_publisher.publish(roslibpy.Message(msg))
144
+ SDKLogger.info("[Robot Audio] Requested to stop audio playback")
145
+ return True
146
+ except Exception as e:
147
+ SDKLogger.error(f"[Robot Audio] Failed to stop audio playback: {str(e)}")
148
+ return False
149
+
150
+ def text_to_speech(self, text: str, volume: float = 0.5) -> bool:
151
+ """Synthesize and play the specified text through WebSocket.
152
+
153
+ Args:
154
+ text (str): Text to be played
155
+ volume (float): Volume level (0.0 to 1.0)
156
+
157
+ Returns:
158
+ bool: True if the play request was successfully sent, False otherwise
159
+ """
160
+ try:
161
+ websocket = WebSocketKuavoSDK()
162
+ service = roslibpy.Service(websocket.client, 'speech_synthesis', 'kuavo_msgs/SpeechSynthesis')
163
+
164
+ request = {
165
+ "data": text,
166
+ "volume": volume
167
+ }
168
+
169
+ response = service.call(request)
170
+ SDKLogger.info(f"[Robot Audio] Requested to play audio text: {text}")
171
+ return True
172
+ except Exception as e:
173
+ SDKLogger.error(f"[Robot Audio] Failed to play audio text: {str(e)}")
174
+ return False
175
+
176
+