Versions Compared

Key

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

...

self.start() activates the connection. Under the hood a thread is started allowing the connector to receive actions and events. self.stop() gracefully closes the connection. self.set_language('en-US') and self.say('Hello, world!') are the two actions send to the robot to make it say ‘Hello, world!’ in English. 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).

The on_robot_event() method will print all incoming events, which are: LanguageChanged, TextStarted, and TextDone. If you, for example, touch a robot's head sensors (while the program is running), the events FrontTactilTouched, MiddleTactilTouched, and/or RearTactilTouchedwill also be printed.

The sleep statements avoid the program to stop before all the events are generated. See what happens when you remove the sleep statements. Most of the time you do not know how long you have to wait for an action to finish. Therefore, sleep statements are not the way to go. Ideally, you want the device to wait until it has received the necessary data and select it’s next action based on the available data.

This class contains extensive documentation for each available action and on_* trigger.

Basic SIC Connector

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 the action is finished or a result becomes available that callback function is called. For device actions, e.g. wake_up(), say() or set_eye_color(), a callback function is only called once. You can also register callback functions that listen to touch events (e.g. MiddleTactilTouched) or the result of vision operations (e.g. on_face_recognized(identifier)). These callback functions are called each time that event or result becomes available.

...

To wait until the Nao has finished standing up, the program is locked by the self.awake_lock.wait() statement. 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. Once the robot is finished standing up, awake() is called, and the “lock is lifted”, allowing the program to continue.

A different callback function is that_tickles(). It is subscribed to the MiddleTactilTouched event. Whenever the program is running, that_tickles() is called each time the middle head sensor is touched.

Action, ActionFactory, and ActionRunner

To help define the behaviors, the Python API offers the action packagesome additional facilities related to actions. An Action allows you to prepare an action and (re)use it when necessary. 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=… to do so.

...

To ActionFactory helps build actions and especially can save you the trouble of managing all the different locks you might need. The build_action() method return a regular action. But instead of providing a reference to the BasicSICConnector method, you can use its name.

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

The build_waiting_action() method return a waiting action. The ActionFactory creates a lock with 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')

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.

The build_vision_listener() method work similar to the build_touch_listener() method. It lets you build a listener for three 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, and Emotion Detection services (via the SIC web portal) and Corona Checker services respectively. To build a vision listener for these three events, you the label ‘people’, ‘face’, ‘emotion’ or ‘emotion’ ‘corona’ respectively for the vision_type parameter.

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')

...

This is where pytransitions come in. It is “a lightweight, object-oriented finite state machine implementation in Python with many extensions”. Read their guide to learn more about pytransitions. Let’s look at an example of how to use it together with the SIC python Python API. It starts with creating a model class for a robot that has states and link it to a state machine:

...

If we have an instantiation of the ExampleRobot class we can now call the start() method (trigger) to cause a transition from the initial asleep state (source) the the awake state (destination):

...

Before the robot becomes awake it needs to wake up (SIC method to let the robot stand up). We can add a before='some_method' to our add_transition() statement to trigger some_method() before the state transition happens:

...

Often there are no external triggers to trigger a state transition in the human-robot interaction flow. For example, when the robot is awake and ready it should automatically move to a next state. This can be achieved by adding an after='some_method' statement to the transition. In this case some_method() is not a dedicated method, but the trigger of the next transition. Using this approach you can create a whole chain of states, neatly separating each interaction step in different states and methods. It does not have to be a linear sequence. You can create branches and cycles, depending on the indented interaction flow.

...

For the complete state machine example see state_machine_example.py included in the API.