ROS in AIRWiki
The page you are reading is an introduction to ROS. If you are a ROS beginner, we suggest that you start from it. AIRWiki pages dedicated to ROS users also include more advanced pages such as:
- ROS summary, dealing with ROS installation/configuration, ROS packages, interaction of ROS with external systems (e.g.: Gazebo, Eclipse)
- ROS components, dealing with specific ROS components (such as tf, the parameter server, rviz...)
ROS in general
ROS (Robot Operating System) is an open-source framework for the creation of software for robots. It is a very interesting tool, since it promises to take care of many of the lower-level issues that make realizing the software for autonomous robots so difficult and time-consuming. By leaving such issues (e.g., communication among modules) to ROS, a researcher can focus on the more interesting high-level issues (e.g., perception). In the words of its creators:
"ROS provides libraries and tools to help software developers create robot applications. It provides hardware abstraction, device drivers, libraries, visualizers, message-passing, package management, and more."
ROS includes a large collection of packages that you can incorporate into your own system. A ROS package is a bundle of software dedicated to a single functionality (e.g., data acquisition from a laser scanner). Your own ROS-based applications will take the form of one or more ROS packages. If they can be useful to other people as well, once they are completed and tested such packages could become part of ROS: much of ROS was born in this way.
Though striving to be as easy-to-use as possible, ROS is a complex tool. This is unavoidable, as autonomous robots themselves are very complex systems. Before you can start writing your own ROS-based software, you have to devote a fair amount of time to studying how it works and how to use it. This HOWTO will help you to start using ROS as quickly as possible.
How to get ROS
This is probably the single aspect where the ROS team succeeded best in removing all the difficulties, even for beginners. Installing ROS is very simple: this webpage tells you how (just follow the link dedicated to your Operating System). Please note that the instructions there include permanently setting the appropriate environment variables: you can check that with
$ export | grep ROS
You won't need to do that again, so ignore any instructions about this (e.g. about using command source) that subsequent tutorials will give you.
Installing additional packages
[Note: this section may be partly obsolete. Starting from ROS version Groovy, stacks are no longer used and the basic unit of ROS is the package. (Yes, there is the new concept of metapackage that in some ways substitutes that of stacks, but metapackages are indeed packages.)]
ROS packages are subdivided into groups called stacks. When you install ROS, not all the stacks are installed. You can see which of them are available on your PC by opening a terminal and running
Sometimes you need a ROS package that is not installed, i.e. that is not present in any of the installed stacks. For example, let's say that you need the driver for a Hokuyo laser range scanner. The best way to get it is to install the stack that includes the package. To know the name of such stack, you can go to the webpage dedicated to the package in the ROS wiki (so you need to know the name of the package). In our example the package is called hokuyo_node, and its webpage is this one. At the top of the package webpage, just under the name of the package, you will find the name of the stack it belongs to (and the name of the other packages of the stack) in the form
- name_of_the_stack: name_of_package_1 / name_of_package_2 ...
In our example, the stack is called laser_drivers.
Now you can install the stack. As we said above, ROS is usually installed using the same tools used for all the other software available for your operating system. To install an additional ROS stack, you will use those same tools. For instance, in Ubuntu Linux you can use Ubuntu Software Center, Synaptic or (from the command-line) apt-get.
Generally the name of the software package corrisponding to a ROS stack is the name of the stack with additional information attached to identify what version of ROS it belongs to. For instance, if your version of ROS is the one called "electric", you will have to look for something called ros-electric-laser-drivers. If you are using apt-get you can install this software package (which, as we said, includes the ROS stack named laser_drivers) with
sudo apt-get install ros-electric-laser-drivers
(you will be asked for your administrative password).
Finally, sometimes the stack you are looking for is not available as a software package because it's experimental. In this case you can install its source code by following these instructions.
More about ROS packages
There are many other things that can be said about ROS packages, such as what is their internal structure. You can find this information in the AIRWiki page ROS summary.
How to get information about ROS
The ROS website includes a good deal of information, structured as a wiki (just like AIRWiki). You are invited to use it heavily, and it's important that you learn to find things in it. That said, not always the information provided by the ROS wiki is very clear for someone who is not an expert in ROS, nor all topics are equally covered.
To make things worse, the ROS wiki distinctly lacks structure. It is basically a collection of pages dedicated to single packages, and little structure or classification information is provided. The result is that, more often than not, even when you are looking for information that is in the wiki you are not able to reach it easily. Usually you have to google for the topic you're interested in, read something here and there on the Internet, try to identify a set of ROS packages that could be interesting for your problem, and only then you can go to the ROS wiki and look for such packages. Often you get to interesting wiki pages by chance: i.e., by clicking on a promising link located in a page which only marginally deals with the topic. So... explore!
This page of AIRWiki tries to complement what's provided by the ROS website with additional information, instead of saying the same things in a different way. In a nutshell, this HOWTO is a structured collection of whatever it would have been nice to find in the official documentation about ROS, but wasn't there (or was hidden too well).
ROS includes a rather comprehensive set of tutorials, some of which are listed here. Most tutorials, however, are only accessible from the "Package links" section of the relevant packages: so, unfortunately, you will have to hunt through the ROS wiki to find them.
ROS tutorials are extremely useful, though not always 100% accurate. (E.g.: something does not work as described, or leads to unexpected errors, and you have to work out why for yourself. By doing so you learn a lot, but you also lose a great deal of time.) The ROS tutorials are subdivided in "difficulty levels". Currently the levels are "basic" and "intermediate". Keep in mind that the tutorials have been written by different people at different times: so don't expect two tutorial of the same "level" to be consistent in what they assume you already know!
Arguably, the most useful tools to learn how to use ROS are the basic tutorials. Be sure to go through them before writing a single line of code (except those that you will write for the tutorial, of course!). Once you have worked your way through the tutorials, the next thing to do is to write your own ROS package and apply what you have learned. The "Nodes" section below is the suggested starting point for that. Maybe you should start experimenting with a "test" package, before passing to a real application: in this way you can experiment without worrying if the end result is a mess :-) You are strongly invited to experiment: as usually happens in computer programming, that's the best way to understand and check if you have correctly understood, all at the same time.
Before you start with the tutorials, please read this introduction to the concepts behind ROS: you will not necessarily understand how things are done in practice until you will have completed the tutorials, but reading it before passing to these will provide you with useful background.
As already said, the most common page of the ROS website is the one dedicated to a specific package. It is the starting point to learn everything about that package. One of the most important elements of package pages is the blue rectangle called Package Links. It includes links to many useful resources for users of such package, such as tutorials, FAQ and more.
ROS features and basic usage
ROS, per se, does not force the developer to use a specific programming language. In practice, while there are expansion plans for the future, at present only two languages are supported: C++ and Python. That is to say, only for these two languages ROS provides client libraries that enable non-ROS software to interface with ROS. Such libraries are called roscpp (for C++) and rospy (for Python).
You can choose what language to use on a module-per-module basis, choosing C++ or Python (or whatever other language will be supported in the future) separately for each software module of your ROS system. As we will explain shortly, ROS software modules are called nodes.
Measurement units and conventions
ROS faces the same problems that any software system which has to deal with physical quantities (to cite but one: position in space!) has to tackle: namely, (i) choice of measurement units and (ii) choice of measurement conventions (e.g., orientation of coordinate systems). Unfortunately, programming languages do not allow physical dimensions or conventions to be attached to data; therefore, these have to be specified outside the software. For ROS, units and conventions are specified here.
In particular, ROS measurement units are those of the Metric System: metre, kilogram, second, Ampére, radian, Hertz, Newton, Watt, Volt, Celsius.
Starting and stopping ROS components
One powerful feature of ROS is that (provided that roscore is running) you can start or stop any element of ROS (both nodes and debugging tools like rxconsole or rviz) whenever you like: the ROS system will automatically react to the changes and reconfigure itself. (To stop a ROS element you can simply highlight the terminal window where it was launched and press Ctrl+C.)
In some cases ROS or one of its elements take a few seconds to react to the starting or stopping of a ROS module. In other cases, something just gets stuck and has to be stopped and started again (rxgraph, notably): however, this happens rarely.
The basic element, or module, of a ROS-based software system is the node. A node executes one or more tasks and communicates with other nodes using the ROS infrastructure. Such communication takes the form of an exchange of messages. For instance, messages can be used to pass sensor data to a processing node, or to issue commands to a motor-controlling node. Many predefined types of ROS messages are available; if none of them suits your requirements, you can define new message types. A message can include a timestamp, which tells to the recipient when it has been generated: this is especially useful when dealing with sensor data. ROS usually calls "Stamped" a message type that includes a timestamp; this is useful to keep in mind while choosing among available message types.
Communications among modules of a ROS system should always be performed using ROS messages. In fact, the many powerful tools provided by ROS to collect, analyze and debug such communications are all targeted to the processing of messages, and become useless if your system does not use these.
Of course, there are real-world situations when communicating data through messages is not feasible because it would require too many resources. For instance, this happens when a large set of data (such as a complex map) has to be processed by different modules. Using messages to provide the map to each module would require the frequent generation of copies of it (thus wasting both CPU time, for creation, and RAM space, for storage). The typical solution to such problems is to let all the modules involved access the RAM area where the data are stored, thus exchanging only pointers; however, ROS provides its own solutions, which you should investigate first. One of these is the use of nodelets.
A key aspect of the communications among ROS nodes is that, as they are based on the exchange of ROS messages, they are asynchronous. Outgoing messages are delivered as soon as the ROS system manages to free the necessary resources, are stored in a queue by the receiving node(s), and the node(s) processes them as soon as it is able to (i.e., as soon as it "awakens" if it is executed periodically). An important consequence of this is that you must never count on correct message timing, or even on correct ordering, for critical aspects of your system's functioning. Your ROS system must be tolerant of alterations in the message flow such as delays, misordering, or loss.
Messages that deal with the same aspect of the functioning of the robot can be grouped by publishing them on the same topic. A topic identifies a "communication channel", shared by nodes that deal with the same aspect of the robot. Each node can subscribe to the topics it is interested in (thus receiving all the messages that are published on them, without being bothered by messages published on other topics) and/or publish messages on them (thus ensuring that its messages reach all interested nodes).
A node can perform several types of activities, including:
- publishing messages on a ROS topic;
- requesting a service from ROS servers, i.e. acting as a ROS client;
- acting as a ROS server, i.e. providing services to ROS clients;
- executing a task whenever a message is published on a ROS topic;
- managing a timeout and executing a task if it expires;
- execute a task periodically.
These are the activities that are concerned with the interaction between the node and the whole ROS system it is part of. In addition to them, the node can perform internal activities, such as (for instance) data processing. While the ways in which a node interacts with the ROS system are defined by ROS and are based on the use of ROS tools, the internal activities of a node are not constrained by ROS in any way (though of course if they use up too much resources, such as RAM or processing power, they can affect the rest of the system indirectly).
In practice, a node is implemented as a single executable file. This is produced from a source code file written in one of the programming languages supported by ROS. For instance, if you use C++ you will have to write a .cpp file comprising a main block (and anything else your program needs to work, such as additional functions, data types, #include directives and so on).
AIRLab's general node template
AIRLab's general ROS node template provides all the elements that you need to set up the structure of the .cpp file of a basic ROS node. By using the template, you can immediately write a node capable of all the types of activities that a node can perform (as described above). The template includes a single C++ class comprehending a suitable set of member variables and member functions, plus a very simple main block. By uncommenting the parts of the template that you require for your ROS node, you can quickly set up the structure of the node. Moreover, the template includes notes and comments that explain how a ROS node is built and works in practice.
Building ROS software
[Note: this section only deals with C++ software. TODO: expand to the Python case.]
ROS packages, either provided by ROS or user-generated, take the form of C++ or Python software. Such software defines the structure and operation of the ROS nodes of the package. To know what a node can do, see the section dedicated to ROS nodes.
When C++ is used, the .cpp files that define ROS nodes can have any name, provided that such names are legal.
Currently, the system used by ROS to manage package building is called catkin. Refer to this tutorial for instructions about using catkin to compile ROS software. The key command used to compile software is
When a .cpp file is compiled by ROS the files that are generated by the compilation process (which include the executables in the /bin subdirectory) take the names that are specified by the CMakeLists.txt file. The syntax used in this file is described here. For instance, putting in CMakeLists.txt the line
instructs catkin_make to compile file name_of.cpp and call name_of_executable the resulting binary file.
The name of the executable takes the role of the name of type of node. All ROS nodes which are run using such executable are of the same type (i.e., they work in the exact same way) but take different names depending on the way they are run.
If a single instance of such type of node is run with rosrun, it will take as its own name the name of its type; when, instead, one or more instances of such type of node are run with roslaunch, each of them can be given an arbitrary individual name.
When a ROS node is run using rosrun, the running ROS node takes the name specified in the associated .cpp file. Precisely, the .cpp file that defines the node always includes a statement like
ros::init(argc, argv, "name_of_the_node");
The string between quotes is the name given to the running node.
Usually (for complex ROS systems, at least) nodes are not run with rosrun. roslaunch is used instead, to perform several operations at the same time (including, but not limited to, running nodes). In this case, the names taken by the running nodes are specified by the .launch file passed to roslaunch. For instance, if file launchfile includes the line
<node pkg="name_of_package" type="name_of_executable" name="name_of_the_node" />
a node called name_of_the_node will be run using the executable name_of_the_executable contained in package name_of_package.
In a running ROS system, each node must have a unique name. If a new node with the same name of one that is already active is run, the older node is stopped by ROS (and a warning is generated). A consequence of this is that you can't run two or more nodes of the same type with rosrun, as only one of them (the last) would remain active. This is a case when running the nodes with roslaunch is a necessity, because you need to assign a different name to each instance of the node.