Skip to content

Commit b03e62c

Browse files
authored
Merge pull request #375 from grupo-avispa/back_to_idle
More improvments in BTActionNode
2 parents de0ee79 + f176dcb commit b03e62c

File tree

11 files changed

+317
-50
lines changed

11 files changed

+317
-50
lines changed

plansys2_bringup/params/plansys2_params.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
plansys2:
22
ros__parameters:
33
use_real_time: false
4-
54
planner_client:
65
ros__parameters:
7-
plan_solver_timeout: 15.0
6+
plan_solver_timeout: 15.0
87
planner:
98
ros__parameters:
10-
plan_solver_timeout: 15.0
9+
plan_solver_timeout: 15.0
1110
plan_solver_plugins: ["POPF"]
1211
POPF:
1312
plugin: "plansys2/POPFPlanSolver"

plansys2_bt_actions/include/plansys2_bt_actions/BTAction.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ class BTAction : public plansys2::ActionExecutorClient
130130
* @param tree BT to monitor
131131
* @param server_port Groot2 Server port, first of the pair (server_port, publisher_port)
132132
*/
133-
void addGrootMonitoring(BT::Tree * tree, uint16_t server_port);
133+
void add_groot_monitoring(BT::Tree * tree, uint16_t server_port);
134134

135135
/**
136136
* @brief Reset Groot2 monitor
137137
*/
138-
void resetGrootMonitor();
138+
void reset_groot_monitor();
139139

