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
Hi
I think there is a small issue in
TransformListenerwhen it creates its own internal node.If i use the simple constructor (without passing a node):
then
TransformListenercreates the internal nodetransform_listener_impl_...(this part is already known, see #361).In
init()whenspin_threadis true, a dedicated callback group is created and it is used only for/tfand/tf_static: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
TimeSourcealways subscribes to/parameter_eventswhen a node is created (to watchuse_sim_time), and it does not pass any callback group: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./tfand/tf_staticwork 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
TransformListenercreates the node itself, it could add the whole node instead of only the callback group, something like:When i pass my own node to
TransformListenerthere is no problem, because my executor spins the default callback group.Info:
thanks