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

Added preemption to recovery server #1644

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions nav2_behavior_tree/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ list(APPEND plugin_libs nav2_transform_available_condition_bt_node)
add_library(nav2_goal_reached_condition_bt_node SHARED plugins/condition/goal_reached_condition.cpp)
list(APPEND plugin_libs nav2_goal_reached_condition_bt_node)

add_library(nav2_goal_updated_condition_bt_node SHARED plugins/condition/goal_updated_condition.cpp)
list(APPEND plugin_libs nav2_goal_updated_condition_bt_node)

add_library(nav2_initial_pose_received_condition_bt_node SHARED plugins/condition/initial_pose_received_condition.cpp)
list(APPEND plugin_libs nav2_initial_pose_received_condition_bt_node)

Expand Down
76 changes: 76 additions & 0 deletions nav2_behavior_tree/plugins/condition/goal_updated_condition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#ifndef NAV2_BEHAVIOR_TREE__GOAL_UPDATED_CONDITION_HPP_
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am missing the copyright here, because I don't really know what I am supposed to write :)
Any ideas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy paste from another file and just change to your name

(literally)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect, thanks!

#define NAV2_BEHAVIOR_TREE__GOAL_UPDATED_CONDITION_HPP_
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this guard is wrong, check with ament_cpplint and ament_uncrustify. I'm sure they're complaining and will give you the exact header guard to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm I think uncrustify wasn't complaining on this one, I run it through and it only complaint about the copyright, but I'll check it again. If you say it because of the HPP, I also thought maybe it should be called NAV2_BEHAVIOR_TREE__GOAL_UPDATED_CONDITION_CPP_, but then all other plugins are using HPP instead of CPP on the guard (even when they are cpp files). Is that what you saw?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just see what it says when you fix the copyright and work from there. Maybe I'm mistaken.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For sure, thanks.


#include <memory>
#include <string>

#include "behaviortree_cpp_v3/condition_node.h"
#include "geometry_msgs/msg/pose_stamped.hpp"
#include "nav2_util/robot_utils.hpp"
#include "rclcpp/rclcpp.hpp"
#include "tf2_ros/buffer.h"

namespace nav2_behavior_tree
{

class GoalUpdatedCondition : public BT::ConditionNode
{
public:
GoalUpdatedCondition(
const std::string & condition_name,
const BT::NodeConfiguration & conf)
: BT::ConditionNode(condition_name, conf), initialized_(false)
{
}

GoalUpdatedCondition() = delete;

BT::NodeStatus tick() override
{
if (!initialized_) {
initialize();
}

if (isGoalUpdated()) {
return BT::NodeStatus::SUCCESS;
}
return BT::NodeStatus::FAILURE;
}

void initialize()
{
goal_ = config().blackboard->get<geometry_msgs::msg::PoseStamped>("goal");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add this stuff to the constructor and remove the initialize function and the variable.

Copy link
Contributor Author

@gimait gimait May 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can remove the function and add the logic in the tick, but I think this initialization needs to be here (in the tick). I think the blackboard goal might not be set when the constructor is called (i.e., when we build the tree).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And now that I see it, I'm also missing to set back to false the initialized_ variable to false if the node in halt. I just realized that the goal needs to be updated in the first tick of the condition, otherwise if the goal changed between restarts of this node, this one will succeed on the first tick!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you set goal to a bogus value, then the first goal that comes in will trigger the updated condition. I think that would be fine, wouldn't it? Then the comparison for is current would be that there's a new goal and therefore replan.

I think the goal_ could actually be constructed as default so that it is always out of date on startup causing the updated condition to be true on the first tick.

Does that make sense / correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is, how do you know if you are in the first tick? What we are checking is wether the goal has changed, meaning that return success means "goal has changed" and failure means "goal has not changed".

What I thought is:

1st tick: A flag tells that is the first tick, so we check the goal (and a return could be here straight away)
2nd and from there on: The flag is gone, so we just check the value in the blackboard, if it changes, return success, else failure.

The reason behind this is how the parent ReactiveFallback node works. That node will halt all children and return success when one of its children returns success. This means that this new condition node needs to return failure in each tick, except from when the goal changes. This also means that we need to reset the current goal any time we enter the parent control node, so we don't return success in the first tick. I think first tick should always be initialization and return failure.

What do you think? Maybe I don't understand what you mean.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is the first iteration, lets game out the 2 things this does:

  • you're not going to be entering a recovery state by default. So the case of recovery being killed won't happen (also even if it was killed because that was default behavior, its not like that matters).
  • the planner you want to plan when you have start up, so the mismatch of the default constructor goal and the goal actually passed by the BT navigator will cause the planner to plan.

Isn't that what we want? For subsequent calls, you're updating the goal_ param with the new blackboard param, correct, so then in the future the goal_ is the same and it won't replan. Once a new goal comes in, it'll again mismatch and cause a replan.

Your 1st tick I think right now wouldn't actually cause the planner to plan at all. You set the goal_ to the initial request goal and then you check if they're the same, which they now are. That would make this return false that there is no new goal and then wouldn't navigate or plan ever until a new preempted goal was requested.

You could test this by just running this code and sending the first goal and seeing if the robot actually tries to plan on the initial goal sent.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should continue this in the new PR.

initialized_ = true;
}

bool
isGoalUpdated()
{
auto current_goal = config().blackboard->get<geometry_msgs::msg::PoseStamped>("goal");
if (goal_ != current_goal) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was clever. I thought about setting a flag and required a new BT node in the root. This gets around that. Good idea.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think on other way to check if there if the goal was preempted in more than one place. Thanks!

goal_ = current_goal;
return true;
} else {
return false;
}
}

static BT::PortsList providedPorts()
{
return {};
}

private:
geometry_msgs::msg::PoseStamped goal_;
bool initialized_;
};

} // namespace nav2_behavior_tree

