Tutorial 1: ROS basic concepts and development tools
Objective: This tutorial session is first devoted to introduce the basic concepts of ROS, to give set-up instructions and to use some command-line tools to get an insight of ROS. Secondly, the ROS 2 ecosystem system is presented to learn how to structure and build ROS programs.
Contents
After this session you will be able to:
Describe the basic concepts of ROS.
Run a simple ROS 2 program and use the main basic ROS 2 command-line tools to analyze it: rosnode, rqt_graph, rostopic, rosmsg.
Understand the ROS 2 build system and create a simple package.
References:
This tutorial is partially based on:
The official ROS 2 tutorials.
The ROS 2 Concepts documentation.
Chapters 1 and 2 of the book Concise Introduction to Robot Programming With Ros2 by Francisco Martín Rico (examples: https://github.com/fmrico/book_ros2).
Introduction
Objective and motivation
Imagine that you want to execute several software processes in one or multiple computers, which analyze data from multiple sensors (e.g., cameras or LiDARs); process it and/or compute actuation commands (algorithms); and interact with actuation devices (e.g., motors).
At first glance, you would start a piece of code (e.g., Python or C++) that uses external libraries to interact with sensors’ and actuators’ drivers (e.g., read and write operations) while performing some processing (algorithms). Even though this option might work in some cases, it’s not actually modular, and you would end up with a gigantic monolithic piece of software.
A potential solution to gain modularity is to find a middleware software that can handle communications between different processes, hence drivers and algorithms might live in different pieces of code and be executed as separated processes (even in different computers), while the middleware is in charge of moving data between them.
Middleware
A middleware is a computer software that connects software components or applications (middleware sits “in the middle” between software applications that may be run on different operating systems).
Middleware is useful for:
Portability: they offer a common programming model across languages and/or platforms.
Reliability: they are developed and tested separately from the final application.
Managing complexity: they manage low-level aspects using suitable libraries that abstract specific operating system and hardware aspects.
Robotic systems are usually complex systems built on many hardware and software components, as sensors and actuators as well as planners and control algorithms.
The use of a robotic middleware allows different components to exchange data using a common communication channel and using interfaces that are consistent between different applications. This eases the sharing and reuse of code among different projects.
What is ROS?
ROS is a robotic middleware and more:
“The Robot Operating System (ROS) is a flexible framework for writing robot software. It is a collection of tools, libraries, and conventions that aim to simplify the task of creating complex and robust robot behavior across a wide variety of robotic platforms.”
Some of the main features of ROS are:
Distributed computation: Many modern robot systems rely on software that spans different processes and runs across several computers.
ROS covers the need for communication between multiple processes that may or may not live on the same computer.
ROS’s message passing interface is becoming a de facto standard for robot software interoperability.
Software reuse: The rapid progress of robotics research has resulted in a growing collection of good algorithms for common tasks such as navigation, motion planning, mapping, and many others.
ROS’s standard packages provide stable, debugged implementations of many important robotics algorithms.
Rapid testing: The testing of real robotic systems applications can be time-consuming and error-prone.
ROS facilitates the separation of high-level processing and decision-making programs from the low-level direct control of the hardware, which can be temporarily replaced by a simulator.
ROS also provides a simple way to record and play back sensor data and other kinds of message.
The real robot, the simulator, and the bag playback mechanism can all provide identical (or at least very similar) interfaces.
Development toolset: One of the strongest features of ROS is the powerful development toolset. These tools support introspecting, debugging, plotting, and visualizing the state of the system being developed.
ROS is licensed under an open source, BSD license.
What ROS is not
An actual operating system. Although it offers services you would expect from an operating system, including hardware abstraction, low-level device control, implementation of commonly-used functionality, message-passing between processes, and package management.
A programming language. ROS programs are written in C++ and client libraries are also available in Python, Java, Lisp, …
A programming environment / IDE. It is common to simply use a text editor and the command line, without any IDE.
A hard real-time architecture. ROS is fast, and is used for online operation in many robots, it is inherently best-effort in many cases, and does not provide guarantees about the timing of operations (should not use ROS for operations that have strict timing requirements).
Communication infrastructure
ROS applications are composed of several processes loosely coupled, connected as nodes of a peer-to-peer network (the ROS runtime graph) using different types of strongly-typed communication.
At the lowest level, ROS offers a message passing interface that provides inter-process communication. The ROS middleware provides:
Publish/subscribe anonymous message passing: ROS’s built-in and well-tested messaging system saves you time by managing the details of communication between distributed nodes via the anonymous publish/subscribe mechanism. Topics are named buses over which nodes exchange messages.
Request/response remote procedure calls: Asynchronous/synchronous request/response interactions between processes. A Service is defined by a pair of messages: one for the request and one for the response.
Preemptable Remote Procedure Calls: Used for communications where you need to initiate a goal-seeking behavior, monitor its progress, be able to preempt it along the way, and receive notification when it is complete.
Recording and playback of messages: Because the publish/subscribe system is anonymous and asynchronous, the data can be easily captured and replayed without any changes to code.
Robot-specific features
Some of the robot-specific capabilities that ROS provides are:
Standard Message Definitions for Robots: There are message definitions for geometric concepts like poses, transforms, and vectors; for sensors like cameras, IMUs and lasers; and for navigation data like odometry, paths, and maps; among many others.
Robot Geometry Library: The tf library manages coordinate transform data for robots (both static transforms, such as a camera that is fixed to a mobile base, and dynamic transforms, such as a joint in a robot arm).
Robot Description Language: The Unified Robot Description Format (URDF) allows you to describe, using an XML document, the physical properties of your robot, from the lengths of limbs and sizes of wheels to the locations of sensors and the visual appearance of each part of the robot.
ROS versions and distributions
There exist two versions of ROS:
ROS 1, that ends its official support on May 2025
ROS 2
Be aware that we will use ROS 2 during this course. However, there could be cases where you find robots using ROS 1 implementations due to slow development timelines or production requirements. In ROS 1 cases, what you will see in this course is still valid at high level but you will need to check the ROS 1 wiki to adapt your API. The ROS 1 version of this course is: Introduction to ROS 1.
An interesting comparison between versions is explained at this talk ROS1 vs ROS2 - Practical Overview for ROS Developers by Edouard Renard from Robotics Back-End.
The packages in a ROS version are organized in distributions, with the purpose of letting developers work against a relatively stable codebase. ROS 2 uses Ubuntu Linux as a reference and since Ubuntu LTS releases come with 5 years of standard support, ROS 2 distributions match the same duration: in even years new ROS 2 releases will happen one month after the Ubuntu LTS has been released.
The current ROS 2 distribution is Jazzy Jalisco, tied to Ubuntu 24.04, that was released on May 23rd, 2024, and the EOL is May 2029. However, in this course we will use the Humble Hawksbill distribution because in the ETSEIB computer rooms the operating system during academic year 2024-25 is Ubuntu 22.04. Follow this link for further details on distributions.
Some applications
Teleoperation of a hand-arm system
A system composed of two industrial robots equipped with mechanical hands is teleoperated by an operator equipped with two sensorized gloves and two wrist trackers.
Nodes #1 to #4 broadcast glove and tracker data using Topics. Nodes #5 and#6 subscribe to them.
Nodes #5 and#6 perform the mapping and send queries to Node #7 (the collision-check node) using Services.
Nodes #5 and#6 broadcasted collision-free configurations using Topics. Nodes #8 and #9 subscribe to them.
Nodes #8 and #9 send the motion commands to Nodes #10 to #13 (the nodes that control the mechanical hands and the robots) using Actions.
An industrial mobile platform
Core components
The ROS 2 Design
ROS 2 has been designed with different layers:
OS Layer: ROS 2 runs on different operating systems (Linux, Windows, Mac).
ROS Middleware Layer (RMW): ROS 2 is based on the Data Distribution Service (DDS), an open standard for communications implemented over the User Datagram Protocol (UDP). Since different DDS vendors can be used, a DDS API is provided, the ROS Middleware interface (rmw). The adoption of DDS allows the exchange of information between processes with real-time characteristics, security capabilities, and custom quality of service of each connection.
ROS client library (RCL): The basic functionality of all ROS 2 elements are implemented in a single C library called rcl; then the libraries rclcpp and rclpy adapt this functionality to the particularities of each language, C++ and Python, respectively. APIs for other languages can be implemented. This allows new functionalities to be available sooner in any language.
User Code: ROS 2 provides a convention on how to write nodes by forcing implemented nodes to inherit from a Node class that already has all the functionalities required. This saves a lot of time, facilitates a good modular structure and eases the cooperation between projects.
Further reading
Graph Concepts
Nodes: A node is an entity that uses ROS to communicate with other nodes.
A node is a participant in the ROS graph.
ROS nodes use a ROS client library to communicate with other nodes.
Nodes can publish or subscribe to Topics.
Nodes can also provide or use Services and Actions.
There are configurable Parameters associated with a node.
Connections between nodes are established through a distributed discovery process.
Nodes may be located in the same process, in different processes, or on different machines.
Discovery: By using the DDS communication middleware protocol, in ROS 2 each node has the capacity to discover any other node, which gives rise to fully distributed systems.
DDS allows to have different logical networks sharing a single physical network by using the Domain ID mechanism. ROS 2 nodes on the same domain can freely discover and send messages to each other, while ROS 2 nodes on different domains cannot. All ROS 2 nodes use domain ID 0 by default. To avoid interference between different groups of computers running ROS 2 on the same network, a different domain ID should be set for each group.
Discovery of nodes happens automatically following these steps:
When a node is started, it advertises its presence to other nodes on the network with the same ROS domain (set with the ROS_DOMAIN_ID environment variable).
Nodes respond to this advertisement with information about themselves so that the appropriate connections can be made, and the nodes can communicate.
Nodes periodically advertise their presence so that connections can be made with new-found entities, even after the initial discovery period.
Nodes advertise to other nodes when they go offline.
Nodes will only establish connections with other nodes if they have compatible Quality of Service (QoS).
Parameters: Parameters are specific to the node, i.e. nodes declare and manage their parameters, which are destroyed when the nodes are killed.
Parameters are used to configure nodes at startup (and during runtime), without changing the code.
When a node is started, some services are created to interact with the parameters. Additionally, the created parameters can be modified by binding a callback to each one, that can be called on the fly to modify their values.
Further reading
Publish/subscribe anonymous message passing
Topics: Topics are the communication channel funneling the messages.
Nodes can publish messages to a topic as well as subscribe to a topic to receive messages.
Any publishers and subscribers that are on the same topic name can directly communicate with each other. There may be zero or more publishers and zero or more subscribers on any particular topic. When data is published to the topic by any of the publishers, all subscribers in the system will receive the data.
Messages: ROS data type used when subscribing or publishing to a topic.
Nodes communicate with each other using standard or user-defined (compound) messages.
Standard primitive types are supported (integer, floating point, boolean, …).
Standard messages for common geometric primitives such as points, vectors, and poses are provided.
Standard messages for commonly used sensors, including cameras and scanning laser range finders are also available.
The types of each field in a ROS message are typed, e.g. Vector3 and Twist from the geometric messages package:
# Vector3 represents a vector in free space.
float64 x
float64 y
float64 z
# Twist expresses velocity in free space broken into its linear and angular parts.
Vector3 linear
Vector3 angular
A user-defined message could be:
uint32 field1
string field2
Twist field3
Further reading
Communication using topics will be explained in Tutorial 2: Communications using topics.
Request/response remote procedure calls
The request/response remote procedure calls implemented by the ROS 2 services are asynchronous, i.e. the client thread is not stuck when calling the service, although they can be made synchronous (in ROS 1 they were just synchronous).
With this asynchronous implementation, the service call immediately return future, a value that indicates whether the call and response is finished (not the value of the response itself). This value may be queried for a response at any time, e.g. using a timer or callback, a dedicated thread, or by another method. It is up to the client to decide how to store future, check on its status, and retrieve the response.
The service call can be made synchronous with a loop that waits for future to have a given value:
auto result = client->async_send_request(request);
// Wait for the result.
if (rclcpp::spin_until_future_complete(node, result) ==
rclcpp::FutureReturnCode::SUCCESS)
{
...
} else {
...
}
Further reading
Communication using services will be explained in Tutorial 4: Communications using services.
Preemptable Remote Procedure Calls
The preemptable remote procedure calls implemented by the ROS 2 actions are part of the core functionalities i.e. they are included in the client library implementations (in ROS 1 they were implemented as the actionlib separate library).
The API for actions use a combination of topics and services:
Topics are used for getting feedback and goal status.
Services are used for setting the goal, canceling a goal or requesting a result.
Further reading
Communication using actions will be explained in Tutorial 6: Communications using actions.
Interfaces
Messages, services and actions are described in text files using an Interface Definition Language (IDL), that allows ROS tools to automatically generate source code for the interface type in several target languages:
msg: .msg files are simple text files that describe the fields of a ROS message. They are used to generate source code for messages in different languages.srv: .srv files describe a service. They are composed of two parts: a request and a response. The request and response are message declarations.action: .action files describe actions. They are composed of three parts: a goal, a result, and feedback. Each part is a message declaration itself.
Further reading
Quality of Service (QoS)
In ROS 2 we can choose how nodes handle information, according to the application and the network, e.g.:
We may want to be sure all messages are received, or
it may be ok to lose some messages as long as the data is frequently updated, or
we want to maintain a queue of messages for the case when the node is not able to handle them all, or
it may be ok to drop any new message that arrives while the callback of the previous one is running.
i.e., ROS 2 allows for data flow configuration by settings message reliability, deadline, and priority, which can ensure that critical messages are delivered on time.
More details in tutorial 2 The Quality of service (QoS) section.
Further reading
ROS 2 Documentation - Concepts: About Quality of Service Settings.
Security
ROS 2 includes the ability to secure communications among nodes within the ROS 2 computational graph. Similar to discovery, security happens through the underlying ROS 2 middleware (provided it has support for the corresponding security plugins). No additional software installation is needed to enable security; however, the middleware requires configuration files for each ROS graph participant. These files enable encryption and authentication, and define policies both for individual nodes and for the overall ROS graph. ROS 2 also adds a master “on/off” switch to control security behavior.
ROS utilities can create the authoritative trust anchor for a ROS application, or an external certificate authority can be used.
Further reading
Command-line tools
Command-line usage
First, follow the Configuring the ROS user environment to check if your ROS 2 setup is well configured.
With a correct ROS 2 environment set, you should be able to:
Use the ROS 2 command-line tools: Set of instructions to execute in a bash session (e.g., in the Terminal).
Create and build a ROS 2 workspace: Here you will store you code in specific structures of folders and files and compile them so ROS 2 can run the scripts and binaries.
Use ROS 2 libraries inside your own code (i.e., include or import the ROS 2 libraries).
ROS 2 includes a suite of command-line tools for introspecting a ROS 2 system using a bash session (i.e., a Terminal).
The main entry point for the tools is the command ros2, which itself has various sub-commands for introspecting and working with nodes, topics, services, and more.
To see all available sub-commands, open a terminal (e.g., Ctrl+Alt+T) and execute:
ros2 --help
Examples of sub-commands that are available include (highlighted in bold the most common ones):
action: Introspect/interact with ROS actions
bag: Record/play a rosbag
component: Manage component containers
daemon: Introspect/configure the ROS 2 daemon
doctor: Check ROS setup for potential issues
interface: Show information about ROS interfaces
launch: Run/introspect a launch file
lifecycle: Introspect/manage nodes with managed lifecycles
node: Introspect ROS nodes
param: Introspect/configure parameters on a node
pkg: Introspect ROS packages
run: Run ROS nodes
security: Configure security settings
service: Introspect/call ROS services
test: Run a ROS launch test
topic: Introspect/publish ROS topics
trace: Tracing tools to get information on ROS nodes execution (only available on Linux)
Clean up
To kill ROS 2 processes in a specific terminal you can execute Ctrl+C in it.
Exercise
Using ros2 topic command:
Publish a string into a topic named chatter (directly from the command line) and listen to chatter topic to see its content (directly from command line):
In a terminal (
Ctrl+Alt+T), execute this command:
ros2 topic pub /chatter std_msgs/msg/String "data: Already using ROS 2!"
Open a second terminal (
Ctrl+Alt+T), and now execute this command:
ros2 topic echo /chatter
Behind the scenes: ROS 2 uses a distributed discovery process for nodes to connect to each other.
As this process purposefully does not use a centralized discovery mechanism (like the ROS Master in ROS 1),
it can take time for ROS nodes to discover all other participants in the ROS graph.
Because of this, there is a long-running daemon in the background that stores information about the ROS graph to provide faster responses to queries, e.g. the list of node names.
The daemon is automatically started when the relevant command-line tools are used for the first time. You can run ros2 daemon --help for more options for interacting with the daemon.
Exercise
Running nodes using ros2 run command:
run allows you to use the package name to directly run a node within a package (without having to know the package path).
ros2 run [package_name] [executable_name]
In two separate terminals, execute these commands:
ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key
You should see a graphical window with a turtle-shaped robot that lives in a square world. If you give the input focus to the second terminal (the one executing the turtle_teleop_key command) and press the Up, Down, Left, or Right keys, the turtle will move in response to your commands, leaving a trail behind it.
To run simultaneously a collection of nodes, ROS provides a mechanism to configure and launch them, based on a lightweight xml file and the roslaunch program: The launch utility.
Getting information of the ROS graph
You can see the nodes, and their associated topics, services, and actions, using the following commands and their respective subcommands:
ros2 node --help
ros2 topic --help
ros2 service --help
ros2 action --help
ros2 node
ros2 node displays information about the ROS nodes that are currently running.
For example, the following command:
ros2 node list
lists the names of the nodes that are running. Doing this after having executed the previous exercise results:
/teleop_turtle/turtlesim
Notice that node names are not necessarily the same as the names of the executables underlying those nodes. In fact, the node name can be overridden adding a parameter to ros2 run: ros2 run [package_name] [executable_name] __name:=node_name.
Exercise
Use ros 2 node info to check the basic information of the /teleop_turtle and /turtlesim nodes.
ros2 topic
The ros2 topic tool allows you to get information about ROS topics (datarate, actual data, publishers, subscribers). Its options are:
bw Display bandwidth used by topic
delay Display delay of topic from timestamp in header
echo Output messages from a topic
find Output a list of available topics of a given type
hz Print the average publishing rate to screen
info Print information about a topic
list Output a list of available topics
pub Publish a message to a topic
type Print a topic's type
Exercise
List all topics being broadcasted and check the publishing rate and the bandwidth consumed by the topic published by the /teleop_turtle node while teleoperating the turtle with the keys.
Even if you do not care much about the specific rates, these commands can be useful for debugging, because they provide an easy way to verify that messages are indeed being published regularly on particular topics.
Exercise
Echo the topic /turtle1/cmd_vel and visualize the output while teleoperating the turtle with the keys.
Exercise
Check the type of the topic /turtle1/cmd_vel.
ros2 messages
While topics are the communication channels, messages are the structure of the data being sent. To see particular messages structures we can use the command ros2 interface show <msg type>, notice how ros2 interface has other usage aside from show.
Exercise
Analyze the structure of the message type of the topic /turtle1/pose.
Exercise
Check all the interfaces of the turtlesim package.
Graph visualization
rqt_graph is the easiest way to visualize the publish-subscribe relationships between ROS nodes.
The graph of the turtlesim example is:
rqt_graph
By selecting the option Nodes/Topics (all) the graph shown is:
Exercise
Play with the options of the GUI menu.
The ROS 2 ecosystem
In ROS projects, source code is organized into ‘packages’ where each package typically consists of one or more targets when built (executables, libraries, or message definitions).
The ROS 2 ecosystem defines the structure of these packages, and how these packages should be organized and built.
The file system
The ROS 2 file system main components are:
Packages: Packages are the main unit for organizing software in ROS. A package may contain:
ROS runtime processes (nodes),
a ROS-dependent library,
datasets,
configuration files.
Packages are the most atomic build item and release item in ROS.
Metapackages: Metapackages are specialized Packages which only serve to represent a group of related other packages (formerly called Stacks).
Repositories: A collection of packages which share a common Version Control System.
To obtain a list of all the installed ROS packages use:
ros2 pkg list
To find the directory of a single package, use:
ros2 pkg prefix <package_name>
(you can use tab completion to avoid the need to remember names and to prevent any kind of typos).
The ROS 2 workspaces
A workspace is a directory that contain packages. The ROS 2 installation workspace for the current ROS 2 distribution is /opt/ros/humble/.
Packages that you create should also live together in a workspace (for this tutorial you can name it colcon_ws1), from where you will build them.
The ROS workspaces have a particular folder structure. Commonly there is a src subdirectory where the source code of ROS packages will be located, and other folders for ROS 2 internal use and that are autogenerated during compilation:
\<your_workspace>
\build # Autogenerated at compilation with ROS 2
\install # Autogenerated at compilation with ROS 2
\log # Autogenerated at compilation with ROS 2
\src # USER PACKAGES
package_1/
package.xml
...
package_2/
package.xml
...
...
The
builddirectory will be where intermediate files are stored. For each package a subfolder will be created.The
installdirectory is where each package will be installed to. By default, each package will be installed into a separate subdirectory.The
logdirectory contains various logging information about each colcon invocation.The
srcdirectory contains the source code of the packages, one folder per package containing a package manifest file (package.xml) and different subfolders depending on the programming language (Python or C++) and other particularities.
Recall that, as explained in Section Command-line usage, to use ROS 2 from any directory, and from your workspace in particular, it is necessary to source your ROS 2 installation workspace in the terminal you plan to work (or have the source command in the .bashrc to have this done automatically). This makes ROS 2 packages available for you to use in that terminal.
Similarly, in order to make your own packages available to use in the same terminal, once the packages are built you will need to source the setup.bash file of the install folder:
source install/setup.bash
For many users, there is no real need to use more than one ROS workspace, although it may be useful to maintain several independent workspaces if you are working on many packages or have several distinct projects.
As an advanced feature of ROS 2, workspaces can be stacked using the new concept of overlays. This allows to have versions of the same package in different overlays, the one in the higher level being used as shown in the figure that compares ROS 1 vs. ROS 2. With this infrastructure we can, for instance, override an installed package e.g. if you have a package that you are building from source in one directory, but have also installed using apt, sourcing the directory will ensure that the source version will be used).
Sourcing the local_setup.bash of the overlay will only add the packages available in the overlay to your environment, while sourcing setup.bash sources the overlay as well as the underlay it was created in, allowing the utilization of both workspaces.
Package structure
Each package contains a package manifest (package.xml) and different files and subfolders depending on the programming language (Python or C++), on the creation or not of proprietary ROS 2 messages or services, and on other particularities.
For CMake packages (i.e. using C++) the structure with the minimum required contents is:
my_package/
CMakeLists.txt
include/my_package/
package.xml
src/
CMakeLists.txtfile that describes how to build the code within the packageinclude/<package_name>directory containing the public headers for the packagepackage.xmlfile containing meta information about the packagesrcdirectory containing the source code for the package
For Python packages the structure with the minimum required contents is:
my_package/
package.xml
resource/my_package
setup.cfg
setup.py
my_package/
package.xmlfile containing meta information about the packageresource/<package_name>marker file for the packagesetup.cfgis required when a package has executables, so ros2 run can find themsetup.pycontaining instructions for how to install the package<package_name>- a directory with the same name as your package, used by ROS 2 tools to find your package, contains __init__.py
In these tutorials we will work with CMake packages.
The package manifest, package.xml, is a file which marks the root of a package and contains meta information about the package including its name, version, description, maintainer, license, dependencies, and more.
The contents of the package.xml are in machine readable XML format and the contents are described in the REPs 127 and 140. For CMake packages:
<?xml version="1.0"?>
<?xml-model
href="http://download.ros.org/schema/package_format3.xsd"
schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>my_package</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="user@todo.todo">user</maintainer>
<license>TODO: License declaration</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>
The build system
A build system is responsible for generating ‘targets’ from raw source code that can be used by end users.
To build targets, the build system needs information such as:
the locations of tool chain components (e.g. C++ compiler)
source code locations
code dependencies
external dependencies
where those dependencies are located
which targets should be built
where targets should be built
where targets should be installed
See Writing, compiling and linking C++ programs to see all the alternative ways to build targets.
The ROS 2 build system is called ament and is the responsible to make it easier to develop and maintain ROS 2 core packages, as well as to establish conventions so that developers should be able to pick up any ament based package and make some assumptions about how it works, how to introspect it, and how to build or use it.
The ament build system is composed of:
ament_package: Setup tools Python package that follows the ament packaging guidelines.ament_cmake: Repository containing packages that provide the necessary CMake functions/macros and CMake Modules to facilitate the use of CMake to create ament packages, i.e. packages that follow the ament packaging guidelines. (ament_cmake does not support the concept of the devel space like the ROS 1 build system and requires the package to be installed).ament_lint: Repository providing packages with linting and testing services.
The CMakeLists.txt files using the ament build system are similar to the ones using the ROS 1 build system catkin, e.g.:
1cmake_minimum_required(VERSION 3.5) 2project(br2_basics) 3 4find_package(ament_cmake REQUIRED) 5find_package(rclcpp REQUIRED) 6find_package(std_msgs REQUIRED) 7 8set(dependencies 9 rclcpp 10 std_msgs 11) 12 13add_executable(publisher src/publisher.cpp) 14ament_target_dependencies(publisher ${dependencies}) 15 16install(TARGETS 17 publisher 18 ... 19 logger 20 logger_class 21 param_reader 22 ARCHIVE DESTINATION lib 23 LIBRARY DESTINATION lib 24 RUNTIME DESTINATION lib/${PROJECT_NAME} 25) 26 27install(DIRECTORY launch config DESTINATION share/${PROJECT_NAME}) 28 29if(BUILD_TESTING) 30 find_package(ament_lint_auto REQUIRED) 31 ament_lint_auto_find_test_dependencies() 32endif() 33 34ament_export_dependencies(${dependencies}) 35ament_package()
The ROS 2 build tool is called colcon and is used to perform the task of building a workspace of packages together at once with a single invocation, i.e. it determines the dependency graph and invokes the specific build system for each package in topological order.
colcon’s workflow is very similar to CMake’s but adds support for automatic ‘find package’ infrastructure and building multiple, dependent projects at the same time.
colcon build
source install/setup.bash
Note: The setup.sh file to be sourced is in the install folder since the devel is not supported in ament.
To only process a specific package or a specific package including all the recursive dependencies it needs:
colcon build --packages-select <name-of-pkg>
colcon build --packages-up-to <name-of-pkg>
Further reading
Creating a ROS 2 workspace and a package
The workspace
First, create a directory (e.g., colcon_ws1) to contain our workspace (following the user folder structure in the ETSEIB computers do it inside the data folder):
mkdir -p ~/data/colcon_ws1/src
cd ~/data/colcon_ws1
At this point the workspace contains a single empty directory src:
.
└── src
1 directory, 0 files
The new package
Now, let’s create a new CMake package in the src folder of the workspace you just created (colcon_ws1):
cd ~/data/colcon_ws1/src
ros2 pkg create my_first_pkg --dependencies rclcpp
This command creates the skeleton of the package. The –dependencies option allows you to add the dependencies of this package and tune the package.xml and CMakeLists.txt files accordingly. Here we will use rclcpp, which are the C++ client libraries.
Once done, you will now have a new folder within your workspace src directory called my_first_pkg.
Exercise
Take a look at the my_first_pkg folder structure, the CMakeList.txt file and the package.xml file.
Now, let’s create a ROS 2 node inside your package.
Exercise
Add a new cpp file called my_node.cpp in the src package folder (i.e.,
my_first_pkg/src/my_node.cpp) with the following contents:
#include "rclcpp/rclcpp.hpp"
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("my_node");
RCLCPP_INFO(node->get_logger(), "Hello World !!!");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
Some comments on the code:
#include “rclcpp/rclcpp.hpp” allows access to most of the ROS2 types and functions in C++,
The node called my_node is created with the make_shared method of class
rclcpp::Node, and a pointer to it of typestd::shared ptris returned. The make_shared method of classrclcpp::Nodeis equivalent to:std::shared_ptr<rclcpp::Node> node = std::shared_ptr<rclcpp::Node>(new rclcpp::Node("my_node"));
The spin function creates a default single-threaded executor to spin the specified node, the function blocks the execution of the program so that it does not terminate immediately, the variant spin_some creates a default single-threaded executor and executes any immediately available work, returning once there are no left to handle.
#include "rclcpp/rclcpp.hpp" using namespace std::chrono_literals; int main(int argc, char * argv[]) { rclcpp::init(argc, argv); auto node = rclcpp::Node::make_shared("my_node"); rclcpp::Rate loop_rate(500ms); int counter = 0; while (rclcpp::ok()) { RCLCPP_INFO(node->get_logger(), "Hello %d", counter++); rclcpp::spin_some(node); loop_rate.sleep(); } rclcpp::shutdown(); return 0; }
Exercise
Edit the package CMakeList.txt to include your new node, adding the following instructions:
add_executable: the indicate the name of the result and its sources,
ament_target_dependencies: to make headers and libraries from other packages accessible for the current target (to create the dependencies variable is a good habit),
install: to indicate where to install the target.
Non-relevant parts have been removed for clarity:
1cmake_minimum_required(VERSION 3.8)
2project(my_first_pkg)
3
4if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
5 add_compile_options(-Wall -Wextra -Wpedantic)
6endif()
7
8find_package(ament_cmake REQUIRED)
9find_package(rclcpp REQUIRED)
10
11set(dependencies
12 rclcpp
13)
14add_executable(my_node src/my_node.cpp)
15ament_target_dependencies(my_node ${dependencies})
16install(TARGETS
17 my_node
18 DESTINATION lib/${PROJECT_NAME}
19)
20
21if(BUILD_TESTING)
22 find_package(ament_lint_auto REQUIRED)
23 # the following line skips the linter which checks for copyrights
24 # comment the line when a copyright and license is added to all source files
25 set(ament_cmake_copyright_FOUND TRUE)
26 # the following line skips cpplint (only works in a git repo)
27 # comment the line when this package is in a git repo and when
28 # a copyright and license is added to all source files
29 set(ament_cmake_cpplint_FOUND TRUE)
30 ament_lint_auto_find_test_dependencies()
31endif()
32
33ament_package()
Build and run the package
To build your workspace, execute the following:
cd ~/data/colcon_ws1 # Move to the workspace root
colcon build # Compile the workspace (i.e., compile all packages)
Because colcon is designed to build all of the packages in your workspace, this command must be run from your workspace directory.
It performs several configuration steps and creates subdirectories called
build,installandlogwithin your workspace.These three new directories contain build- and log-related files like automatically-generated makefiles, object code, and the executables themselves.
It is advisable to do a fresh build by running the colcon clean workspace command, that eliminates the build, install and log folders, before running the colcon build command (if not available it can be installed with sudo apt install python3-colcon-clean).
However, cleaning the workspace means that the next compilation colcon will compile all packages in it, thus taking much more time.
You can also clean a specific package with colcon clean --packages-select <package_name>.
Use clean at your own convenience.
Use your new package
To use your new package and executable, first open a new terminal and source your ROS 2 workspace, for instance
cd ~/data/colcon_ws1 # Move to the workspace root
source install/setup.bash # Call the ROS 2 directives to find the packages in your workspace
Run the node executing the following:
ros2 run my_first_pkg my_node
EXERCISE 1
Exercise
Follow the instruction in Setup in order to set up your git repository to implement the solution of the proposed exercises.