ros2 / ros2/rclcpp

Type Adapter cannot convert one RosMsg to another RosMsg

Open
#2,446 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

backlog help wanted
Dominant language
C++
Stars
805
Forks
564
Avg merge
1d 17h
Merged PRs (30d)
27

Description

Bug report

Required Info:

  • Operating System:
    • Ubuntu 22.04 + VMware
  • Installation type:
    • source build
  • Version or commit hash:
    • humble
  • DDS implementation:
    • Fast-RTPS and Eclipse Cyclone DDS
  • Client library (if applicable):
    • rclcpp
Steps to reproduce issue
# TestMsg.msg
uint32 id
uint64 time
// rosAdapter.hpp
#ifndef ROS_ADAPTER_HPP
#define ROS_ADAPTER_HPP

#include <string>
#include <iostream>
#include "rclcpp/type_adapter.hpp"
#include "rclcpp/rclcpp.hpp"
#include "std_msgs/msg/byte_multi_array.hpp"
#include "std_msgs/msg/header.hpp"
#include "tutorial_interfaces/msg/test_msg.hpp"

#define ROS_TYPE
#ifdef ROS_TYPE
// fail
using MsgT = tutorial_interfaces::msg::TestMsg;
#else
// work fine
using MsgT = std::string;
#endif // ROS_TYPE

template <>
struct rclcpp::TypeAdapter<MsgT, std_msgs::msg::ByteMultiArray>
{
    using is_specialized = std::true_type;
    using custom_type = MsgT;
    using ros_message_type = std_msgs::msg::ByteMultiArray;

    static void
    convert_to_ros_message(
        const custom_type &source,
        ros_message_type &destination)
    {
        std::cout << "convert_to_ros_message!!!" << std::endl;
#ifdef ROS_TYPE
        destination.data.resize(12);
        destination.data[0] = source.id;
        destination.data[1] = source.time;
#else
        destination.data.resize(source.size());
        memcpy(destination.data.data(), source.data(), source.size());
#endif
    }

    static void
    convert_to_custom(
        const ros_message_type &source,
        custom_type &destination)
    {
        std::cout << "convert_to_custom!!!" << std::endl;
#ifdef ROS_TYPE
        std::cout << "source size = " << source.data.size() << std::endl;
        destination.id = source.data[0];
        std::cout << "destination.id = " << destination.id << std::endl;
        destination.time = source.data[4];
#else
        destination.resize(source.data.size());
        memcpy(destination.data(), source.data.data(), source.data.size());
#endif
    }
};

RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(MsgT, std_msgs::msg::ByteMultiArray);

#endif // ROS_ADAPTER_HPP
// pub.cpp
#include <chrono>
#include <memory>
#include <string>
#include "rclcpp/rclcpp.hpp"
#include "rosAdapter.hpp"

using namespace std::chrono_literals;

class MinimalPublisher : public rclcpp::Node
{
public:
  MinimalPublisher()
      : Node("minimal_publisher"), count_(0)
  {
    auto timer_callback =
        [this]() -> void
    {
#ifdef ROS_TYPE
      MsgT msg;
      msg.id = this->count_++;
      msg.time = 0x12345678;
#else
      MsgT msg = "my name!";
#endif
      this->publisher_->publish(msg);
    };

    publisher_ = this->create_publisher<MsgT>("topic", 10);
    timer_ = create_wall_timer(30ms, timer_callback);
  }

private:
  rclcpp::TimerBase::SharedPtr timer_;
  rclcpp::Publisher<MsgT>::SharedPtr publisher_;
  size_t count_;
};

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalPublisher>());
  rclcpp::shutdown();
  return 0;
}
// sub.cpp
#include <memory>
#include "rclcpp/rclcpp.hpp"
#include <chrono>
#include "rosAdapter.hpp"

class MinimalSubscriber : public rclcpp::Node
{
public:
  MinimalSubscriber()
      : Node("minimal_subscriber")
  {
    auto callback = [this](const MsgT &msg)
    {
#ifdef ROS_TYPE
      std::cout << "msg.id = " << msg.id << std::endl;
      std::cout << "msg.time = " << msg.time << std::endl;
#else
      std::cout << "msg = " << msg << std::endl;
#endif
    };

    subscription_ = this->create_subscription<MsgT>("topic", 10, callback);
  }

private:
  rclcpp::Subscription<MsgT>::SharedPtr subscription_;
};

int main(int argc, char *argv[])
{
  rclcpp::init(argc, argv);
  rclcpp::spin(std::make_shared<MinimalSubscriber>());
  rclcpp::shutdown();
  return 0;
}
Expected behavior

The subscriber (sub.cpp) can output the message.

Actual behavior

A segmentation fault occurs because the subscriber receives zero data when calling the convert_to_custom function in TypeAdapter.

$ ros2 run topics sub
convert_to_custom!!!
source size = 0
[ros2run]: Segmentation fault

From the above-presented result, the subscriber enters the convert_to_custom function with a data size of zero. Consequently, the subscriber is unable to switch the ros_message_type to the custom_type (which is another RosMsg Type).

Additional information

It appears that TypeAdapter is unable to convert one RosMsg Type to another RosMsg Type.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the TypeAdapter specialization in rosAdapter.hpp and reproduce the failure using pub.cpp and sub.cpp on the stated Humble source build. Inspect why convert_to_custom receives an empty ByteMultiArray when adapting tutorial_interfaces::msg::TestMsg, and consider the issue done when the subscriber receives the published fields without a segmentation fault.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
robotics
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.