6
ROS Concepts
6.1 Introduction
Before diving into the vast world of ROS , it is worth looking at some concepts and ideas which are used. Most of the concept we will cover will be relatively high-level and therefore are here to give you more of an idea of the cogs and gears working in the system.
In
a
nutshell,
ROS
is
a
middle-ware
At the heart of any ROS system is the ROS graph where the ROS graph refers to the network of nodes in a ROS system and the connections between them by which they communicate.
6.2 Publisher and Subscriber Architecture
A common design pattern in used
-
One or more threads or processes act as a producer, where it adds elements to some shared data structure,
-
One or more other threads act as a consumer where it removes items from that structure and does something with them.
To demonstrate, imagine the producer is preparing coffee for the customers. This makes the customers, consumers. As the barista
makes coffee, it creates a
An important factor to consider is the need of a protection to make sure the producer won’t try to add data to the queue when it’s full and the consumer won’t try to remove data from an empty buffer. Some programming languages may include implementations of a queue that’s considered thread-safe and handles all of these challenges under the hood, so we don’t have to, but some languages does not include that support. Therefore we need to use the combination of a mutex (mutual exclusion) and condition variables to implement your own thread-safe, synchronised queue.
We may run into scenarios where the producer cannot be paused if the queue fills up, for example, when it is an
external source of streaming data that we can’t slow down, so it’s important to consider the rate at which items are
produced and consumed from the queue. If the consumer can’t keep up with production, then we face a
We should consider the average rate at which items are produced and consumed as we want the average rate of production to be less than the average rate of consumption. But if more steps were required to process this data, then we could expand our simple producer-consumer setup into a pipeline of tasks. A pipeline consists of a chain of processing elements arranged so that the output of each element is the input to the next one. It’s basically a series of producer-consumer pairs connected together with some sort of buffer, like a queue, between each consecutive element.
6.3 Nodes - The Building Blocks
A
node
is
an
actor
in
the
overall
ROS
graph,
which
uses
a
client
library
rospy
or roscpp
depending
on
the
chosen
language.
to
communicate
with
Nodes can also communicate with other nodes within the same process, in a different process, or on a different
machine and are considered the
Nodes can either
For long-running calculations, a node can act as an action client to have another node perform it on their behalf, or as an action server to provide functionality to other nodes. Nodes can provide configurable parameters to change behaviour during run-time.
Nodes are often a complex combination of publishers, subscribers, service servers, service clients, action servers, and action clients, all at the same time.
An advantage of using nodes is that it allows
For all these actions to work, a node needs to know where others are. Therefore, to achieve this we use a method called a
6.4 The Discovery Process
Discovery of nodes happens automatically through the underlying middle-ware of ROS . The sequence of operations can be summarised as follows:
- 1.
-
When
a
node
is
started,
it
advertises
its
presence
to
other
nodes
on
the
network
with
the
same
ROS
domain.
3 3 This is set with theROS_DOMAIN_ID
environment variable which is used to isolate multiple ROS systems from each other on the same network. Nodes respond to this advertisement with information about themselves so that the appropriate connections can be made and the nodes can communicate. - 2.
-
Nodes
periodically announce their presence so connections can be made with new-found entities,even after the initial discovery period . - 3.
- Nodes advertise to other nodes when they go offline.
Nodes will only establish connections with others having compatible Quality of Service.
As an example to see whats going on let’s used the built-in example of talker-listener
. As the name implies, one node advertises
the presence and the other listens.
Now it is running in one, open another terminal window, and type:
Running the C++ talker node in one terminal will publish messages on a topic, and the Python listener node running in another terminal will subscribe to messages on the same topic.
We should see that these nodes discover each other automatically, and begin to exchange messages.
6.5 Communication Between Nodes
6.5.1 Description
ROS
applications generally communicate through interfaces of one of three
- 1.
- topics,
- 2.
- services,
- 3.
- actions.
ROS
uses a simplified description language
-
msg
-
simple text files describing the fields of a ROS message. They are used to generate source code for messages in different languages.
-
srv
-
describes a service and composed of two
(2) parts:-
a request, and
-
a response.
The request and response are
message declarations . -
-
action
-
describes actions and composed of three
(3) parts:-
a goal,
-
a result, and
-
a feedback.
Each part is
a message declaration itself . -
6.5.2 Messages
Messages are a way for a
ROS
node to
As
an
example,
let’s
say
a
ROS
node
reads
temperature
data
from
a
sensor.
It
can
then
publish
that
data
on
the
ROS
network
using
a Temperature
message.
Other
nodes
on
the
ROS
network
can
Temperature
message.
Messages are described and defined in .msg
files in the msg/
directory of a
ROS
package.
.msg
files are composed of two
- Fields
-
Information about a data type and its name
- Constants
-
Similar to other languages, define an
unchangeable variable.
Fields
Each field consists of a type and a name , separated by a space. The syntax for this would be the following:
For example, lets say we want to define a 32-bit integer and a string variable. For that we would have to write the following:
Field Types
As with most statically-typed languages we need to define what type of variables we are working. In
ROS
, the fields type can be
one of two
-
A built-in type,
-
names of Message descriptions defined on their own, such as
geometry_msgs/PoseStamped
The following is a table of all currently built-in variable types.
Type name | C++ | Python | DDS type |
bool
|
bool
|
builtins.bool
|
boolean
|
byte
|
uint8_t
|
builtins.bytes*
|
octet
|
char
|
char
|
builtins.int*
|
char
|
float32
|
float
|
builtins.float*
|
float
|
float64
|
double
|
builtins.float*
|
double
|
int8
|
int8_t
|
builtins.int*
|
octet
|
uint8
|
uint8_t
|
builtins.int*
|
octet
|
int16
|
int16_t
|
builtins.int*
|
short
|
uint16
|
uint16_t
|
builtins.int*
|
unsigned short
|
int32
|
int32_t
|
builtins.int*
|
long
|
uint32
|
uint32_t
|
builtins.int*
|
unsigned long
|
int64
|
int64_t
|
builtins.int*
|
long long
|
uint64
|
uint64_t
|
builtins.int*
|
unsigned long long
|
string
|
std::string
|
builtins.str
|
string
|
wstring
|
std::u16string
|
builtins.str
|
wstring
|
In addition, array-like feature are also supported
Type name | C++ | Python | DDS type |
static array
|
std::array<T, N>
|
builtins.list*
|
T[N]
|
unbounded dynamic array
|
std::vector
|
builtins.list
|
sequence
|
bounded dynamic array
|
custom_class<T, N>
|
builtins.list*
|
sequence<T,N>
|
bounded string
|
std::string
|
builtins.str*
|
string
|
All types which are more permissive than their ROS definition enforce the ROS constraints in range and length by software.
Example of message definition using arrays and bounded types:
int32[] unbounded_integer_array int32[5] five_integers_array int32[<=5] up_to_five_integers_array string string_of_unbounded_size string<=10 up_to_ten_characters_string string[<=5] up_to_five_unbounded_strings string<=10[] unbounded_array_of_strings_up_to_ten_characters_each string<=10[<=5] up_to_five_strings_up_to_ten_characters_each
Naming Conventions
There are some restrictions in how these variables are named and therefore are worth of mention. Field names
must be
lowercase alphanumeric characters with underscores for separating words
. They must start with an alphabetic
character, and they must
NOT
end with an underscore or have two
Default Values
Default
values
can
be
set
to
any
field
in
the
message
type.
Currently
default
values
are
NOT
supported
for
string
arrays
and
complex
types.
Defining
a
default
value
is
done
by
adding
a
An example implementation would be:
For
example,
in
the
first
line
we
define
a
8-bit
unsigned
integer (uint8
),
call
it
,
and
give
it
a
default
value
of
42
.
String values must be defined in '
or "
quotes and currently string values are
NOT
escaped.
Constants
Each constant definition is like a field description with a
=
) sign. The syntax is:
For example:
Constants names have to be UPPERCASE .
Services
Services are a request/response communication, where the client (requester) is waiting for the server (responder) to make a short
computation and return a result. Services are described and defined in .srv
files in the srv
directory of a
ROS
package.
A service description file consists of a request and a response msg type, separated by ---
. Any two .msg
files concatenated with a ---
are a legal service description. Here is a very simple example of a service that takes in a string and returns a
string:
We
can
of
course
get
much
more
complicated:
We cannot embed another service inside of a service.
Actions
Actions
are
a
long-running
request/response
communication,
where
the
action
client
Action definitions have the following form:
Similar
to
services,
the
request
fields
are
before
and
the
response
fields
are
after
the
first
triple-dash (---
),
respectively.
There
is
also
a
third
set
of
fields
after
the
second
triple-dash,
which
is
the
fields
to
be
sent
when
sending
feedback.
There
can
be:
-
arbitrary numbers of request fields (including zero),
-
arbitrary numbers of response fields (including zero), and
-
arbitrary numbers of feedback fields (including zero).
The <request_type>
, <response_type>
,
and <feedback_type>
follow
all
of
the
same
rules
as
the <type>
for
a
message.
The <request_fieldname>
, <response_fieldname>
,
and <feedback_fieldname>
follow
all
of
the
same
rules
as
the <fieldname>
for
a
message.
As
an
example,
the
This
is
an
action
definition
where
the
action
client
is
sending
a
single int32
field
representing
the
number
of
Fibonacci
steps
to
take,
and
expecting
the
action
server
to
produce
an
array
of int32
containing
the
complete
steps.
Along
the
way,
the
action
server
may
also
provide
an
intermediate
array
of int32
contains
the
steps
accomplished
up
until
a
certain
point.
6.6 Topics
Topics
are
one
of
the
three
6.6.1 Publisher - Subscriber Architecture
A publish/subscribe system is one in which there are:
- 1.
- producers of data (publishers),
- 2.
- consumers of data (subscribers).
The
publishers
and
subscribers
know
how
to
contact
each
other
through
the
concept
of
a
When we create a publisher, we must also give it a string which is the name of the topic and the same goes for the subscriber.
Any
publishers
and
subscribers
that
are
on
the
same
topic
name
can
This
system
is
also
known
as
a
As
an
example,
if
we
want
to
record
data,
we
can
use
the ros2 bag record
command.
Under
the
hood, ros2 bag record
creates
a
new
subscriber
to
whatever
topic
we
tell
it,
without
interrupting
the
flow
of
data
to
the
other
parts
of
the
system.
6.6.2 Anonymity
Another
fact
mentioned
in
the
introduction
is
that
ROS
is
6.6.3 Strongly-Typed
As a last point, the publish/subscribe system is
strongly-typed
. That has two
-
The types of each field in a ROS message are typed, and that type is
enforced at various levels. For instance, if the ROS message contains: -
Then the code will ensure the
field1
is always anunsigned integer andfield2
is always astring . -
The semantics of each field are
well-defined .-
There is no automated mechanism to ensure this, but all of the core ROS types have strong semantics associated with them.
-
For instance, the IMU message contains a 3-dimensional vector for the measured angular velocity, and each of the dimensions is specified to be in radians/second. Other interpretations should NOT be placed into the message.
-
6.7 Services
In ROS , a service refers to a remote procedure call. In other words,
a node can make a remote procedure call to another node which will do a computation and return a result.
This structure is reflected in how a service message definition looks:
In
ROS
, services are expected to return quickly, as the client is generally waiting on the result.
If we have a service that will be doing a long-running computation, consider using an action .
Services
are
identified
by
a
service
name,
which
looks
much
like
a
topic
name.
-
the service server,
-
the service client.
6.7.1 Service Server
A service server is the entity that will accept a remote procedure request, and perform some computation on it. For instance, suppose the ROS message contains the following:
The
service
server
would
be
the
entity
that
receives
this
message,
adds a
and b
together,
and
returns
the sum
.
There
should
only
ever
be
one
Service Client
A
service
client
is
an
entity
that
will
request
a
remote
service
server
to
perform
a
computation
on
its
behalf.
Following
the
previous
example,
the
service
client
is
the
entity
that
creates
the
initial
message
containing a
and b
,
and
waits
for
the
service
server
to
compute
the sum
and
return
the
result.
Unlike service server, there can be a number of service clients using the same service name.
6.8 Actions
In
ROS
,
an
action
refers
to
a
This structure is reflected in how an action message definition looks:
In ROS , actions are expected to be long running procedures, as there is overhead in setting up and monitoring the connection.
If we need a short running remote procedure call, consider using a service instead.
Actions
are
identified
by
an action name
,
which
looks
much
like
a
topic
name
(but
is
in
a
different
namespace)
and
consists
of
two
parts:
- 1.
- the action server,
- 2.
- the action client.
6.8.1 Action Server
The action server is the entity that will accept the remote procedure request and perform some procedure on it. It is also responsible for sending out feedback as the action progresses and should react to cancellation/preemption requests.
For instance, consider an action to calculate the Fibonacci sequence with the following interface we looked at previously:
The action server is the entity that receives this message, starts calculating the sequence up to order (providing feedback along the way), and finally returns a full result in sequence.
There should only ever be one action server per action name. It is undefined which action server will receive client requests in the case of multiple action servers on the same action name.
6.8.2 Action Client
An action client is an entity that will request a remote action server to perform a procedure on its behalf. Following the previous example, the action client is the entity that creates the initial message containing the order, and waits for the action server to compute the sequence and return it (with feedback along the way).
Unlike action server, there can be a number of action clients using the same action name.
6.9 Parameters
Parameters
in
ROS
are
associated
with
Providing a parameter namespace is optional.
Each parameter consists of a key , a value , and a descriptor . The key is a string and the value is one of the following types:
bool
, int64
, float64
, string
, byte[]
, bool[]
, int64[]
, float64[]
or string[]
.
By default all descriptors are empty, but can contain parameter descriptions, value ranges, type information, and additional constraints.
6.9.1 A Detailed Look
Declaring Parameters
By default, a node needs to declare all of the parameters that it will accept during its lifetime.
We do this so the type and name of the parameters are well-defined at node startup time, which reduces the chances of misconfiguration later on.
For some types of nodes,
allow_undeclared_parameters
set to true
, which will allow parameters to be get and set on the node even if they haven’t been declared.
Types of Parameters
Each parameter on a ROS node has one of the pre-defined parameter types as mentioned.
By default, any attempts to change the type of a declared parameter at runtime will fail . This prevents common mistakes, such as putting a boolean value into an integer parameter.
If a parameter needs to be multiple different types, and the code using the parameter can handle it, this default behaviour can be changed.
When the parameter is declared, it should be declared using a ParameterDescriptor
with the dynamic_typing
member variable set to true
.
Parameter Callbacks
A
ROS
node can register two
- 1.
-
The first is known as a
set parameter
callback, and can be set by calling from the node
Application Programming
Interface (API)
,
13 13 A way for two or more computer programs or components to communicate with each other. It is a type of software interface, offering a service to other pieces of software.add_on_set_parameters_callback
. The callback is passed a list of immutableParameter
objects, and returns anrcl\_interfaces/msg/SetParametersResult
.The primary purpose of set parameter callback is to give the user the ability to inspect the upcoming change to the parameter and explicitly reject the change.
It is important to make sure that set parameter callbacks have no side-effects. Since multiple set parameter callbacks can be chained, there is no way for an individual callback to know if a later callback will reject the update. If the individual callback were to make changes to the class it is in, for instance, it may get out-of-sync with the actual parameter. To get a callback after a parameter has been successfully changed, we may need to look into the lower option.
- 2.
-
The second type of callback is known as an
on parameter event
callback, and can be set by calling
on_parameter_event
from the parameter client API s. The callback is passed anrcl\_interfaces/msg/ParameterEvent
object, and returns nothing. This callback will be called after all parameters in the input event have been declared, changed, or deleted.The primary purpose of on parameter event callback is to give the user the ability to react to changes from parameters that have successfully been accepted.
6.9.2 Parameter Interaction
ROS 2 nodes can perform parameter operations through node API s. External processes can perform parameter operations via parameter services that are created by default when a node is instantiated.
The services that are created by default are:
-
node\_name/describe\_parameters
: Uses service typercl\_interfaces/srv/DescribeParameters
. Given a list of parameter names, returns a list of descriptors associated with the parameters. -
node\_name/get\_parameter\_types
: Uses service typercl\_interfaces/srv/GetParameterTypes
. Given a list of parameter names, returns a list of parameter types associated with the parameters. -
node\_name/get\_parameters
: Uses service typercl\_interfaces/srv/GetParameters
. Given a list of parameter names, returns a list of parameter values associated with the parameters. -
node\_name/list\_parameters
: Uses service typercl\_interfaces/srv/ListParameters
. Given an optional list of parameter prefixes, returns a list of the available parameters with that prefix. If the prefixes are empty, returns all parameters. -
node\_name/set\_parameters
: Uses service typercl\_interfaces/srv/SetParameters
. Given a list of parameter names and values, attempts to set the parameters on the node. Returns a list of results from trying to set each parameter; some of them may have succeeded and some may have failed. -
node\_name/set\_parameters\_atomically
: Uses service typercl\_interfaces/srv/SetParametersAtomically
. Given a list of parameter names and values, attempts to set the parameters on the node. Returns a single result from trying to set all parameters, so if one failed, all of them failed.
Some additional information regarding parameters are as follows:
Setting Initial Parameters Values when a Node is Running Initial parameter values can be set when running the node either through individual command-line arguments, or through YAML files.
Information
: The .yaml
Format
A human-readable data serialization language. It is commonly used for configuration files and in applications
where data is being stored or transmitted. Stands for
Setting Initial Parameters Values when a Node is Launching Initial parameter values can also be set when running the node through the ROS 2 launch facility.
Manipulating parameter values at runtime
The ros2 param
command is the general way to interact with parameters for nodes that are already
running. ros2 param
uses the parameter service
API
as described above to perform the various operations.
6.10 Working with Command Line
ROS 2 includes a suite of command-line tools for introspecting a ROS 2 system.
The main entry point for the tools is the command ros2
, which itself has various
sub-commands for introspecting and working with nodes, topics, services, and more.
To see all available sub-commands run:
usage: ros2 [-h] [--use-python-default-buffering] Call `ros2 <command> -h` for more detailed usage. ... ros2 is an extensible command-line tool for ROS 2. options: -h, --help show this help message and exit --use-python-default-buffering Do not force line buffering in stdout and instead use the python default buffering, which might be affected by PYTHONUNBUFFERED/-u and depends on whatever stdout is interactive or not Commands: action Various action related sub-commands bag Various rosbag related sub-commands component Various component related sub-commands daemon Various daemon related sub-commands doctor Check ROS setup and other potential issues interface Show information about ROS interfaces launch Run a launch file lifecycle Various lifecycle related sub-commands multicast Various multicast related sub-commands node Various node related sub-commands param Various param related sub-commands pkg Various package related sub-commands run Run a package specific executable security Various security related sub-commands service Various service related sub-commands topic Various topic related sub-commands wtf Use `wtf` as alias to `doctor` Call `ros2 <command> -h` for more detailed usage.
ROS 2 uses a distributed discovery process for nodes to connect to each other. As this process purposefully does not use a centralized discovery mechanism, it can take time for ROS nodes to discover all other participants in the ROS graph. Because of this, there is a long-running daemon in the background that stores information about the ROS graph to provide faster responses to queries, e.g. the list of node names.
The daemon is automatically started when the relevant command-line tools are used for the first time. We can run ros2 daemon –help for more options for interacting with the daemon.
6.11 Launch File
A ROS 2 system typically consists of many nodes running across many different processes (and even different machines). While it is possible to run each of these nodes separately, it gets cumbersome quite quickly.
The launch system in ROS 2 is meant to automate the running of many nodes with a single command. It helps the user describe the configuration of their system and then executes it as described. The configuration of the system includes what programs to run, where to run them, what arguments to pass them, and ROS-specific conventions which make it easy to reuse components throughout the system by giving them each a different configuration. It is also responsible for monitoring the state of the processes launched, and reporting and/or reacting to changes in the state of those processes.
All of the above is specified in a launch file, which can be written in Python, XML, or YAML. This
launch file can then be run using the ros2 launch
command, and all of the nodes specified will be run.