Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This brief guide introduces the main components and concepts of this API and provides some code samples that can be used as a starting point.

...

  1. The Python-SIC connector requires Python 3 (with tkinter enabled).

    1. Make sure Python can compile native extensions (e.g. for Windows see https://www.scivision.dev/python-windows-visual-c-14-required).

    2. You can use a Python editor of your choice (Pycharm for example).

  2. The Python skeleton project itself can be found in the ‘python’ folder at https://bitbucket.org/socialroboticshub/connectors. It is recommended to simply download this repository or even fork it directly (which you can do using the + button at the top-left of the page). Once you have your download or cloned fork, make sure the required dependencies are installed

    1. It is possible to install all of the dependencies by running the command: pip install -r requirements.txt in the folder.

Table of Contents

Abstract SIC Connector

Introduction

The first main component is the AbstractSICConnector class. It requires the IP address of the SIC with which it sets up the connection to the SIC and enables you to send action commands to the robot and receive data generated by either the robot or the SIC itself. The data

  • action commands

    • use the methods available in the SIC connector

    • allow you to create your application using the SIC framework

  • data generated

    • contains events (e.g.: when a button is pressed

...

    • LeftBumperPressed or when an action is finished

...

    • WakeUpDone)

...

    • the results of certain actions (e.g.: a

...

    • recognised intent after a speech recognition attempt

...

    • )

Input

  • IP address of the used SIC server (usually localhost)

Usage

The AbstractSICConnector class is abstract, meaning that it itself does not do anything with the incoming data. To process the incoming data you can implement your own concrete SIC Connector class by inheriting the AbstractSICConnector class and overriding the empty event handlers.

...

In the below MyConnector example, you see that it uses the AbstractSICConnectorclass as a parent, inheriting all its methods. Two things have been added:

...

  1. the on_robot_event method is overridden to print all the events generated by the robot.

  2. Secondly, a the run method is added that sends actions to the SIC.

Code Block
languagepy
from social_interaction_cloud.abstract_connector import AbstractSICConnector
from time import sleep


class MyConnector(AbstractSICConnector):
    def __init__(self, server_ip):
        super(MyConnector, self).__init__(server_ip)

    def run(self):
        self.start()
        self.set_language('en-US')
        sleep(1)  # wait for the language to change
        self.say('Hello, world!')
        sleep(3)  # wait for the robot to be done speaking (to see the relevant prints)
        self.stop()

    def on_robot_event(self, event):
        print(event)


# Run the application
my_connector = MyConnector(server_ip='127.0.0.1')
my_connector.run()

...

Note: These methods, as are all actions, are asynchronous. This means that they do not wait for a result before continuing. It also allows, if supported by the connected device(s), to execute actions in parallel (e.g. simultaneously speaking and gesturing).

You will find extensive documentation for each available action and on_* trigger on (put link)

Basic SIC Connector

Introduction

The Python API also provides its own concrete implementation of the AbstractSICConnector class, called the BasicSICConnector. It allows you to register callback functions for each action you send. Whenever

  • callback functions

    • called when the action is finished or a result becomes available

...

    • e.g.:

      • for device actions (e.g.: wake_up, say or set_eye_color),

...

      • the callback function is called only

...

      • once

      • for touch events (e.g. MiddleTactilTouched)

...

      • , the callback function is called every time the event becomes available

      • the result of vision operations (e.g. on_face_recognized(identifier))

...

      • , the callback function is called every time the result becomes available

...

Example

Code Block
languagepy
import threading
from social_interaction_cloud.basic_connector import BasicSICConnector
from time import sleep


class Example:

    def __init__(self, server_ip):
        self.sic = BasicSICConnector(server_ip)

        self.awake_lock = threading.Event()

    def run(self):
        # active Social Interaction Cloud connection
        self.sic.start()

        # set language to English
        self.sic.set_language('en-US')

        # stand up and wait until this action is done (whenever the callback function self.awake is called)
        self.sic.wake_up(self.awake)
        self.awake_lock.wait()  # see https://docs.python.org/3/library/threading.html#event-objects

        self.sic.say_animated('You can tickle me by touching my head.')
        # Execute that_tickles call each time the middle tactile is touched
        self.sic.subscribe_touch_listener('MiddleTactilTouched', self.that_tickles)

        # You have 10 seconds to tickle the robot
        sleep(10)
        
        # Unsubscribe the listener if you don't need it anymore.
        self.sic.unsubscribe_touch_listener('MiddleTactilTouched')

        # Go to rest mode
        self.sic.rest()

        # close the Social Interaction Cloud connection
        self.sic.stop()

    def awake(self):
        """Callback function for wake_up action. Called only once.
        It lifts the lock, making the program continue from self.awake_lock.wait()"""

        self.awake_lock.set()

    def that_tickles(self):
        """Callback function for touch listener. Everytime the MiddleTactilTouched event is generated, this
         callback function is called, making the robot say 'That tickles!'"""
        
        self.sic.say_animated('That tickles!')


example = Example('127.0.0.1')
example.run()

...

  1. A connected Nao robot will stand up, say saying “You can tickle me by touching my head”

    1. To wait until the Nao has finished standing up, the program is locked by the self.awake_lock.wait() statement.

      1. awake_lock is an threading.Event() object, that blocks the main thread until the threading.Event() is set by calling self.awake_lock.set(). This is done in the awake callback function. This callback function is added to the wake_up action.

    2. Once the robot is finished standing up, awake is called, and the “lock is lifted”, allowing the program to continue.

  2. For 10 seconds will say “that tickles” every time you touch the sensor on the middle of its head.

  3. After 10 seconds, the Nao will sit down again.

...

It requires a reference to a method of BasicSICConnector and the input arguments for that method. Optionally you can give it a callback function and a threading.Event() object as lock.

Note: that you have to explicitly state callback=... and lock=... The following snippet provides an example of how to do so:

Code Block
languagepy
sic = BasicSICConnector(server_ip)
hello_action_lock = threading.Event()

hello_action = Action(self.sic.say, 'Hello, Action!', callback=hello_action_callback,
                      lock=hello_action_lock)
hello_action.perform().wait()  # perform() returns the lock, so you can immediately call wait() on it.
hello_action.perform().wait()  # you can reuse an action.

def hello_action_callback():
    print('Hello Action Done')
    hello_action_lock.set()
    hello_action_lock.clear()  # necessary for reuse

...

  1. The build_action method returns a regular action. But instead of providing a reference to the BasicSICConnector method, you can use its name.

Code Block
languagepy
sic = BasicSICConnector(server_ip)
action_factory = ActionFactory(sic)
hello_action_factory = action_factory.build_action('say', 'Hello, Action Factory!')
hello_action_factory.peform()

...

  1. The build_waiting_action method returns a waiting action. The ActionFactory creates a lock with a callback function that releases the lock for you. Optionally you can also add your own callback function, that will be embedded in the callback function created by the ActionFactory.

Code Block
languagepy
wake_up_action = action_factory.build_waiting_action('wake_up', additional_callback=awake)
wake_up_action.perform().wait()

def awake():
  print('I am fully awake now')

  1. The build_touch_listener method lets you build a touch listener that is registered to the BasicSICConnector.

    • You can register to all the robot’s sensor events (e.g. MiddleTactilTouched).

    • You can use it to wait until a button is pressed or to do something when a button is pressed.

    • The listener can be notified only once or every time (until you unregister the listener) the button is pressed.

  2. The build_vision_listener method works similarly to the build_touch_listener method. It lets you build a listener for four specific vision events: on_person_detected, on_face_recognized, on_emotion_detected, and on_corona_check_passed.

    • For these events to be generated you need to turn on the People Detection, Face Recognition, Emotion Detection, and Corona Checker services respectively. To build a vision listener for these three events, you the label ‘people’, ‘face’, ‘emotion’ or ‘corona’ respectively for the vision_type parameter.

  3. Finally, the last component of the action package is the ActionRunner.

    • It allows you to run regular and waiting actions right away with run_action and run_waiting_action respectively.

    • It takes the same input as the ActionFactory methods because that is being called under the hood first.

    • ActionRunner also allows you to preload a set of actions, run them in parallel, and wait for all of the waiting actions (not regular actions) to finish before continuing. Use load_waiting_action and load_waiting_action to preload actions and run_loaded_actions to run them.

Code Block
languagepy
sic = BasicSICConnector(server_ip)
action_runner = ActionRunner(sic)
action_runner.run_waiting_action('say', 'Hello, Action Runner!',
                                 additional_callback=hello_action_runner_callback)
# run_waiting_action('say', ...) waits to finish talking before continuing

def hello_action_runner_callback():
  print('Hello Action Runner Done')

# The following actions will run in parallel.
action_runner.load_waiting_action('wake_up')
action_runner.load_waiting_action('set_eye_color', 'green')
action_runner.load_waiting_action('say', 'I am standing up now')
action_runner.run_loaded_actions()  # If you want to keep an action sequence for reuse, add clear=False as input.

action_runner.run_action('say', 'I will start sitting down as I say this. Because I am not a waiting action')
action_runner.run_action('set_eye_color', 'white')
action_runner.run_waiting_action('rest')