../_images/tiago-icon.png ../_images/tiagopro-icon.png ../_images/ari-icon.png

Create an application with pal_app#

pal_app is an utility that quickly creates an application template, ready for deployment on the robot.

Important

As of pal-sdk-23.12, you must run pal_app from inside an SDK Docker image to be able to develop an app.

See Developing with the SDK Docker image and ROS if you do not yet have your SDK Docker image set up.

Create an application skeleton#

  1. Create on your computer a development workspace (that we will mount in the SDK Docker image):

mkdir ~/ws
  1. Start the SDK Docker image (replacing customer_id by your own PAL-provided customer ID):

docker run -it -v $HOME/ws:/home/pal/ws registry.gitlab.com/pal-robotics/customer_id/dockers:latest bash
  1. Inside the container, create a new app skeleton:

cd ws
mkdir src && cd src
pal_app create

Follow the instructions on screen. You will be asked to introduce:

  1. ID: An ID for your new app. Must be a valid ROS identifier without spaces or hyphens. For instace, first_app.

  2. App name: Full name of your application. For instance, My First App.

  3. Robot type: The target robot your application will be deployed on. For now, only PAL’s robots is available.

  4. Application controller type: The type of controller for you application. For now, only Python script is available.

Note

Since we are creating files in a shared (mounted) directory, the files are accessible (and can be freely edited/modified) from the host machine, and will not be lost when you close the Docker session.

The script generates the following file structure:

 1first_app
 2├── pages
 3│   ├── en_GB
 4│   │   ├── landing_page
 5│   │   │   ├── js
 6│   │   │   │   ├── main.js
 7│   │   │   ├── style
 8│   │   │   │   └── style.css
 9│   │   │   └── index.html
10│   │   └── question_sample_page
11│   │       └── ...
12│   ├── README.md
13│   └── zip_pages.sh
14├── res
15│   └── data.yaml
16├── scripts
17│   └── run_app
18├── src
19│   └── first_app
20│       ├── application_controller.py
21│       └── __init__.py
22├── CMakeLists.txt
23├── package.xml
24├── README.md
25└── setup.py

The pal_app generates a complete simple application as an example to start working on. It works as follows:

  • You’ll be able to access two web pages: landing page and content page. These are located in the pages folder.

  • The supervisor, aplicatoin_controller.py manages the pages to be displayed based on the touch input on the touchscreen. At the landing page, you will be able to START the interaction, which will trigger the content page. This latter displays a question, and based on the user response, the robot would do one action or another (not implemented since the goal is just to provide an skeleton of the code for the developer to further implement as desired).

  • The EXIT button in the content page takes you back to the landing page.

Let’s deploy it onto your robot.

Deploy the application on the robot#

To install your application on the robot, you need to follow the following two steps:

  1. pal_deploy your ROS package to the robot

  2. Install the touchscreen content (if you are using any)

Deploying the ROS package#

  • from inside your SDK Docker image, go to your development workspace:

cd ~/ws
  • then run:

rosrun pal_deploy deploy.py --package first_app ari-0c

(replace ari-0c by your actual robot’s hostname)

With the code deployed, you can now ssh onto the robot (ssh pal@ari-0c, password pal).

Install the touchscreen content#

The pages/ subfolder contains the web content meant to be displayed on the robot’s touchscreen. You will find one subfolder per language, and further subfolders for each touchscreen page.

Note

Your application controller can easily change the active (displayed) page via the ROS topic /web/go_to.

However, the pages must first be published on the robot, via the robot’s ARI’s Touchscreen manager.

Installing the pages on the robot#

You first need to create a zip archive for each of your pages. You can either manually zip the content of each subfolders, or use the provided zip_pages.sh script:

cd pages
sh zip_pages.sh

Each of the created zip file must then be uploaded to the robot’s Touchscreen manager, following the instructions to upload Custom HTML.

Updating web content

If you modify one of the pages, you must re-zip the corresponding folder, and re-upload it to the Touchscreen manager.

Run your application#

ssh onto the robot (ssh pal@ari-0c, password pal), and start your application:

rosrun first_app run_app

You should see a slowly-increasing counter and… not much else! You can press the buttons in the screens and navigate from one the other.

Background behaviours and event-driven programming#

We will see in a minute how to customize this default behaviour. We can however already see that applications can have background behaviours (like the counter).

They can also react to events by registering callbacks on specific ROS topics. The most important topic is the /intents topic. When the robot receives a command (or autonomously decides it must do something), it will normally be published as an intent (see below) on the /intents topic.

Our application can already react to some intents. Open another terminal on the robot, and type:

rostopic pub /intents hri_actions_msgs/Intent "{intent: '__intent_engage_with__'}"

By doing so you manually trigger the intent ENGAGE_WITH, and you should hear the robot say: “Hello! Nice to meet you!” while bowing.

