-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Spin the node from the blackboard before calling getCurrentPose #808
Spin the node from the blackboard before calling getCurrentPose #808
Conversation
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.
Is there a way to just spin the node in a background thread, so we don't need to manually spin every time we want to take an action?
@crdelsey that's generally how the
|
@crdelsey @SteveMacenski Yes, we could put a spinning node on the blackboard, but we'd also need a non-spinning one for service calls. Would you prefer this? This would be similar to what I have in the lifecycle nodes, where there is an rclcpp_node (a spinning one) and a client_node (non-spinning). |
Does the problem come from the |
Yes, the main issue is that a node used with an executor can't already be on another executor. spin_until_future_complete, used in service calls, puts it on an executor (spin_node_until_future_complete is called by spin_until_future_complete): template<typename ResponseT, typename TimeRepT = int64_t, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_node_until_future_complete(
rclcpp::executor::Executor & executor,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::shared_future<ResponseT> & future,
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
{
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
// inside a callback executed by an executor.
executor.add_node(node_ptr);
auto retcode = executor.spin_until_future_complete(future, timeout);
executor.remove_node(node_ptr);
return retcode;
} That's a great idea about using the future's wait_util. As a test, I did a quick hack of the minimal_client and got this to work: void spin_node(rclcpp::Node::SharedPtr node)
{
rclcpp::spin(node);
}
int main(int argc, char * argv[])
{
rclcpp::init(argc, argv);
auto node = rclcpp::Node::make_shared("minimal_client");
auto client = node->create_client<AddTwoInts>("add_two_ints");
std::thread t(spin_node, node);
while (!client->wait_for_service(std::chrono::seconds(1))) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(node->get_logger(), "client interrupted while waiting for service to appear.");
return 1;
}
RCLCPP_INFO(node->get_logger(), "waiting for service to appear...");
}
auto request = std::make_shared<AddTwoInts::Request>();
request->a = 41;
request->b = 1;
auto result_future = client->async_send_request(request);
std::chrono::system_clock::time_point two_seconds_passed
= std::chrono::system_clock::now() + std::chrono::seconds(2);
auto status = result_future.wait_until(two_seconds_passed);
printf("status: %d\n", (int) status);
#if 0
if (rclcpp::spin_until_future_complete(node, result_future) !=
rclcpp::executor::FutureReturnCode::SUCCESS)
{
RCLCPP_ERROR(node->get_logger(), "service call failed :(");
return 1;
}
#endif
auto result = result_future.get();
RCLCPP_INFO(node->get_logger(), "result of %" PRId64 " + %" PRId64 " = %" PRId64,
request->a, request->b, result->sum);
rclcpp::shutdown();
t.join();
return 0;
}
If we want to take this approach, we can eliminate several "client_nodes" that we're holding just for service calls. Perhaps we could integrate this PR to make sure that IsLocalized and GoalReached work and then start a new PR to get rid of all service client (non-spinning) nodes. |
By the way, here is the node list for the system running in simulation. I count 8 nodes that can be eliminated with this method. Next, after this, we can focus on eliminating the rclcpp_nodes, which we've been using to bridge to ROS2 functionality that doesn't yet support lifecycle nodes. /amcl |
Maybe a dumb question -- but why not just use 1 node with a multithreaded executor? That's how I would go about something like this in ROS1 as well. One node spinning but spins up multiple threads so you can have many callbacks executed simultaneously. ROS2 documentation says it supports as well. |
@SteveMacenski Yes, I agree: I would like to begin to consolidate the nodes using compostion and not have so many separate processes. It's just on the TODO list. We could bring the nodes together into a single main with a multi-threaded executor. We'd be able to use the intra process communication as well, which should avoid the overhead of marshaling and unmarshaling messages. I think this should be a high priority. |
Description