welcome: please sign in
location: oro-server-bindings

oro-server/oro-server.png

Interfacing with ORO

ORO provides an extended set of interfaces to ease its integration with other components.

The main communication channel with oro-server is a simple socket interface. The protocole is very simple, and detailled below.

However several bridges and wrapper have been developped for convenience. Two categories exist: bindings for specific languages and modules for some popular robotics middleware or integration framework.

If needed, new ones could be easily added (you just need to be able to access sockets, and basically every language can do that).

Summary

Raw sockets, debugging with Telnet

The underlying socket protocol is ASCII-based (ie, you can connect to the server with telnet to test everything). The examples, below, reproduce inputs on a Telnet session.

Requests

The general structure of a request to the server is:

 method_name ↵
 [parameter1] ↵
 [parameter2] ↵
 [...] ↵
 #end# ↵

parameters can be either:

Please note that collections of collections are not supported.

Responses

The server response has this structure if the request succeeded:

 ok
 [return_value]
 #end#

And this structure in case of failure:

 error
 [name of the exception, if available]
 [human-readable error message - that *may* spend over several lines]
 #end#

Some examples

You can test this example by directly connecting to the server with a tool like telnet.

Retrieving a human-friendly list of available methods on the server

 > help

Retrieving a machine-friendly list of available methods on the server

 > listMethods
 > #end#

Adding facts to the knowledge base

 > add
 > [human rdf:type Human, human rdfs:label "Ramses", myself sees Ramses]
 > #end#

Retrieving facts

This command should return the list of humans the robot currently sees.

 > find
 > [?humans rdf:type Human, myself sees ?humans]
 > #end#

More complete example

This Telnet recorded session demonstrates a more complete interaction with the server. It exhibits as well some of the inference abilities (it assumes the server has been started with the common_sense.oro.owl ontology).

add
[kitchen_table rdf:type Table, fork1 rdf:type Fork, glass1 rdf:type Glass] //we add some objects
#end#
ok

#end#


add
[fork1 isOn kitchen_table] //we assert that the object "fork1" is on the table
#end#
ok

#end#


find
o
[?o isAt kitchen_table] //we try to retrieve the object co-located with the table

#end#
ok
["fork1","kitchen_table"] //the server infers that "isOn" implies "isAt" and returns, as expected, the fork
#end#


registerEvent
NEW_INSTANCE
ON_TRUE
o
[?o isAt kitchen_table, ?o rdf:type Artifact] //we register an event that should be triggered when some artifact "isAt" the table
#end#
ok
78445b39-5cac-49a2-a970-623bbcb09c27 //the server register the event and returns and unique identifier for it
#end# //note that no event has been yet triggered. That's because the server doesn't know that a Fork is an Artifact


add
[Fork rdfs:subClassOf Tableware,Glass rdfs:subClassOf Tableware] //we now add that forks and glasses are tableware (and the server already knows from the common sens ontology that tableware are kind of artifacts)
#end#
ok

#end#
event
78445b39-5cac-49a2-a970-623bbcb09c27 //...so the event is now triggered
["fork1"]
#end#


add
[glass1 isOn kitchen_table]
#end#
ok

#end#
event
78445b39-5cac-49a2-a970-623bbcb09c27 //and if you add as well a glass on the table, a new event will also be triggered
["glass1"]
#end#

C++ bindings

C++ bindings for ORO can be found in the liboro library.

Installation

Via robotpkg

The supported way to install liboro is through robotpkg:

Robotpkg is a package management system for robotics module we use at the LAAS. It handles dependencies and compilation automatically. If you don't know it, have a look here.

> cd $ROBOTPKG_BASE/knowledge/liboro
> make update

From the sources

You can grab a snapshot of the sources on the public FTP: ftp://softs.laas.fr/pub/openrobots/liboro/

Or, to get the latest version of liboro, you can check-out the sources with GIT:

> git clone git://trac.laas.fr/robots/liboro

liboro uses CMake (>= 2.6) to compile, and depends only on boost (>= 1.34).

You can compile it with:

> cd liboro
> cmake .
> make
> make install

API coverage

Usage & library reference

The liboro reference is available as Doxygen comments in the source code. It is available on-line as well.

Some of methods are available as a high-level object-oriented abstraction. For instance:

   1 //Registers an event on a class:
   2 EventCallback ec;
   3 Classes::Human.onNewInstance(ec);
   4 
   5 //Creates a new object ("object" in the ontology meaning):
   6 Object table = Object::create(Classes::Table);
   7 table.assertThat(Properties::isIn, livingRoom);
   8 
   9 //Binds concepts:
  10 myself.sees(table);

Complete example

