ROS is more than a development framework. We can refer to ROS as a meta-operating system, since it offers not only tools and libraries but even OS-like functions, such as hardware abstraction, package management, and a developer toolchain. Like a real operating system, ROS files are organized on the hard disk in a particular manner, as depicted in the following figure:
Figure 1: ROS filesystem level
Here are the explanations for each block in the filesystem:
- Packages: The ROS packages are the most basic unit of the ROS software. They contain one or more ROS programs (nodes), libraries, configuration files, and so on, which are organized together as a single unit. Packages are the atomic build item and release item in the ROS software.
- Package manifest: The package manifest file is inside a package that contains information about the package, author, license, dependencies, compilation flags, and so on. The
package.xml
file inside the ROS package is the manifest file of that package. - Metapackages: The term metapackage refers to one or more related packages which can be loosely grouped together. In principle, metapackages are virtual packages that don't contain any source code or typical files usually found in packages.
- Metapackages manifest: The metapackage manifest is similar to the package manifest, the difference being that it might include packages inside it as runtime dependencies and declare an
export
tag. - Messages (
.msg
): The ROS messages are a type of information that is sent from one ROS process to the other. We can define a custom message inside themsg
folder inside a package (my_package/msg/MyMessageType.msg
). The extension of the message file is.msg
. - Services (
.srv
): The ROS service is a kind of request/reply interaction between processes. The reply and request data types can be defined inside thesrv
folder inside the package (my_package/srv/MyServiceType.srv
). - Repositories: Most of the ROS packages are maintained using a Version Control System (VCS), such as Git, Subversion (svn), Mercurial (hg), and so on. The collection of packages that share a common VCS can be called repositories. The package in the repositories can be released using a catkin release automation tool called
bloom
.
The following screenshot gives you an idea of the files and folders of a package that we are going to create in the upcoming sections:
Figure 2: List of files inside the exercise package
A typical structure of an ROS package is shown here:
Figure 3: Structure of a typical C++ ROS package
We can discuss the use of each folder as follows:
config
: All configuration files that are used in this ROS package are kept in this folder. This folder is created by the user and it is a common practice to name the folderconfig
to keep the configuration files in it.include/package_name
: This folder consists of headers and libraries that we need to use inside the package.script
: This folder keeps executable Python scripts. In the block diagram, we can see two example scripts.src
: This folder stores the C++ source codes.launch
: This folder keeps the launch files that are used to launch one or more ROS nodes.msg
: This folder contains custom message definitions.srv
: This folder contains the services definitions.action
: This folder contains the action files. We will see more about these kind of files in the next chapter.package.xml
: This is the package manifest file of this package.CMakeLists.txt
: This files contains the directives to compile the package.
We need to know some commands to create, modify, and work with the ROS packages. Here are some of the commands used to work with ROS packages:
catkin_create_pkg
: This command is used to create a new packagerospack
: This command is used to get information about the package in the filesystemcatkin_make
: This command is used to build the packages in the workspacerosdep
: This command will install the system dependencies required for this package
To work with packages, ROS provides a bash-like command called rosbash
(http://wiki.ros.org/rosbash), which can be used to navigate and manipulate the ROS package. Here are some of the rosbash
commands:
roscd
: This command is used to change the current directory using a package name, stack name, or a special location. If we give the argument a package name, it will switch to that package folder.roscp
: This command is used to copy a file from a package.rosed
: This command is used to edit a file using the vim editor.rosrun
: This command is used to run an executable inside a package.
The definition of package.xml
of a typical package is shown in the following screenshot:
Figure 4: Structure of package.xml
The package.xml
file consists of the package name, version of the package, the package description, author details, package build dependencies, and runtime dependencies. The <build_depend></build_depend>
tag includes the packages that are necessary to build the source code of the package. The packages inside the <run_depend></run_depend>
tags are necessary during runtime of the package node.
Metapackages are specialized packages in ROS that only contain one file, that is, a package.xml
file. They don't contain folders and files like a normal package.
Metapackages simply group a set of multiple packages as a single logical package. In the package.xml
file, the metapackage contains an export
tag, as shown here:
<export> <metapackage/> </export>
Also, in metapackages, there are no <buildtool_depend>
dependencies for catkin
; there are only <run_depend>
dependencies, which are the packages grouped in the metapackage.
The ROS navigation stack is a good example of metapackages. If ROS and its navigation package are installed, we can try the following command, by switching to the navigation metapackage folder:
$ roscd navigation
Open package.xml
using your favorite text editor (gedit
in the following case):
$ gedit package.xml
This is a lengthy file; here is a stripped-down version of it:
Figure 5: Structure of meta-package package.xml
The ROS nodes can write or read data that has a different type. The types of data are described using a simplified message description language, also called ROS messages. These datatype descriptions can be used to generate source code for the appropriate message type in different target languages.
The data type description of ROS messages is stored in .msg
files in the msg
subdirectory of a ROS package. Even though the ROS framework provides a large set of robotic-specific messages already implemented, developers can define their own message type inside their nodes.
The message definition can consist of two types: fields
and constants
. The field is split into field types and field names. The field type is the data type of the transmitting message and field name is the name of it. The constants define a constant value in the message
file.
Here is an example of message definitions:
int32 number string name float32 speed
Here, the first part is the field type and the second is the field name. The field type is the data type and the field name can be used to access the value from the message. For example, we can use msg.number
for accessing the value of the number from the message.
Here is a table showing some of the built-in field types that we can use in our message:
Primitive type | Serialization | C++ | Python |
| Unsigned 8-bit int |
|
|
| Signed 8-bit int |
|
|
| Unsigned 8-bit int |
|
|
| Signed 16-bit int |
|
|
| Unsigned 16-bit int |
|
|
| Signed 32-bit int |
|
|
| Unsigned 32-bit int |
|
|
| Signed 64-bit int |
|
|
| Unsigned 64-bit int |
|
|
| 32-bit IEEE float |
|
|
| 64-bit IEEE float |
|
|
| ascii string(4) |
|
|
| secs/nsecs unsigned 32-bit ints |
|
|
| secs/nsecs signed 32-bit ints |
|
|
Other kinds of messages are designed to cover a specific application necessity, such as exchanging common geometrical (geometry_msgs
) or sensor (sensor_msgs
) information. A special type of ROS message is called a message header. Headers can carry information, such as time, frame of reference or frame_id
, and sequence number. Using headers, we will get numbered messages and more clarity in who is sending the current message. The header information is mainly used to send data such as robot joint transforms (TF). Here is an example of the message header:
uint32 seq time stamp string frame_id
The rosmsg
command tool can be used to inspect the message header and the field types. The following command helps to view the message header of a particular message:
$ rosmsg show std_msgs/Header
This will give you an output like the preceding example message header. We will look at the rosmsg
command and how to work with custom message definitions further in the upcoming sections.
The ROS services are a type request/response communication between ROS nodes. One node will send a request and wait until it gets a response from the other. The request/response communication is also using the ROS message description.
Similar to the message definitions using the ".msg"
file, we have to define the service definition in another file called ".srv"
, which has to be kept inside the srv
subdirectory of the package. Similar to the message definition, a service description language is used to define the ROS service types.
An example service description format is as follows:
#Request message type string str --- #Response message type string str
The first section is the message type of the request that is separated by ---
and in the next section is the message type of the response. In these examples, both Request
and Response
are strings.
In the upcoming sections, we will look at how to work with ROS services.