PhaROS is a collection of Pharo libraries that implements the ROS (Robot Operating System ) based client protocol. It allows developing robotic applications right in the Pharo environment by providing an abstract software layer between Pharo and ROS. This guide makes an assumption that readers already have some basic knowledge about ROS, if this is not the case, please check the following links before going any further on this page:
Before installing PhaROS, make sure you have everything set up:
Please note that PhaROS and ROS are not necessarily in the same environment. Since ROS protocol is based on the XMLRPC protocol, which is a network protocol, one can have a distributed configuration where PhaROS runs on a machine (e.g. a desktop PC) and ROS is installed on another machine (e.g. Raspberry Pi). As long as the two machines connect to the same network, they can communicate to each other via the ROS protocol. In this case, the ROS machine plays the role of the Master while all other machines connected to it are clients.
NOTE: if you opt for a distributed environment, make sure the two machines can see each other properly. For example, if the hostname of the ROS machine is walle
and the hostname of the PhaROS machine is jarvis
, please verify if the two can ping
each other:
# you need to add the hostname of each machine to the /etc/hosts file of another machine
#From wall-E
ping jarvis
# From jarvis
ping walle
Installing Pharos is really simple by evaluating the following snippet (from pharo 6.1) on the PhaROS machine:
Gofer it
smalltalkhubUser: 'CAR' project: 'PhaROS';
configurationOf: 'PhaROS';
loadStable.
In this guide, we will re-implement this example in PhaROS.
We will develop a publisher that publishes string messages to a topic, and a subscriber that connects to that topic and retrieves the published messages.
In Pharo, start by creating a new package named PhaROS-tutorial
, then add a class named PhaROSNodeHelper
. This class, a subclass of PhaROSPackage
, provides an abstract interface to all of our publishers/subscribers in this example:
PhaROSPackage subclass: #PhaROSNodeHelper
instanceVariableNames: 'proc'
classVariableNames: ''
package: ''PhaROS-tutorial'
The class has an abstract method spin
which should be implemented by its subclasses:
PhaROSNodeHelper >> spin
^ self subclassResponsibility
and a terminate
method to clean up and terminate all processes created by the node:
PhaROSNodeHelper >> terminate
proc
ifNotNil: [
proc terminate.
proc := nil
].
self cleanupProcesses.
OSProcess accessor restartChildWatcherProcess.
PhaROSNodeHelper >> cleanupProcesses
|pb |
pb := ProcessBrowser new.
"Terminate non-critic processes"
pb processList do: [ :p |
(pb nameAndRulesFor: p) second
ifTrue: [
p priority = Processor userSchedulingPriority
ifFalse:[
pb class terminateProcess: p
]].
].
pb updateProcessList.
self inform: 'PhaROS processes were terminated.'
Create a new class named PhaROSTalker
which is a subclass of PhaROSNodeHelper
:
PhaROSNodeHelper subclass: #PhaROSTalker
instanceVariableNames: ''
classVariableNames: ''
package: 'PhaROS-tutorial'
Inside this class, re-implement the abstract method spin
:
PhaROSTalker >> spin
|pub|
pub := self controller node topicPublisher: '/chatter' typedAs: 'std_msgs/String'.
proc := [
[ true ] whileTrue: [
pub send: [ :msg| msg data: 'helloworld ', (DateAndTime new asString) ].
500 milliSeconds wait
]
] forkAt: 31
This method is simple, we create a new publisher (line 3) that publishes messages to a topic named /chatter
of type: std_msgs/String
, a ROS message type of string.
We then create a new process that periodically publishes a hello world message to that topic each 500 milliseconds.
That's all for the publisher. To run it, make sure that the master is on-line on the ROS machine:
# open a terminal on the ROS machine and run
roscore
we can now run the publisher in a playground on the PhaROS machine:
"Run the publisher on PhaROS machine"
"Change theses IPs with your own:"
"The IP address of the PhaROS machine"
phaROSMachineIp := '192.168.1.11'.
"The ip address of the ROS master "
rosMasterIp := '192.168.1.84'.
rosMasterUri := 'http://',rosMasterIp,':11311'.
"Setting the environment variables"
OSEnvironment default setEnv: 'ROS_HOSTNAME' value: phaROSMachineIp.
OSEnvironment default setEnv: 'ROS_MASTER_URI' value: rosMasterUri.
"Now run the publisher"
pub := PhaROSTalker new.
pub spin.
To verify if the publisher works properly, open a terminal on the ROS (master) machine, and run:
rostopic echo /chatter
You should see the messages published by our publisher :
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
data: "helloworld 1901-01-01T02:00:00+02:00"
---
The publisher can be terminated by evaluating :
"on the same playground"
pub terminate
We will create a simple subscriber that subscribes to our /chatter
topic and prints the received messages on Transcript.
Create a new class named PhaROSListener
which is a subclass of PhaROSNodeHelper
:
PhaROSNodeHelper subclass: #PhaROSListener
instanceVariableNames: ''
classVariableNames: ''
package: 'PhaROS-tutorial'
Then re-implement the spin
method:
PhaROSListener>>spin
|sub|
sub := (self controller node buildConnectionFor: '/chatter')
typedAs: 'std_msgs/String';
for: [ :msg | Transcript show: msg data; cr ];
connect
This method explains it all. Now we can run the subscriber/publisher in a playground:
"Change theses IPs with your own:"
"The IP address of the PhaROS machine"
phaROSMachineIp := '192.168.1.11'.
"The ip address of the ROS master "
rosMasterIp := '192.168.1.84'.
rosMasterUri := 'http://',rosMasterIp,':11311'.
"Setting the environment variables"
OSEnvironment default setEnv: 'ROS_HOSTNAME' value: phaROSMachineIp.
OSEnvironment default setEnv: 'ROS_MASTER_URI' value: rosMasterUri.
"Now run the publisher"
pub := PhaROSTalker new.
pub spin.
"Then the subscriber"
sub := PhaROSListener new.
sub spin.
Open the Transcript, you should see the messages printed out:
To close all:
pub terminate.
sub terminate.
That's all folks!!