-
Notifications
You must be signed in to change notification settings - Fork 198
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reduce transform listener nodes #442
Conversation
Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>
Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>
i don't know how to fix the CI failures, i need some helps, thank you! |
Not your fault, I don't believe |
callback_group_ = node_base_interface_->create_callback_group( | ||
rclcpp::CallbackGroupType::MutuallyExclusive, false); | ||
// Duplicate to modify option of subscription | ||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options2 = options; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like you can do better than options2
here. How about tf_options and tf_static_options?
node, "/tf", qos, std::move(cb), options2); | ||
message_subscription_tf_static_ = rclcpp::create_subscription<tf2_msgs::msg::TFMessage>( | ||
node, "/tf_static", static_qos, std::move(static_cb), static_options2); | ||
// Create executor with dedicated thread to spin. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A space here might be nice for a logical break between creating the subscribers and then setting up the executor
Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>
@tfoote I'd try to assign you as a reviewer if I had the permissions to do so 😄 Edit: Whoops, looks like @clalancette is the listed maintainer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From a quick read through this looks good. I haven't fully validated or tested it. But the internal node creation was a workaround for the lack of callback groups. Switching back to the dedicated thread with a callback group is a good step forward.
@tfoote what are the next steps here? I'd looked over it again and I don't see any reason this wouldn't be functionally the same. |
please review this PR, i need your suggestion, thank you! @clalancette @tfoote. |
This pull request has been mentioned on ROS Discourse. There might be relevant details there: https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2021-9-16/22372/1 |
@ahcorde any other action required for merging? Not that I'm a maintainer here, but I took a look and I see no issues that would pop up either. |
|
||
// Create executor with dedicated thread to spin. | ||
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>(); | ||
executor_->add_callback_group(callback_group_, node_base_interface_); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this broke an existing use-case where we expect the node to have other callbacks processed, not just the the two subscriptions added here. E.g. previously I could write something like this:
#include <rclcpp/rclcpp.hpp>
#include <tf2_ros/buffer.h>
#include <tf2_ros/transform_listener.h>
#include <std_msgs/msg/string.hpp>
#include <iostream>
#include <memory>
#include <chrono>
#include <thread>
int main(int argc, char ** argv)
{
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("foo_node");
auto callback = [](const std_msgs::msg::String & msg) {
std::cerr << "Message received: " << msg.data << std::endl;
};
auto sub = node->create_subscription<std_msgs::msg::String>("foo", 1, callback);
tf2_ros::Buffer buffer(node->get_clock());
tf2_ros::TransformListener listener(buffer, node, true);
while (rclcpp::ok()) {
// rclcpp::spin_some(node);
std::this_thread::sleep_for(std::chrono::seconds(1));
}
rclcpp::shutdown();
return 0;
}
Maybe assuming callbacks will be handled by the transform listener is a bad assumption, but the signature where we provide our own Node (and we set spin_thread=true
) seems ill-defined.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my_node
is never spun in this example -- how could the subscription receive messages? The spin_thread
is w.r.t. the TF callbacks to isolate them from the rest of the system, its not there to process your own callbacks in your own application. That was the intent of this PR to make TF2's system decoupled from the application code. You should spin_some()
in your while loop to resolve this issue (which you should have been doing before this PR anyway?). This PR is creating an executor / callback group for the TF internal needs to be processed, so that's not connected to your application subscriber using the default executor that you haven't called in the application example. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be wrong about what I think is happening; I will report back with a complete example.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've updated the code in my comment to be a compilable example. It appears that I can get the example to work if I uncomment the rclcpp::spin_some()
call in the while-loop. IIRC, this would result in a runtime error previously. Should it be the new recommended approach?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes -- before I think it was a design error to have it spin for the user application too, that seems like a breakage in separation of concerns to have TF2 processing your execution model for your application. I would have assumed that it did what it is now doing and just spin a thread for TF2 to process its information internally. This was made because we want TF2 to process its own info and not be blocked or blocking other application callbacks & reducing the number of Node
objects in complex stacks like Nav2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that it makes sense to not have users rely on the TransformListener executor, however the current change silently breaks anyone who was relying on this behavior (it took me a while to figure out why my callbacks were not being triggered). It would be nice if we could detect this scenario and warn users. At the very least, I think we should add a release note for ROS Humble.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% agree
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not seem straight forward to add a warning, but I've added a release note in ros2/ros2_documentation#2127
It might also be worth pointing out here that the Python implementation still follows the old logic of spinning on the entire node (instead of a callback group containing only the TF topics): geometry2/tf2_ros_py/tf2_ros/transform_listener.py Lines 93 to 96 in 16562ce
I can create an issue to track updating it as well. |
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org>
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org>
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org> (cherry picked from commit d864ce8)
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org> (cherry picked from commit d864ce8)
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org> (cherry picked from commit d864ce8) Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Related PR ros2/geometry2#442 Signed-off-by: Jacob Perron <jacob@openrobotics.org> (cherry picked from commit d864ce8) Co-authored-by: Jacob Perron <jacob@openrobotics.org>
Signed-off-by: zhenpeng ge zhenpeng.ge@qq.com
As described in #440, I use new callback group and executor with dedicated thread so that we could avoid creating internal node which is overhead.
currently, i don't break the API, but i think the constructor
TransformListener(tf2::BufferCore & buffer, bool spin_thread = true)
need be deprecated, due to create internal Node.the another constructor is enough to use like this.