diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7e6fed842ab..ef14bf0d667 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -75,6 +75,9 @@ references:
rosdep install -q -y \
--from-paths src \
--ignore-src \
+ --skip-keys " \
+ slam_toolbox \
+ " \
--verbose | \
awk '$1 ~ /^resolution\:/' | \
awk -F'[][]' '{print $2}' | \
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index d2d3108e77b..884227ea104 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -11,6 +11,8 @@ For Bug report or feature requests, please fill out the relevant category below
- Operating System:
-
+- ROS2 Version:
+ -
- Version or commit hash:
-
diff --git a/nav2_behavior_tree/package.xml b/nav2_behavior_tree/package.xml
index 5c678f58e42..604c3b5c288 100644
--- a/nav2_behavior_tree/package.xml
+++ b/nav2_behavior_tree/package.xml
@@ -2,7 +2,7 @@
nav2_behavior_tree
- 0.3.4
+ 0.4.7
TODO
Michael Jeronimo
Carlos Orduno
@@ -17,10 +17,10 @@
rclcpp
rclcpp_action
rclcpp_lifecycle
- std_msgs
behaviortree_cpp_v3
builtin_interfaces
geometry_msgs
+ sensor_msgs
nav2_msgs
nav_msgs
tf2
@@ -39,6 +39,7 @@
behaviortree_cpp_v3
builtin_interfaces
geometry_msgs
+ sensor_msgs
nav2_msgs
nav_msgs
tf2
diff --git a/nav2_behavior_tree/plugins/action/clear_costmap_service.cpp b/nav2_behavior_tree/plugins/action/clear_costmap_service.cpp
index 8482e464c34..aa269bdbcc3 100644
--- a/nav2_behavior_tree/plugins/action/clear_costmap_service.cpp
+++ b/nav2_behavior_tree/plugins/action/clear_costmap_service.cpp
@@ -12,34 +12,25 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NAV2_BEHAVIOR_TREE__CLEAR_COSTMAP_SERVICE_HPP_
-#define NAV2_BEHAVIOR_TREE__CLEAR_COSTMAP_SERVICE_HPP_
-
#include
#include
-#include
-#include "nav2_behavior_tree/bt_service_node.hpp"
-#include "nav2_msgs/srv/clear_entire_costmap.hpp"
+#include "nav2_behavior_tree/plugins/action/clear_costmap_service.hpp"
namespace nav2_behavior_tree
{
-class ClearEntireCostmapService : public BtServiceNode
+ClearEntireCostmapService::ClearEntireCostmapService(
+ const std::string & service_node_name,
+ const BT::NodeConfiguration & conf)
+: BtServiceNode(service_node_name, conf)
{
-public:
- ClearEntireCostmapService(
- const std::string & service_node_name,
- const BT::NodeConfiguration & conf)
- : BtServiceNode(service_node_name, conf)
- {
- }
+}
- void on_tick() override
- {
- increment_recovery_count();
- }
-};
+void ClearEntireCostmapService::on_tick()
+{
+ increment_recovery_count();
+}
} // namespace nav2_behavior_tree
@@ -48,5 +39,3 @@ BT_REGISTER_NODES(factory)
{
factory.registerNodeType("ClearEntireCostmap");
}
-
-#endif // NAV2_BEHAVIOR_TREE__CLEAR_COSTMAP_SERVICE_HPP_
diff --git a/nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp b/nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp
index be53df37fe5..f4f551eb588 100644
--- a/nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp
+++ b/nav2_behavior_tree/plugins/action/compute_path_to_pose_action.cpp
@@ -12,61 +12,39 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NAV2_BEHAVIOR_TREE__COMPUTE_PATH_TO_POSE_ACTION_HPP_
-#define NAV2_BEHAVIOR_TREE__COMPUTE_PATH_TO_POSE_ACTION_HPP_
-
#include
#include
-#include "nav2_msgs/action/compute_path_to_pose.hpp"
-#include "nav_msgs/msg/path.h"
-#include "nav2_behavior_tree/bt_action_node.hpp"
+#include "nav2_behavior_tree/plugins/action/compute_path_to_pose_action.hpp"
namespace nav2_behavior_tree
{
-class ComputePathToPoseAction : public BtActionNode
+ComputePathToPoseAction::ComputePathToPoseAction(
+ const std::string & xml_tag_name,
+ const std::string & action_name,
+ const BT::NodeConfiguration & conf)
+: BtActionNode(xml_tag_name, action_name, conf)
{
-public:
- ComputePathToPoseAction(
- const std::string & xml_tag_name,
- const std::string & action_name,
- const BT::NodeConfiguration & conf)
- : BtActionNode(xml_tag_name, action_name, conf)
- {
- }
-
- void on_tick() override
- {
- getInput("goal", goal_.pose);
- getInput("planner_id", goal_.planner_id);
- }
+}
- BT::NodeStatus on_success() override
- {
- setOutput("path", result_.result->path);
+void ComputePathToPoseAction::on_tick()
+{
+ getInput("goal", goal_.pose);
+ getInput("planner_id", goal_.planner_id);
+}
- if (first_time_) {
- first_time_ = false;
- } else {
- config().blackboard->set("path_updated", true);
- }
- return BT::NodeStatus::SUCCESS;
- }
+BT::NodeStatus ComputePathToPoseAction::on_success()
+{
+ setOutput("path", result_.result->path);
- static BT::PortsList providedPorts()
- {
- return providedBasicPorts(
- {
- BT::OutputPort("path", "Path created by ComputePathToPose node"),
- BT::InputPort("goal", "Destination to plan to"),
- BT::InputPort("planner_id", ""),
- });
+ if (first_time_) {
+ first_time_ = false;
+ } else {
+ config().blackboard->set("path_updated", true);
}
-
-private:
- bool first_time_{true};
-};
+ return BT::NodeStatus::SUCCESS;
+}
} // namespace nav2_behavior_tree
@@ -83,5 +61,3 @@ BT_REGISTER_NODES(factory)
factory.registerBuilder(
"ComputePathToPose", builder);
}
-
-#endif // NAV2_BEHAVIOR_TREE__COMPUTE_PATH_TO_POSE_ACTION_HPP_
diff --git a/nav2_behavior_tree/plugins/action/follow_path_action.cpp b/nav2_behavior_tree/plugins/action/follow_path_action.cpp
index 820362912e3..62823f40328 100644
--- a/nav2_behavior_tree/plugins/action/follow_path_action.cpp
+++ b/nav2_behavior_tree/plugins/action/follow_path_action.cpp
@@ -12,59 +12,42 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NAV2_BEHAVIOR_TREE__FOLLOW_PATH_ACTION_HPP_
-#define NAV2_BEHAVIOR_TREE__FOLLOW_PATH_ACTION_HPP_
-
#include
#include
-#include "nav2_msgs/action/follow_path.hpp"
-#include "nav2_behavior_tree/bt_action_node.hpp"
+#include "nav2_behavior_tree/plugins/action/follow_path_action.hpp"
namespace nav2_behavior_tree
{
-class FollowPathAction : public BtActionNode
+FollowPathAction::FollowPathAction(
+ const std::string & xml_tag_name,
+ const std::string & action_name,
+ const BT::NodeConfiguration & conf)
+: BtActionNode(xml_tag_name, action_name, conf)
{
-public:
- FollowPathAction(
- const std::string & xml_tag_name,
- const std::string & action_name,
- const BT::NodeConfiguration & conf)
- : BtActionNode(xml_tag_name, action_name, conf)
- {
- config().blackboard->set("path_updated", false);
- }
-
- void on_tick() override
- {
- getInput("path", goal_.path);
- getInput("controller_id", goal_.controller_id);
- }
+ config().blackboard->set("path_updated", false);
+}
- void on_wait_for_result() override
- {
- // Check if the goal has been updated
- if (config().blackboard->get("path_updated")) {
- // Reset the flag in the blackboard
- config().blackboard->set("path_updated", false);
+void FollowPathAction::on_tick()
+{
+ getInput("path", goal_.path);
+ getInput("controller_id", goal_.controller_id);
+}
- // Grab the new goal and set the flag so that we send the new goal to
- // the action server on the next loop iteration
- getInput("path", goal_.path);
- goal_updated_ = true;
- }
- }
+void FollowPathAction::on_wait_for_result()
+{
+ // Check if the goal has been updated
+ if (config().blackboard->get("path_updated")) {
+ // Reset the flag in the blackboard
+ config().blackboard->set("path_updated", false);
- static BT::PortsList providedPorts()
- {
- return providedBasicPorts(
- {
- BT::InputPort("path", "Path to follow"),
- BT::InputPort("controller_id", ""),
- });
+ // Grab the new goal and set the flag so that we send the new goal to
+ // the action server on the next loop iteration
+ getInput("path", goal_.path);
+ goal_updated_ = true;
}
-};
+}
} // namespace nav2_behavior_tree
@@ -81,5 +64,3 @@ BT_REGISTER_NODES(factory)
factory.registerBuilder(
"FollowPath", builder);
}
-
-#endif // NAV2_BEHAVIOR_TREE__FOLLOW_PATH_ACTION_HPP_
diff --git a/nav2_behavior_tree/plugins/action/navigate_to_pose_action.cpp b/nav2_behavior_tree/plugins/action/navigate_to_pose_action.cpp
index a65e6433f6f..f779a682ade 100644
--- a/nav2_behavior_tree/plugins/action/navigate_to_pose_action.cpp
+++ b/nav2_behavior_tree/plugins/action/navigate_to_pose_action.cpp
@@ -12,58 +12,38 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NAV2_BEHAVIOR_TREE__NAVIGATE_TO_POSE_ACTION_HPP_
-#define NAV2_BEHAVIOR_TREE__NAVIGATE_TO_POSE_ACTION_HPP_
-
#include
#include
-#include "geometry_msgs/msg/point.hpp"
-#include "geometry_msgs/msg/quaternion.hpp"
-#include "nav2_msgs/action/navigate_to_pose.hpp"
-#include "nav2_behavior_tree/bt_action_node.hpp"
+#include "nav2_behavior_tree/plugins/action/navigate_to_pose_action.hpp"
namespace nav2_behavior_tree
{
-class NavigateToPoseAction : public BtActionNode
+NavigateToPoseAction::NavigateToPoseAction(
+ const std::string & xml_tag_name,
+ const std::string & action_name,
+ const BT::NodeConfiguration & conf)
+: BtActionNode(xml_tag_name, action_name, conf)
{
-public:
- NavigateToPoseAction(
- const std::string & xml_tag_name,
- const std::string & action_name,
- const BT::NodeConfiguration & conf)
- : BtActionNode(xml_tag_name, action_name, conf)
- {
- }
-
- void on_tick() override
- {
- // Use the position and orientation fields from the XML attributes to initialize the goal
- geometry_msgs::msg::Point position;
- geometry_msgs::msg::Quaternion orientation;
-
- if (!getInput("position", position) || !getInput("orientation", orientation)) {
- RCLCPP_ERROR(
- node_->get_logger(),
- "NavigateToPoseAction: position or orientation not provided");
- return;
- }
+}
- goal_.pose.pose.position = position;
- goal_.pose.pose.orientation = orientation;
+void NavigateToPoseAction::on_tick()
+{
+ // Use the position and orientation fields from the XML attributes to initialize the goal
+ geometry_msgs::msg::Point position;
+ geometry_msgs::msg::Quaternion orientation;
+
+ if (!getInput("position", position) || !getInput("orientation", orientation)) {
+ RCLCPP_ERROR(
+ node_->get_logger(),
+ "NavigateToPoseAction: position or orientation not provided");
+ return;
}
- // Any BT node that accepts parameters must provide a requiredNodeParameters method
- static BT::PortsList providedPorts()
- {
- return providedBasicPorts(
- {
- BT::InputPort("position", "Position"),
- BT::InputPort("orientation", "Orientation")
- });
- }
-};
+ goal_.pose.pose.position = position;
+ goal_.pose.pose.orientation = orientation;
+}
} // namespace nav2_behavior_tree
@@ -80,5 +60,3 @@ BT_REGISTER_NODES(factory)
factory.registerBuilder(
"NavigateToPose", builder);
}
-
-#endif // NAV2_BEHAVIOR_TREE__NAVIGATE_TO_POSE_ACTION_HPP_
diff --git a/nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.cpp b/nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.cpp
index edbde1af24a..388f16ccb25 100644
--- a/nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.cpp
+++ b/nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.cpp
@@ -12,29 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-#ifndef NAV2_BEHAVIOR_TREE__REINITIALIZE_GLOBAL_LOCALIZATION_SERVICE_HPP_
-#define NAV2_BEHAVIOR_TREE__REINITIALIZE_GLOBAL_LOCALIZATION_SERVICE_HPP_
-
#include
-#include
-#include
-
-#include "nav2_behavior_tree/bt_service_node.hpp"
-#include "std_srvs/srv/empty.hpp"
+#include "nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.hpp"
namespace nav2_behavior_tree
{
-class ReinitializeGlobalLocalizationService : public BtServiceNode
-{
-public:
- ReinitializeGlobalLocalizationService(
- const std::string & service_node_name,
- const BT::NodeConfiguration & conf)
- : BtServiceNode(service_node_name, conf)
- {
- }
-};
+ReinitializeGlobalLocalizationService::ReinitializeGlobalLocalizationService(
+ const std::string & service_node_name,
+ const BT::NodeConfiguration & conf)
+: BtServiceNode(service_node_name, conf)
+{}
} // namespace nav2_behavior_tree
@@ -44,5 +32,3 @@ BT_REGISTER_NODES(factory)
factory.registerNodeType(
"ReinitializeGlobalLocalization");
}
-
-#endif // NAV2_BEHAVIOR_TREE__REINITIALIZE_GLOBAL_LOCALIZATION_SERVICE_HPP_
diff --git a/nav2_behavior_tree/plugins/action/truncate_path_action.cpp b/nav2_behavior_tree/plugins/action/truncate_path_action.cpp
new file mode 100644
index 00000000000..7f919a30f3a
--- /dev/null
+++ b/nav2_behavior_tree/plugins/action/truncate_path_action.cpp
@@ -0,0 +1,89 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Francisco Martin Rico
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+
+#include "nav_msgs/msg/path.hpp"
+#include "geometry_msgs/msg/pose_stamped.hpp"
+#include "nav2_util/geometry_utils.hpp"
+#include "behaviortree_cpp_v3/decorator_node.h"
+
+#include "nav2_behavior_tree/plugins/action/truncate_path_action.hpp"
+
+namespace nav2_behavior_tree
+{
+
+TruncatePath::TruncatePath(
+ const std::string & name,
+ const BT::NodeConfiguration & conf)
+: BT::ActionNodeBase(name, conf),
+ distance_(1.0)
+{
+ getInput("distance", distance_);
+}
+
+inline BT::NodeStatus TruncatePath::tick()
+{
+ setStatus(BT::NodeStatus::RUNNING);
+
+ nav_msgs::msg::Path input_path;
+
+ getInput("input_path", input_path);
+
+ if (input_path.poses.empty()) {
+ setOutput("output_path", input_path);
+ return BT::NodeStatus::SUCCESS;
+ }
+
+ geometry_msgs::msg::PoseStamped final_pose = input_path.poses.back();
+
+ double distance_to_goal = nav2_util::geometry_utils::euclidean_distance(
+ input_path.poses.back(), final_pose);
+
+ while (distance_to_goal < distance_ && input_path.poses.size() > 2) {
+ input_path.poses.pop_back();
+ distance_to_goal = nav2_util::geometry_utils::euclidean_distance(
+ input_path.poses.back(), final_pose);
+ }
+
+ double dx = final_pose.pose.position.x - input_path.poses.back().pose.position.x;
+ double dy = final_pose.pose.position.y - input_path.poses.back().pose.position.y;
+
+ double final_angle = atan2(dy, dx);
+
+ if (std::isnan(final_angle) || std::isinf(final_angle)) {
+ RCLCPP_WARN(
+ config().blackboard->get("node")->get_logger(),
+ "Final angle is not valid while truncating path. Setting to 0.0");
+ final_angle = 0.0;
+ }
+
+ input_path.poses.back().pose.orientation = nav2_util::geometry_utils::orientationAroundZAxis(
+ final_angle);
+
+ setOutput("output_path", input_path);
+
+ return BT::NodeStatus::SUCCESS;
+}
+
+} // namespace nav2_behavior_tree
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+BT_REGISTER_NODES(factory)
+{
+ factory.registerNodeType("TruncatePath");
+}
diff --git a/nav2_behavior_tree/plugins/condition/is_battery_low_condition.cpp b/nav2_behavior_tree/plugins/condition/is_battery_low_condition.cpp
new file mode 100644
index 00000000000..4aad6d16769
--- /dev/null
+++ b/nav2_behavior_tree/plugins/condition/is_battery_low_condition.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2020 Sarthak Mittal
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+
+#include "nav2_behavior_tree/plugins/condition/is_battery_low_condition.hpp"
+
+namespace nav2_behavior_tree
+{
+
+IsBatteryLowCondition::IsBatteryLowCondition(
+ const std::string & condition_name,
+ const BT::NodeConfiguration & conf)
+: BT::ConditionNode(condition_name, conf),
+ battery_topic_("/battery_status"),
+ min_battery_(0.0),
+ is_voltage_(false),
+ is_battery_low_(false)
+{
+ getInput("min_battery", min_battery_);
+ getInput("battery_topic", battery_topic_);
+ getInput("is_voltage", is_voltage_);
+ node_ = config().blackboard->get("node");
+ battery_sub_ = node_->create_subscription(
+ battery_topic_,
+ rclcpp::SystemDefaultsQoS(),
+ std::bind(&IsBatteryLowCondition::batteryCallback, this, std::placeholders::_1));
+}
+
+BT::NodeStatus IsBatteryLowCondition::tick()
+{
+ std::lock_guard lock(mutex_);
+ if (is_battery_low_) {
+ return BT::NodeStatus::SUCCESS;
+ }
+ return BT::NodeStatus::FAILURE;
+}
+
+void IsBatteryLowCondition::batteryCallback(sensor_msgs::msg::BatteryState::SharedPtr msg)
+{
+ std::lock_guard lock(mutex_);
+ if (is_voltage_) {
+ is_battery_low_ = msg->voltage <= min_battery_;
+ } else {
+ is_battery_low_ = msg->percentage <= min_battery_;
+ }
+}
+
+} // namespace nav2_behavior_tree
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+BT_REGISTER_NODES(factory)
+{
+ factory.registerNodeType("IsBatteryLow");
+}
diff --git a/nav2_behavior_tree/plugins/decorator/goal_updater_node.cpp b/nav2_behavior_tree/plugins/decorator/goal_updater_node.cpp
new file mode 100644
index 00000000000..5279613498b
--- /dev/null
+++ b/nav2_behavior_tree/plugins/decorator/goal_updater_node.cpp
@@ -0,0 +1,71 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Francisco Martin Rico
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+
+#include "geometry_msgs/msg/pose_stamped.hpp"
+#include "behaviortree_cpp_v3/decorator_node.h"
+
+#include "nav2_behavior_tree/plugins/decorator/goal_updater_node.hpp"
+
+#include "rclcpp/rclcpp.hpp"
+
+namespace nav2_behavior_tree
+{
+
+using std::placeholders::_1;
+
+GoalUpdater::GoalUpdater(
+ const std::string & name,
+ const BT::NodeConfiguration & conf)
+: BT::DecoratorNode(name, conf)
+{
+ auto node = config().blackboard->get("node");
+
+ std::string goal_updater_topic;
+ node->get_parameter_or("goal_updater_topic", goal_updater_topic, "goal_update");
+
+ goal_sub_ = node->create_subscription(
+ goal_updater_topic, 10, std::bind(&GoalUpdater::callback_updated_goal, this, _1));
+}
+
+inline BT::NodeStatus GoalUpdater::tick()
+{
+ geometry_msgs::msg::PoseStamped goal;
+
+ getInput("input_goal", goal);
+
+ if (rclcpp::Time(last_goal_received_.header.stamp) > rclcpp::Time(goal.header.stamp)) {
+ goal = last_goal_received_;
+ }
+
+ setOutput("output_goal", goal);
+ return child_node_->executeTick();
+}
+
+void
+GoalUpdater::callback_updated_goal(const geometry_msgs::msg::PoseStamped::SharedPtr msg)
+{
+ last_goal_received_ = *msg;
+}
+
+} // namespace nav2_behavior_tree
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+BT_REGISTER_NODES(factory)
+{
+ factory.registerNodeType("GoalUpdater");
+}
diff --git a/nav2_behavior_tree/src/behavior_tree_engine.cpp b/nav2_behavior_tree/src/behavior_tree_engine.cpp
index 36b852e61c9..93841615d9e 100644
--- a/nav2_behavior_tree/src/behavior_tree_engine.cpp
+++ b/nav2_behavior_tree/src/behavior_tree_engine.cpp
@@ -1,4 +1,5 @@
// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Florian Gramss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,8 +22,6 @@
#include "rclcpp/rclcpp.hpp"
#include "behaviortree_cpp_v3/utils/shared_library.h"
-using namespace std::chrono_literals;
-
namespace nav2_behavior_tree
{
@@ -62,13 +61,54 @@ BehaviorTreeEngine::run(
}
BT::Tree
-BehaviorTreeEngine::buildTreeFromText(
+BehaviorTreeEngine::createTreeFromText(
const std::string & xml_string,
BT::Blackboard::Ptr blackboard)
{
- BT::XMLParser p(factory_);
- p.loadFromText(xml_string);
- return p.instantiateTree(blackboard);
+ return factory_.createTreeFromText(xml_string, blackboard);
+}
+
+BT::Tree
+BehaviorTreeEngine::createTreeFromFile(
+ const std::string & file_path,
+ BT::Blackboard::Ptr blackboard)
+{
+ return factory_.createTreeFromFile(file_path, blackboard);
+}
+
+void
+BehaviorTreeEngine::addGrootMonitoring(
+ BT::Tree * tree,
+ uint16_t publisher_port,
+ uint16_t server_port,
+ uint16_t max_msg_per_second)
+{
+ // This logger publish status changes using ZeroMQ. Used by Groot
+ groot_monitor_ = std::make_unique(
+ *tree, max_msg_per_second, publisher_port,
+ server_port);
+}
+
+void
+BehaviorTreeEngine::resetGrootMonitor()
+{
+ groot_monitor_.reset();
+}
+
+// In order to re-run a Behavior Tree, we must be able to reset all nodes to the initial state
+void
+BehaviorTreeEngine::haltAllActions(BT::TreeNode * root_node)
+{
+ // this halt signal should propagate through the entire tree.
+ root_node->halt();
+
+ // but, just in case...
+ auto visitor = [](BT::TreeNode * node) {
+ if (node->status() == BT::NodeStatus::RUNNING) {
+ node->halt();
+ }
+ };
+ BT::applyRecursiveVisitor(root_node, visitor);
}
} // namespace nav2_behavior_tree
diff --git a/nav2_behavior_tree/test/CMakeLists.txt b/nav2_behavior_tree/test/CMakeLists.txt
index 93d1356d40a..1486afac1db 100644
--- a/nav2_behavior_tree/test/CMakeLists.txt
+++ b/nav2_behavior_tree/test/CMakeLists.txt
@@ -4,3 +4,4 @@ ament_target_dependencies(test_bt_conversions ${dependencies})
add_subdirectory(plugins/condition)
add_subdirectory(plugins/decorator)
add_subdirectory(plugins/control)
+add_subdirectory(plugins/action)
diff --git a/nav2_behavior_tree/test/plugins/action/CMakeLists.txt b/nav2_behavior_tree/test/plugins/action/CMakeLists.txt
new file mode 100644
index 00000000000..3f05ae112d5
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/CMakeLists.txt
@@ -0,0 +1,25 @@
+ament_add_gtest(test_action_clear_costmap_service test_clear_costmap_service.cpp)
+target_link_libraries(test_action_clear_costmap_service nav2_clear_costmap_service_bt_node)
+ament_target_dependencies(test_action_clear_costmap_service ${dependencies})
+
+ament_add_gtest(
+ test_action_reinitialize_global_localization_service test_reinitialize_global_localization_service.cpp)
+target_link_libraries(
+ test_action_reinitialize_global_localization_service nav2_reinitialize_global_localization_service_bt_node)
+ament_target_dependencies(test_action_reinitialize_global_localization_service ${dependencies})
+
+ament_add_gtest(test_action_compute_path_to_pose_action test_compute_path_to_pose_action.cpp)
+target_link_libraries(test_action_compute_path_to_pose_action nav2_compute_path_to_pose_action_bt_node)
+ament_target_dependencies(test_action_compute_path_to_pose_action ${dependencies})
+
+ament_add_gtest(test_action_follow_path_action test_follow_path_action.cpp)
+target_link_libraries(test_action_follow_path_action nav2_follow_path_action_bt_node)
+ament_target_dependencies(test_action_follow_path_action ${dependencies})
+
+ament_add_gtest(test_action_navigate_to_pose_action test_navigate_to_pose_action.cpp)
+target_link_libraries(test_action_navigate_to_pose_action nav2_navigate_to_pose_action_bt_node)
+ament_target_dependencies(test_action_navigate_to_pose_action ${dependencies})
+
+ament_add_gtest(test_truncate_path_action test_truncate_path_action.cpp)
+target_link_libraries(test_truncate_path_action nav2_truncate_path_action_bt_node)
+ament_target_dependencies(test_truncate_path_action ${dependencies})
diff --git a/nav2_behavior_tree/test/plugins/action/test_back_up_action.cpp b/nav2_behavior_tree/test/plugins/action/test_back_up_action.cpp
new file mode 100644
index 00000000000..8906d03d7fc
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_back_up_action.cpp
@@ -0,0 +1,173 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/back_up_action.hpp"
+
+class BackUpActionServer : public TestActionServer
+{
+public:
+ BackUpActionServer()
+ : TestActionServer("backup")
+ {}
+
+protected:
+ void execute(
+ const typename std::shared_ptr>)
+ override
+ {}
+};
+
+class BackUpActionTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("backup_action_test_fixture");
+ factory_ = std::make_shared();
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+ config_->blackboard->set("number_recoveries", 0);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique(
+ name, "backup", config);
+ };
+
+ factory_->registerBuilder("BackUp", builder);
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ action_server_.reset();
+ factory_.reset();
+ }
+
+ void SetUp() override
+ {
+ config_->blackboard->set("number_recoveries", 0);
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr action_server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr BackUpActionTestFixture::node_ = nullptr;
+std::shared_ptr BackUpActionTestFixture::action_server_ = nullptr;
+BT::NodeConfiguration * BackUpActionTestFixture::config_ = nullptr;
+std::shared_ptr BackUpActionTestFixture::factory_ = nullptr;
+std::shared_ptr BackUpActionTestFixture::tree_ = nullptr;
+
+TEST_F(BackUpActionTestFixture, test_ports)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(tree_->rootNode()->getInput("backup_dist"), 0.15);
+ EXPECT_EQ(tree_->rootNode()->getInput("backup_speed"), 0.025);
+
+ xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(tree_->rootNode()->getInput("backup_dist"), 2.0);
+ EXPECT_EQ(tree_->rootNode()->getInput("backup_speed"), 0.26);
+}
+
+TEST_F(BackUpActionTestFixture, test_tick)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 0);
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 1);
+
+ auto goal = action_server_->getCurrentGoal();
+ EXPECT_EQ(goal->target.x, 2.0);
+ EXPECT_EQ(goal->speed, 0.26f);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize action server and spin on new thread
+ BackUpActionTestFixture::action_server_ = std::make_shared();
+ std::thread server_thread([]() {
+ rclcpp::spin(BackUpActionTestFixture::action_server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_clear_costmap_service.cpp b/nav2_behavior_tree/test/plugins/action/test_clear_costmap_service.cpp
new file mode 100644
index 00000000000..c5a65c52a1f
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_clear_costmap_service.cpp
@@ -0,0 +1,130 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_service.hpp"
+#include "nav2_behavior_tree/plugins/action/clear_costmap_service.hpp"
+
+class ClearEntireCostmapService : public TestService
+{
+public:
+ ClearEntireCostmapService()
+ : TestService("clear_entire_costmap")
+ {}
+};
+
+class ClearEntireCostmapServiceTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("clear_entire_costmap_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+ config_->blackboard->set("number_recoveries", 0);
+
+ factory_->registerNodeType("ClearEntireCostmap");
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ server_.reset();
+ factory_.reset();
+ }
+
+ void SetUp() override
+ {
+ config_->blackboard->set("number_recoveries", 0);
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr ClearEntireCostmapServiceTestFixture::node_ = nullptr;
+std::shared_ptr ClearEntireCostmapServiceTestFixture::server_ = nullptr;
+BT::NodeConfiguration * ClearEntireCostmapServiceTestFixture::config_ = nullptr;
+std::shared_ptr ClearEntireCostmapServiceTestFixture::factory_ = nullptr;
+std::shared_ptr ClearEntireCostmapServiceTestFixture::tree_ = nullptr;
+
+TEST_F(ClearEntireCostmapServiceTestFixture, test_tick)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 0);
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::SUCCESS);
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 1);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize service and spin on new thread
+ ClearEntireCostmapServiceTestFixture::server_ = std::make_shared();
+ std::thread server_thread([]() {
+ rclcpp::spin(ClearEntireCostmapServiceTestFixture::server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_compute_path_to_pose_action.cpp b/nav2_behavior_tree/test/plugins/action/test_compute_path_to_pose_action.cpp
new file mode 100644
index 00000000000..dd059315379
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_compute_path_to_pose_action.cpp
@@ -0,0 +1,198 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "nav_msgs/msg/path.hpp"
+#include "geometry_msgs/msg/pose_stamped.hpp"
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/compute_path_to_pose_action.hpp"
+
+class ComputePathToPoseActionServer : public TestActionServer
+{
+public:
+ ComputePathToPoseActionServer()
+ : TestActionServer("compute_path_to_pose")
+ {}
+
+protected:
+ void execute(
+ const typename std::shared_ptr<
+ rclcpp_action::ServerGoalHandle> goal_handle)
+ override
+ {
+ const auto goal = goal_handle->get_goal();
+ auto result = std::make_shared();
+ result->path.poses.resize(1);
+ result->path.poses[0].pose.position.x = goal->pose.pose.position.x;
+ goal_handle->succeed(result);
+ }
+};
+
+class ComputePathToPoseActionTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("compute_path_to_pose_action_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique(
+ name, "compute_path_to_pose", config);
+ };
+
+ factory_->registerBuilder(
+ "ComputePathToPose", builder);
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ action_server_.reset();
+ factory_.reset();
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr action_server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr ComputePathToPoseActionTestFixture::node_ = nullptr;
+std::shared_ptr
+ComputePathToPoseActionTestFixture::action_server_ = nullptr;
+BT::NodeConfiguration * ComputePathToPoseActionTestFixture::config_ = nullptr;
+std::shared_ptr ComputePathToPoseActionTestFixture::factory_ = nullptr;
+std::shared_ptr ComputePathToPoseActionTestFixture::tree_ = nullptr;
+
+TEST_F(ComputePathToPoseActionTestFixture, test_tick)
+{
+ // create tree
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+
+ // create new goal and set it on blackboard
+ geometry_msgs::msg::PoseStamped goal;
+ goal.header.stamp = node_->now();
+ goal.pose.position.x = 1.0;
+ config_->blackboard->set("goal", goal);
+
+ // first tick should send the goal to our server
+ EXPECT_EQ(config_->blackboard->get("path_updated"), false);
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(tree_->rootNode()->getInput("planner_id"), std::string("GridBased"));
+ EXPECT_EQ(action_server_->getCurrentGoal()->pose.pose.position.x, 1.0);
+ EXPECT_EQ(action_server_->getCurrentGoal()->planner_id, std::string("GridBased"));
+
+ // tick until node succeeds
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+
+ // not set to true after the first goal
+ EXPECT_EQ(config_->blackboard->get("path_updated"), false);
+
+ // check if returned path is correct
+ nav_msgs::msg::Path path;
+ config_->blackboard->get("path", path);
+ EXPECT_EQ(path.poses.size(), 1u);
+ EXPECT_EQ(path.poses[0].pose.position.x, 1.0);
+
+ // halt node so another goal can be sent
+ tree_->rootNode()->halt();
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::IDLE);
+
+ // set new goal
+ goal.pose.position.x = -2.5;
+ config_->blackboard->set("goal", goal);
+
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(action_server_->getCurrentGoal()->pose.pose.position.x, -2.5);
+
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+
+ // path is updated on new goal
+ EXPECT_EQ(config_->blackboard->get("path_updated"), true);
+
+ config_->blackboard->get("path", path);
+ EXPECT_EQ(path.poses.size(), 1u);
+ EXPECT_EQ(path.poses[0].pose.position.x, -2.5);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize action server and spin on new thread
+ ComputePathToPoseActionTestFixture::action_server_ =
+ std::make_shared();
+
+ std::thread server_thread([]() {
+ rclcpp::spin(ComputePathToPoseActionTestFixture::action_server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_follow_path_action.cpp b/nav2_behavior_tree/test/plugins/action/test_follow_path_action.cpp
new file mode 100644
index 00000000000..2bd07f42323
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_follow_path_action.cpp
@@ -0,0 +1,188 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "nav_msgs/msg/path.hpp"
+#include "geometry_msgs/msg/pose_stamped.hpp"
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/follow_path_action.hpp"
+
+class FollowPathActionServer : public TestActionServer
+{
+public:
+ FollowPathActionServer()
+ : TestActionServer("follow_path")
+ {}
+
+protected:
+ void execute(
+ const typename std::shared_ptr<
+ rclcpp_action::ServerGoalHandle> goal_handle)
+ override
+ {
+ const auto goal = goal_handle->get_goal();
+ auto result = std::make_shared();
+ goal_handle->succeed(result);
+ }
+};
+
+class FollowPathActionTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("follow_path_action_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique(
+ name, "follow_path", config);
+ };
+
+ factory_->registerBuilder(
+ "FollowPath", builder);
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ action_server_.reset();
+ factory_.reset();
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr action_server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr FollowPathActionTestFixture::node_ = nullptr;
+std::shared_ptr
+FollowPathActionTestFixture::action_server_ = nullptr;
+BT::NodeConfiguration * FollowPathActionTestFixture::config_ = nullptr;
+std::shared_ptr FollowPathActionTestFixture::factory_ = nullptr;
+std::shared_ptr FollowPathActionTestFixture::tree_ = nullptr;
+
+TEST_F(FollowPathActionTestFixture, test_tick)
+{
+ // create tree
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ config_->blackboard->set("path_updated", true);
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(config_->blackboard->get("path_updated"), false);
+
+ // set new path on blackboard
+ nav_msgs::msg::Path path;
+ path.poses.resize(1);
+ path.poses[0].pose.position.x = 1.0;
+ config_->blackboard->set("path", path);
+
+ // first tick should send the goal to our server
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(tree_->rootNode()->getInput("controller_id"), std::string("FollowPath"));
+ EXPECT_EQ(action_server_->getCurrentGoal()->path.poses.size(), 1u);
+ EXPECT_EQ(action_server_->getCurrentGoal()->path.poses[0].pose.position.x, 1.0);
+ EXPECT_EQ(action_server_->getCurrentGoal()->controller_id, std::string("FollowPath"));
+
+ // tick until node succeeds
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
+
+ // halt node so another goal can be sent
+ tree_->rootNode()->halt();
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::IDLE);
+
+ // set new goal
+ path.poses[0].pose.position.x = -2.5;
+ config_->blackboard->set("path", path);
+
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(action_server_->getCurrentGoal()->path.poses.size(), 1u);
+ EXPECT_EQ(action_server_->getCurrentGoal()->path.poses[0].pose.position.x, -2.5);
+ config_->blackboard->set("path_updated", true);
+
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+
+ // path is updated on new goal
+ EXPECT_EQ(config_->blackboard->get("path_updated"), false);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize action server and spin on new thread
+ FollowPathActionTestFixture::action_server_ =
+ std::make_shared();
+
+ std::thread server_thread([]() {
+ rclcpp::spin(FollowPathActionTestFixture::action_server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_navigate_to_pose_action.cpp b/nav2_behavior_tree/test/plugins/action/test_navigate_to_pose_action.cpp
new file mode 100644
index 00000000000..c57ca23bce4
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_navigate_to_pose_action.cpp
@@ -0,0 +1,179 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "geometry_msgs/msg/pose_stamped.hpp"
+#include "geometry_msgs/msg/point.hpp"
+#include "geometry_msgs/msg/quaternion.hpp"
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/navigate_to_pose_action.hpp"
+
+class NavigateToPoseActionServer : public TestActionServer
+{
+public:
+ NavigateToPoseActionServer()
+ : TestActionServer("navigate_to_pose")
+ {}
+
+protected:
+ void execute(
+ const typename std::shared_ptr<
+ rclcpp_action::ServerGoalHandle> goal_handle)
+ override
+ {
+ const auto goal = goal_handle->get_goal();
+ auto result = std::make_shared();
+ goal_handle->succeed(result);
+ }
+};
+
+class NavigateToPoseActionTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("navigate_to_pose_action_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique(
+ name, "navigate_to_pose", config);
+ };
+
+ factory_->registerBuilder(
+ "NavigateToPose", builder);
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ action_server_.reset();
+ factory_.reset();
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr action_server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr NavigateToPoseActionTestFixture::node_ = nullptr;
+std::shared_ptr
+NavigateToPoseActionTestFixture::action_server_ = nullptr;
+BT::NodeConfiguration * NavigateToPoseActionTestFixture::config_ = nullptr;
+std::shared_ptr NavigateToPoseActionTestFixture::factory_ = nullptr;
+std::shared_ptr NavigateToPoseActionTestFixture::tree_ = nullptr;
+
+TEST_F(NavigateToPoseActionTestFixture, test_tick)
+{
+ // create tree
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+
+ geometry_msgs::msg::PoseStamped pose;
+
+ // first tick should send the goal to our server
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(action_server_->getCurrentGoal()->pose, pose);
+
+ // tick until node succeeds
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
+
+ // halt node so another goal can be sent
+ tree_->rootNode()->halt();
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::IDLE);
+
+ // set new goal
+ pose.pose.position.x = -2.5;
+ pose.pose.orientation.x = 1.0;
+ config_->blackboard->set("position", pose.pose.position);
+ config_->blackboard->set("orientation", pose.pose.orientation);
+
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(action_server_->getCurrentGoal()->pose, pose);
+
+ while (tree_->rootNode()->status() != BT::NodeStatus::SUCCESS) {
+ tree_->rootNode()->executeTick();
+ }
+
+ EXPECT_EQ(tree_->rootNode()->status(), BT::NodeStatus::SUCCESS);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize action server and spin on new thread
+ NavigateToPoseActionTestFixture::action_server_ =
+ std::make_shared();
+
+ std::thread server_thread([]() {
+ rclcpp::spin(NavigateToPoseActionTestFixture::action_server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_reinitialize_global_localization_service.cpp b/nav2_behavior_tree/test/plugins/action/test_reinitialize_global_localization_service.cpp
new file mode 100644
index 00000000000..03078d384d0
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_reinitialize_global_localization_service.cpp
@@ -0,0 +1,126 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_service.hpp"
+#include "nav2_behavior_tree/plugins/action/reinitialize_global_localization_service.hpp"
+
+class ReinitializeGlobalLocalizationService : public TestService
+{
+public:
+ ReinitializeGlobalLocalizationService()
+ : TestService("reinitialize_global_localization")
+ {}
+};
+
+class ReinitializeGlobalLocalizationServiceTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("reinitialize_global_localization_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+
+ factory_->registerNodeType(
+ "ReinitializeGlobalLocalization");
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ server_.reset();
+ factory_.reset();
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr ReinitializeGlobalLocalizationServiceTestFixture::node_ = nullptr;
+std::shared_ptr
+ReinitializeGlobalLocalizationServiceTestFixture::server_ = nullptr;
+BT::NodeConfiguration * ReinitializeGlobalLocalizationServiceTestFixture::config_ = nullptr;
+std::shared_ptr
+ReinitializeGlobalLocalizationServiceTestFixture::factory_ = nullptr;
+std::shared_ptr ReinitializeGlobalLocalizationServiceTestFixture::tree_ = nullptr;
+
+TEST_F(ReinitializeGlobalLocalizationServiceTestFixture, test_tick)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::SUCCESS);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize service and spin on new thread
+ ReinitializeGlobalLocalizationServiceTestFixture::server_ =
+ std::make_shared();
+ std::thread server_thread([]() {
+ rclcpp::spin(ReinitializeGlobalLocalizationServiceTestFixture::server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_spin_action.cpp b/nav2_behavior_tree/test/plugins/action/test_spin_action.cpp
new file mode 100644
index 00000000000..e24b3b251e1
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_spin_action.cpp
@@ -0,0 +1,168 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Sarthak Mittal
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/spin_action.hpp"
+
+class SpinActionServer : public TestActionServer
+{
+public:
+ SpinActionServer()
+ : TestActionServer("spin")
+ {}
+
+protected:
+ void execute(
+ const typename std::shared_ptr>)
+ override
+ {}
+};
+
+class SpinActionTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("spin_action_test_fixture");
+ factory_ = std::make_shared();
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+ config_->blackboard->set(
+ "server_timeout",
+ std::chrono::milliseconds(10));
+ config_->blackboard->set("path_updated", false);
+ config_->blackboard->set("initial_pose_received", false);
+ config_->blackboard->set("number_recoveries", 0);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique(
+ name, "spin", config);
+ };
+
+ factory_->registerBuilder("Spin", builder);
+ }
+
+ static void TearDownTestCase()
+ {
+ delete config_;
+ config_ = nullptr;
+ node_.reset();
+ action_server_.reset();
+ factory_.reset();
+ }
+
+ void SetUp() override
+ {
+ config_->blackboard->set("number_recoveries", 0);
+ }
+
+ void TearDown() override
+ {
+ tree_.reset();
+ }
+
+ static std::shared_ptr action_server_;
+
+protected:
+ static rclcpp::Node::SharedPtr node_;
+ static BT::NodeConfiguration * config_;
+ static std::shared_ptr factory_;
+ static std::shared_ptr tree_;
+};
+
+rclcpp::Node::SharedPtr SpinActionTestFixture::node_ = nullptr;
+std::shared_ptr SpinActionTestFixture::action_server_ = nullptr;
+BT::NodeConfiguration * SpinActionTestFixture::config_ = nullptr;
+std::shared_ptr SpinActionTestFixture::factory_ = nullptr;
+std::shared_ptr SpinActionTestFixture::tree_ = nullptr;
+
+TEST_F(SpinActionTestFixture, test_ports)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(tree_->rootNode()->getInput("spin_dist"), 1.57);
+
+ xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(tree_->rootNode()->getInput("spin_dist"), 3.14);
+}
+
+TEST_F(SpinActionTestFixture, test_tick)
+{
+ std::string xml_txt =
+ R"(
+
+
+
+
+ )";
+
+ tree_ = std::make_shared(factory_->createTreeFromText(xml_txt, config_->blackboard));
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 0);
+ EXPECT_EQ(tree_->rootNode()->executeTick(), BT::NodeStatus::RUNNING);
+ EXPECT_EQ(config_->blackboard->get("number_recoveries"), 1);
+ EXPECT_EQ(action_server_->getCurrentGoal()->target_yaw, 3.14f);
+}
+
+int main(int argc, char ** argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+
+ // initialize ROS
+ rclcpp::init(argc, argv);
+
+ // initialize action server and spin on new thread
+ SpinActionTestFixture::action_server_ = std::make_shared();
+ std::thread server_thread([]() {
+ rclcpp::spin(SpinActionTestFixture::action_server_);
+ });
+
+ int all_successful = RUN_ALL_TESTS();
+
+ // shutdown ROS
+ rclcpp::shutdown();
+ server_thread.join();
+
+ return all_successful;
+}
diff --git a/nav2_behavior_tree/test/plugins/action/test_truncate_path_action.cpp b/nav2_behavior_tree/test/plugins/action/test_truncate_path_action.cpp
new file mode 100644
index 00000000000..11c56ef082d
--- /dev/null
+++ b/nav2_behavior_tree/test/plugins/action/test_truncate_path_action.cpp
@@ -0,0 +1,156 @@
+// Copyright (c) 2018 Intel Corporation
+// Copyright (c) 2020 Francisco Martin Rico
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include
+#include
+#include
+#include
+
+#include "nav_msgs/msg/path.hpp"
+#include "geometry_msgs/msg/pose_stamped.hpp"
+#include "nav2_util/geometry_utils.hpp"
+
+#include "behaviortree_cpp_v3/bt_factory.h"
+
+#include "../../test_action_server.hpp"
+#include "nav2_behavior_tree/plugins/action/truncate_path_action.hpp"
+
+
+class TruncatePathTestFixture : public ::testing::Test
+{
+public:
+ static void SetUpTestCase()
+ {
+ node_ = std::make_shared("change_goal_test_fixture");
+ factory_ = std::make_shared();
+
+ config_ = new BT::NodeConfiguration();
+
+ // Create the blackboard that will be shared by all of the nodes in the tree
+ config_->blackboard = BT::Blackboard::create();
+ // Put items on the blackboard
+ config_->blackboard->set(
+ "node",
+ node_);
+
+ BT::NodeBuilder builder =
+ [](const std::string & name, const BT::NodeConfiguration & config)
+ {
+ return std::make_unique