Commit 1bfa53e7 authored by Aleš Horák's avatar Aleš Horák
Browse files

update

parent 6c8f497f
# Come with me Dialog
Developed at [NLP Centre](https://nlp.fi.muni.cz/en), [FI MU](https://www.fi.muni.cz/index.html.en) for [Karel Pepper](https://nlp.fi.muni.cz/trac/pepper)
This is a dialog application, in both Czech and English, that enables the robot to come with a person hand in hand.
Start with "Pepper, come with me." / "Karle, pojď se mnou"
## Installation
* [make and install](https://nlp.fi.muni.cz/trac/pepper/wiki/InstallPkg) the package as usual for the Pepper robot
<?xml version="1.0" encoding="UTF-8" ?>
<Package name="dialog_coming" format_version="4">
<Manifest src="manifest.xml" />
<BehaviorDescriptions/>
<Dialogs>
<Dialog name="dlg_coming" src="dlg_coming/dlg_coming.dlg" />
</Dialogs>
<Resources>
<File name="icon" src="icon.png" />
<File name="dialog_coming_service" src="scripts/dialog_coming_service.py" />
<File name="__init__" src="scripts/stk/__init__.py" />
<File name="events" src="scripts/stk/events.py" />
<File name="logging" src="scripts/stk/logging.py" />
<File name="runner" src="scripts/stk/runner.py" />
<File name="services" src="scripts/stk/services.py" />
</Resources>
<Topics>
<Topic name="dlg_coming_czc" src="dlg_coming/dlg_coming_czc.top" topicName="dlg_coming" language="cs_CZ" />
<Topic name="dlg_coming_enu" src="dlg_coming/dlg_coming_enu.top" topicName="dlg_coming" language="en_US" />
</Topics>
<IgnoredPaths>
<Path src=".metadata" />
</IgnoredPaths>
</Package>
multilanguage:dlg_coming
enu:dlg_coming_enu.top
czc:dlg_coming_czc.top
topic: ~dlg_coming()
language: czc
concept:(call_robot_name) [Karle Pepře]
u:(e:FrontTactilTouched)
\pau=100\
u:(e:RearTactilTouched)
\pau=100\
u:(~call_robot_name [pojď poď] se mnou) Vem mě za ruku ^sCall(ALMotionComing.comeWithUser())
u:(~call_robot_name ["[pojď poď] ke mně" "sleduj mě" "následuj mě"]) Okej ^sCall(ALMotionComing.trackUser())
u:^private({[ještě pořád]} mě {[ještě pořád]} [sleduješ následuješ]) ^call(ALMotionComing.isTracking())
c1:(true) Ano
c1:(_*) Ne
u:^private([stop dost stačí]) ^call(ALMotionComing.isTracking())
c1:(true) Jasně ^sCall(ALMotionComing.stopTrackingUser())
c1:(_*) Okej
topic: ~dlg_coming()
language: enu
concept:(call_robot_name) [Karel Pepper]
u:(e:FrontTactilTouched)
\pau=100\
u:(e:RearTactilTouched)
\pau=100\
u:(~call_robot_name come with me) Catch my hand ^sCall(ALMotionComing.comeWithUser())
u:(~call_robot_name ["come to me" "follow me" "track me"]) Okay ^sCall(ALMotionComing.trackUser())
u:^private(are you {still} [following tracking] me) ^call(ALMotionComing.isTracking())
c1:(true) Yes
c1:(_*) No
u:^private([stop enough]) ^call(ALMotionComing.isTracking())
c1:(true) No problem ^sCall(ALMotionComing.stopTrackingUser())
c1:(_*) Okay
icon.png

28 KB

<package uuid="dialog_coming" version="0.1.92">
<names>
<name lang="en_US">dialog_coming</name>
<name lang="cs_CZ">dialog_coming</name>
</names>
<descriptions>
<description lang="en_US">Come with me dialog</description>
<description lang="cs_CZ">Come with me dialog</description>
</descriptions>
<supportedLanguages>
<language>en_US</language>
<language>cs_CZ</language>
</supportedLanguages>
<descriptionLanguages>
<language>en_US</language>
</descriptionLanguages>
<contents>
<behaviorContent />
<dialogContent topicName="dlg_coming" typeVersion="1.0">
<topic language="cs_CZ" path="dlg_coming/dlg_coming_czc.top" />
<topic language="en_US" path="dlg_coming/dlg_coming_enu.top" />
</dialogContent>
</contents>
<requirements>
<naoqiRequirement minVersion="2.3" />
<robotRequirement model="JULIETTE" />
</requirements>
<services>
<service autorun="true" execStart="/usr/bin/python2 scripts/dialog_coming_service.py" name="ALMotionComing" />
</services>
</package>
This diff is collapsed.
"""
STK - A collection of libraries useful for making apps with NAOqi.
"""
"""
stk.events.py
Provides misc. wrappers for ALMemory and Signals (using the same syntax for
handling both).
"""
__version__ = "0.1.1"
__copyright__ = "Copyright 2015, Aldebaran Robotics"
__author__ = 'ekroeger'
__email__ = 'ekroeger@aldebaran.com'
import qi
def on(*keys):
"""Decorator for connecting a callback to one or several events.
Usage:
class O:
@on("MyMemoryKey")
def my_callback(self,value):
print "I was called!", value
o = O()
events = EventsHelper()
events.connect_decorators(o)
After that, whenever MyMemoryKey is raised, o.my_callback will be called
with the value.
"""
def decorator(func):
func.__event_keys__ = keys
return func
return decorator
class EventHelper(object):
"Helper for ALMemory; takes care of event connections so you don't have to"
def __init__(self, session=None):
self.session = None
self.almemory = None
if session:
self.init(session)
self.handlers = {} # a handler is (subscriber, connections)
self.subscriber_names = {}
self.wait_value = None
self.wait_promise = None
def init(self, session):
"Sets the NAOqi session, if it wasn't passed to the constructor"
self.session = session
self.almemory = session.service("ALMemory")
def connect_decorators(self, obj):
"Connects all decorated methods of target object."
for membername in dir(obj):
member = getattr(obj, membername)
if hasattr(member, "__event_keys__"):
for event in member.__event_keys__:
self.connect(event, member)
def connect(self, event, callback):
"""Connects an ALMemory event or signal to a callback.
Note that some events trigger side effects in services when someone
subscribes to them (such as WordRecognized). Those will *not* be
triggered by this function, for those, use .subscribe().
"""
if event not in self.handlers:
if "." in event:
# if we have more than one ".":
service_name, signal_name = event.split(".")
service = self.session.service(service_name)
self.handlers[event] = (getattr(service, signal_name), [])
else:
# It's a "normal" ALMemory event.
self.handlers[event] = (
self.almemory.subscriber(event).signal, [])
signal, connections = self.handlers[event]
connection_id = signal.connect(callback)
connections.append(connection_id)
return connection_id
def subscribe(self, event, attachedname, callback):
"""Subscribes to an ALMemory event so as to notify providers.
This is necessary for things like WordRecognized."""
connection_id = self.connect(event, callback)
dummyname = "on_" + event.replace("/", "")
self.almemory.subscribeToEvent(event, attachedname, dummyname)
self.subscriber_names[event] = attachedname
return connection_id
def disconnect(self, event, connection_id=None):
"Disconnects a connection, or all if no connection is specified."
if event in self.handlers:
signal, connections = self.handlers[event]
if connection_id:
if connection_id in connections:
signal.disconnect(connection_id)
connections.remove(connection_id)
else:
# Didn't specify a connection ID: remove all
for connection_id in connections:
signal.disconnect(connection_id)
del connections[:]
if event in self.subscriber_names:
name = self.subscriber_names[event]
self.almemory.unsubscribeToEvent(event, name)
del self.subscriber_names[event]
def clear(self):
"Disconnect all connections"
for event in list(self.handlers):
self.disconnect(event)
def get(self, key):
"Gets ALMemory value."
return self.almemory.getData(key)
def get_int(self, key):
"Gets ALMemory value, cast as int."
try:
return int(self.get(key))
except RuntimeError:
# Key doesn't exist
return 0
except ValueError:
# Key exists, but can't be parsed to int
return 0
def set(self, key, value):
"Sets value of ALMemory key."
return self.almemory.raiseEvent(key, value)
def remove(self, key):
"Remove key from ALMemory."
try:
self.almemory.removeData(key)
except RuntimeError:
pass
def _on_wait_event(self, value):
"Internal - callback for an event."
if self.wait_promise:
self.wait_promise.setValue(value)
self.wait_promise = None
def _on_wait_signal(self, *args):
"Internal - callback for a signal."
if self.wait_promise:
self.wait_promise.setValue(args)
self.wait_promise = None
def cancel_wait(self):
"Cancel the current wait (raises an exception in the waiting thread)"
if self.wait_promise:
self.wait_promise.setCanceled()
self.wait_promise = None
def wait_for(self, event, subscribe=False):
"""Block until a certain event is raised, and returns it's value.
If you pass subscribe=True, ALMemory.subscribeToEvent will be called
(sometimes necessary for side effects, i.e. WordRecognized).
This will block a thread so you should avoid doing this too often!
"""
if self.wait_promise:
# there was already a wait in progress, cancel it!
self.wait_promise.setCanceled()
self.wait_promise = qi.Promise()
if subscribe:
connection_id = self.subscribe(event, "EVENTHELPER",
self._on_wait_event)
elif "." in event: # it's a signal
connection_id = self.connect(event, self._on_wait_signal)
else:
connection_id = self.connect(event, self._on_wait_event)
try:
result = self.wait_promise.future().value()
finally:
self.disconnect(event, connection_id)
return result
"""
stk.logging.py
Utility library for logging with qi.
"""
__version__ = "0.1.2"
__copyright__ = "Copyright 2015, Aldebaran Robotics"
__author__ = 'ekroeger'
__email__ = 'ekroeger@aldebaran.com'
import functools
import traceback
import qi
def get_logger(session, app_id):
"""Returns a qi logger object."""
logger = qi.logging.Logger(app_id)
try:
qicore = qi.module("qicore")
log_manager = session.service("LogManager")
provider = qicore.createObject("LogProvider", log_manager)
log_manager.addProvider(provider)
except RuntimeError:
# no qicore, we're not running on a robot, it doesn't matter
pass
except AttributeError:
# old version of NAOqi - logging will probably not work.
pass
return logger
def log_exceptions(func):
"""Catches all exceptions in decorated method, and prints them.
Attached function must be on an object with a "logger" member.
"""
@functools.wraps(func)
def wrapped(self, *args):
try:
return func(self, *args)
except Exception as exc:
self.logger.error(traceback.format_exc())
raise exc
return wrapped
def log_exceptions_and_return(default_value):
"""If an exception occurs, print it and return default_value.
Attached function must be on an object with a "logger" member.
"""
def decorator(func):
@functools.wraps(func)
def wrapped(self, *args):
try:
return func(self, *args)
except Exception:
self.logger.error(traceback.format_exc())
return default_value
return wrapped
return decorator
"""
stk.runner.py
A helper library for making simple standalone python scripts as apps.
Wraps some NAOqi and system stuff, you could do all this by directly using the
Python SDK, these helper functions just isolate some frequently used/hairy
bits so you don't have them mixed in your logic.
"""
__version__ = "0.1.3"
__copyright__ = "Copyright 2015, Aldebaran Robotics"
__author__ = 'ekroeger'
__email__ = 'ekroeger@aldebaran.com'
import sys
import qi
from distutils.version import LooseVersion
#
# Helpers for making sure we have a robot to connect to
#
def check_commandline_args(description):
"Checks whether command-line parameters are enough"
import argparse
parser = argparse.ArgumentParser(description=description)
parser.add_argument('--qi-url', help='connect to specific NAOqi instance')
args = parser.parse_args()
return args
def is_on_robot():
"Returns whether this is being executed on an Aldebaran robot."
import platform
return "aldebaran" in platform.platform()
def get_debug_robot():
"Returns IP address of debug robot, complaining if not found"
try:
import qiq.config
qiqrobot = qiq.config.defaultHost()
if qiqrobot:
robot = raw_input(
"connect to which robot? (default is {0}) ".format(qiqrobot))
if robot:
return robot
else:
return qiqrobot
else:
print "qiq found, but it has no default robot configured."
except ImportError:
# qiq not installed
print "qiq not installed (you can use it to set a default robot)."
return raw_input("connect to which robot? ")
def init(qi_url=None):
"Returns a QiApplication object, possibly with interactive input."
if qi_url:
sys.argv.extend(["--qi-url", qi_url])
else:
args = check_commandline_args('Run the app.')
if bool(args.qi_url):
qi_url = args.qi_url
elif not is_on_robot():
print "no --qi-url parameter given; interactively getting debug robot."
debug_robot = get_debug_robot()
if debug_robot:
sys.argv.extend(["--qi-url", debug_robot])
qi_url = debug_robot
else:
raise RuntimeError("No robot, not running.")
qiapp = None
sys.argv[0] = str(sys.argv[0])
# In versions bellow 2.3, look for --qi-url in the arguemnts and call accordingly the Application
if qi_url and LooseVersion(qi.__version__) < LooseVersion("2.3"):
position = 0
qiapp = qi.Application(url="tcp://"+qi_url+":9559")
# In versions greater than 2.3 the ip can simply be passed through argv[0]
else:
# In some environments sys.argv[0] has unicode, which qi rejects
qiapp = qi.Application()
qiapp.start()
return qiapp
# Main runner
def run_activity(activity_class, service_name=None):
"""Instantiate the given class, and runs it.
The given class must take a qiapplication object as parameter, and may also
have on_start and on_stop methods, that will be called before and after
running it."""
qiapp = init()
activity = activity_class(qiapp)
service_id = None
try:
# if it's a service, register it
if service_name:
# Note: this will fail if there is already a service. Unregistering
# it would not be a good practice, because it's process would still
# be running.
service_id = qiapp.session.registerService(service_name, activity)
if hasattr(activity, "on_start"):
def handle_on_start_done(on_start_future):
"Custom callback, for checking errors"
if on_start_future.hasError():
try:
msg = "Error in on_start(), stopping application: %s" \
% on_start_future.error()
if hasattr(activity, "logger"):
activity.logger.error(msg)
else:
print msg
finally:
qiapp.stop()
qi.async(activity.on_start).addCallback(handle_on_start_done)
# Run the QiApplication, which runs until someone calls qiapp.stop()
qiapp.run()
finally:
# Cleanup
if hasattr(activity, "on_stop"):
# We need a qi.async call so that if the class is single threaded,
# it will wait for callbacks to be finished.
qi.async(activity.on_stop).wait()
if service_id:
qiapp.session.unregisterService(service_id)
def run_service(service_class, service_name=None):
"""Instantiate the given class, and registers it as a NAOqi service.
The given class must take a qiapplication object as parameter, and may also
have on_start and on_stop methods, that will be called before and after
running it.
If the service_name parameter is not given, the classes' name will be used.
"""
if not service_name:
service_name = service_class.__name__
run_activity(service_class, service_name)
"""
stk.services.py
Syntactic sugar for accessing NAOqi services.
"""
__version__ = "0.1.2"
__copyright__ = "Copyright 2015, Aldebaran Robotics"
__author__ = 'ekroeger'
__email__ = 'ekroeger@aldebaran.com'
class ServiceCache(object):
"A helper for accessing NAOqi services."
def __init__(self, session=None):
self.session = None
self.services = {}
if session:
self.init(session)
def init(self, session):
"Sets the session object, if it wasn't passed to constructor."
self.session = session
def reset_service(self, servicename):
try:
del self.services[servicename]
except:
pass
def __getattr__(self, servicename):
"We overload this so (instance).ALMotion returns the service, or None."
if (not servicename in self.services) or (
servicename == "ALTabletService"):
# ugly hack: never cache ALtabletService, always ask for a new one
if servicename.startswith("__"):
# Behave like a normal python object for those
raise AttributeError
try:
self.services[servicename] = self.session.service(servicename)
except RuntimeError: # Cannot find service
self.services[servicename] = None
return self.services[servicename]
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment