Create a ROS2 Cpp Package

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.

Let’s get started!

Setup your ROS2 Cpp package

Before you can create a ROS2 Python package, make sure you have :

  • 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.


You are learning ROS2...

As a complete beginner? Check out ROS2 For Beginners and learn ROS2 in 1 week.

As a ROS1 developer? Check out Learn ROS2 as a ROS1 Developer and Migrate Your ROS Projects.


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:

Want to learn how to program with ROS2?

Don't miss this opportunity:

ROS2 For Beginners - Step by Step Course


>> Learn ROS2 in 1 Week <<

...or are you already a ROS1 Developer?

ROS2 For ROS1 Developers and Migrate Your ROS Projects


>> Learn ROS2 as a ROS1 Developer and Migrate Your ROS Projects <<

LEARN HOW TO PROGRAM ROBOTS

Did you find this tutorial useful?

Do you want to become better at programming robots, with Arduino, Raspberry Pi, or ROS2?

If yes, subscribe to receive exclusive content and special offers!