This example can be found in the source code: $PREFIX/oro-apps-src/oro_test.cpp.

   1 #include <iostream>
   2 #include <iterator>
   3 #include <set>
   4 
   5 #include "liboro/oro.h"
   6 #include "liboro/oro_library.h"
   7 #include "liboro/socket_connector.h"
   8 
   9 using namespace std;
  10 using namespace oro;
  11 
  12 class EventCallback : public OroEventObserver {
  13         void operator()(const OroEvent& evt) {
  14                         cout << "Event triggered!" << endl << "Event content: " << endl;
  15                 set<Concept> evt_content = boost::get<set<Concept> >(evt.content);
  16                 copy(evt_content.begin(), evt_content.end(), ostream_iterator<Concept>(cout, "\n"));
  17         }
  18 };
  19 
  20 int main(void) {
  21         set<Concept> result;
  22         set<string> partial_stmts;
  23 
  24         //liboro currently relies on sockets for the RPCs with the server.
  25         //we imagine here that the server runs on the same machine, on port 6969.
  26         SocketConnector connector("localhost", "6969");
  27 
  28         //The "oro" object is here built as a singleton.
  29         //This actually connects the application to the ontology server.
  30         Ontology *oro = Ontology::createWithConnector(connector);
  31 
  32         //First, create some instances (ie, objects).
  33 
  34         //a new instance of Agent has been created. It is named "Nice Robot" and its
  35         // type (or "class") is set to be a Robot (which is a subconcept of Agent).
  36         Agent robot1 = Agent::create("Nice Robot", Classes::Robot);
  37         //another agent...
  38 
  39         Agent human = Agent::create("Young PhD", Classes::Human);
  40 
  41         //Let's try a first, simple query
  42 
  43         partial_stmts.insert("?mysterious rdf:type Agent");
  44         oro->find("mysterious", partial_stmts, result);
  45 
  46         //display the results on std_out. It should display two ID (that are the
  47 
  48         //internal unique identifiers for "Nice Robot" and "Young PhD") + "myself"
  49         //which is always defined and refers to the robot itself.
  50         copy(result.begin(), result.end(), ostream_iterator<Concept>(cout, "\n"));
  51 
  52         partial_stmts.clear();
  53         result.clear();
  54 
  55         //here, an object is created. No name (or "label") has been set up, but the
  56         //class is refined: it's not only an object, but more precisely a table.
  57 
  58         Object table = Object::create(Classes::Table);
  59         //here, an unknown object has been identified, without any more infos.
  60         Object unknown_object = Object::create();
  61 
  62         //if no setter is available for a given property, then direct assertion can
  63         //be made. The list of existing properties and classes come from the oro
  64         //ontology itself (from which oro_library.h/cpp is automatically generated)
  65         unknown_object.assertThat(Properties::isOnTopOf, table);
  66 
  67         //We can as well access the ontology at a lower level
  68         oro->add(Statement("oro:isOnTopOf rdfs:subClassOf oro:isAt"));
  69 
  70         //"myself" is a special, unique instance representing the robot itself.
  71 
  72         //This instance is built from the already existing identifier "myself".
  73         //Hence the constructor of the Agent class can be directly called.
  74         Agent myself("myself");
  75 
  76         myself.sees(unknown_object);
  77         myself.sees(human);
  78 
  79         //Then, try to find back the unknown object...
  80         partial_stmts.insert("?mysterious oro:isAt ?table");
  81         partial_stmts.insert("?table rdf:type oro:Table");
  82         partial_stmts.insert("oro:myself oro:sees ?mysterious");
  83 
  84         oro->find("mysterious", partial_stmts, result);
  85 
  86         copy(result.begin(), result.end(), ostream_iterator<Concept>(cout, "\n"));
  87 
  88         /** EVENTS **/
  89         EventCallback ec;
  90         Classes::Human.onNewInstance(ec);
  91         Agent superman = Agent::create("Superman", Classes::Human);
  92 
  93         cout << "Sleeping for 1 sec..." << endl;
  94         sleep(1);
  95 
  96         set<string> event_pattern;
  97         Property flyingProp = Property("isFlying");
  98 
  99         event_pattern.insert( superman.id() + " isFlying true");
 100         oro->registerEvent(ec, FACT_CHECKING, ON_TRUE_ONE_SHOT, event_pattern, "");
 101 
 102         superman.assertThat(flyingProp, "true");
 103 
 104         cout << "Sleeping for 1 sec..." << endl;
 105         sleep(1);
 106 
 107         return 0;
 108 }

Python bindings

Installation

Via robotpkg

The supported way to install pyoyo is through robotpkg:

Robotpkg is a package management system for robotics module we use at the LAAS. It handles dependencies and compilation automatically. If you don't know it, have a look here.

> cd $ROBOTPKG_BASE/knowledge/py-oro
> make update

From the sources

To get the latest version of pyoro, you can check-out the sources with GIT:

> git clone git://trac.laas.fr/robots/pyoro

To install pyoro on your system, simply run with root privileges

> python setup.py install

Usage & library reference

The connection to an oro-server instance is initialized this way:

   1 import time
   2 from pyoro import Oro
   3 
   4 #We suppose the server to be running on the same machine, on port 6969
   5 oro = Oro("localhost", 6969)

   1         oro.<method name>(<param1>, <param2>, <...>))

For instance, the following code snippet would iterate over all concepts asserted of inferred to be humans:

   1   for human in oro["* rdf:type Human"]:
   2         ...

API coverage

Simple example

   1 import time
   2 from pyoro import Oro, OroServerError
   3 
   4 def onevent(evt):
   5         print("God save the queen! " + evt + " killed Bond!")
   6 
   7 try:
   8         oro = Oro()
   9 
  10         oro += ["Spy rdfs:subclassOf Human"
  11                 "bond rdf:type Spy"
  12                 "bond rdfs:label \"Bond, James Bond\""]
  13 
  14         if "bond rdf:type Human" in oro:
  15             print("Alright, Bond is a human")
  16 
  17         oro += "hrp2 rdf:type Robot"
  18 
  19         for ag in oro["* rdf:type Agent"]:
  20             print("Agent " + ag + " is here.")
  21 
  22         oro.subscribe(["?a kills bond"], onevent)
  23         oro += "hrp2 kills bond"
  24 
  25         time.sleep(1)
  26 
  27 except OroServerError as ose:
  28         print('Oups! An error occured!')
  29         print(ose)
  30 
  31 finally:
  32         oro.close()

TCL bindings

Installation

From the sources

To get the latest version of oro-tcl, you can check-out the sources with GIT:

> git clone git://trac.laas.fr/robots/oro-tcl

Simple usage example

   1 source oro.tcl
   2 
   3 global sockChan
   4 
   5 oro_add {[fsh4r5g2 rdf:type Human, fsh4r5g2 rdfs:label "Raquel"]}
   6 oro_add {[Akin rdf:type Human]}
   7 
   8 oro_getInfos "fsh4r5g2"
   9 oro_lookup "fsh4r5g2"
  10 oro_lookup "Raquel"
  11 oro_find "?var" {[?var rdf:type Human]}
  12 
  13 oro_getDirectInstancesOf "Human"

Bindings with robotic middlewares

ROS bindings

The ROS bindings are available as a Python ROS node. This node relies on the Python bindings to communicate with the server.

The code is available in the TUM ROS repository or can be provided on demand.

At start-up, the ROS node will fetch from ORO the list of available methods and will create a new ROS service for each of them (like oro/add or oro/findForAgent - names are case-sensitive).

All services take as parameter one array of parameters. This array may contain 0, 1 or more strings. The arity of methods varies for each service. Please refer the the list of RPC methods (and please note as well that some methods are polymorphic: the server will rely on the number and the type of the parameters you provide to select the right one).

The elements of the parameters array are always strings. But some (actually, a lot!) of ORO methods take an array as parameter. For instance, add expect an array of statements to add to the ontology. A statement is a string of this kind: "subject predicate object" (for instance, "robot likes disco_music"). In JSON, we can write ["robot likes disco_music", "disco_music rdf:type Music"] to represent an array of string.

We need to serialize this array of string in one single string, since ROS expect only strings as parameter. So we need to double escape our strings, and send "[\"robot likes disco_music\", \"disco_music rdf:type Music\"]"

At the end, we would call the ROS service in Python, like that:

   1 oro_add = rospy.ServiceProxy('oro/add', OroServerQuery)
   2 response = oro_add(["[\"robot likes disco_music\", \"disco_music rdf:type Music\"]"])

Below a sample Python program that ask for ORO methods with parameters and query the knowledge base through ROS:

   1 import roslib; roslib.load_manifest('oro_ros')
   2 
   3 import sys
   4 
   5 import rospy
   6 from oro_ros.srv import *
   7 
   8 def oro_ros_client(query, params):
   9     rospy.wait_for_service('oro/' + query)
  10 
  11     try:
  12         query_oro_server = rospy.ServiceProxy('oro/' + query, OroServerQuery)
  13         resp1 = query_oro_server(params)
  14         return resp1.res
  15 
  16     except rospy.ServiceException, e:
  17         print "Service call failed: %s"%e
  18 
  19 def usage():
  20     return "%s query [param1, param2, ...]"%sys.argv[0]
  21 
  22 if __name__ == "__main__":
  23 
  24     if len(sys.argv) == 1:
  25         print usage()
  26 
  27         sys.exit(1)
  28 
  29     query = sys.argv[1]
  30 
  31     params = sys.argv[2:]
  32 
  33     print "%s %s => %s"%(query, params, oro_ros_client(query, params))

YARP bindings

[AVAILABLE, BUT DOCUMENTATION TO DO... if needed, drop me a mail]

OpenPRS bindings

OpenPRS is an open source version of PRS (Procedural Reasoning Systems). More infos here.

Installation

To get the latest version of the oro-oprs connector, you can check-out the sources with GIT:

> git clone http://trac.laas.fr/git/robots/oro-oprs-connector.git

oro-oprs-connector uses CMake (>= 2.6) to compile.

It depends on boost (>= 1.37), `liboro` and OpenPRS.

You can compile it with:

> cd oro-oprs-connector
> cmake .
> make
> make install

It creates a executable, oro-oprs, that must be started with 6 parameters. For instance:

> oro-oprs localhost 6969 localhost 3300 OPRS-MODULE ONTO_OPRS_INTERFACE

Usage

Index of available methods

(Last updated on 2010-10-19 02:24:26)

OpenrobotsWiki: oro-server-bindings (last edited 2014-02-14 16:07:13 by mallet)