TransformListener spins only the tf callback group, the default callback group of its internal node is never spun
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 153
- Forks
- 256
- Avg merge
- 3d 23h
- Merged PRs (30d)
- 14
Description
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
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in TransformListener::init(), where the dedicated executor adds only callback_group_, and compare that with the internal node created by the simple constructor. Read rclcpp/src/rclcpp/time_source.cpp to understand the default-group subscription. Done means the internal node's default callback group is served when needed while /tf and /tf_static continue working.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- robotics
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100