How to add libraries to ROS2 Cpp package an easy guide

Introduction

When developing projects in robotics, the need for additional features often arises, such as advanced motion planning or sensor fusion techniques. Implementing these functionalities from scratch can be time-consuming. In such cases, leveraging existing libraries that offer these features can significantly accelerate development. For example, in the domain of mobile robotics, utilizing libraries for path planning or simultaneous localization and mapping (SLAM) can greatly enhance the capabilities of autonomous robots. This article aims to clarify concepts around incorporating external libraries into ROS 2 C++ packages, facilitating faster development and expanding the capabilities of robotic applications.

Starting Point

No understanding of

  • ROS2 C++ libraries and components
  • ROS2 C++ Package Structure
  • Creating custom libraries

Learning outcomes

Easily able to

  • Creating Custom Modules into ROS2 Packages
  • Install External Libraries to your ROS2 Packages
  • Concept clearning of Python Package, Library and module

Understanding C++ Terminologies

You can find our very useful open source C++-based libraries, but to utilize them in your projects, you need to understand that C++ requires the step of getting compiled to run it. For that, we need to have a system like CmakeLists.txt to compile and produce an executable that we can run. Lets first understand difference between a library and simple code inclusion.

C++ Included Code

Including a C++ source file (#include "my_functions.cpp") directly into another file is more like copying and pasting the contents of that file. It’s not a standard way to distribute reusable code and this is not a library.


// my_functions.cpp
#include <iostream>

void sayHello() {
    std::cout << "Hello, Robotisim!" << std::endl;
}
// main.cpp
#include "my_functions.cpp"

int main() {
    sayHello();
    return 0;
}
c++ programming

C++ Libraries

A library is a precompiled set of functions and code that can be reused by other programs. It is typically distributed as a binary file (libexample.a for static libraries or libexample.so for dynamic/shared libraries) that can be linked to an executable.

Lets understand through a popular example

  • Header only library but no compiled .a or .so file
    • In this example, #include <iostream> includes the iostream header file, which provides functionality for input and output operations
#include <iostream>

int main() {
    std::cout << "Hello, World!" << std::endl;
    return 0;
}

Creating Custom C++ Library

Lets look at an example of static library creation , to build our understanding about the process and get rid of copy pasting of source codes.

Header File

#ifndef BOT_MOVE_H
#define BOT_MOVE_H

enum DIR { FORWARD, BACKWARD};

bool botMove(DIR direction);

void botStop();

#endif

Source File

#include "bot_move.h"

bool botMove(Dir direction) 
{std::cout << "Moving robot";
return true;}

void botStop() {
std::cout << "Stop";}

Compile Library

g++ -c bot_move.cpp

ar rcs libbot_move.a bot_move.o

Library Files Understanding

After compiling, you will obtain a static library file libbot_move.a that contains the compiled code for the botMove and botStop functions.

This library can be linked to other programs that need to control a robot’s movement.

Utilising our custom Library

Once these library files are created then you can utilize them or link them into your C++ ROS2 Code

Main Code

#include "bot_move.h"

int main() {
    moveRobot(FORWARD);  // Move the robot forward at speed 50
    stopRobot();              // Stop the robot
    return 0;
}

This code is inlcuding the header file but when we will compile we will mention that do not take definations of header files from .cpp instead take from .a compile library

Linking Command

g++ main.cpp -L. -lbot_move -o robot_movement

Explanation

  • main.cpp is your main C++ file that uses the functions from the bot_move library.
  • -L. tells the compiler to look for libraries in the current directory.
  • -lbot_move specifies the name of the library to link (libbot_move.a).
  • -o robot_movement specifies the output executable name.

ROS2 C++ Package Strucutre

When we create a python package using ROS2 command, we have couple of things to understand

  • ros2 pkg create : This is the command used to create a new ROS 2 package.
  • --build-type ament_cmake : This option specifies the build system to use for the package. In this case, ament_cmake is selected.
  • --node-name sensor_fusion_node : This option specifies that a node named
  • sensor_fusion_pkg : This is the name of the package that will be created.
ros2 pkg create --build-type ament_cmake --node-name sensor_fusion_node sensor_fusion_pkg

When we execute this command, we see a package with a node automatically created with a fixed structure. This below structure is produced mainly for keeping standards fixed for compatibility between tools and ease of maintenance. Lets look at its structure and important files

Node

sensor_fusion_pkg/
├── include/
│   └── sensor_fusion_pkg/
│       └── sensor_fusion_node.hpp
├── src/
│   ├── sensor_fusion_node.cpp
│   └── CMakeLists.txt
├── CMakeLists.txt
├── package.xml
└── README.md

Node

#include <rclcpp>
#include libraries

class Node{
 publishers.create();
 subscriber.create();
}
void main (){
Node.spin();
}


CmakeLists.txt

include_directories(include)
add_executable(sensor_fusion_node src/sensor_fusion_node.cpp)
ament_target_dependencies(sensor_fusion_node rclcpp)

Including OpenCV and Eigen Library into ROS2 C++ Package

Lets bring in some popular libraries to be included into our ROS2 C++ node afterinstallation , just to understand the process.

sensor_fusion_node.cpp

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include <opencv2/core.hpp>
#include <Eigen/Dense>

class SensorFusion : public rclcpp::Node
{
public:
    SensorFusion()
        : Node("sensor_fusion_node")
    {
        // Use OpenCV
        cv::Mat image(100, 100, CV_8UC3, cv::Scalar(0, 255, 0));
        cv::imshow("Image", image);
        cv::waitKey(0);

        // Use Eigen
        Eigen::MatrixXd matrix(3, 3);
        matrix << 1, 2, 3, 4, 5, 6, 7, 8, 9;
        std::cout << "Eigen Matrix:\n" << matrix << std::endl;
    }
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<SensorFusion>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

CmakeList.txt

cmake_minimum_required(VERSION 3.5)
project(sensor_fusion_pkg)

# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
  set(CMAKE_CXX_STANDARD 14)
endif()

# Find OpenCV package
find_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})

