1
$\begingroup$

I followed the basic C++ publisher tutorial to create a minimal publisher. This works. I then followed the basic launch file tutorial to create a launch file for the minimal publisher. This also works. I then try to follow the composable node tutorial to modify the minimal publisher, and this does not work; after updating the C++ code, launch file, package.xml, and CMakeLists.txt, running the launch file fails with the following error with ROS2 Humble (Ubuntu 20.04, NVIDIA ROS2 buildfarm):

user@computer:~/workspaces/composable$ ros2 launch cpp_pubsub talker.launch.py 
[INFO] [launch]: All log files can be found below /home/bgarage/.ros/log/2024-06-26-10-57-31-278954-bgarage-tp101-1016491
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [component_container-1]: process started with pid [1016547]
[component_container-1] [INFO] [1719424651.596867939] [a_buncha_nodes]: Load Library: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a
[component_container-1] [ERROR] [1719424651.596994147] [a_buncha_nodes]: Failed to load library: Could not load library dlopen error: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a: invalid ELF header, at /tmp/binarydeb/ros-humble-rcutils-5.1.6/src/shared_library.c:99
[ERROR] [launch_ros.actions.load_composable_nodes]: Failed to load node 'test_name' of type 'talker::MinimalPublisher' in container '/a_buncha_nodes': Failed to load library: Could not load library dlopen error: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a: invalid ELF header, at /tmp/binarydeb/ros-humble-rcutils-5.1.6/src/shared_library.c:99

And the following error with ROS2 Foxy (Ubuntu 20.04):

bgarage@bgarage-tp101:~/workspaces/composable$ ros2 launch cpp_pubsub talker.launch.py
[INFO] [launch]: All log files can be found below /home/bgarage/.ros/log/2024-06-26-11-04-36-659513-bgarage-tp101-1026834
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [component_container-1]: process started with pid [1026852]
[component_container-1] [INFO] [1719425077.001315326] [a_buncha_nodes]: Load Library: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a
[component_container-1] [ERROR] [1719425077.001467709] [a_buncha_nodes]: Failed to load library: Could not load library LoadLibrary error: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a: invalid ELF header, at /tmp/binarydeb/ros-foxy-rcutils-1.1.5/src/shared_library.c:84
[ERROR] [launch_ros.actions.load_composable_nodes]: Failed to load node 'test_name' of type 'talker::MinimalPublisher' in container '/a_buncha_nodes': Failed to load library: Could not load library LoadLibrary error: /home/bgarage/workspaces/composable/install/cpp_pubsub/lib/libtalker_component.a: invalid ELF header, at /tmp/binarydeb/ros-foxy-rcutils-1.1.5/src/shared_library.c:84

Any idea why this is happening? I made sure to delete logs/, build/, and install/ between attempts with different ROS2 distros.

CPP:

#include <chrono>
#include <memory>

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

using namespace std::chrono_literals;

namespace talker
{

/* This example creates a subclass of Node and uses std::bind() to register a
 * member function as a callback from the timer. */

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher(const rclcpp::NodeOptions & options)
    : Node("minimal_publisher", options), count_(0)
  {
    publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);
    timer_ = this->create_wall_timer(
      500ms, std::bind(&MinimalPublisher::timer_callback, this));
  }

private:
  void timer_callback()
  {
    auto message = std_msgs::msg::String();
    message.data = "Hello, world! " + std::to_string(count_++);
    RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());
    publisher_->publish(message);
  }
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;
  size_t count_;
};

}

#include "rclcpp_components/register_node_macro.hpp"
RCLCPP_COMPONENTS_REGISTER_NODE(talker::MinimalPublisher)

Launch file:

from launch import LaunchDescription
from launch_ros.actions import Node
from launch_ros.actions import ComposableNodeContainer
from launch_ros.descriptions import ComposableNode

def generate_launch_description():
    ld = LaunchDescription()
    ld.add_action(ComposableNodeContainer(
        name='a_buncha_nodes',
        namespace='',
        package='rclcpp_components',
        executable='component_container',
        composable_node_descriptions=[
            ComposableNode(
                package='cpp_pubsub',
                plugin='talker::MinimalPublisher',
                name='test_name',
                # ..
                extra_arguments=[{'use_intra_process_comms': True}],
            ),
        ]
    ))
    return ld

CMakeLists.txt:

cmake_minimum_required(VERSION 3.8)
project(cpp_pubsub)

if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rclcpp_components REQUIRED)

add_library(talker_component src/publisher_member_function.cpp)
ament_target_dependencies(talker_component rclcpp std_msgs rclcpp_components)

rclcpp_components_register_node(
    talker_component
    PLUGIN "talker::MinimalPublisher"
    EXECUTABLE talker
)

install(TARGETS talker_component
        EXPORT export_talker_component
        ARCHIVE DESTINATION lib
        LIBRARY DESTINATION lib
        RUNTIME DESTINATION bin
)

install(DIRECTORY
  launch
  DESTINATION share/${PROJECT_NAME}/
)

ament_package()

```
$\endgroup$

1 Answer 1

1
$\begingroup$

I found the ROS2 demo for composition, and noticed their add_library calls in CMakeLists.txt had SHARED in them. I updated my CMakeLists.txt:

add_library(talker_component SHARED src/publisher_member_function.cpp)

And the nodes now load. This makes sense: static archives (*.a) can only be included at compile time, whereas shared objects (*.so) are designed to be included at runtime, which is how ROS composition works. With SHARED, *.so objects are created instead of *.a ones.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.