Introduction
There are a lot of ros2 tutorials out there. This is the simplest one.
This is the sequel to my ROS(1) tutorial of the same name. This tutorial demonstrates how to build a ros2 “Hello world” executable written in C++ without getting into the details of ros2 packages, workspaces, launchfiles, or other standard practices. This tutorial is meant to demonstrate the bare minimum of what is required to interact with a ros2 system. It is not meant to be an example of good ros2 development practices, but rather is meant to be useful for someone new to ros2 and software development in general. That being said, this could be informative for someone who wants to integrate with ros2 without pulling in the entire build and process management system.
NOTE: This tutorial was written for the ros2 Galactic Distribution. Assuming the commands are still accurate, if you wish to follow this tutorial with a different distribution of ros2, any time
galactic
is mentioned, simply replace it with the shortname for that distribution.
Pre-Requisites
- A computer running Ubuntu Linux1 20.04 installation
- Minimal experience with the Linux and the command-line interface
- Minimal experience with C++
Tools Used
- Ubuntu Linux1
- The bash shell2
- C++3
- The GNU Compiler Collection (GCC)4
- Any plain-text editor (I like vim5).
ros2 Packages Used
Number of Windows Needed
- Browser for these instructions
- Window for your text editor
- Terminal to run
hello_world_node
- Terminal to run introspection commands like
ros2 node
andros2 topic
Contents
- Introduction
- Installing ROS2 (if it hasn’t already been installed)
- Create THE SIMPLEST ROS2 (C++) PROGRAM
- Compile the simplest ros2 (C++) program
- Run the simplest ros2 (C++) program (and fail)
- Inspecting the simplest ROS (C++) program
- Distributed Logging with rosconsole
- Play Around
- Conclusion
Installing ROS2 (if it hasn’t already been installed)
For Ubuntu Linux, you can follow these instructions, and for other platforms, see the main ros2 installation instructions.
Install pre-requisites and add the universe
repository if it’s not already enabled:
sudo apt update && sudo apt install locales curl gnupg lsb-release software-properties-common
sudo add-apt-repository universe
Get the ros GPG key and authorize it:
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
Add the ROS repository:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
Update sources and install the base set of ros2 packages.
sudo apt update
sudo apt install ros-galactic-ros-base
Create THE SIMPLEST ROS2 (C++) PROGRAM
After you’ve installed the pre-requisites listed above, you can create a
new directory for this tutorial. You can call this directory anything you want,
but in this case, we’ll call it hello_world_tutorial
and make it anywhere. Then, enter
the directory:
mkdir hello_world_tutorial
cd hello_world_tutorial
The first step is writing the simplest C++ program that can interact with ros2
in a meaningful way. All it does is prepare to announce itself as a ros2
node called hello_world_node
, then broadcast a
Hello-world message over the standard /rosout
topic, and then wait for a
SIGINT
or ctrl-c
.
This program will be built from single file named hello_world_node.cpp
with
the following contents:
hello_world_node.cpp
Compile the simplest ros2 (C++) program
Since we’re building a program with C++, we need to compile it into an
executable that we can actually run. In this very simple case, this can be
done by invoking g++
directly with the following build command (make sure
to get all of it):
g++ --std=c++17 hello_world_node.cpp -o hello_world_node -I/opt/ros/galactic/include/ -L/opt/ros/galactic/lib -Wl,-rpath,/opt/ros/galactic/lib -lrclcpp -lrcutils
If you are unfamiliar with command-line usage of g++, the arguments passed to g++
have the following meanings:
hello_world_node.cpp
The source file(s) to compile-o hello_world_node
The name of the output file (the executable, in this case)-I/opt/ros/galactic/include
An instruction to look for C++ header files in/opt/ros/galactic/include
-L/opt/ros/galactic/lib
An instruction to look for static libraries in/opt/ros/galactic/lib
-Wl,-rpath,/opt/ros/galactic/lib
An instruction to look for shared libraries in/opt/ros/galactic/lib
-lrclcpp
Link against the librarylibrclcpp.so
(ros2 C++ bindings)-lrcutils
Link against the librarylibrcutils.so
(ros2 C utilities)
NOTE: The standard method to build software in ROS/ros2 is to use ROS packages and catkin/ament/colcon. This tutorial avoids using these tools for simplicity, as they are not needed to use the ros2 middleware.
Run the simplest ros2 (C++) program (and fail)
After successfully compiling the program, you can run it right away.
./hello_world_node
If you’ve been following this tutorial verbatim, then you’ll get an error message like the following:
./hello_world_node: error while loading shared libraries: libament_index_cpp.so: cannot open shared object file: No such file or directory
This error is due to the ros2’s special installation to /opt/ros/galactic
.
Specifically, the rclcpp
library depends on the ament_index_cpp
library and
others. Take a look at ldd hello_world_node
to see the rest. There are a few.
To add the appropriate paths to LD_LIBRARY_PATH
so that those libraries can
be found, source the ros2 setup script:
source /opt/ros/galactic/setup.sh
At this point, you can try running hello_world_node
again:
./hello_world_node
You should see the following output (with different timestamps and network interfaces):
1638160396.202275 [0] hello_worl: using network interface wlp4s0 (udp/192.168.0.128) selected arbitrarily from: wlp4s0, lxcbr0, lxdbr0
[INFO] [1638160396.207924768] [hello_world_node]: Hello, world!
Inspecting the simplest ROS (C++) program
In a new shell with a proper environment, you can now inspect your node running in the first shell with standard ros2 command-line tools.
One of the simplest, ros2 node
, is a command-line program for listing and
querying information about ros2 nodes. For example, the info
subcommand will
give you all the metadata ros2 knows about a given node. You can get the info
for /hello_world_node
like so:
ros2 node info /hello_world_node
This will give you information similar to the following:
/hello_world_node
Subscribers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
Publishers:
/parameter_events: rcl_interfaces/msg/ParameterEvent
/rosout: rcl_interfaces/msg/Log
Service Servers:
/hello_world_node/describe_parameters: rcl_interfaces/srv/DescribeParameters
/hello_world_node/get_parameter_types: rcl_interfaces/srv/GetParameterTypes
/hello_world_node/get_parameters: rcl_interfaces/srv/GetParameters
/hello_world_node/list_parameters: rcl_interfaces/srv/ListParameters
/hello_world_node/set_parameters: rcl_interfaces/srv/SetParameters
/hello_world_node/set_parameters_atomically: rcl_interfaces/srv/SetParametersAtomically
Service Clients:
Action Servers:
Action Clients:
Note that if you ctrl-c
hello_world_node
in the other window, and try to re-run
this command, you will see something similar to the following:
Unable to find node '/hello_world_node'
Distributed Logging with rosconsole
Simple as it may be, hello_world_node
is actually doing much more than announcing
itself to the ros2 master and then outputting “Hello, world!” to the
console. This is actually being broadcast to any other ros2 nodes which have
subscribed to the standard ros2 log message topic6 called /rosout
.
To see this, first make sure hello_world_node
is terminated. Then, run
the following rostopic
command in another window to display any messages
on the /rosout
topic:
ros2 topic echo /rosout
If you run hello_world_node
in another shell, in the window running ros2
topic
, you should now see the content of a single
rosgraph_msgs/Log
message beneath the ros2 topic
command, with content similar to the
following:
stamp:
sec: 1638160674
nanosec: 252424895
level: 20
name: hello_world_node
msg: Hello, world!
file: hello_world_node.cpp
function: HelloWorldNode
line: 8
This is the ros2 message generated by the RCLCPP_INFO(...)
command in
hello_world_node.cpp
! You’ve just transmitted your first ros2 message
from a publisher (hello_world_node
) to a subscriber (ros2 topic
).
You’ll notice that this message contains far more than just the string passed
to RCLCPP_INFO(...)
, it also contains metadata about on which line and in
which function the message was generated. This can be very useful both when
debugging your own code, or when inspecting errors in code written by others.
Play Around
Now that you’ve been given some tools, play around with different messages and
the ros2 node
and ros2 topic
tools. Note that both of these tools have built-in
documentation that you can read by passing the --help
argument in the
following way:
ros2 topic --help
ros2 topic echo --help
Conclusion
This tutorial has hopefully introduced you to some of the core ros2 concepts
like the ros2 environment, ros2 nodes, and distributed logging with rosconsole and the
/rosout
topic. Additionally, it has hopefully introduced you to how ros2 libraries
are installed on a Linux system, which environment variables are needed to use
those libraries, and how to set those environment variables with the provided ros2
setup files.