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#
Create on your computer a development workspace (that we will mount in the SDK Docker image):
mkdir ~/ws
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
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:
ID: An ID for your new app. Must be a valid ROS identifier without spaces or hyphens. For instace,
first_app
.App name: Full name of your application. For instance,
My First App
.Robot type: The target robot your application will be deployed on. For now, only PAL’s robots is available.
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 andcontent
page. These are located in thepages
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 thecontent
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 thelanding
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:
pal_deploy
your ROS package to the robotInstall 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.
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.
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.
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.
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.
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#
Follow Tutorial: Creating a simple multi-modal interaction to learn how to create a first custom application from scratch.