140140
/**
141141
* @brief Factory for creating Behavior Trees instances.

plansys2_bt_actions/include/plansys2_bt_actions/BTActionNode.hpp

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,20 @@ class BtActionNode : public BT::ActionNodeBase
5656
if (!config().blackboard->get("node", node_)) {
5757
RCLCPP_ERROR(node_->get_logger(), "Failed to get 'node' from the blackboard");
5858
}
59+
callback_group_ = node_->create_callback_group(
60+
rclcpp::CallbackGroupType::MutuallyExclusive, false);
61+
callback_group_executor_.add_callback_group(callback_group_, node_->get_node_base_interface());
5962

6063
// Get the required items from the blackboard
64+
auto bt_loop_duration =
65+
config().blackboard->template get<std::chrono::milliseconds>("bt_loop_duration");
6166
getInputOrBlackboard("server_timeout", server_timeout_);
6267
wait_for_service_timeout_ =
6368
config().blackboard->get<std::chrono::milliseconds>("wait_for_service_timeout");
6469

70+
// Timeout should be less than bt_loop_duration to be able to finish the current tick
71+
max_timeout_ = std::chrono::duration_cast<std::chrono::milliseconds>(bt_loop_duration * 0.5);
72+
6573
// Initialize the input and output messages
6674
goal_ = typename ActionT::Goal();
6775
result_ = typename rclcpp_action::ClientGoalHandle<ActionT>::WrappedResult();
@@ -88,7 +96,7 @@ class BtActionNode : public BT::ActionNodeBase
8896
bool createActionClient(const std::string & action_name)
8997
{
9098
// Now that we have the ROS node to use, create the action client for this BT action
91-
action_client_ = rclcpp_action::create_client<ActionT>(node_, action_name);
99+
action_client_ = rclcpp_action::create_client<ActionT>(node_, action_name, callback_group_);
92100

93101
// Make sure the server is actually there before continuing
94102
RCLCPP_INFO(node_->get_logger(), "Waiting for \"%s\" action server", action_name.c_str());
@@ -116,10 +124,10 @@ class BtActionNode : public BT::ActionNodeBase
116124
{
117125
BT::PortsList basic = {
118126
BT::InputPort<std::string>("server_name", "Action server name"),
119-
BT::InputPort<double>(
127+
BT::InputPort<std::chrono::milliseconds>(
120128
"server_timeout",
121-
5.0,
122-
"The amount of time to wait for a response from the action server, in seconds")
129+
5000,
130+
"The amount of time to wait for a response from the action server, in milliseconds")
123131
};
124132
// The user defined ports are added to the basic ports
125133
basic.insert(addition.begin(), addition.end());
@@ -221,14 +229,15 @@ class BtActionNode : public BT::ActionNodeBase
221229
case GOAL_SENT:
222230
{
223231
RCLCPP_DEBUG(node_->get_logger(), "%s GOAL_SENT", node_->get_name());
224-
if (future_goal_handle_.valid()) {
225-
goal_handle_ = future_goal_handle_.get();
226-
227-
if (!goal_handle_) {
232+
if (future_goal_handle_) {
233+
auto elapsed =
234+
(node_->now() - goal_sent_ts_).template to_chrono<std::chrono::milliseconds>();
235+
if (!is_future_goal_handle_complete(elapsed)) {
228236
RCLCPP_ERROR(
229237
node_->get_logger(),
230238
"Goal was rejected by action server %s", action_name_.c_str());
231239
state_ = GOAL_FAILURE;
240+
future_goal_handle_.reset();
232241
return BT::NodeStatus::FAILURE;
233242
} else {
234243
state_ = GOAL_EXECUTING;
@@ -240,6 +249,7 @@ class BtActionNode : public BT::ActionNodeBase
240249
node_->get_logger(),
241250
"Failed to send goal to action server %s", action_name_.c_str());
242251
state_ = GOAL_FAILURE;
252+
future_goal_handle_.reset();
243253
return BT::NodeStatus::FAILURE;
244254
} else {
245255
return BT::NodeStatus::RUNNING;
@@ -267,6 +277,8 @@ class BtActionNode : public BT::ActionNodeBase
267277
state_ = GOAL_SENT;
268278
}
269279

280+
callback_group_executor_.spin_some();
281+
270282
if (goal_result_available_) {
271283
state_ = GOAL_FINISHING;
272284
}
@@ -308,6 +320,7 @@ class BtActionNode : public BT::ActionNodeBase
308320
node_->get_logger(),
309321
"Failed to cancel action server for %s", action_name_.c_str());
310322
state_ = GOAL_FAILURE;
323+
future_goal_handle_.reset();
311324
return BT::NodeStatus::FAILURE;
312325
}
313326
}
@@ -316,13 +329,15 @@ class BtActionNode : public BT::ActionNodeBase
316329
case GOAL_FINISHED:
317330
{
318331
RCLCPP_DEBUG(node_->get_logger(), "%s GOAL_FINISHED", node_->get_name());
332+
state_ = IDLE;
319333
return BT::NodeStatus::SUCCESS;
320334
}
321335
break;
322336

323337
case GOAL_FAILURE:
324338
{
325339
RCLCPP_DEBUG(node_->get_logger(), "%s GOAL_FAILURE", node_->get_name());
340+
state_ = IDLE;
326341
return BT::NodeStatus::FAILURE;
327342
}
328343
break;
@@ -331,6 +346,7 @@ class BtActionNode : public BT::ActionNodeBase
331346
break;
332347
}
333348

349+
goal_handle_.reset();
334350
return BT::NodeStatus::RUNNING;
335351
}
336352

@@ -355,8 +371,15 @@ class BtActionNode : public BT::ActionNodeBase
355371
*/
356372
void cancel_goal()
357373
{
358-
if (goal_handle_ == nullptr) {
374+
if (!goal_handle_) {
359375
future_cancer_handle_ = action_client_->async_cancel_goal(goal_handle_);
376+
if (callback_group_executor_.spin_until_future_complete(future_cancer_handle_,
377+
server_timeout_) != rclcpp::FutureReturnCode::SUCCESS)
378+
{
379+
RCLCPP_ERROR(
380+
node_->get_logger(), "Failed to cancel action server for %s", action_name_.c_str());
381+
}
382+
360383
} else {
361384
if (!createActionClient(action_name_)) {
362385
RCLCPP_ERROR(node_->get_logger(), "Failed to create action client");
@@ -380,6 +403,7 @@ class BtActionNode : public BT::ActionNodeBase
380403
return true;
381404
}
382405

406+
callback_group_executor_.spin_some();
383407
auto status = goal_handle_->get_status();
384408

385409
// Check if the goal is still executing
@@ -411,14 +435,52 @@ class BtActionNode : public BT::ActionNodeBase
411435
};
412436

413437
RCLCPP_INFO(
414-
node_->get_logger(),
415-
"Sending goal to action server %s",
416-
action_name_.c_str());
438+
node_->get_logger(), "Sending goal to action server %s", action_name_.c_str());
417439

418-
future_goal_handle_ = action_client_->async_send_goal(goal_, send_goal_options);
440+
future_goal_handle_ = std::make_shared<
441+
std::shared_future<typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr>>(
442+
action_client_->async_send_goal(goal_, send_goal_options));
419443
goal_sent_ts_ = node_->now();
420444
}
421445

