Skip to content

TransformListener spins only the tf callback group, the default callback group of its internal node is never spun #968

Description

@benaliabderrahmane

Hi

I think there is a small issue in TransformListener when it creates its own internal node.

If i use the simple constructor (without passing a node):

class MyNode : public rclcpp::Node
{
  // ...
  tf2_ros::Buffer m_tfBuffer;
  tf2_ros::TransformListener m_tfListener;
};

MyNode::MyNode()
: rclcpp::Node("my_node"),
  m_tfBuffer(this->get_clock()), m_tfListener(m_tfBuffer)
{}

then TransformListener creates the internal node transform_listener_impl_... (this part is already known, see #361).

In init() when spin_thread is true, a dedicated callback group is created and it is used only for /tf and /tf_static:

callback_group_ = node_base_interface_->create_callback_group(
  rclcpp::CallbackGroupType::MutuallyExclusive, false);
// ...
tf_options.callback_group = callback_group_;
tf_static_options.callback_group = callback_group_;
// ...
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
executor_->add_callback_group(callback_group_, node_base_interface_);
dedicated_listener_thread_ = std::make_unique<std::thread>([&]() {executor_->spin();});

So the executor spins only this callback group. The default callback group of the internal node is never added to any executor, so it is never spun.

The problem is that the internal node still has things in the default callback group. For example rclcpp TimeSource always subscribes to /parameter_events when a node is created (to watch use_sim_time), and it does not pass any callback group:

// rclcpp/src/rclcpp/time_source.cpp
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
  node_topics_,
  [this](std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
    this->on_parameter_event(event);
  });

So this subscription goes into the default callback group of transform_listener_impl_... and nobody spins it. The subscription exists in the graph but its callback is never called.

/tf and /tf_static work fine, because they are in the group that is spun. Only the default group is not served.

Also the internal node is a private member (optional_default_node_), so from my code i cannot add it to my own executor to fix it.

Maybe when TransformListener creates the node itself, it could add the whole node instead of only the callback group, something like:

if (optional_default_node_) {
  executor_->add_node(optional_default_node_);
} else {
  executor_->add_callback_group(callback_group_, node_base_interface_);
}

When i pass my own node to TransformListener there is no problem, because my executor spins the default callback group.

Info:

  • ROS 2 Jazzy
  • tf2_ros 0.36.20

thanks

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions