In this tutorial you’ll learn how to create and setup a ROS2 Cpp package.
I will explain every step, what every file does, how to install nodes and launch files. This will give you a good foundation for any future ROS2 Cpp package you create.
>> Watch this video as an additional resource to this article:
After watching the video, subscribe to the Robotics Back-End Youtube channel so you don’t miss the next tutorials!
Table of Contents
Setup your ROS2 Cpp package
Before you can create a ROS2 Python package, make sure you have :
You want to learn ROS2 efficiently?
Check out ROS2 For Beginners and learn ROS2 step by step, in 1 week.
- correctly installed ROS2,
- setup your environment (add
source /opt/ros/ROS_VERSION/setup.bash
in your .bashrc – don’t forget to replace “ROS_VERSION”), - and created a ROS2 workspace (
$ mkdir -p ~/ros2_ws/src && cd ~/ros2_ws/ && colcon build
).
Now, to create a Cpp package, execute:
$ cd ~/ros2_ws/src/ $ ros2 pkg create my_cpp_pkg --build-type ament_cmake going to create a new package package name: my_cpp_pkg destination directory: /home/user/ros2_ws/src package format: 3 version: 0.0.0 description: TODO: Package description maintainer: ['Name <your@email.com>'] licenses: ['TODO: License declaration'] build type: ament_cmake dependencies: [] creating folder ./my_cpp_pkg creating ./my_cpp_pkg/package.xml creating source and include folder creating folder ./my_cpp_pkg/src creating folder ./my_cpp_pkg/include/my_cpp_pkg creating ./my_cpp_pkg/CMakeLists.txt
ros2 pkg create
will create a bunch of files required for a ROS2 package. Thanks to the --build-type ament_cmake
option, only files specific to a Cpp package will be created.
Here’s how the package looks like now:
my_cpp_pkg/ ├── CMakeLists.txt ├── include │ └── my_cpp_pkg ├── package.xml └── src
Explanation of files inside a ROS2 Cpp package
Let’s see what each file/folder does.
package.xml
Use this file to provide some general information about the package, and also specify which dependencies are required.
<?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_cpp_pkg</name> <version>0.0.0</version> <description>TODO: Package description</description> <maintainer email="your@email.com">Name</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>
If you ever share or publish your package, you’ll need to edit 4 lines in order to provide more information:
- version.
- description: quickly say what your package does.
- maintainer: fill in your name and email so that people can reach to you in case they have an issue with the package. You can also add an author tag in addition to the maintainer tag, if for example you wrote the package but someone else is in charge of maintaining it. And you can of course have multiple author and maintainer tags.
- license: especially useful if you publish your project online. Having a license tells people what they can do (and can’t do) with your package.
Of course the package will correctly work even if you don’t edit those lines now, but don’t forget to add required info before you share your work.
CMakeLists.txt
In this file you’ll tell the compiler how to create your nodes, where to install them, where to install other files, how to link dependencies, etc.
By default CMakeLists.txt is already filled with the basics. You can clean it up a bit so it doesn’t become bloated too soon.
Here’s the minimal CMakeLists.txt I use. I removed the comments, the “Default to C99” section (since we’ll only use Cpp 14), and the test sections. Note: testing is very important. In this tutorial we only cover how to write minimal code, so we don’t need tests.
cmake_minimum_required(VERSION 3.5) project(my_cpp_pkg) if(NOT CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 14) endif() if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wall -Wextra -Wpedantic) endif() find_package(ament_cmake REQUIRED) ament_package()
From there, place the additions you’ll make between find_package(ament_cmake REQUIRED)
and ament_package()
.
include/<package_name>/ folder
In ROS2 the recommended way to write nodes is to use OOP. Thus in this folder you’ll write the .hpp files containing class and method declarations for your nodes, as well as library headers you wish to export.
For our example this folder is named “include/my_cpp_package/”.
src/ folder
In this src/ folder you’ll write the implementation for the classes you create.
Also this will be the place to write standalone nodes and main() functions that you want to execute.
Compile your package
To compile your package, navigate into your ROS2 workspace and use colcon build
. Add the option --packages-select my_cpp_pkg
so you only build this package (this can save you some time if you have many other packages in your workspace).
$ colcon build --packages-select my_cpp_pkg Starting >>> my_cpp_pkg Finished <<< my_cpp_pkg [1.71s] Summary: 1 package finished [1.84s]
This will compile any Cpp executable you defined in CMakeLists.txt, and install them (+ some other files) into the install/ folder of your workspace.
Build a node inside a ROS2 Cpp package
Well now you know what’s inside your Cpp package. Let’s see how you can create a Cpp node in this package, compile it, and run it.
Create a Cpp file inside the src/ directory of your package.
$ cd ~/ros2_ws/src/my_cpp_pkg/src/ $ touch my_cpp_node.cpp
Here’s a simple Cpp node for the purpose of this tutorial.
#include "rclcpp/rclcpp.hpp" class MyNode : public rclcpp::Node { public: MyNode() : Node("my_node") { RCLCPP_INFO(this->get_logger(), "Hello cpp"); } private: }; int main(int argc, char **argv) { rclcpp::init(argc, argv); auto node = std::make_shared<MyNode>(); rclcpp::spin(node); rclcpp::shutdown(); }
This node does just one thing: say Hello when it’s started, and that’s it. Then it spins until you kill it. For more info about how to write Cpp node, check out this ROS2 Cpp node tutorial.
Add those lines into your CMakeLists.txt, between find_package(ament_cmake REQUIRED)
and ament_package()
.
... find_package(rclcpp REQUIRED) add_executable(test_node src/my_cpp_node.cpp) ament_target_dependencies(test_node rclcpp) install(TARGETS test_node DESTINATION lib/${PROJECT_NAME} ) ...
This will create an executable named “test_node”, from the file you created inside the src/ folder. This “test_node” executable will be installed into ~/ros2_ws/install/my_cpp_pkg/lib/my_cpp_pkg/.
Make sure you don’t mix the executable name (“test_node”), the source file name (“my_cpp_node”), and the node name (“my_node”, defined in the source file). If you want though, you can make them 3 identical, this will also work – but be careful about not mixing things up.
Note: if you want to split your file, one .hpp and one .cpp, and place the .hpp in the include/my_cpp_package/ directory, then you’ll need to add this line: include_directories(include)
, just after the find_package(...)
lines. Otherwise the compiler won’t look into the include/ folder and you’ll get an error.
One final addition: add <depend>rclcpp</depend>
just after <buildtool_depend>ament_cmake</buildtool_depend>
in your package.xml file.
Now you can compile your package with colcon build --packages-select my_cpp_pkg
.
Open a new terminal, source your environment, and run your node:
$ ros2 run my_cpp_pkg test_node [INFO] [my_node]: Hello cpp
Add other files in your ROS2 Cpp package
You can install any file you want from your package, you’re absolutely not limited to Cpp nodes. Installing a file, here, simply means that it will be copied into the install/ folder of your ROS2 workspace.
Let’s see an example with some of the most used ones: launch files and YAML config files.
Launch files
Take the habit of placing your launch files inside a launch/ folder. So, create a launch/ folder at the root of your package.
$ cd ~/ros2_ws/src/my_cpp_pkg/ $ mkdir launch
To install your launch files you’ll need to add this to your CMakeLists.txt:
... install(DIRECTORY launch DESTINATION share/${PROJECT_NAME} ) ...
The launch/ folder will be copied and install into ~/ros2_ws/install/my_cpp_pkg/share/my_cpp_pkg/launch/.
You only need to configure this once for all launch files.
YAML config files
The technique is the same as for launch files.
First, create a config/ folder at the root of your package.
$ cd ~/ros2_ws/src/my_cpp_pkg/ $ mkdir config
And add this to your CMakeLists.txt:
... install(DIRECTORY config DESTINATION share/${PROJECT_NAME} ) ...
After the compilation you will find YAML files inside the ~/ros2_ws/install/my_cpp_pkg/share/my_cpp_pkg/config/ folder.
Now, for any other file or folder you wish to install from your ROS2 Cpp package, you can do the same.
ROS2 Cpp package: going further
In this tutorial you’ve seen how to create, configure, and compile a ROS2 Cpp package, with nodes, launch files, and YAML config files.
Here’s the final architecture that we have after all those steps.
my_cpp_pkg/ ├── CMakeLists.txt ├── config │ └── test.yaml ├── include │ └── my_cpp_pkg ├── launch │ └── test.launch.py ├── package.xml └── src └── my_cpp_node.cpp
With this foundation you’re ready to deal with complete ROS2 applications in Cpp.
To go further, learn how to: