Skip to content
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

Closed

Conversation

orduno
Copy link
Contributor

@orduno orduno commented Jun 27, 2019


Basic Info

Info Please fill out this column
Ticket(s) this addresses #816
Primary OS tested on Ubuntu 18.04
Robotic platform tested on gazebo simulation of turtlebot 3

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.

  • Several improvements to ServiceClient. It will no longer create a node for interfacing with services if one is provided.
  • Updated GetRobotPoseClient and CostmapServiceClient to use improved ServiceClient.
  • Updated 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

  • Remove service nodes from amcl, costmaps, bt navigator and lifecycle.
  • Replace the multi-threaded executor with a custom async service executor.

@orduno orduno added the 2 - Medium Medium Priority label Jun 27, 2019
@orduno orduno added this to the E Turtle Release milestone Jun 27, 2019
@orduno orduno requested review from mjeronimo and crdelsey June 27, 2019 21:16
@orduno orduno self-assigned this Jun 27, 2019
@orduno orduno requested a review from SteveMacenski June 27, 2019 21:17
@orduno orduno requested a review from mkhansenbot July 1, 2019 18:03
@orduno orduno modified the milestones: E Turtle Release, July 2019 Jul 15, 2019
@crdelsey crdelsey requested review from bpwilcox and removed request for mkhansenbot and SteveMacenski July 22, 2019 21:31
Copy link
Contributor

@crdelsey crdelsey left a 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
image

Construction Sequence
image

OnConfigure service call
image

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;
Copy link
Contributor

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_;}
Copy link
Contributor

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.

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")]]
Copy link
Contributor

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
Copy link
Contributor

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()

@crdelsey
Copy link
Contributor

@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);

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)
{

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:

  1. All Three - rclcpp::Node, rclcpp_lifecycle::LifecycleNode, and node_interfaces
  2. Node and interfaces (like ROS2)
  3. Node and LifecycleNode

Let's talk as a group to decide.

Copy link

@bpwilcox bpwilcox left a 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_;}

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.

@crdelsey crdelsey modified the milestones: July 2019, E Turtle Release Jul 29, 2019
@bpwilcox
Copy link

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
Copy link
Member

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.

@orduno
Copy link
Contributor Author

orduno commented Aug 9, 2019

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 rclcpp package to enable this feature.

@orduno orduno closed this Aug 9, 2019
@orduno orduno deleted the 816_remove_service_client_nodes branch August 9, 2019 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 - Medium Medium Priority
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants