Get started¶
Requirements¶
rosonic
has been developed for ROS Melodic and above, i.e Python 2.7,
Python 3.6 and above. However, at writing moment only Python 3.6 has been
tested. As soon as I’m transitioning my other projects to Noetic then I will
drop any support for Python 2.7.
Installation¶
This package is available on PyPI and can be installed as a normal pip package.
pip install rosonic
Usage¶
There will be two nodes for counting numbers in this example. The first simply
publishes a uint32
that increases each iteration until it wraps around
and starts over. The other node subscribes to the uint32
and publishes a
string
with binary, and possibly hexadecimal, representation. Let’s start
by breaking down the code for the first node.
Example 1¶
#! /usr/bin/env python
from rosonic import Node, Parameter
from rospy import Publisher
from std_msgs.msg import UInt32
class uint_counter(Node):
START = Parameter('~start', 0)
rate = 4
def __init__(self):
self.value = self.START
self.pub_counter = Publisher('counter', UInt32, queue_size=1)
def spin(self):
self.pub_counter.publish(self.value)
self.value += 1
self.value %= 32
if __name__ == '__main__':
uint_counter(anonymous=True)
Example 1 – Explained¶
We import Node
and Parameter
from rosonic
. You should still import other
rospy
tools as you need them. There are neither a special Publisher
or
Subscriber
in rosonic
yet so we need to use rospy
‘s for now.
from rosonic import Node, Parameter
from rospy import Publisher
from std_msgs.msg import UInt32
We setup a class to declare the node. The name of the class will be the default
name of the node (just as when you call rospy.init_node('<name>')
. The
special field rate
will be turned into a rospy.Rate
used to control how
often spin
will be called.
class uint_counter(Node):
START = Parameter('~start', 0)
rate = 4
One of the benefits of viewing nodes as classes is the clear separation of
initialization and the rest of its lifetime. During initialization we want
to create resources like publishers, subscribers, services, action-servers
and more. It is here we use parameters and setup the runtime behvaiour. In
rosonic
we naturally utilize the __init__
method with no arguments but
self
.
class uint_counter(Node):
...
def __init__(self):
self.value = self.START
self.pub_counter = Publisher('counter', UInt32, queue_size=1)
The innovation of rosonic
nodes is very simple, but constitutes of three
different methods. __init__
that was previously mentioned, main
and spin
.
In most cases you will not want to change main
whose default behaviour
will loop over a call spin
. spin
, on the other hand, is meant to be
overloaded. In this example we publish and update the uint32
value.
class uint_counter(Node):
...
def spin(self):
self.pub_counter.publish(self.value)
self.value += 1
self.value %= 32
Finally we are ready to start our node! As if it were a normal function we can
call uint_counter
(with key-word arguments passed to rospy.init_node
) and
run the code. This code snippet is required for every rosonic
-style node.
if __name__ == '__main__':
uint_counter(anonymous=True)
Example 2¶
#! /usr/bin/env python
from rosonic import Node, Parameter
from rospy import Publisher, Subscriber
from std_msgs.msg import UInt32, String
class str_counter(Node):
HEX = Parameter('~hex', False)
def __init__(self):
self.pub_bin_counter = Publisher(
'bin',
String,
queue_size=1,
)
self.pub_hex_counter = Publisher(
'hex',
String,
queue_size=1,
)
self.sub_uint_counter = Subscriber(
'counter',
UInt32,
self.uint_counter_cb,
)
def uint_counter_cb(self, msg):
data = msg.data
hex_msg = String('hex=' + hex(data))
bin_msg = String('bin=' + bin(data))
if self.HEX:
self.pub_hex_counter.publish(hex_msg)
self.pub_bin_counter.publish(bin_msg)
if __name__ == '__main__':
str_counter(anonymous=True)
Example 2 – Explained¶
Just like in Example 1 we start by declaring a Node
class, any parameters
and its initialization. Notice that we have a subscriber this time!
class str_counter(Node):
HEX = Parameter('~hex', False)
def __init__(self):
self.pub_bin_counter = Publisher(
'bin',
String,
queue_size=1,
)
self.pub_hex_counter = Publisher(
'hex',
String,
queue_size=1,
)
self.sub_uint_counter = Subscriber(
'counter',
UInt32,
self.uint_counter_cb,
)
There is no problem using a method as the callback. Having self
in the
callback allow us to use resources belonging to the class. It is therefore no
need to have any global variables and we can rest assured that data
,
hex_str
and bin_str
are all contained in this scope.
class str_counter(Node):
...
def uint_counter_cb(self, msg):
data = msg.data
hex_msg = String('hex=' + hex(data))
bin_msg = String('bin=' + bin(data))
if self.HEX:
self.pub_hex_counter.publish(hex_msg)
self.pub_bin_counter.publish(bin_msg)
Then we start the node, just as we did last time! This time we did not overload
spin
which is also fine. Let’s look at Example 3 to investigate this.
if __name__ == '__main__':
str_counter(anonymous=True)
Example 3¶
#! /usr/bin/env python
from rosonic import Node
class dummy(Node):
pass
if __name__ == '__main__':
dummy(anonymous=True)
Example 3 – Explained¶
The purpose of str_counter
was to react to uint_counter
and only use its
one callback. Normally we would call rospy.spin
at the end in such nodes.
However, this happens automatically in rosonic
. Remember __init__
, main
and spin
? Well, by default spin
calls rospy.spin
! Lets look at the
default behaviour of our dummy
node.
When dummy
is called it will (as with any Python class) run __init__
for
initialization. This is up to you to decide if it’s necessary or not.
if __name__ == '__main__':
dummy(anonymous=True)
The next step, called internally by rosonic
, is main
. It will look
something like a while-loop that keeps calling spin
. The loop is conditioned
by keep_alive
(that you can also overload if necessary) that by default
returns not rospy.is_shutdown()
.
class Node(...):
...
def main(self):
while self.keep_alive():
self.spin()
self.rate.sleep()
self.shutdown()
Then there is spin
that calls rospy.spin
. This essentially means that
after __init__
is done, if neither main
or spin
is overloaded, then the
node will fall into rospy.spin
until the node is shutdown.
class Node(...):
...
def spin(self):
rospy.spin()
What’s next?¶
If you want to learn more about rosonic
‘s features I recommend you to read
the source code. You are also free to contribute with ideas through issue
tickets or PR’s.