Note

If you are not running the code directly on the robot, you can also type rostopic echo /tts/goal to display the text that is sent to the text-to-speech engine.

Hint

If you want to know more about the main concepts and components required to build an application, head now to Introduction to robot app programming.

Implement your application logic#

Let see how to customize the default application skeleton.

Intents are usually generated by your robot’s users. For instance, an intent might be generated:

  • through automatic perception (eg, someone approaches and seems to interact),

  • through verbal interaction (eg, someone tells the robot to go somewhere)

  • through interactions with the touchscreen (eg, someone presses a button to trigger a behaviour).

Your application template already contains examples of each of these possible interactions.

Let’s go through the generate application controller template. Open first_app/src/first_app/application_controller.py in your favourite text editor (outside or inside of the Docker image), and read on.

Code generated explanation:#

The Application Controller class begins with the __init__() function.

application_controller.py#
13def __init__(self) -> None:
14
15    rospy.loginfo("[APPLICATION test] initialising...")
16
17
18    # this member variable will be set to 'True' if the application manager
19    # or the user request the application to stop.
20    self.cancellation_requested = False
21
22    #######################################
23    #
24    # TODO: Add here any initialization steps
25    # that should occurs only once.
26    #

This function initializes the class and performs any necessary one-time initialization steps. Currently, it sets the self.cancellation_requested member variable to False.

The next function is on_intent(), which is responsible for handling each published intent. It gets called whenever an intent is published. Inside this function, the first action is to print the received intent using rospy.loginfo(). After that, you can define how your application should react to the received user intent.

application_controller.py#
24def on_intent(self, msg):
25
26    rospy.loginfo("[APPLICATION test] Received an intent: %s" % msg.intent)
27
28    #######################################
29    #
30    # TODO: Define here how your application
31    # should react when receiving an user
32    # intent.

Following the log message, the code extracts and saves the data provided by the intent, if it exists.

application_controller.py#
34data = json.loads(msg.data) if msg.data else {}
35source = msg.source
36modality = msg.modality
37confidence = msg.confidence
38priority_hint = msg.priority

After printing the intent and saving all the information, the next step is the management of the specific intents.

After processing the intent information, the code proceeds to handle specific intents.

The ENGAGE_WITH intent and the PRESENT_CONTENT are made as an example of what you can do.

application_controller.py#
48if msg.intent == Intent.ENGAGE_WITH:
49    # the 'data' dictionary should contain at least the following keys:
50    # - recipient
51
52    # As an example, we call here the TTS and play_motion action server
53    # to implement a very simple behaviour when someone engages with the
54    # robot.
55    import actionlib
56    from pal_interaction_msgs.msg import TtsAction, TtsGoal
57    from play_motion_msgs.msg import PlayMotionAction, PlayMotionGoal
58
59    tts = actionlib.SimpleActionClient('tts', TtsAction)
60    play_motion = actionlib.SimpleActionClient('play_motion', PlayMotionAction)
61
62    tts_goal = TtsGoal()
63    tts_goal.rawtext.text = "Hello, I'm ARI. Nice to meet you!"
64    tts_goal.rawtext.lang_id = "en_GB"
65
66    tts.send_goal_and_wait(tts_goal)
67
68    motion_goal = PlayMotionGoal(motion_name="bow")
69
70    play_motion.send_goal_and_wait(motion_goal)

The code starts handling the ENGAGE_WITH intent. It imports necessary modules and creates action clients for the TTS and play_motion actions. This serves as a simple example of how an intent can be utilized. You can customize this example to define the behavior you want for the ENGAGE_WITH intent.

Similarly, the code snippet starting from line 117 deals with the PRESENT_CONTENT intent. It imports the necessary module and creates a publisher to the /web/go_to topic. The code sets the appropriate parameters in the WebGoTo message and publishes it, causing the robot to display the desired web page.

application_controller.py#
117elif msg.intent == Intent.PRESENT_CONTENT:
118    # the 'data' dictionary should contain at least the following keys:
119    # - object (the content identifier)
120
121    # handle intents to present specific content by simply
122    # loading the corresponding page onto the touchscreen
123    from pal_web_msgs.msg import WebGoTo
124
125    web_goto_pub = rospy.Publisher('/web/go_to', WebGoTo, queue_size=1)
126
127    rospy.loginfo("Displaying page %s on the touchscreen..." % data["object"])
128    msg = WebGoTo()
129    msg.type = WebGoTo.TOUCH_PAGE
130    msg.value = data["object"]
131    web_goto_pub.publish(msg)

These code snippets provide examples of how to handle specific intents, but you can modify them according to your application’s requirements and desired behavior for each intent.

Configure for automatic launch at start-up#

You can configure your app to automatically start when the robot is turned on. See Configure an application to launch at start-up for the details.

Next steps#