How to create a ros2 C++ Library

Create a ros2 C++ library

Written by Bayode Aderinola

27/10/2022

In this post, you will learn how to create and use your own ros2 C++ library. I’ll show you how to create the library and then use it in a demo package.

Step 1: Fire up a system with ROS2 installation

“Hey, do you mean I have to install ros2 first?” Absolutely not! Just log in to The Construct to get access to virtual machines pre-installed with ROS.

Once logged in, click on My Rosjects, then Create a New Rosject, supply the information as shown in the video and click Create. Then RUN the rosject.

Create a new Rosject

You might also want to try this on a local PC if you have ros2 installed. However, please note that we cannot support local PCs and you will have to fix any errors you run into on your own. The rest of the instruction assumes that you are working on The Construct; please adapt them to your local PC and ros2 installation.

Step 2: Create a new package that contains the library’s source code (logic)

Open Code Editor
Open a web shell

Open a web shell and run the following commands to create the package.

cd ros2_ws/src
source /opt/ros/humble/setup.bash/
ros2 pkg create my_value_converter_library --build-type ament_cmake --license BSD-3-Clause

Now that your package is created, we need to create a header file for the library. Move inside the include/my_value_converter_library directory and create a header file named library_header.h.  

cd ~/ros2_ws/src/my_value_converter_library/include/my_value_converter_library
touch library_header.h

Now head over to the Code Editor to make changes to the header file. Check the image below for how to open the Code Editor.

Open the Code Editor

Locate the header file in the code editor: ros2_ws > src > my_value_converter_library > include > my_value_converter_library > library_header.h and paste in the following code.

#ifndef MAP_VALUE_H
#define MAP_VALUE_H

double map_value(double x, double in_min, double in_max, double out_min, double out_max);

#endif

Next, we create the C++ source code file for the library. In the same web shell:

cd ~/ros2_ws/src/my_value_converter_library/src
touch my_value_converter_library.cpp

Locate the my_value_converter_library.cpp file in the code editor and paste in the following code:

#include "my_value_converter_library/library_header.h"

double map_value(double x, double in_min, double in_max, double out_min, double out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

Now we need to edit the CMakeLists.txt file of our package to recognize the source code we have just added. Open the file ros2_ws > src > my_value_converter_library > CMakeLists.txt and add the following lines before the ament_package() call.

# let the compiler search for headers in the include folder
include_directories(include)

# define a library target called my_value_converter_library
add_library(my_value_converter_library src/my_value_converter_library.cpp) 

# this line to exports the library
ament_export_targets(my_value_converter_library HAS_LIBRARY_TARGET)

# install the include/my_cpp_library directory to the install/include/my_cpp_library
install(
  DIRECTORY include/my_value_converter_library
  DESTINATION include
)

install(
  TARGETS my_value_converter_library
  EXPORT my_value_converter_library
  LIBRARY DESTINATION lib
  ARCHIVE DESTINATION lib
  RUNTIME DESTINATION bin
  INCLUDES DESTINATION include
)

So much for all the hard work – now is the time to see if it works. Time to compile the code. In the same web shell, run the following commands:

cd ~/ros2_ws
colcon build

Success! We have now created our library. Next, we are going to use it!

PS: if your code did not compile correctly, please go over the instructions and ensure you have created the files in the exact locations specified.

Step 3: Create a new package that uses the library you just created

Create the new package in the open shell. Note that my_value_converter_library is passed as a dependency during the creation of the new package. This will simplify some of the work that needs to be done when modifying the CMakeLists.txt file.

cd ros2_ws/src
ros2 pkg create my_value_converter_node --build-type ament_cmake --license BSD-3-Clause --dependencies rclcpp std_msgs my_value_converter_library

Now verify that the dependencies have been properly added. If the build runs successfully, it is so. If not, please correct the errors before you proceed.

cd ~/ros2_ws
colcon build

If you got to this point, great! Now we will create the C++ source code that will use your library.

cd ~/ros2_ws/src/my_value_converter_node/src
touch my_value_converter_node.cpp

Now locate the my_value_converter_node.cpp file in the code editor and paste in the following source code.

#include <memory>
#include <string>

#include "my_value_converter_library/library_header.h"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/float32.hpp"

class ConverterNode : public rclcpp::Node {
public:
  explicit ConverterNode() : Node("converter_node") {

    auto callback =
        [this](const std_msgs::msg::Float32::SharedPtr input_msg) -> void {
      RCLCPP_DEBUG(this->get_logger(), "I heard: [%f]", input_msg->data);

      // Use the library
      // Map an value from 0 - 1023 range to -1.0 to +1.0 range
      double new_value = map_value(input_msg->data, 0, 1023, -1.0, 1.0);

      output_msg_ = std::make_unique<std_msgs::msg::Float32>();
      output_msg_->data = new_value;
      RCLCPP_DEBUG(this->get_logger(), "Publishing: '%f'", output_msg_->data);
      pub_->publish(std::move(output_msg_));
    };

    sub_ = create_subscription<std_msgs::msg::Float32>("output_topic", 10,
                                                       callback);
    // Create a publisher
    rclcpp::QoS qos(rclcpp::KeepLast(7));
    pub_ = this->create_publisher<std_msgs::msg::Float32>("input_topic", qos);
  }

private:
  std::unique_ptr<std_msgs::msg::Float32> output_msg_;
  rclcpp::Publisher<std_msgs::msg::Float32>::SharedPtr pub_;
  rclcpp::Subscription<std_msgs::msg::Float32>::SharedPtr sub_;
};

int main(int argc, char *argv[]) {
  rclcpp::init(argc, argv);
  auto node = std::make_shared<ConverterNode>();
  RCLCPP_INFO(node->get_logger(), "My value converter node started.");
  rclcpp::spin(node);
  rclcpp::shutdown();
  return 0;
}

As you might have expected, we need to modify the CMakeLists.txt file for our new package. Locate the ros2_ws > src > my_value_converter_node > CMakeLists.txt in the code editor and paste in the following code before the ament_package() call.

# define the binary to be built and identify the source files with with which to build it
add_executable(main src/my_value_converter_node.cpp)
# tell CMake that the executable "main" depends on the library "my_value_converter_library"
ament_target_dependencies(main my_value_converter_library rclcpp std_msgs)
# install the executable in the lib folder to make it detectable through setup.bash
install(TARGETS 
  main
  DESTINATION lib/${PROJECT_NAME}/
)

Now build and use your package!

cd ~/ros2_ws
colcon build
source install/setup.bash

ros2 run my_value_converter_node main

Step 4: Check your learning

Do you understand how to create a ros2 C++ library? If you don’t know it yet, please go over the post again, more carefully this time.

(Extra) Step 5: Watch the video to understand how to install a ros2 binary package

Here you go:

Feedback

Did you like this post? Do you have any questions about how to create a ros2 C++ library? Please leave a comment in the comments section below, so we can interact and learn from each other.

If you want to learn about other ROS2 topics, please let us know in the comments area and we will do a video or post about it.

Masterclass 2023 batch2 blog banner

Check Out These Related Posts

1 Comment

  1. Matheus Sousa

    Great tutorial! Thank you!

    Reply

Submit a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Pin It on Pinterest

Share This