Voice Output

Introduction

The Kachaka robot is equipped with voice output functionality, enabling voice communication in various situations. This lesson teaches how to make the Kachaka robot speak using ROS2.

Learning Objectives

By the end of this lesson, you will be able to:

  • Understand how to use ROS2 actions for robot control
  • Implement voice output functionality using Kachaka’s action server
  • Create nodes that send commands to the robot
  • Handle action client callbacks and responses

Voice Output Functionality of Kachaka

The Kachaka robot can control voice output through an action server. Using this function enables the following uses:

  • Robot status notifications
  • Operation instructions to users
  • Error and warning notifications
  • Interactive communication

Code Explanation

Full Code

The following is the code for speak.py:

speak.py

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import rclpy
from kachaka_interfaces.action import ExecKachakaCommand
from kachaka_interfaces.msg import KachakaCommand
from rclpy.action import ActionClient
from rclpy.node import Node


class Speak(Node):
    """
    Node for making Kachaka robot speak
    """
    def __init__(self) -> None:
        """
        Node initialization
        - Set up action client
        - Wait for server
        """
        super().__init__("speak")
        self._action_client = ActionClient(
            self, ExecKachakaCommand, "/kachaka/kachaka_command/execute"
        )
        self._action_client.wait_for_server()

    def send_goal(self):
        """
        Send speech command
        Returns:
            Future: Future object representing the action execution result
        """
        # Create a KachakaCommand message
        # This message specifies what command to execute
        command = KachakaCommand()
        # Set the command type to SPEAK_COMMAND (tells the robot to speak)
        command.command_type = KachakaCommand.SPEAK_COMMAND
        # Set the text that the robot should speak
        command.speak_command_text = "Hello, I am Kachaka"

        # Create a goal message for the action
        # Goals are what you send to action servers to request an action
        goal_msg = ExecKachakaCommand.Goal()
        # Attach the command to the goal
        goal_msg.kachaka_command = command

        # Send the goal asynchronously (non-blocking)
        # This returns a Future object that will be completed when the server responds
        # The action will execute on the robot, and we can check the result later
        return self._action_client.send_goal_async(goal_msg)


def main(args=None):
    """
    Main function
    - Initialize ROS2
    - Create and run node
    - Release resources
    """
    # Initialize ROS2
    rclpy.init(args=args)

    # Create node
    speak = Speak()
    
    # Send speech command to the robot
    # This returns a Future object representing the action execution
    future = speak.send_goal()
    
    # Wait for the action to complete
    # spin_until_future_complete processes ROS callbacks until the future is done
    # This blocks until the robot finishes speaking (or the action fails)
    rclpy.spin_until_future_complete(speak, future)

    # Destroy node
    speak.destroy_node()
    
    # Shutdown ROS2
    rclpy.shutdown()


if __name__ == "__main__":
    main()

Required Imports

import rclpy
from kachaka_interfaces.action import ExecKachakaCommand
from kachaka_interfaces.msg import KachakaCommand
from rclpy.action import ActionClient
from rclpy.node import Node

Explanation of each import:

  • rclpy: ROS2 Python client library. Provides basic ROS2 functionality.
  • ExecKachakaCommand: Action interface for executing Kachaka commands. Can execute various commands including voice output.
  • KachakaCommand: Kachaka command message type. Defines command types and parameters.
  • ActionClient: ROS2 action client. Provides functionality to communicate with action servers.
  • Node: Base class for ROS2 nodes. All ROS2 nodes inherit from this class.

Implementation of Speak Class

class Speak(Node):
    def __init__(self) -> None:
        super().__init__("speak")
        self._action_client = ActionClient(
            self, ExecKachakaCommand, "/kachaka/kachaka_command/execute"
        )
        self._action_client.wait_for_server()

About class initialization:

  1. super().__init__("speak"): Initialize with node name “speak”.
  2. ActionClient initialization:
    • 1st argument: Node instance (self)
    • 2nd argument: Action type (ExecKachakaCommand)
    • 3rd argument: Action server name ("/kachaka/kachaka_command/execute")
  3. wait_for_server(): Wait until the action server becomes available.

Sending Voice Commands

def send_goal(self):
    command = KachakaCommand()
    command.command_type = KachakaCommand.SPEAK_COMMAND
    command.speak_command_text = "Hello, I am Kachaka"
    goal_msg = ExecKachakaCommand.Goal()
    goal_msg.kachaka_command = command
    return self._action_client.send_goal_async(goal_msg)

Steps for sending commands:

  1. Create KachakaCommand message
  2. Set command type to SPEAK_COMMAND
  3. Set text to speak
  4. Create goal message
  5. Send goal asynchronously

Main Function

def main(args=None):
    rclpy.init(args=args)
    speak = Speak()
    future = speak.send_goal()
    rclpy.spin_until_future_complete(speak, future)
    speak.destroy_node()
    rclpy.shutdown()

Flow of main function processing:

  1. rclpy.init(args=args): Initialize ROS2
  2. speak = Speak(): Create instance of Speak node
  3. future = speak.send_goal(): Send voice command
  4. rclpy.spin_until_future_complete(speak, future): Wait for command completion
  5. speak.destroy_node(): Destroy node
  6. rclpy.shutdown(): Shutdown ROS2

Usage

Prerequisites

  • Kachaka robot is running
  • ROS2 environment is correctly configured
  • kachaka_speak package is built

Execution Steps

  1. Run the node:
ros2 run kachaka_speak speak

Exercises

  1. Make it speak different messages

    • Change the content of speak_command_text to make it speak different messages.
  2. Make it speak multiple messages in sequence

    • Add functionality to manage messages in an array and make it speak them in order.
  3. Check command results

    • Process the return value of send_goal_async to check command execution results.

Notes

  • If the Kachaka robot is not running, it cannot connect to the action server
  • It may take time to start as it waits for the action server to become available
  • By default, it speaks the message “Hello, I am Kachaka”