Tutorial: Building a first touchscreen interaction#
🏁 Goal of this tutorial
By the end of this tutorial, you will know how to integrate your interactive screen with the robot’s ROS framework, by publishing to ROS topics.
Pre-requisites#
You should first have completed ARI’s Touchscreen manager tutorial. More specifically, you must know how to upload your own custom HTML page.
Note
This tutorial guides you to create interactive HTML content for the touchscreen ‘from scratch’.
Follow instead Create an application with pal_app if you wish to start from a template.
How to publish in a ROS topic from a custom HTML page#
The goal is to display a question to the user and a list of buttons with possible answers. When the user presses a button, the selected answer is sent through a ROS topic.
Step 1: Start your HTML page.
Build your own custom HTML page, or download a simple example from
here
.
Step 2: Import the ROS library and initiliaze it.
Use rrlib.js
to access the robot’s topics. The library is already
installed in your robot in ~/.pal/www/webapps/pages/js/modules/
.
Import the library in your code as follows:
import * as RRLIB from '../../js/modules/rrlib.js'
In the sample code, you can find this line in the main.js
file.
To use the module, you need to initialize it:
let ros = new RRLIB.Ros({
host: 'http://' + window.location.hostname
});
Step 3: Link to the topic.
Create a variable that will link to a ROS topic you want to publish in. In this
example, we are using the user_input
topic:
let user_input_topic = new RRLIB.Topic({
ros: ros,
name: 'user_input'
});
Step 4: Publish to the topic.
Publish to the topic by passing a JSON object that follows the ROS message structure. In our example:
user_input.publish({
action: 'some action',
args: [{
key: 'some key',
value: 'a value'
}]
Step 5: Upload the .zip
file containing the HTML page you just created
(follow the steps in Custom HTML) and publish the page
(follow the steps in Publishing a page).
Step 6: Try it! Open a terminal, log into the robot and watch the output
of the user_input
topic:
ssh pal@ari-0c
rostopic echo /user_input
If you’re running the sample page in this tutorial, you should observe the following output in your terminal every time you press a button on the touchscreen:
pal@ari-0c:~$ rostopic echo /user_input
action: "Yes"
args:
-
key: ''
value: ''
---
That’s all!
Writing a simple interactive behavior#
Let’s write a simple script that reacts to user input. In this example, the robot will answer one way or another, based on the button pressed by the user.
Here is the code of the interaction script:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
from actionlib import SimpleActionClient
from pal_interaction_msgs.msg import TtsAction, TtsGoal, Input
from pal_web_msgs.msg import WebGoTo
class RobotBase(object):
def __init__(self):
rospy.loginfo("RobotBase::init node")
# Connect to the text-to-speech action server
self.tts = SimpleActionClient('/tts', TtsAction)
self.tts.wait_for_server()
rospy.loginfo("RobotBase::Connected to TTS action client")
# Create a publisher to publish web pages
self.web_publisher = rospy.Publisher(
"/web/go_to", WebGoTo, queue_size=10)
rospy.sleep(0.5) # allow some time to properly connect the publisher
rospy.loginfo("RobotBase::Publishing in: " +
self.web_publisher.resolved_name)
# Create a subscriber to the touch screen inputs
self.user_input_sub = rospy.Subscriber(
'/user_input', Input, self.user_input_touch_cb)
rospy.loginfo("RobotBase::Subscribed to " +
self.user_input_sub.resolved_name)
def publish_web_page(self, web_name):
msg = WebGoTo()
msg.type = WebGoTo.TOUCH_PAGE
msg.value = web_name
self.web_publisher.publish(msg)
def say(self, msg):
goal = TtsGoal()
goal.rawtext.text = msg
goal.rawtext.lang_id = "en_GB"
self.tts.send_goal_and_wait(goal)
def user_input_touch_cb(self, data):
""" Callback called when the user presses a button on the
touchscreen.
"""
if data.action:
data = str(data.action).lower()
rospy.loginfo("RobotBase::Button pressed: " + data)
if data == "yes":
self.say("Wonderful")
elif data == "no":
self.say("No problem! Maybe later")
self.publish_web_page('question_sample')
if __name__ == '__main__':
print("Starting robot demo...")
try:
rospy.init_node('first_interaction_demo')
robot = RobotBase()
robot.publish_web_page('question_sample')
rospy.spin()
except rospy.ROSInterruptException:
pass
Important
While this script is absolutely fine, we would normally recommend to use Intents to communicate between the touchscreen and an application controller.
Check the templates generated by the pal_app tool to see an example.
Let’s go through the code above:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import rospy
from actionlib import SimpleActionClient
from pal_interaction_msgs.msg import TtsAction, TtsGoal, Input
from pal_web_msgs.msg import WebGoTo
We need to import actionlib
to send actions to the robot. In our example,
the robot will provide a verbal answer through speech synthesis (text-to-speech,
or TTS). We thus need to import the TTS related messages TtsAction
and
TtsGoal
. To receive the user input based on the button pressed by the user
we need to import the Input
message. Finally, we will also need to publish
a web page on the screen. Thus, we need to import the WebGoTo
message.
class RobotBase(object):
def __init__(self):
rospy.loginfo("RobotBase::init node")
# Connect to the text-to-speech action server
self.tts = SimpleActionClient('/tts', TtsAction)
self.tts.wait_for_server()
rospy.loginfo("RobotBase::Connected to TTS action client")
# Create a publisher to publish web pages
self.web_publisher = rospy.Publisher(
"/web/go_to", WebGoTo, queue_size=10)
rospy.sleep(2) # allow some time to properly connect the publisher
rospy.loginfo("RobotBase::Publishing in: " +
self.web_publisher.resolved_name)
# Create a subscriber to the touch screen inputs
self.user_input_sub = rospy.Subscriber(
'/user_input', Input, self.user_input_touch_cb)
rospy.loginfo("RobotBase::Subscribed to " +
self.user_input_sub.resolved_name)
We define our class, where we start by creating the TTS client, to send the utterances we want the robot to reproduce.
Next, our node needs to publish the web pages we want to show on the touch screen. Finally, we subscribe to the touch screen input to get the user’s answers.
def publish_web_page(self, web_name):
msg = WebGoTo()
msg.type = WebGoTo.TOUCH_PAGE
msg.value = web_name
self.web_publisher.publish(msg)
This function publishes a message indicating the name of the custom HTML page (
web_name
) that we want to show.
def say(self, msg):
goal = TtsGoal()
goal.rawtext.text = msg
goal.rawtext.lang_id = "en_GB"
self.tts.send_goal_and_wait(goal)
This function sends the text
we want the robot to say. In this case, in
English.
def user_input_touch_cb(self, data):
if data.action:
data = str(data.action).lower()
rospy.loginfo("RobotBase::Button pressed: " + data)
if data == "yes":
self.say("Wonderful")
elif data == "no":
self.say("No problem! Maybe later")
self.publish_web_page('question_sample')
This callback function is called every time a new messages arrives through the
Input
topic, i.e. every time a user presses a button on the touch screen. In
this example, to simplify the code, we process the data received in the same
callback and act accordingly by verbally replying to the user. However, it is
good practice to leave any processing outside a callback function.
if __name__ == '__main__':
print("Starting robot demo...")
try:
rospy.init_node('first_interaction_demo')
robot = RobotBase()
robot.publish_web_page('question_sample')
rospy.spin()
except rospy.ROSInterruptException:
pass
In the main function, we start our node, publish the page we want to show and we are ready to start interacting with the robot!
Note
Remember to upload your custom HTML web page via the touchscreen manager before running your node. You can follow the steps in Publishing a page.
If you don’t know how to deploy your package in the robot, check the Tutorial: Deploying ROS packages on the robot page.