446+
/**
447+
* @brief Function to check if the action server acknowledged a new goal
448+
* @param elapsed Duration since the last goal was sent and future goal handle has not completed.
449+
* After waiting for the future to complete, this value is incremented with the timeout value.
450+
* @return boolean True if future_goal_handle_ returns SUCCESS, False otherwise
451+
*/
452+
bool is_future_goal_handle_complete(std::chrono::milliseconds & elapsed)
453+
{
454+
auto remaining = server_timeout_ - elapsed;
455+
456+
// server has already timed out, no need to sleep
457+
if (remaining <= std::chrono::milliseconds(0)) {
458+
future_goal_handle_.reset();
459+
return false;
460+
}
461+
462+
auto timeout = remaining > max_timeout_ ? max_timeout_ : remaining;
463+
auto result =
464+
callback_group_executor_.spin_until_future_complete(*future_goal_handle_, timeout);
465+
elapsed += timeout;
466+
467+
if (result == rclcpp::FutureReturnCode::INTERRUPTED) {
468+
future_goal_handle_.reset();
469+
throw std::runtime_error("send_goal failed");
470+
}
471+
472+
if (result == rclcpp::FutureReturnCode::SUCCESS) {
473+
goal_handle_ = future_goal_handle_->get();
474+
future_goal_handle_.reset();
475+
if (!goal_handle_) {
476+
throw std::runtime_error("Goal was rejected by the action server");
477+
}
478+
return true;
479+
}
480+
481+
return false;
482+
}
483+
422484
/**
423485
* @brief Function to increment recovery count on blackboard if this node wraps a recovery
424486
*/
@@ -437,7 +499,7 @@ class BtActionNode : public BT::ActionNodeBase
437499
typename ActionT::Goal goal_;
438500
bool goal_updated_{false};
439501
bool goal_result_available_{false};
440-
std::shared_future<typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr>
502+
std::shared_ptr<std::shared_future<typename rclcpp_action::ClientGoalHandle<ActionT>::SharedPtr>>
441503
future_goal_handle_;
442504
std::shared_future<typename ActionT::Impl::CancelGoalService::Response::SharedPtr>
443505
future_cancer_handle_;
@@ -447,11 +509,16 @@ class BtActionNode : public BT::ActionNodeBase
447509

448510
// The node that will be used for any ROS operations
449511
rclcpp_lifecycle::LifecycleNode::SharedPtr node_;
512+
rclcpp::CallbackGroup::SharedPtr callback_group_;
513+
rclcpp::executors::SingleThreadedExecutor callback_group_executor_;
450514

451515
// The timeout value while waiting for response from a server when a
452516
// new action goal is sent or canceled
453517
std::chrono::milliseconds server_timeout_;
454518

519+
// The timeout value for BT loop execution
520+
std::chrono::milliseconds max_timeout_;
521+
455522
// The timeout value for waiting for a service to response
456523
std::chrono::milliseconds wait_for_service_timeout_;
457524

plansys2_bt_actions/src/plansys2_bt_actions/BTAction.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ BTAction::BTAction(const std::string & action)
4141
declare_parameter<bool>("bt_minitrace_logging", false);
4242
declare_parameter<bool>("enable_groot_monitoring", false);
4343
declare_parameter<int>("server_port", -1);
44-
declare_parameter<int>("server_timeout", 20);
44+
declare_parameter<int>("server_timeout", 5000);
4545
declare_parameter<int>("wait_for_service_timeout", 1000);
4646
}
4747

@@ -54,7 +54,7 @@ BTAction::BTAction(const std::string & action, const std::chrono::nanoseconds &
5454
declare_parameter<bool>("bt_minitrace_logging", false);
5555
declare_parameter<bool>("enable_groot_monitoring", false);
5656
declare_parameter<int>("server_port", -1);
57-
declare_parameter<int>("server_timeout", 20);
57+
declare_parameter<int>("server_timeout", 5000);
5858
declare_parameter<int>("wait_for_service_timeout", 1000);
5959
}
6060