# Find Eigen package
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})

# Find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)

# Create an executable
add_executable(SensorFusion_node src/SensorFusion.cpp)

# Link the OpenCV and Eigen libraries
target_link_libraries(SensorFusion_node ${OpenCV_LIBRARIES} ${EIGEN3_LIBRARIES})

ament_target_dependencies(my_node rclcpp std_msgs)

# Install the executable
install(TARGETS my_node
  DESTINATION lib/${PROJECT_NAME})

ament_package()

Utilising Custom Library in ROS2 C++ Package

  • Includes necessary headers: rclcpp for ROS 2 functionality, std_msgs/msg/string.hpp for standard message types, and bot_move.h for your custom library.
  • Defines a class BotMove that inherits from rclcpp::Node, providing a constructor that initializes the node with the name “bot_move”.
  • Inside the constructor, it calls a function (moveRobot()) from your custom library bot_move and logs the result using RCLCPP_INFO.

sensor_fusion_node.cpp

#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/string.hpp"
#include "bot_move.h"  

class BotMove : public rclcpp::Node
{
public:
    BotMove()
        : Node("bot_move")
    {
        int result = moveRobot();
        RCLCPP_INFO(this->get_logger(), "Result: %d", result);
    }
};

int main(int argc, char *argv[])
{
    rclcpp::init(argc, argv);
    auto node = std::make_shared<BotMove>();
    rclcpp::spin(node);
    rclcpp::shutdown();
    return 0;
}

CmakeLists

cmake_minimum_required(VERSION 3.5)
project(sensor_fusion_pkg)

# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)

include_directories(
  include
  ${CMAKE_CURRENT_SOURCE_DIR}/lib
)

# create an executable
add_executable(bot_move_node src/bot_move.cpp)

# link your custom library
target_link_libraries(bot_move_node ${CMAKE_CURRENT_SOURCE_DIR}/lib/libmove_bot.a)

ament_target_dependencies(sensor_fusion_node rclcpp)

# install the executable
install(TARGETS bot_move_node
  DESTINATION lib/${PROJECT_NAME})

ament_package()
  • Includes directories for the headers of your custom library (${CMAKE_CURRENT_SOURCE_DIR}/lib).
  • Creates an executable named BotMove from bot_move.cpp and links it with your custom library (target_link_libraries).
  • Specifies the dependencies (ament_target_dependencies).

FAQ

Q: What is the standard structure of a ROS 2 C++ package?
A: A standard ROS 2 C++ package structure includes directories for include files, source files, a CMakeLists.txt file, a package.xml file, and other necessary files for building and running the package.

Q: How do you create a ROS 2 C++ package?
A: You can create a ROS 2 C++ package using the command:

bashCopy coderos2 pkg create --build-type ament_cmake --node-name sensor_fusion_node sensor_fusion_pkg

Q: How do you include OpenCV and Eigen libraries in a ROS 2 C++ package?
A: To include OpenCV and Eigen libraries, you need to find these packages and include their directories in your CMakeLists.txt file:

cmakeCopy codefind_package(OpenCV REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
find_package(Eigen3 REQUIRED)
include_directories(${EIGEN3_INCLUDE_DIR})

Q: How do you use a custom library in a ROS 2 C++ package?
A: You include the header of your custom library in your node’s source file and link the compiled library in your CMakeLists.txt file. For example, include the header in your .cpp file:

#include "bot_move.h"

And link the library in CMakeLists.txt:

cmakeCopy codetarget_link_libraries(bot_move_node ${CMAKE_CURRENT_SOURCE_DIR}/lib/libmove_bot.a)

Q: Why is EIGEN3 not included in the target_link_libraries?
A: Eigen is a header-only library, which means its functionality is included directly through headers, and there are no compiled binaries to link against.

Q: Why is OpenCV written as OpenCV_LIBS while Boost is written as Boost_LIBRARIES?
A: The naming conventions for linking libraries in CMakeLists.txt can differ based on how the CMake configuration files for those libraries are set up. OpenCV_LIBS and Boost_LIBRARIES are the variables defined in their respective CMake configurations to hold the library linking information.

Lets take a Quiz

Leave a Comment

Your email address will not be published. Required fields are marked *

Review Your Cart
0
Add Coupon Code
Subtotal
Total Installment Payments
Bundle Discount

 
Scroll to Top