In this article by Anil Mahtani, Luis Sánchez, Enrique Fernández, and Aaron Martinez, authors of the book Effective Robotics Programming with ROS, Third Edition, you will learn the structure of ROS and the parts it is made up of. Furthermore, you will start to create nodes and packages and use ROS with examples using Turtlesim.
The ROS architecture has been designed and divided into three sections or levels of concepts:
The Filesystem level
The Computation Graph level
The Community level
(For more resources related to this topic, see here.)
The first level is the Filesystem level. In this level, a group of concepts are used to explain how ROS is internally formed, the folder structure, and the minimum number of files that it needs to work.
The second level is the Computation Graph level where communication between processes and systems happens. In this section, we will see all the concepts and mechanisms that ROS has to set up systems, handle all the processes, and communicate with more than a single computer, and so on.
The third level is the Community level, which comprises a set of tools and concepts to share knowledge, algorithms, and code between developers. This level is of great importance; as with most open source software projects, having a strong community not only improves the ability of newcomers to understand the intricacies of the software as well as solve the most common issues, it is also the main force driving its growth.
Understanding the ROS Filesystem level
The ROS Filesystem is one of the strangest concepts to grasp when starting to develop projects in ROS, but with time and patience, the reader will easily become familiar with it and realize its value for managing projects and its dependencies. The main goal of the ROS Filesystem is to centralize the build process of a project while at the same time provide enough flexibility and tooling to decentralize its dependencies.
Similar to an operating system, an ROS program is divided into folders, and these folders have files that describe their functionalities:
Packages: Packages form the atomic level of ROS. A package has the minimum structure and content to create a program within ROS. It may have ROS runtime processes (nodes), configuration files, and so on.
Package manifests: Package manifests provide information about a package, licenses, dependencies, compilation flags, and so on. A package manifest is managed with a file called package.xml.
Metapackages: When you want to aggregate several packages in a group, you will use metapackages. In ROS Fuerte, this form for ordering packages was called Stacks. To maintain the simplicity of ROS, the stacks were removed, and now, metapackages make up this function. In ROS, there exists a lot of these metapackages; for example, the navigation stack.
Metapackage manifests: Metapackage manifests (package.xml) are similar to a normal package, but with an export tag in XML. It also has certain restrictions in its structure.
Message (msg) types: A message is the information that a process sends to other processes. ROS has a lot of standard types of messages. Message descriptions are stored in my_package/msg/MyMessageType.msg.
Service (srv) types: Service descriptions, stored in my_package/srv/MyServiceType.srv, define the request and response data structures for services provided by each process in ROS.
In the following screenshot, you can see the content of the turtlesim package. What you see is a series of files and folders with code, images, launch files, services, and messages. Keep in mind that the screenshot was edited to show a short list of files; the real package has more:
The workspace
In general terms, the workspace is a folder which contains packages, those packages contain our source files and the environment or workspace provides us with a way to compile those packages. It is useful when you want to compile various packages at the same time and it is a good way to centralize all of our developments.
A typical workspace is shown in the following screenshot. Each folder is a different space with a different role:
The source space: In the source space (the src folder), you put your packages, projects, clone packages, and so on. One of the most important files in this space is CMakeLists.txt. The src folder has this file because it is invoked by cmake when you configure the packages in the workspace. This file is created with the catkin_init_workspace command.
The build space: In the build folder, cmake and catkin keep the cache information, configuration, and other intermediate files for our packages and projects.
Development (devel) space: The devel folder is used to keep the compiled programs. This is used to test the programs without the installation step. Once the programs are tested, you can install or export the package to share with other developers.
You have two options with regard to building packages with catkin. The first one is to use the standard CMake workflow. With this, you can compile one package at a time, as shown in the following commands:
$ cmakepackageToBuild/
$ make
If you want to compile all your packages, you can use the catkin_make command line, as shown in the following commands:
$ cd workspace
$ catkin_make
Both commands build the executable in the build space directory configured in ROS.
Another interesting feature of ROS is its overlays. When you are working with a package of ROS, for example, turtlesim, you can do it with the installed version, or you can download the source file and compile it to use your modified version.
ROS permits you to use your version of this package instead of the installed version. This is very useful information if you are working on an upgrade of an installed package.
Packages
Usually, when we talk about packages, we refer to a typical structure of files and folders. This structure looks as follows:
include/package_name/: This directory includes the headers of the libraries that you would need.
msg/: If you develop nonstandard messages, put them here.
scripts/: These are executable scripts that can be in Bash, Python, or any other scripting language.
src/: This is where the source files of your programs are present. You can create a folder for nodes and nodelets or organize it as you want.
srv/: This represents the service (srv) types.
CMakeLists.txt: This is the CMake build file.
package.xml: This is the package manifest.
To create, modify, or work with packages, ROS gives us tools for assistance, some of which are as follows:
rospack: This command is used to get information or find packages in the system.
catkin_create_pkg: This command is used when you want to create a new package.
catkin_make: This command is used to compile a workspace.
rosdep: This command installs the system dependencies of a package.
rqt_dep: This command is used to see the package dependencies as a graph. If you want to see the package dependencies as a graph, you will find a plugin called package graph in rqt. Select a package and see the dependencies.
To move between packages and their folders and files, ROS gives us a very useful package called rosbash, which provides commands that are very similar to Linux commands. The following are a few examples:
roscd: This command helps us change the directory. This is similar to the cd command in Linux.
rosed: This command is used to edit a file.
roscp: This command is used to copy a file from a package.
rosd: This command lists the directories of a package.
rosls: This command lists the files from a package. This is similar to the ls command in Linux.
Every package must contain a package.xml file, as it is used to specify information about the package. If you find this file inside a folder, it is very likely that this folder is a package or a metapackage.
If you open the package.xml file, you will see information about the name of the package, dependencies, and so on. All of this is to make the installation and the distribution of these packages easy.
Two typical tags that are used in the package.xml file are <build_depend> and <run _depend>.
The <build_depend> tag shows which packages must be installed before installing the current package. This is because the new package might use functionality contained in another package.
The <run_depend> tag shows the packages that are necessary to run the code of the package. The following screenshot is an example of the package.xml file:
Metapackages
As we have shown earlier, metapackages are special packages with only one file inside; this file is package.xml. This package does not have other files, such as code, includes, and so on.
Metapackages are used to refer to others packages that are normally grouped following a feature-like functionality, for example, navigation stack, ros_tutorials, and so on.
You can convert your stacks and packages from ROS Fuerte to Kinetic and catkin using certain rules for migration. These rules can be found at http://wiki.ros.org/catkin/migrating_from_rosbuild.
In the following screenshot, you can see the content from the package.xml file in the ros_tutorialsmetapackage. You can see the <export> tag and the <run_depend> tag. These are necessary in the package manifest, which is also shown in the following screenshot:
If you want to locate the ros_tutorialsmetapackage, you can use the following command:
$ rosstack find ros_tutorials
The output will be a path, such as /opt/ros/kinetic/share/ros_tutorials.
To see the code inside, you can use the following command line:
$ vim /opt/ros/kinetic/ros_tutorials/package.xml
Remember that Kinetic uses metapackages, not stacks, but the rosstack find command-line tool is also capable of finding metapackages.
Messages
ROS uses a simplified message description language to describe the data values that ROS nodes publish. With this description, ROS can generate the right source code for these types of messages in several programming languages.
ROS has a lot of messages predefined, but if you develop a new message, it will be in the msg/ folder of your package. Inside that folder, certain files with the .msg extension define the messages.
A message must have two main parts: fields and constants. Fields define the type of data to be transmitted in the message, for example, int32, float32, and string, or new types that you have created earlier, such as type1 and type2. Constants define the name of the fields.
An example of an msg file is as follows:
int32 id
float32vel
string name
In ROS, you can find a lot of standard types to use in messages, as shown in the following table list:
Primitive type
Serialization
C++
Python
bool (1)
unsigned 8-bit int
uint8_t(2)
bool
int8
signed 8-bit int
int8_t
int
uint8
unsigned 8-bit int
uint8_t
int(3)
int16
signed 16-bit int
int16_t
int
uint16
unsigned 16-bit int
uint16_t
int
int32
signed 32-bit int
int32_t
int
uint32
unsigned 32-bit int
uint32_t
int
int64
signed 64-bit int
int64_t
long
uint64
unsigned 64-bit int
uint64_t
long
float32
32-bit IEEE float
float
float
float64
64-bit IEEE float
double
float
string
ascii string (4)
std::string
string
time
secs/nsecs signed 32-bit ints
ros::Time
rospy.Time
duration
secs/nsecs signed 32-bit ints
ros::Duration
rospy.Duration
A special type in ROS is the header type. This is used to add the time, frame, and sequence number. This permits you to have the messages numbered, to see who is sending the message, and to have more functions that are transparent for the user and that ROS is handling.
The header type contains the following fields:
uint32seq
time stamp
string frame_id
You can see the structure using the following command:
$ rosmsg show std_msgs/Header
Thanks to the header type, it is possible to record the timestamp and frame of what is happening with the robot.
ROS provides certain tools to work with messages. The rosmsg tool prints out the message definition information and can find the source files that use a message type.
In upcoming sections, we will see how to create messages with the right tools.
Services
ROS uses a simplified service description language to describe ROS service types. This builds directly upon the ROS msg format to enable request/response communication between nodes. Service descriptions are stored in .srv files in the srv/ subdirectory of a package.
To call a service, you need to use the package name, along with the service name; for example, you will refer to the sample_package1/srv/sample1.srv file as sample_package1/sample1.
Several tools exist to perform operations on services. The rossrv tool prints out the service descriptions and packages that contain the .srv files, and finds source files that use a service type.
If you want to create a service, ROS can help you with the service generator. These tools generate code from an initial specification of the service. You only need to add the gensrv() line to your CMakeLists.txt file.
In upcoming sections, you will learn how to create your own services.
Understanding the ROS Computation Graph level
ROS creates a network where all the processes are connected. Any node in the system can access this network, interact with other nodes, see the information that they are sending, and transmit data to the network:
The basic concepts in this level are nodes, the master, Parameter Server, messages, services, topics, and bags, all of which provide data to the graph in different ways and are explained in the following list:
Nodes: Nodes are processes where computation is done. If you want to have a process that can interact with other nodes, you need to create a node with this process to connect it to the ROS network. Usually, a system will have many nodes to control different functions. You will see that it is better to have many nodes that provide only a single functionality, rather than have a large node that makes everything in the system. Nodes are written with an ROS client library, for example, roscpp or rospy.
The master: The master provides the registration of names and the lookup service to the rest of the nodes. It also sets up connections between the nodes. If you don't have it in your system, you can't communicate with nodes, services, messages, and others. In a distributed system, you will have the master in one computer, and you can execute nodes in this or other computers.
Parameter Server: Parameter Server gives us the possibility of using keys to store data in a central location. With this parameter, it is possible to configure nodes while it's running or to change the working parameters of a node.
Messages: Nodes communicate with each other through messages. A message contains data that provides information to other nodes. ROS has many types of messages, and you can also develop your own type of message using standard message types.
Topics: Each message must have a name to be routed by the ROS network. When a node is sending data, we say that the node is publishing a topic. Nodes can receive topics from other nodes by simply subscribing to the topic. A node can subscribe to a topic even if there aren't any other nodes publishing to this specific topic. This allows us to decouple the production from the consumption. It's important that topic names are unique to avoid problems and confusion between topics with the same name.
Services: When you publish topics, you are sending data in a many-to-many fashion, but when you need a request or an answer from a node, you can't do it with topics. Services give us the possibility of interacting with nodes. Also, services must have a unique name. When a node has a service, all the nodes can communicate with it, thanks to ROS client libraries.
Bags: Bags are a format to save and play back the ROS message data. Bags are an important mechanism to store data, such as sensor data, that can be difficult to collect but is necessary to develop and test algorithms. You will use bags a lot while working with complex robots.
In the following diagram, you can see the graphic representation of this level. It represents a real robot working in real conditions. In the graph, you can see the nodes, the topics, which node is subscribed to a topic, and so on. This graph does not represent messages, bags, Parameter Server, and services. It is necessary for other tools to see a graphic representation of them. The tool used to create the graph is rqt_graph.
These concepts are implemented in the ros_comm repository.
Nodes and nodelets
Nodes are executable that can communicate with other processes using topics, services, or the Parameter Server. Using nodes in ROS provides us with fault tolerance and separates the code and functionalities, making the system simpler.
ROS has another type of node called nodelets. These special nodes are designed to run multiple nodes in a single process, with each nodelet being a thread (light process). This way, we avoid using the ROS network among them, but permit communication with other nodes. With that, nodes can communicate more efficiently, without overloading the network. Nodelets are especially useful for camera systems and 3D sensors, where the volume of data transferred is very high.
A node must have a unique name in the system. This name is used to permit the node to communicate with another node using its name without ambiguity. A node can be written using different libraries, such as roscpp and rospy; roscpp is for C++ and rospy is for Python. Throughout we will use roscpp.
ROS has tools to handle nodes and give us information about it, such as rosnode. The rosnode tool is a command-line tool used to display information about nodes, such as listing the currently running nodes. The supported commands are as follows:
rosnodeinfo NODE: This prints information about a node
rosnodekill NODE: This kills a running node or sends a given signal
rosnodelist: This lists the active nodes
rosnode machine hostname: This lists the nodes running on a particular machine or lists machines
rosnode ping NODE: This tests the connectivity to the node
rosnode cleanup: This purges the registration information from unreachable nodes
A powerful feature of ROS nodes is the possibility of changing parameters while you start the node. This feature gives us the power to change the node name, topic names, and parameter names. We use this to reconfigure the node without recompiling the code so that we can use the node in different scenes.
An example of changing a topic name is as follows:
$ rosrun book_tutorials tutorialX topic1:=/level1/topic1
This command will change the topic name topic1 to /level1/topic1. To change parameters in the node, you can do something similar to changing the topic name. For this, you only need to add an underscore (_) to the parameter name; for example:
$ rosrun book_tutorials tutorialX _param:=9.0
The preceding command will set param to the float number 9.0.
Bear in mind that you cannot use names that are reserved by the system. They are as follows:
__name: This is a special, reserved keyword for the name of the node
__log: This is a reserved keyword that designates the location where the node's log file should be written
__ip and __hostname: These are substitutes for ROS_IP and ROS_HOSTNAME
__master: This is a substitute for ROS_MASTER_URI
__ns: This is a substitute for ROS_NAMESPACE
Topics
Topics are buses used by nodes to transmit data. Topics can be transmitted without a direct connection between nodes, which means that the production and consumption of data is decoupled. A topic can have various subscribers and can also have various publishers, but you should be careful when publishing the same topic with different nodes as it can create conflicts.
Each topic is strongly typed by the ROS message type used to publish it, and nodes can only receive messages from a matching type. A node can subscribe to a topic only if it has the same message type.
The topics in ROS can be transmitted using TCP/IP and UDP. The TCP/IP-based transport is known as TCPROS and uses the persistent TCP/IP connection. This is the default transport used in ROS.
The UDP-based transport is known as UDPROS and is a low-latency, lossy transport. So, it is best suited to tasks such as teleoperation.
ROS has a tool to work with topics called rostopic. It is a command-line tool that gives us information about the topic or publishes data directly on the network. This tool has the following parameters:
rostopicbw /topic: This displays the bandwidth used by the topic.
rostopic echo /topic: This prints messages to the screen.
rostopic find message_type: This finds topics by their type.
rostopichz /topic: This displays the publishing rate of the topic.
rostopic info /topic: This prints information about the topic, such as its message type, publishers, and subscribers.
rostopic list: This prints information about active topics.
rostopic pub /topic type args: This publishes data to the topic. It allows us to create and publish data in whatever topic we want, directly from the command line.
rostopic type /topic: This prints the topic type, that is, the type of message it publishes.
We will learn to use this command-line tool in upcoming sections.
Services
When you need to communicate with nodes and receive a reply, in an RPC fashion, you cannot do it with topics; you need to do it with services.
Services are developed by the user, and standard services don't exist for nodes. The files with the source code of the services are stored in the srv folder.
Similar to topics, services have an associated service type that is the package resource name of the .srv file. As with other ROS filesystem-based types, the service type is the package name and the name of the .srv file.
ROS has two command-line tools to work with services: rossrv and rosservice. With rossrv, we can see information about the services' data structure, and it has exactly the same usage as rosmsg.
With rosservice, we can list and query services. The supported commands are as follows:
rosservice call /service args: This calls the service with the arguments provided
rosservice find msg-type: This finds services by service type
rosservice info /service: This prints information about the service
rosservice list: This lists the active services
rosservice type /service: This prints the service type
rosserviceuri /service: This prints the ROSRPC URI service
Messages
A node publishes information using messages which are linked to topics. The message has a simple structure that uses standard types or types developed by the user.
Message types use the following standard ROS naming convention; the name of the package, then /, and then the name of the .msg file. For example, std_msgs/ msg/String.msg has the std_msgs/String message type.
ROS has the rosmsg command-line tool to get information about messages. The accepted parameters are as follows:
rosmsg show: This displays the fields of a message
rosmsg list: This lists all messages
rosmsg package: This lists all of the messages in a package
rosmsg packages: This lists all of the packages that have the message
rosmsg users: This searches for code files that use the message type
rosmsgmd5: This displays the MD5 sum of a message
Bags
A bag is a file created by ROS with the .bag format to save all of the information of the messages, topics, services, and others. You can use this data later to visualize what has happened; you can play, stop, rewind, and perform other operations with it.
The bag file can be reproduced in ROS just as a real session can, sending the topics at the same time with the same data. Normally, we use this functionality to debug our algorithms.
To use bag files, we have the following tools in ROS:
rosbag: This is used to record, play, and perform other operations
rqt_bag: This is used to visualize data in a graphic environment
rostopic: This helps us see the topics sent to the nodes
The ROS master
The ROS master provides naming and registration services to the rest of the nodes in the ROS system. It tracks publishers and subscribers to topics as well as services. The role of the master is to enable individual ROS nodes to locate one another. Once these nodes have located each other, they communicate with each other in a peer-to-peer fashion. You can see in a graphic example the steps performed in ROS to advertise a topic, subscribe to a topic, and publish a message, in the following diagram:
The master also provides Parameter Server. The master is most commonly run using the roscore command, which loads the ROS master, along with other essential components.
Parameter Server
Parameter Server is a shared, multivariable dictionary that is accessible via a network. Nodes use this server to store and retrieve parameters at runtime.
Parameter Server is implemented using XMLRPC and runs inside the ROS master, which means that its API is accessible via normal XMLRPC libraries. XMLRPC is a Remote Procedure Call (RPC) protocol that uses XML to encode its calls and HTTP as a transport mechanism.
Parameter Server uses XMLRPC data types for parameter values, which include the following:
32-bit integers
Booleans
Strings
Doubles
ISO8601 dates
Lists
Base64-encoded binary data
ROS has the rosparam tool to work with Parameter Server. The supported parameters are as follows:
rosparam list: This lists all the parameters in the server
rosparam get parameter: This gets the value of a parameter
rosparam set parameter value: This sets the value of a parameter
rosparam delete parameter: This deletes a parameter
rosparam dump file: This saves Parameter Server to a file
rosparam load file: This loads a file (with parameters) on Parameter Server
Understanding the ROS Community level
The ROS Community level concepts are the ROS resources that enable separate communities to exchange software and knowledge. These resources include the following:
Distributions: ROS distributions are collections of versioned metapackages that you can install. ROS distributions play a similar role to Linux distributions. They make it easier to install a collection of software, and they also maintain consistent versions across a set of software.
Repositories: ROS relies on a federated network of code repositories, where different institutions can develop and release their own robot software components.
The ROS Wiki: The ROS Wiki is the main forum for documenting information about ROS. Anyone can sign up for an account, contribute their own documentation, provide corrections or updates, write tutorials, and more.
Bug ticket system: If you find a problem or want to propose a new feature, ROS has this resource to do it.
Mailing lists: The ROS user-mailing list is the primary communication channel about new updates to ROS as well as a forum to ask questions about the ROS software.
ROS Answers: Users can ask questions on forums using this resource.
Blog: You can find regular updates, photos, and news at http://www.ros.org/news.
Summary
This article provided you with general information about the ROS architecture and how it works. You saw certain concepts and tools of how to interact with nodes, topics, and services.
Remember that if you have queries about something, you can use the official resources of ROS from http://www.ros.org. Additionally, you can ask the ROS Community questions at http://answers.ros.org.
Resources for Article:
Further resources on this subject:
The ROS Filesystem levels [article]
Using ROS with UAVs [article]
Face Detection and Tracking Using ROS, Open-CV and Dynamixel Servos [article]
Read more