@@ -111,7 +111,7 @@ rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn
111111
BTAction::on_activate(const rclcpp_lifecycle::State & previous_state)
112112
{
113113
// If a new tree is created, than the Groot2 Publisher must be destroyed
114-
resetGrootMonitor();
114+
reset_groot_monitor();
115115

116116
try {
117117
tree_ = factory_.createTreeFromFile(bt_xml_file_, blackboard_);
@@ -171,7 +171,7 @@ BTAction::on_activate(const rclcpp_lifecycle::State & previous_state)
171171
RCLCPP_WARN(get_logger(), "Groot2 monitoring port not provided, disabling it");
172172
} else {
173173
RCLCPP_INFO(get_logger(), "Enabling Groot2 monitoring on port: %d", server_port);
174-
addGrootMonitoring(&tree_, server_port);
174+
add_groot_monitoring(&tree_, server_port);
175175
}
176176
}
177177

@@ -185,7 +185,7 @@ BTAction::on_deactivate(const rclcpp_lifecycle::State & previous_state)
185185
bt_minitrace_logger_.reset();
186186
bt_file_logger_.reset();
187187
tree_.haltTree();
188-
resetGrootMonitor();
188+
reset_groot_monitor();
189189

190190
return ActionExecutorClient::on_deactivate(previous_state);
191191
}
@@ -222,7 +222,7 @@ void BTAction::do_work()
222222
}
223223
}
224224

225-
void BTAction::addGrootMonitoring(BT::Tree * tree, uint16_t server_port)
225+
void BTAction::add_groot_monitoring(BT::Tree * tree, uint16_t server_port)
226226
{
227227
// This logger publish status changes using Groot2
228228
groot_monitor_ = std::make_unique<BT::Groot2Publisher>(*tree, server_port);
@@ -232,7 +232,7 @@ void BTAction::addGrootMonitoring(BT::Tree * tree, uint16_t server_port)
232232
BT::RegisterJsonDefinition<std_msgs::msg::Header>();
233233
}
234234

235-
void BTAction::resetGrootMonitor()
235+
void BTAction::reset_groot_monitor()
236236
{
237237
if (groot_monitor_) {
238238
groot_monitor_.reset();

plansys2_bt_actions/test/unit/bt_action_test.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,9 @@ TEST_F(BTActionsTestCase, load_plugins)
152152

153153
auto blackboard = BT::Blackboard::create();
154154
blackboard->set("node", node);
155-
blackboard->set<std::chrono::milliseconds>("server_timeout", 20ms);
155+
blackboard->set<std::chrono::milliseconds>("server_timeout", 250ms);
156156
blackboard->set<std::chrono::milliseconds>("wait_for_service_timeout", 10ms);
157+
blackboard->set<std::chrono::milliseconds>("bt_loop_duration", 100ms);
157158
BT::Tree tree = factory.createTreeFromFile(xml_file, blackboard);
158159

159160
rclcpp::Rate rate(10);
@@ -191,8 +192,9 @@ TEST_F(BTActionsTestCase, on_tick_failure)
191192
BT::assignDefaultRemapping<plansys2_bt_tests::OnTickFail>(config);
192193
auto bb = BT::Blackboard::create();
193194
bb->set("node", node);
194-
bb->set<std::chrono::milliseconds>("server_timeout", 20ms);
195+
bb->set<std::chrono::milliseconds>("server_timeout", 250ms);
195196
bb->set<std::chrono::milliseconds>("wait_for_service_timeout", 10ms);
197+
bb->set<std::chrono::milliseconds>("bt_loop_duration", 100ms);
196198
config.blackboard = bb;
197199

198200
plansys2_bt_tests::OnTickFail failure_node("OnTickFail", "move", config);
@@ -234,8 +236,9 @@ TEST_F(BTActionsTestCase, on_feedback_failure)
234236
BT::assignDefaultRemapping<plansys2_bt_tests::OnFeedbackFail>(config);
235237
auto bb = BT::Blackboard::create();
236238
bb->set("node", node);
237-
bb->set<std::chrono::milliseconds>("server_timeout", 20ms);
239+
bb->set<std::chrono::milliseconds>("server_timeout", 250ms);
238240
bb->set<std::chrono::milliseconds>("wait_for_service_timeout", 10ms);
241+
bb->set<std::chrono::milliseconds>("bt_loop_duration", 100ms);
239242
config.blackboard = bb;
240243

241244
plansys2_bt_tests::OnFeedbackFail failure_node("OnFeedbackFail",

0 commit comments

Comments
 (0)