-
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
Removing service client nodes from navfn #897
Removing service client nodes from navfn #897
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.
Here's some design drawings that I gathered from the code.
Class Diagram of Key Functions and methods
I'd like to clean up the main Construction sequence a bit. I'll try to draw something up later. Main shouldn't have to call has_nested_service_requests. Main should be saying something more like - here's the nodes I have, give me an appropriate executor. I think we can achieve that by adding another interface to our nodes that provides the "has_nested_service_requests" method on it. Then main could just pass in the nodes.
Also, I'd like to somehow hide the CallBackGroup from NavFnPlanner if possible.
@@ -67,7 +67,7 @@ class CachedDistanceMap | |||
bool operator<(const CellData & a, const CellData & b) | |||
{ | |||
return a.map_->cells[MAP_INDEX(a.map_, a.i_, | |||
a.j_)].occ_dist > a.map_->cells[MAP_INDEX(b.map_, b.i_, b.j_)].occ_dist; | |||
a.j_)].occ_dist > a.map_->cells[MAP_INDEX(b.map_, b.i_, b.j_)].occ_dist; |
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.
This change should not be part of this PR. It might just be that this needs to be rebased.
@@ -43,6 +43,8 @@ class NavfnPlanner : public nav2_util::LifecycleNode | |||
NavfnPlanner(); | |||
~NavfnPlanner(); | |||
|
|||
bool has_nested_service_requests() {return has_nested_client_;} |
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'd ditch the variable and just make this return true
since it is always true.
I'd also add a comment explaining that the existence of costmap_client_ and get_robot_pose_client_ make it true. That way, there is a chance that someone changing those members will also find this function and change it appropriately.
Alternatively, I expect we could do something with template metaprogramming to automatically detect the existence of service clients and have this function just do the right thing.
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'd be nice if there was a function you could call on the node to return a count of service clients it has, so that we don't have to define that here.
} | ||
|
||
ServiceClient(const std::string & service_name, const std::string & parent_name) | ||
// TODO(orduno) Remove this constructor which relies on generating an internal node | ||
[[deprecated("use alternative constructors")]] |
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.
We don't need to do this deprecated stuff. It's just going to cause our build to fail when we re-enable deprecated warnings, which we should do.
@@ -118,10 +194,22 @@ class ServiceClient | |||
} | |||
} | |||
|
|||
bool nodeIsSpinable() const |
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.
This should be snake case. It's more standard to start the function name with is rather than put it in the middle. And it should be spelt spinnable with two n eg
is_node_spinnable()
@mjeronimo @orduno As I thought about this some more, I don't think we should continue with this approach to removing the service node. As I understand it, we thought there would be a fairly simple, elegant way of removing them using some of the futures facilities available. This turned out not to be the case. To make it work requires these fairly big, intrusive changes. I think we should abandon this change and just get things fixed upstream so we can do reentrant service calls. Thoughts? |
@@ -542,7 +550,7 @@ NavfnPlanner::getCostmap( | |||
auto request = std::make_shared<nav2_util::CostmapServiceClient::CostmapServiceRequest>(); | |||
request->specs.resolution = 1.0; | |||
|
|||
auto result = costmap_client_.invoke(request, 5s); | |||
auto result = costmap_client_->invoke(request, 5s); |
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.
Per feedback on another PR and our group discussion, we should parameterize these timeouts. This doesn't have to happen with this PR, but we should open an issue on this. Probably have a service_call_timeout parameter or something like that. We should standardize the name because it will be needed in several modules and should use the same name (not that they are necessarily the same value).
explicit GetRobotPoseClient( | ||
rclcpp_lifecycle::LifecycleNode::SharedPtr node, | ||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr) | ||
: ServiceClient<nav2_msgs::srv::GetRobotPose>("GetRobotPose", node, group) | ||
{ |
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 noticed that there is not a constructor for the node_interfaces version. With node helper classes like this, we should standardize on the constructors we provide. ROS2 seems to provide the rclcpp::Node version and the node_interfaces version. I think there are three options:
- All Three - rclcpp::Node, rclcpp_lifecycle::LifecycleNode, and node_interfaces
- Node and interfaces (like ROS2)
- Node and LifecycleNode
Let's talk as a group to decide.
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.
@crdelsey I'm in favor of moving this PR forward and working on changes upstream in parallel.
@@ -43,6 +43,8 @@ class NavfnPlanner : public nav2_util::LifecycleNode | |||
NavfnPlanner(); | |||
~NavfnPlanner(); | |||
|
|||
bool has_nested_service_requests() {return has_nested_client_;} |
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'd be nice if there was a function you could call on the node to return a count of service clients it has, so that we don't have to define that here.
I think there could be a test that does a simplified mock of the lifecycle service client use-case on a spinning node as we do in our stack, especially given changes with the use of multithreaded executors and callback groups |
|
||
auto planner = std::make_shared<nav2_navfn_planner::NavfnPlanner>(); | ||
|
||
// The planner needs a special executor |
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.
Feedback: I'd just put in the multithreaded executor, making this wrapper makes it less clear to me than just having it all together.
If there's issues with the planner as a service, maybe it should be an action, but in general I dont think a multi-threaded spinner is something to be avoided, I make heavy use of them in autonomy systems in ROS1 to allow for a bunch of services/callbacks to be triggered without waiting.
As per our discussion in the ROS2 Navigation WG, I'm closing the issue. For now, we will rely on the current pattern to enable calling a service within a (service) callback. The current approach is to create an additional node during node construction (or configuration). Instead of following the pattern proposed on this PR, We'll work upstream on the |
Basic Info
Description of contribution in a few bullet points
As described in #816, several nodes are created and used solely for interfacing to services. This PR proposes an approach for removing such nodes.
ServiceClient
. It will no longer create a node for interfacing with services if one is provided.GetRobotPoseClient
andCostmapServiceClient
to use improvedServiceClient
.Navfn
to use new clients. For obtaining async service responses, it requires a multi-threaded executor.Once we agree on the model, which I'm using
navfn
as an example, I can propagate the changes to the rest of the nodes on a separate PR.Future work that may be required in bullet points