A gentle introduction to the ROS API

7. A gentle introduction to the ROS API#

ROS comprises many software libraries that provide a wide array of useful functionality for building robot applications. The libraries you need will depend on the details of your project.

A ROS client library provides the core data structures, functions, and classes needed to develop ROS programs in a particular programming language. The functionality a client library provides to your code is called an Application Programming Interface (API), as your code can now interact with the ROS system without knowing exactly how this functionality is implemented, or needing to reproduce this functionality from scratch.

Two core client libraries you are likely to interact with frequently when developing ROS programs are:

  • rclpy : Python client library

  • rclcpp : C++ client library

The Python and C++ libraries are the most widely used client libraries in ROS, but you can find ROS client libraries for many other languages too, from Ada to JavaScript to Rust, for instance.

In this chapter, we first aim for a gentle and efficient introduction to the Python ROS API (i.e., rclpy). In service of that goal, we will purposefully ignore and/or violate various conventions and patterns. Afterwards, we will implement similar examples using the C++ ROS API (i.e., rclcpp).

Warning

The programs you will write here are not part of a package or part of any ROS workspace, nor do we provide any metadata on installation dependencies, code licenses, or authors. This would not be the way to create and maintain real-world ROS applications. You will learn how to properly create and develop ROS nodes in a new Python package in Section 8.3, and in a new C++ package in Section 8.4.

7.1. Typical structure of a ROS node#

Before we start programming, let’s identify the common parts we find in all ROS nodes, regardless of which programming language they are written in.

A ROS node normally executes the following steps:

  1. Get parameter values. Retrieve the node’s configuration, considering defaults and what may have been passed in from outside.

  2. Configure. Do whatever is necessary to configure the node, like establish connections to hardware devices.

  3. Set up ROS interfaces. Advertise topics, services, and/or actions, and subscribe to services. Each of these steps supplies a callback function (see the block below) that is registered by ROS for later invocation.

  4. Spin. Now that everything is configured and ready to go, hand control over to ROS to start the main loop. During the main loop, messages flow into the node, and ROS will invoke the callbacks you registered to handle them. We call this spinning, as the code is now repeatedly just checking for messages, and handling them. The callbacks in turn may publish new messages on other topics, call actions, or initiate any other functionality. The node typically spends most time in this step, until it is shutdown (for example, when its process is killed via CTRL-C on the terminal).

Following this structure, a main function in a ROS node is often very short: initialize and configure everything (steps 1-3), then call a spin function to let ROS take over (step 4). When you are trying to understand what is happening in a ROS node, look in the callbacks; that is where the real work is happening!

Asynchrony in Code: Callbacks

Throughout ROS, you will see a common pattern in the code of ROS nodes, which is the use of callback functions, or simply callbacks. For example, when subscribing to a topic, you supply a callback, which is a function that will be invoked each time your node receives a message on that topic. Similarly, when you advertise a service, you supply a callback that is invoked when the service is called. The same goes for actions (for handling of goals, results, and feedback) and parameters (for handling of setting new values).

Programming with callbacks may not be familiar to everyone. It differs from the standard sequential presentation of programming, in which you write a main() function that does A, then B, then C, and so on. By contrast, in ROS (and in most systems that focus on data-processing and/or control), we follow an event-based pattern. In this pattern, we do A whenever X happens, B whenever Y happens, and so on.