Skip to content

Commit 2214a04

Browse files
BehaviorTree refactoring (#1606)
* Proposed refactoring to avoid issues with CoroAction * correctly haltAllActions (related to #1600) * not really needed and will be deprecated soon * Applying changes suggested in the comments of #1606 - fix haltAllActions - changes method signature on_success() - reverts the changes made here: https://github.com/ros-planning/navigation2/pull/1515/files * fix warnings and errors * make uncrustify happy? * Update bt_navigator.cpp * Update bt_navigator.cpp * uncrustify fix Co-authored-by: daf@blue-ocean-robotics.com <Davide Faconti>
1 parent 042dfdb commit 2214a04

File tree

6 files changed

+93
-59
lines changed

6 files changed

+93
-59
lines changed

‎nav2_behavior_tree/include/nav2_behavior_tree/behavior_tree_engine.hpp‎

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,21 +44,19 @@ class BehaviorTreeEngine
4444
const std::string & xml_string,
4545
BT::Blackboard::Ptr blackboard);
4646

47+
// In order to re-run a Behavior Tree, we must be able to reset all nodes to the initial state
4748
void haltAllActions(BT::TreeNode * root_node)
4849
{
49-
auto visitor = [](BT::TreeNode * node) {
50-
if (auto action = dynamic_cast<BT::CoroActionNode *>(node)) {
51-
action->halt();
52-
}
53-
};
54-
BT::applyRecursiveVisitor(root_node, visitor);
55-
}
50+
// this halt signal should propagate through the entire tree.
51+
root_node->halt();
52+
root_node->setStatus(BT::NodeStatus::IDLE);
5653

57-
// In order to re-run a Behavior Tree, we must be able to reset all nodes to the initial state
58-
void resetTree(BT::TreeNode * root_node)
59-
{
54+
// but, just in case...
6055
auto visitor = [](BT::TreeNode * node) {
61-
node->setStatus(BT::NodeStatus::IDLE);
56+
if (node->status() == BT::NodeStatus::RUNNING) {
57+
node->halt();
58+
node->setStatus(BT::NodeStatus::IDLE);
59+
}
6260
};
6361
BT::applyRecursiveVisitor(root_node, visitor);
6462
}

‎nav2_behavior_tree/include/nav2_behavior_tree/bt_action_node.hpp‎

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ namespace nav2_behavior_tree
2626
{
2727

2828
template<class ActionT>
29-
class BtActionNode : public BT::CoroActionNode
29+
class BtActionNode : public BT::ActionNodeBase
3030
{
3131
public:
3232
BtActionNode(
3333
const std::string & xml_tag_name,
3434
const std::string & action_name,
3535
const BT::NodeConfiguration & conf)
36-
: BT::CoroActionNode(xml_tag_name, conf), action_name_(action_name)
36+
: BT::ActionNodeBase(xml_tag_name, conf), action_name_(action_name)
3737
{
3838
node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
3939

@@ -96,66 +96,71 @@ class BtActionNode : public BT::CoroActionNode
9696
}
9797

9898
// Called upon successful completion of the action. A derived class can override this
99-
// method to put a value on the blackboard, for example
100-
virtual void on_success()
99+
// method to put a value on the blackboard, for example.
100+
virtual BT::NodeStatus on_success()
101101
{
102+
return BT::NodeStatus::SUCCESS;
102103
}
103104

104-
// The main override required by a BT action
105-
BT::NodeStatus tick() override
105+
// Called when a the action is aborted. By default, the node will return FAILURE.
106+
// The user may override it to return another value, instead.
107+
virtual BT::NodeStatus on_aborted()
106108
{
107-
on_tick();
109+
return BT::NodeStatus::FAILURE;
110+
}
108111

109-
new_goal_received:
110-
goal_result_available_ = false;
111-
auto send_goal_options = typename rclcpp_action::Client<ActionT>::SendGoalOptions();
112-
send_goal_options.result_callback =
113-
[this](const typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult & result) {
114-
if (result.code != rclcpp_action::ResultCode::ABORTED) {
115-
goal_result_available_ = true;
116-
result_ = result;
117-
}
118-
};
112+
// Called when a the action is cancelled. By default, the node will return SUCCESS.
113+
// The user may override it to return another value, instead.
114+
virtual BT::NodeStatus on_cancelled()
115+
{
116+
return BT::NodeStatus::SUCCESS;
117+
}
119118

120-
auto future_goal_handle = action_client_->async_send_goal(goal_, send_goal_options);
119+
// The main override required by a BT action
120+
BT::NodeStatus tick() override
121+
{
122+
// first step to be done only at the beginning of the Action
123+
if (status() == BT::NodeStatus::IDLE) {
124+
// setting the status to RUNNING to notify the BT Loggers (if any)
125+
setStatus(BT::NodeStatus::RUNNING);
121126

122-
if (rclcpp::spin_until_future_complete(node_, future_goal_handle) !=
123-
rclcpp::executor::FutureReturnCode::SUCCESS)
124-
{
125-
throw std::runtime_error("send_goal failed");
126-
}
127+
// user defined callback
128+
on_tick();
127129

128-
goal_handle_ = future_goal_handle.get();
129-
if (!goal_handle_) {
130-
throw std::runtime_error("Goal was rejected by the action server");
130+
on_new_goal_received();
131131
}
132132

133-
while (rclcpp::ok() && !goal_result_available_) {
133+
// The following code corresponds to the "RUNNING" loop
134+
if (rclcpp::ok() && !goal_result_available_) {
135+
// user defined callback. May modify the value of "goal_updated_"
134136
on_wait_for_result();
135137

136-
auto status = goal_handle_->get_status();
137-
if (goal_updated_ && (status == action_msgs::msg::GoalStatus::STATUS_EXECUTING ||
138-
status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED))
138+
auto goal_status = goal_handle_->get_status();
139+
if (goal_updated_ && (goal_status == action_msgs::msg::GoalStatus::STATUS_EXECUTING ||
140+
goal_status == action_msgs::msg::GoalStatus::STATUS_ACCEPTED))
139141
{
140142
goal_updated_ = false;
141-
goto new_goal_received;
143+
on_new_goal_received();
142144
}
143145

144-
// Yield to any other nodes
145-
setStatusRunningAndYield();
146146
rclcpp::spin_some(node_);
147+
148+
// check if, after invoking spin_some(), we finally received the result
149+
if (!goal_result_available_) {
150+
// Yield this Action, returning RUNNING
151+
return BT::NodeStatus::RUNNING;
152+
}
147153
}
148154

149155
switch (result_.code) {
150156
case rclcpp_action::ResultCode::SUCCEEDED:
151-
on_success();
152-
return BT::NodeStatus::SUCCESS;
157+
return on_success();
153158

154159
case rclcpp_action::ResultCode::ABORTED:
155-
return BT::NodeStatus::FAILURE;
160+
return on_aborted();
156161

157162
case rclcpp_action::ResultCode::CANCELED:
158-
return BT::NodeStatus::SUCCESS;
163+
return on_cancelled();
159164

160165
default:
161166
throw std::logic_error("BtActionNode::Tick: invalid status value");
@@ -178,7 +183,6 @@ class BtActionNode : public BT::CoroActionNode
178183
}
179184

180185
setStatus(BT::NodeStatus::IDLE);
181-
CoroActionNode::halt();
182186
}
183187

184188
protected:
@@ -202,6 +206,33 @@ class BtActionNode : public BT::CoroActionNode
202206
return false;
203207
}
204208

209+
210+
void on_new_goal_received()
211+
{
212+
goal_result_available_ = false;
213+
auto send_goal_options = typename rclcpp_action::Client<ActionT>::SendGoalOptions();
214+
send_goal_options.result_callback =
215+
[this](const typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult & result) {
216+
if (result.code != rclcpp_action::ResultCode::ABORTED) {
217+
goal_result_available_ = true;
218+
result_ = result;
219+
}
220+
};
221+
222+
auto future_goal_handle = action_client_->async_send_goal(goal_, send_goal_options);
223+
224+
if (rclcpp::spin_until_future_complete(node_, future_goal_handle) !=
225+
rclcpp::executor::FutureReturnCode::SUCCESS)
226+
{
227+
throw std::runtime_error("send_goal failed");
228+
}
229+
230+
goal_handle_ = future_goal_handle.get();
231+
if (!goal_handle_) {
232+
throw std::runtime_error("Goal was rejected by the action server");
233+
}
234+
}
235+
205236
const std::string action_name_;
206237
typename std::shared_ptr<rclcpp_action::Client<ActionT>> action_client_;
207238

‎nav2_behavior_tree/include/nav2_behavior_tree/bt_service_node.hpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ namespace nav2_behavior_tree
2626
{
2727

2828
template<class ServiceT>
29-
class BtServiceNode : public BT::CoroActionNode
29+
class BtServiceNode : public BT::SyncActionNode
3030
{
3131
public:
3232
BtServiceNode(
3333
const std::string & service_node_name,
3434
const BT::NodeConfiguration & conf)
35-
: BT::CoroActionNode(service_node_name, conf), service_node_name_(service_node_name)
35+
: BT::SyncActionNode(service_node_name, conf), service_node_name_(service_node_name)
3636
{
3737
node_ = config().blackboard->get<rclcpp::Node::SharedPtr>("node");
3838

‎nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class ComputePathToPoseAction : public BtActionNode<nav2_msgs::action::ComputePa
4747
getInput("planner_id", goal_.planner_id);
4848
}
4949

50-
void on_success() override
50+
BT::NodeStatus on_success() override
5151
{
5252
setOutput("path", result_.result->path);
5353

@@ -56,6 +56,7 @@ class ComputePathToPoseAction : public BtActionNode<nav2_msgs::action::ComputePa
5656
} else {
5757
config().blackboard->set("path_updated", true);
5858
}
59+
return BT::NodeStatus::SUCCESS;
5960
}
6061

6162
static BT::PortsList providedPorts()

‎nav2_bt_navigator/include/nav2_bt_navigator/bt_navigator.hpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ class BtNavigator : public nav2_util::LifecycleNode
110110
void onGoalPoseReceived(const geometry_msgs::msg::PoseStamped::SharedPtr pose);
111111
rclcpp::Subscription<geometry_msgs::msg::PoseStamped>::SharedPtr goal_sub_;
112112

113+
BT::Tree tree_;
114+
113115
// The blackboard shared by all of the nodes in the tree
114116
BT::Blackboard::Ptr blackboard_;
115117

‎nav2_bt_navigator/src/bt_navigator.cpp‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,9 @@ BtNavigator::on_configure(const rclcpp_lifecycle::State & /*state*/)
129129
RCLCPP_DEBUG(get_logger(), "Behavior Tree file: '%s'", bt_xml_filename.c_str());
130130
RCLCPP_DEBUG(get_logger(), "Behavior Tree XML: %s", xml_string_.c_str());
131131

132+
// Create the Behavior Tree from the XML input
133+
tree_ = bt_->buildTreeFromText(xml_string_, blackboard_);
134+
132135
return nav2_util::CallbackReturn::SUCCESS;
133136
}
134137

@@ -159,7 +162,6 @@ BtNavigator::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
159162

160163
// TODO(orduno) Fix the race condition between the worker thread ticking the tree
161164
// and the main thread resetting the resources, see #1344
162-
163165
goal_sub_.reset();
164166
client_node_.reset();
165167
self_client_.reset();
@@ -172,6 +174,7 @@ BtNavigator::on_cleanup(const rclcpp_lifecycle::State & /*state*/)
172174
plugin_lib_names_.clear();
173175
xml_string_.clear();
174176
blackboard_.reset();
177+
bt_->haltAllActions(tree_.root_node);
175178
bt_.reset();
176179

177180
RCLCPP_INFO(get_logger(), "Completed Cleaning up");
@@ -211,11 +214,7 @@ BtNavigator::navigateToPose()
211214
return action_server_->is_cancel_requested();
212215
};
213216

214-
215-
// Create the Behavior Tree from the XML input
216-
BT::Tree tree = bt_->buildTreeFromText(xml_string_, blackboard_);
217-
218-
RosTopicLogger topic_logger(client_node_, tree);
217+
RosTopicLogger topic_logger(client_node_, tree_);
219218

220219
auto on_loop = [&]() {
221220
if (action_server_->is_preempt_requested()) {
@@ -227,7 +226,10 @@ BtNavigator::navigateToPose()
227226
};
228227

229228
// Execute the BT that was previously created in the configure step
230-
nav2_behavior_tree::BtStatus rc = bt_->run(&tree, on_loop, is_canceling);
229+
nav2_behavior_tree::BtStatus rc = bt_->run(&tree_, on_loop, is_canceling);
230+
// Make sure that the Bt is not in a running state from a previous execution
231+
// note: if all the ControlNodes are implemented correctly, this is not needed.
232+
bt_->haltAllActions(tree_.root_node);
231233

232234
switch (rc) {
233235
case nav2_behavior_tree::BtStatus::SUCCEEDED:

0 commit comments

Comments
 (0)