#include "behaviortree_cpp_v3/bt_factory.h"
BT_REGISTER_NODES(factory)
{
factory.registerNodeType<nav2_behavior_tree::GoalUpdatedCondition>("GoalUpdated");
}

#endif // NAV2_BEHAVIOR_TREE__GOAL_UPDATED_CONDITION_HPP_
1 change: 1 addition & 0 deletions nav2_bringup/bringup/params/nav2_multirobot_params_1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ bt_navigator:
- nav2_clear_costmap_service_bt_node
- nav2_is_stuck_condition_bt_node
- nav2_goal_reached_condition_bt_node
- nav2_goal_updated_condition_bt_node
- nav2_initial_pose_received_condition_bt_node
- nav2_reinitialize_global_localization_service_bt_node
- nav2_rate_controller_bt_node
Expand Down
1 change: 1 addition & 0 deletions nav2_bringup/bringup/params/nav2_multirobot_params_2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ bt_navigator:
- nav2_clear_costmap_service_bt_node
- nav2_is_stuck_condition_bt_node
- nav2_goal_reached_condition_bt_node
- nav2_goal_updated_condition_bt_node
- nav2_initial_pose_received_condition_bt_node
- nav2_reinitialize_global_localization_service_bt_node
- nav2_rate_controller_bt_node
Expand Down
1 change: 1 addition & 0 deletions nav2_bringup/bringup/params/nav2_params.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ bt_navigator:
- nav2_clear_costmap_service_bt_node
- nav2_is_stuck_condition_bt_node
- nav2_goal_reached_condition_bt_node
- nav2_goal_updated_condition_bt_node
- nav2_initial_pose_received_condition_bt_node
- nav2_reinitialize_global_localization_service_bt_node
- nav2_rate_controller_bt_node
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
<ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/>
</RecoveryNode>
</PipelineSequence>
<SequenceStar name="RecoveryActions">
<ClearEntireCostmap name="ClearLocalCostmap-Subtree" service_name="local_costmap/clear_entirely_local_costmap"/>
<ClearEntireCostmap name="ClearGlobalCostmap-Subtree" service_name="global_costmap/clear_entirely_global_costmap"/>
<Spin spin_dist="1.57"/>
<Wait wait_duration="5"/>
</SequenceStar>
<ReactiveFallback name="RecoveryFallback">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please show images of the new BTs for any BTs you change with the tool. You'll also need to update those images in the readme files / anywhere else the BT images are displayed. Also look over the readmes for descriptions of these BTs and update them with this new logic flow.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I'll include the documentation changes in the new pr.

<GoalUpdated name="GoalUpdatedCondition"/>
<SequenceStar name="RecoveryActions">
<ClearEntireCostmap name="ClearLocalCostmap-Subtree" service_name="local_costmap/clear_entirely_local_costmap"/>
<ClearEntireCostmap name="ClearGlobalCostmap-Subtree" service_name="global_costmap/clear_entirely_global_costmap"/>
<Spin spin_dist="1.57"/>
<Wait wait_duration="5"/>
</SequenceStar>
</ReactiveFallback>
</RecoveryNode>
</BehaviorTree>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,30 @@
<RateController hz="1.0">
<RecoveryNode number_of_retries="4" name="ComputePathToPose">
<ComputePathToPose goal="{goal}" path="{path}" planner_id="GridBased"/>
<RoundRobin name="GlobalPlannerRecoveryActions">
<ClearEntireCostmap name="ClearGlobalCostmap-Context" service_name="global_costmap/clear_entirely_global_costmap"/>
<Wait wait_duration="2"/>
</RoundRobin>
<ReactiveFallback name="PathToPoseFallback">
<GoalUpdated name="GoalUpdatedCondition"/>
<RoundRobin name="GlobalPlannerRecoveryActions">
<ClearEntireCostmap name="ClearGlobalCostmap-Context" service_name="global_costmap/clear_entirely_global_costmap"/>
<Wait wait_duration="2"/>
</RoundRobin>
</ReactiveFallback>
</RecoveryNode>
</RateController>
<RecoveryNode number_of_retries="4" name="FollowPath">
<FollowPath path="{path}" controller_id="FollowPath"/>
<RoundRobin name="PlannerRecoveryActions">
<ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/>
<Spin spin_dist="1.57"/>
</RoundRobin>
<ReactiveFallback name="FollowPathFallback">
<GoalUpdated name="GoalUpdatedCondition"/>
<RoundRobin name="PlannerRecoveryActions">
<ClearEntireCostmap name="ClearLocalCostmap-Context" service_name="local_costmap/clear_entirely_local_costmap"/>
<Spin spin_dist="1.57"/>
</RoundRobin>
</ReactiveFallback>
</RecoveryNode>
</PipelineSequence>
<ReactiveFallback name="RecoveryFallback">
<GoalUpdated name="GoalUpdatedCondition"/>
<Wait wait_duration="5"/>
</ReactiveFallback>
</RecoveryNode>
</BehaviorTree>
</root>
1 change: 1 addition & 0 deletions nav2_bt_navigator/src/bt_navigator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ BtNavigator::BtNavigator()
"nav2_is_stuck_condition_bt_node",
"nav2_goal_reached_condition_bt_node",
"nav2_initial_pose_received_condition_bt_node",
"nav2_goal_updated_condition_bt_node",
"nav2_reinitialize_global_localization_service_bt_node",
"nav2_rate_controller_bt_node",
"nav2_recovery_node_bt_node",
Expand Down