Computation in ROS is done using a network of ROS nodes. This computation network is called the computation graph. The main concepts in the computation graph are ROS nodes, master, parameter server, messages, topics, services, and bags. Each concept in the graph is contributed to this graph in different ways.
The ROS communication-related packages, including core client libraries, such as roscpp
and rospython
, and the implementation of concepts, such as topics, nodes, parameters, and services, are included in a stack called ros_comm
(http://wiki.ros.org/ros_comm).
This stack also consists of tools such as rostopic
, rosparam
, rosservice
, and rosnode
to introspect the preceding concepts.
The ros_comm
stack contains the ROS communication middleware packages, and these packages are collectively called the ROS graph layer:
Figure 1.6 – Structure of the ROS graph layer
Some new elements of the ROS graph are as follows:
- Nodes: Nodes are the processes that have computation. Each ROS node is written using ROS client libraries. Using client library APIs, we can implement different ROS functionalities, such as the communication methods between nodes, which is particularly useful when the different nodes of our robot must exchange information between them. One of the aims of ROS nodes is to build simple processes rather than a large process with all the desired functionalities. Being simple structures, ROS nodes are easy to debug.
- Master: The ROS master provides the name registration and lookup processes for the rest of the nodes. Nodes will not be able to find each other, exchange messages, or invoke services without a ROS master. In a distributed system, we should run the master on one computer; then, the other remote nodes can find each other by communicating with this master.
- Parameter server: The parameter server allows you to store data in a central location. All the nodes can access and modify these values. The parameter server is part of the ROS master.
- Topics: Each message in ROS is transported using named buses called topics. When a node sends a message through a topic, then we can say the node is publishing a topic. When a node receives a message through a topic, then we can say that the node is subscribing to a topic. The publishing node and subscribing node are not aware of each other's existence. We can even subscribe to a topic that might not have any publisher. In short, the production of information and its consumption are decoupled. Each topic has a unique name, and any node can access this topic and send data through it so long as they have the right message type.
- Logging: ROS provides a logging system for storing data, such as sensor data, which can be difficult to collect but is necessary for developing and testing robot algorithms. These are known as bagfiles. Bagfiles are very useful features when we're working with complex robot mechanisms.
The following graph shows how the nodes communicate with each other using topics:
Figure 1.7 – Graph of communication between nodes using topics
As you can see, the topics are represented by rectangles, while the nodes are represented by ellipses. The messages and parameters are not included in this graph. These kinds of graphs can be generated using a tool called rqt_graph
(http://wiki.ros.org/rqt_graph).
ROS nodes
ROS nodes have computations using ROS client libraries such as roscpp
and rospy
.
A robot might contain many nodes; for example, one node processes camera images, one node handles serial data from the robot, one node can be used to compute odometry, and so on.
Using nodes can make the system fault-tolerant. Even if a node crashes, an entire robot system can still work. Nodes also reduce complexity and increase debug-ability compared to monolithic code because each node is handling only a single function.
All running nodes should have a name assigned to help us identify them. For example, /camera_node
could be the name of a node that is broadcasting camera images.
There is a rosbash
tool for introspecting ROS nodes. The rosnode
command can be used to gather information about an ROS node. Here are the usages of rosnode
:
rosnode info [node_name]
: This will print out information about the node.
rosnode kill [node_name]
: This will kill a running node.
rosnode list
: This will list the running nodes.
rosnode machine [machine_name]
: This will list the nodes that are running on a particular machine or a list of machines.
rosnode ping
: This will check the connectivity of a node.
rosnode cleanup
: This will purge the registration of unreachable nodes.
Next, we will look at some example nodes that use the roscpp
client and discuss how ROS nodes that use functionalities such ROS topics, service, messages, and actionlib work.
ROS messages
As we discussed earlier, messages are simple data structures that contain field types. ROS messages support standard primitive data types and arrays of primitive types.
We can access a message definition using the following method. For example, to access std_msgs/msg/String.msg
when we are using the roscpp
client, we must include std_msgs/String.h
for the string message definition.
In addition to the message
data type, ROS uses an MD5 checksum comparison to confirm whether the publisher and subscriber exchange the same message data types.
ROS has a built-in tool called rosmsg
for gathering information about ROS messages. Here are some parameters that are used along with rosmsg
:
rosmsg show [message_type]
: This shows the message's description.
rosmsg list
: This lists all messages.
rosmsg md5 [message_type]
: This displays md5sum
of a message.
rosmsg package [package_name]
: This lists messages in a package.
rosmsg packages [package_1] [package_2]
: This lists all packages that contain messages.
Now, let's take a look at ROS topics.
ROS topics
Using topics, the ROS communication is unidirectional. Differently, if we want a direct request/response communication, we need to implement ROS services.
The ROS nodes communicate with topics using a TCP/IP-based transport known as TCPROS. This method is the default transport method used in ROS. Another type of communication is UDPROS, which has low latency and loose transport and is only suited for teleoperations.
The ROS topic tool can be used to gather information about ROS topics. Here is the syntax of this command:
rostopic bw /topic
: This command will display the bandwidth being used by the given topic.
rostopic echo /topic
: This command will print the content of the given topic in a human-readable format. Users can use the -p
option to print data in CSV format.
rostopic find /message_type
: This command will find topics using the given message type.
rostopic hz /topic
: This command will display the publishing rate of the given topic.
rostopic info /topic
: This command will print information about an active topic.
rostopic list
: This command will list all the active topics in the ROS system.
rostopic pub /topic message_type args
: This command can be used to publish a value to a topic with a message type.
rostopic type /topic
: This will display the message type of the given topic.
Now, let's take a look at ROS services.
ROS services
In ROS services, one node acts as a ROS server in which the service client can request the service from the server. If the server completes the service routine, it will send the results to the service client. For example, consider a node that can provide the sum of two numbers that has been received as input while implementing this functionality through an ROS service. The other nodes of our system might request the sum of two numbers via this service. In this situation, topics are used to stream continuous data flows.
The ROS service definition can be accessed by the following method. For example, my_package/srv/Image.srv
can be accessed by my_package/Image
.
In ROS services, there is an MD5 checksum
that checks in the nodes. If the sum is equal, then only the server responds to the client.
There are two ROS tools for gathering information about the ROS service. The first tool is rossrv
, which is similar to rosmsg
, and is used to get information about service types. The next command is rosservice
, which is used to list and query the running ROS services.
Let's explain how to use the rosservice
tool to gather information about the running services:
rosservice call /service args
: This tool will call the service using the given arguments.
rosservice find service_type
: This command will find the services of the given service type.
rosservice info /services
: This will print information about the given service.
rosservice list
: This command will list the active services running on the system.
rosservice type /service
: This command will print the service type of a given service.
rosservice uri /service
: This tool will print the service's ROSRPC URI.
Now, let's take a look at ROS bagfiles.
ROS bagfiles
The rosbag
command is used to work with rosbag
files. A bag file in ROS is used for storing ROS message data that's streamed by topics. The .bag
extension is used to represent a bag file.
Bag files are created using the rosbag record
command, which will subscribe to one or more topics and store the message's data in a file as it's received. This file can play the same topics that they are recorded from, and it can remap the existing topics too.
Here are the commands for recording and playing back a bag file:
rosbag record [topic_1] [topic_2] -o [bag_name]
: This command will record the given topics into the bag file provided in the command. We can also record all topics using the -a
argument.
rosbag play [bag_name]
: This will play back the existing bag file.
The full, detailed list of commands can be found by using the following command in a Terminal:
rosbag play -h
There is a GUI tool that we can use to handle how bag files are recorded and played back called rqt_bag
. To learn more about rqt_bag
, go to https://wiki.ros.org/rqt_bag.
The ROS master
The ROS master is much like a DNS server, in that it associates unique names and IDs to the ROS elements that are active in our system. When any node starts in the ROS system, it will start looking for the ROS master and register the name of the node in it. So, the ROS master has the details of all the nodes currently running on the ROS system. When any of the node's details change, it will generate a callback and update the node with the latest details. These node details are useful for connecting each node.
When a node starts publishing to a topic, the node will give the details of the topic, such as its name and data type, to the ROS master. The ROS master will check whether any other nodes are subscribed to the same topic. If any nodes are subscribed to the same topic, the ROS master will share the node details of the publisher to the subscriber node. After getting the node details, these two nodes will be connected. After connecting to the two nodes, the ROS master has no role in controlling them. We might be able to stop either the publisher node or the subscriber node according to our requirements. If we stop any nodes, they will check in with the ROS master once again. This same method is used for the ROS services.
As we've already stated, the nodes are written using ROS client libraries, such as roscpp
and rospy
. These clients interact with the ROS master using XML Remote Procedure Call (XMLRPC)-based APIs, which act as the backend of the ROS system APIs.
The ROS_MASTER_URI
environment variable contains the IP and port of the ROS master. Using this variable, ROS nodes can locate the ROS master. If this variable is wrong, communication between the nodes will not take place. When we use ROS in a single system, we can use the IP of a localhost or the name localhost
itself. But in a distributed network, in which computation is done on different physical computers, we should define ROS_MASTER_URI
properly; only then will the remote nodes be able to find each other and communicate with each other. We only need one master in a distributed system, and it should run on a computer in which all the other computers can ping it properly to ensure that remote ROS nodes can access the master.
The following diagram shows how the ROS master interacts with publishing and subscribing nodes, with the publisher node publishing a string type topic with a Hello World
message and the subscriber node subscribing to this topic:
Figure 1.8 – Communication between the ROS master and Hello World publisher and subscriber
When the publisher node starts advertising the Hello World
message in a particular topic, the ROS master gets the details of the topic and the node. It will check whether any node is subscribing to the same topic. If no nodes are subscribing to the same topic at that time, both nodes will remain unconnected. If the publisher and subscriber nodes run at the same time, the ROS master will exchange the details of the publisher to the subscriber, and they will connect and exchange data through ROS topics.
Using the ROS parameter
When programming a robot, we might have to define robot parameters to tune our control algorithm, such as the robot controller gains P, I, and D of a standard proportional–integral–derivative controller. When the number of parameters increases, we might need to store them as files. In some situations, these parameters must be shared between two or more programs. In this case, ROS provides a parameter server, which is a shared server in which all the ROS nodes can access parameters from this server. A node can read, write, modify, and delete parameter values from the parameter server.
We can store these parameters in a file and load them into the server. The server can store a wide variety of data types and even dictionaries. The programmer can also set the scope of the parameter; that is, whether it can be accessed by only this node or all the nodes.
The parameter server supports the following XMLRPC data types:
- 32-bit integers
- Booleans
- Strings
- Doubles
- ISO8601 dates
- Lists
- Base64-encoded binary data
We can also store dictionaries on the parameter server. If the number of parameters is high, we can use a YAML file to save them. Here is an example of the YAML file parameter definitions:
/camera/name : 'nikon' #string type
/camera/fps : 30 #integer
/camera/exposure : 1.2 #float
/camera/active : true #boolean
The rosparam
tool is used to get and set the ROS parameter from the command line. The following are the commands for working with ROS parameters:
rosparam set [parameter_name] [value]
: This command will set a value in the given parameter.
rosparam get [parameter_name]
: This command will retrieve a value from the given parameter.
rosparam load [YAML file]
: The ROS parameters can be saved into a YAML file. It can load them into the parameter server using this command.
rosparam dump [YAML file]
: This command will dump the existing ROS parameters into a YAML file.
rosparam delete [parameter_name]
: This command will delete the given parameter.
rosparam list
: This command will list existing parameter names.
These parameters can be changed dynamically when you're executing a node that uses these parameters by using the dyamic_reconfigure
package (http://wiki.ros.org/dynamic_reconfigure).