Nav2 Navigation Stack - lyrical  lyrical
ROS 2 Navigation Stack
planner_tester.hpp
1 // Copyright (c) 2018 Intel Corporation
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License. Reserved.
14 
15 #ifndef PLANNING__PLANNER_TESTER_HPP_
16 #define PLANNING__PLANNER_TESTER_HPP_
17 
18 #include <gtest/gtest.h>
19 #include <memory>
20 #include <string>
21 #include <thread>
22 #include <vector>
23 #include <algorithm>
24 
25 #include "nav2_ros_common/lifecycle_node.hpp"
26 #include "nav2_msgs/action/compute_path_to_pose.hpp"
27 #include "nav_msgs/msg/occupancy_grid.hpp"
28 #include "nav2_msgs/msg/costmap.hpp"
29 #include "nav2_msgs/srv/get_costmap.hpp"
30 #include "nav2_msgs/srv/is_path_valid.hpp"
31 #include "nav2_util/costmap.hpp"
32 #include "nav2_ros_common/node_thread.hpp"
33 #include "geometry_msgs/msg/pose_stamped.hpp"
34 #include "geometry_msgs/msg/transform_stamped.hpp"
35 #include "nav2_planner/planner_server.hpp"
36 #include "nav2_ros_common/tf2_factories.hpp"
37 
38 namespace nav2_system_tests
39 {
40 
42 {
43 public:
45  : PlannerServer()
46  {
47  }
48 
49  // Set the costmap resolution before configuring the planner server.
50  void setCostmapResolution(double resolution)
51  {
52  costmap_ros_->declare_parameter("resolution", resolution);
53  }
54 
55  void printCostmap()
56  {
57  // print costmap for debug
58  for (size_t i = 0; i != costmap_->getSizeInCellsX() * costmap_->getSizeInCellsY(); i++) {
59  if (i % costmap_->getSizeInCellsX() == 0) {
60  std::cout << "" << std::endl;
61  }
62  std::cout << costmap_ros_->getCostmap()->getCharMap()[i] << " ";
63  }
64  std::cout << "" << std::endl;
65  }
66 
67  void setCostmap(nav2_util::Costmap * costmap)
68  {
69  std::unique_lock<nav2_costmap_2d::Costmap2D::mutex_t> lock(
70  *(costmap_ros_->getCostmap()->getMutex()));
71 
72  nav2_msgs::msg::CostmapMetaData prop;
73  nav2_msgs::msg::Costmap cm = costmap->get_costmap(prop);
74  prop = cm.metadata;
75  costmap_ros_->getCostmap()->resizeMap(
76  prop.size_x, prop.size_y,
77  prop.resolution, prop.origin.position.x, prop.origin.position.x);
78  // Volatile prevents compiler from treating costmap_ptr as unused or changing its address
79  volatile unsigned char * costmap_ptr = costmap_ros_->getCostmap()->getCharMap();
80  delete[] costmap_ptr;
81  costmap_ptr = new unsigned char[prop.size_x * prop.size_y];
82  std::copy(cm.data.begin(), cm.data.end(), costmap_ptr);
83  }
84 
85  bool createPath(
86  const geometry_msgs::msg::PoseStamped & goal,
87  nav_msgs::msg::Path & path)
88  {
89  geometry_msgs::msg::PoseStamped start;
90  if (!nav2_util::getCurrentPose(start, *tf_, "map", "base_link", 0.1)) {
91  return false;
92  }
93  try {
94  auto dummy_cancel_checker = []() {return false;};
95  std::vector<geometry_msgs::msg::PoseStamped> viapoints{start};
96  path = planners_["GridBased"]->createPlan(start, goal, viapoints, dummy_cancel_checker);
97  // The situation when createPlan() did not throw any exception
98  // does not guarantee that plan was created correctly.
99  // So it should be checked additionally that path is correct.
100  if (!path.poses.size()) {
101  return false;
102  }
103  } catch (...) {
104  return false;
105  }
106  return true;
107  }
108 
109  void onCleanup(const rclcpp_lifecycle::State & state)
110  {
111  on_cleanup(state);
112  }
113 
114  void onActivate(const rclcpp_lifecycle::State & state)
115  {
116  on_activate(state);
117  }
118 
119  void onDeactivate(const rclcpp_lifecycle::State & state)
120  {
121  on_deactivate(state);
122  }
123 
124  void onConfigure(const rclcpp_lifecycle::State & state)
125  {
126  on_configure(state);
127  }
128 };
129 
130 enum class TaskStatus : int8_t
131 {
132  SUCCEEDED = 1,
133  FAILED = 2,
134  RUNNING = 3,
135 };
136 
138 {
139 public:
140  using ComputePathToPoseCommand = geometry_msgs::msg::PoseStamped;
141  using ComputePathToPoseResult = nav_msgs::msg::Path;
142 
143  PlannerTester();
144  ~PlannerTester();
145 
146  // Activate the tester before running tests
147  void activate();
148  void deactivate();
149 
150  // Loads the provided map and and generates a costmap from it.
151  void loadDefaultMap();
152 
153  // Alternatively, use a preloaded 10x10 costmap
154  void loadSimpleCostmap(const nav2_util::TestCostmap & testCostmapType);
155 
156  // Runs a single test with default poses depending on the loaded map
157  // Success criteria is a collision free path and a deviation to a
158  // reference path smaller than a tolerance.
159  bool defaultPlannerTest(
160  ComputePathToPoseResult & path,
161  const double deviation_tolerance = 1.0);
162 
163 
164  // Runs multiple tests with random initial and goal poses
165  bool defaultPlannerRandomTests(
166  const unsigned int number_tests,
167  const float acceptable_fail_ratio);
168 
169  std::shared_ptr<nav2_msgs::srv::IsPathValid::Response> isPathValid(
170  nav_msgs::msg::Path & path, unsigned int max_cost,
171  bool consider_unknown_as_obstacle, const std::string & layer_name = "",
172  const std::string & footprint = "", bool stop_at_first_collision = true,
173  double max_lookahead_distance = -1.0);
174 
175 private:
176  void setCostmap();
177 
178  TaskStatus createPlan(
179  const ComputePathToPoseCommand & goal,
180  ComputePathToPoseResult & path
181  );
182 
183  bool is_active_;
184  bool map_set_;
185  bool costmap_set_;
186  bool using_fake_costmap_;
187 
188  // Parameters of the costmap
189  bool trinary_costmap_;
190  bool track_unknown_space_;
191  int lethal_threshold_;
192  int unknown_cost_value_;
193  nav2_util::TestCostmap testCostmapType_;
194 
195  // The static map
196  std::shared_ptr<nav_msgs::msg::OccupancyGrid> map_;
197 
198  // The costmap representation of the static map
199  std::unique_ptr<nav2_util::Costmap> costmap_;
200 
201  // The global planner
202  std::shared_ptr<NavFnPlannerTester> planner_tester_;
203 
204  // The is path valid client
205  nav2::ServiceClient<nav2_msgs::srv::IsPathValid>::SharedPtr path_valid_client_;
206 
207  // A thread for spinning the ROS node
208  std::unique_ptr<nav2::NodeThread> spin_thread_;
209 
210  // The tester must provide the robot pose through a transform
211  std::unique_ptr<geometry_msgs::msg::TransformStamped> base_transform_;
212  nav2::TransformBroadcaster::SharedPtr tf_broadcaster_;
213  rclcpp::TimerBase::SharedPtr transform_timer_;
214  void publishRobotTransform();
215  void startRobotTransform();
216  void updateRobotPosition(const geometry_msgs::msg::Point & position);
217 
218  // Occupancy grid publisher for visualization
219  nav2::Publisher<nav_msgs::msg::OccupancyGrid>::SharedPtr map_pub_;
220  rclcpp::TimerBase::SharedPtr map_timer_;
221  void mapCallback();
222 
223  // Executes a test run with the provided end points.
224  // Success criteria is a collision free path.
225  // TODO(orduno): #443 Assuming a robot the size of a costmap cell
226  bool plannerTest(
227  const geometry_msgs::msg::Point & robot_position,
228  const ComputePathToPoseCommand & goal,
229  ComputePathToPoseResult & path);
230 
231  bool isCollisionFree(const ComputePathToPoseResult & path);
232 
233  bool isWithinTolerance(
234  const geometry_msgs::msg::Point & robot_position,
235  const ComputePathToPoseCommand & goal,
236  const ComputePathToPoseResult & path) const;
237 
238  bool isWithinTolerance(
239  const geometry_msgs::msg::Point & robot_position,
240  const ComputePathToPoseCommand & goal,
241  const ComputePathToPoseResult & path,
242  const double deviationTolerance,
243  const ComputePathToPoseResult & reference_path) const;
244 
245  void printPath(const ComputePathToPoseResult & path) const;
246 };
247 
248 } // namespace nav2_system_tests
249 
250 #endif // PLANNING__PLANNER_TESTER_HPP_
A lifecycle node wrapper to enable common Nav2 needs such as manipulating parameters.
unsigned int getSizeInCellsX() const
Accessor for the x size of the costmap in cells.
Definition: costmap_2d.cpp:548
unsigned int getSizeInCellsY() const
Accessor for the y size of the costmap in cells.
Definition: costmap_2d.cpp:553
An action server implements the behavior tree's ComputePathToPose interface and hosts various plugins...
nav2::CallbackReturn on_configure(const rclcpp_lifecycle::State &state) override
Configure member variables and initializes planner.
nav2::CallbackReturn on_deactivate(const rclcpp_lifecycle::State &state) override
Deactivate member variables.
PlannerServer(const rclcpp::NodeOptions &options=rclcpp::NodeOptions())
A constructor for nav2_planner::PlannerServer.
nav2::CallbackReturn on_cleanup(const rclcpp_lifecycle::State &state) override
Reset member variables.
nav2::CallbackReturn on_activate(const rclcpp_lifecycle::State &state) override
Activate member variables.
Class for a single layered costmap initialized from an occupancy grid representing the map.
Definition: costmap.hpp:45
nav2_msgs::msg::Costmap get_costmap(const nav2_msgs::msg::CostmapMetaData &specifications)
Get a costmap message from this object.
Definition: costmap.cpp:108