test_navigate.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017, the neonavigation authors
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of the copyright holder nor the names of its
14  * contributors may be used to endorse or promote products derived from
15  * this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <cmath>
31 #include <cstddef>
32 
33 #include <ros/ros.h>
34 
35 #include <tf2/utils.h>
38 #include <std_srvs/Empty.h>
39 #include <nav_msgs/Path.h>
40 #include <nav_msgs/GetPlan.h>
41 #include <nav_msgs/OccupancyGrid.h>
42 #include <costmap_cspace_msgs/CSpace3D.h>
43 
44 #include <gtest/gtest.h>
45 
46 class Navigate : public ::testing::Test
47 {
48 protected:
52  nav_msgs::OccupancyGrid::ConstPtr map_;
53  nav_msgs::OccupancyGrid::ConstPtr map_local_;
54  costmap_cspace_msgs::CSpace3D::ConstPtr costmap_;
63 
65  : tfl_(tfbuf_)
66  , local_map_apply_cnt_(0)
67  {
68  sub_map_ = nh_.subscribe("map_global", 1, &Navigate::cbMap, this);
69  sub_map_local_ = nh_.subscribe("map_local", 1, &Navigate::cbMapLocal, this);
70  sub_costmap_ = nh_.subscribe("costmap", 1, &Navigate::cbCostmap, this);
71  srv_forget_ =
72  nh_.serviceClient<std_srvs::EmptyRequest, std_srvs::EmptyResponse>(
73  "forget_planning_cost");
74  pub_map_ = nh_.advertise<nav_msgs::OccupancyGrid>("map", 1, true);
75  pub_map_local_ = nh_.advertise<nav_msgs::OccupancyGrid>("overlay", 1, true);
76  pub_initial_pose_ =
77  nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("initialpose", 1, true);
78  }
79 
80  virtual void SetUp()
81  {
82  geometry_msgs::PoseWithCovarianceStamped pose;
83  pose.header.frame_id = "map";
84  pose.pose.pose.position.x = 2.5;
85  pose.pose.pose.position.y = 0.45;
86  pose.pose.pose.orientation.z = 1.0;
87  pub_initial_pose_.publish(pose);
88 
89  srv_forget_.waitForExistence(ros::Duration(2.0));
90  ros::Rate rate(10.0);
91 
92  while (ros::ok() && !map_)
93  {
94  ros::spinOnce();
95  rate.sleep();
96  }
97  pub_map_.publish(map_);
98  std::cerr << "Map applied." << std::endl;
99 
100  while (ros::ok() && !costmap_)
101  {
102  ros::spinOnce();
103  rate.sleep();
104  }
105 
106  std_srvs::EmptyRequest req;
107  std_srvs::EmptyResponse res;
108  srv_forget_.call(req, res);
109 
110  ros::Duration(3.0).sleep();
111  }
112  void cbCostmap(const costmap_cspace_msgs::CSpace3D::ConstPtr& msg)
113  {
114  costmap_ = msg;
115  std::cerr << "Costmap received." << std::endl;
116  }
117  void cbMap(const nav_msgs::OccupancyGrid::ConstPtr& msg)
118  {
119  map_ = msg;
120  std::cerr << "Map received." << std::endl;
121  }
122  void cbMapLocal(const nav_msgs::OccupancyGrid::ConstPtr& msg)
123  {
124  map_local_ = msg;
125  std::cerr << "Local map received." << std::endl;
126  }
127  void pubMapLocal()
128  {
129  if (map_local_)
130  {
131  pub_map_local_.publish(map_local_);
132  if ((local_map_apply_cnt_++) % 30 == 0)
133  std::cerr << "Local map applied." << std::endl;
134  }
135  }
136 };
137 
139 {
140  ros::Publisher pub_path = nh_.advertise<nav_msgs::Path>("patrol_nodes", 1, true);
141 
142  ros::spinOnce();
143  ASSERT_TRUE(static_cast<bool>(map_));
144 
145  nav_msgs::Path path;
146  path.poses.resize(2);
147  path.header.frame_id = "map";
148  path.poses[0].header.frame_id = path.header.frame_id;
149  path.poses[0].pose.position.x = 1.7;
150  path.poses[0].pose.position.y = 2.8;
151  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
152  path.poses[1].header.frame_id = path.header.frame_id;
153  path.poses[1].pose.position.x = 1.9;
154  path.poses[1].pose.position.y = 2.8;
155  path.poses[1].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -1.57));
156  pub_path.publish(path);
157 
158  tf2::Transform goal;
159  tf2::fromMsg(path.poses.back().pose, goal);
160 
161  ros::Rate wait(10);
162  while (ros::ok())
163  {
164  ros::spinOnce();
165  wait.sleep();
166 
168  try
169  {
170  const ros::Time now = ros::Time::now();
171  geometry_msgs::TransformStamped trans_tmp =
172  tfbuf_.lookupTransform("map", "base_link", now, ros::Duration(0.5));
173  tf2::fromMsg(trans_tmp, trans);
174  }
175  catch (tf2::TransformException& e)
176  {
177  std::cerr << e.what() << std::endl;
178  continue;
179  }
180 
181  auto goal_rel = trans.inverse() * goal;
182  if (goal_rel.getOrigin().length() < 0.2 &&
183  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2)
184  {
185  std::cerr << "Navagation success." << std::endl;
186  ros::Duration(2.0).sleep();
187  return;
188  }
189 
190  for (int x = -2; x <= 2; ++x)
191  {
192  for (int y = -1; y <= 1; ++y)
193  {
194  const tf2::Vector3 pos =
195  trans * tf2::Vector3(x * map_->info.resolution, y * map_->info.resolution, 0);
196  const int map_x = pos.x() / map_->info.resolution;
197  const int map_y = pos.y() / map_->info.resolution;
198  const size_t addr = map_x + map_y * map_->info.width;
199  ASSERT_LT(addr, map_->data.size());
200  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
201  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
202  ASSERT_GE(map_x, 0);
203  ASSERT_GE(map_y, 0);
204  ASSERT_NE(map_->data[addr], 100);
205  }
206  }
207  }
208  ASSERT_TRUE(false);
209 }
210 
211 TEST_F(Navigate, NavigateWithLocalMap)
212 {
213  ros::Publisher pub_path = nh_.advertise<nav_msgs::Path>("patrol_nodes", 1, true);
214 
215  ros::spinOnce();
216  ASSERT_TRUE(static_cast<bool>(map_));
217  ASSERT_TRUE(static_cast<bool>(map_local_));
218  pubMapLocal();
219  ros::Duration(0.2).sleep();
220 
221  nav_msgs::Path path;
222  path.poses.resize(1);
223  path.header.frame_id = "map";
224  path.poses[0].header.frame_id = path.header.frame_id;
225  path.poses[0].pose.position.x = 1.7;
226  path.poses[0].pose.position.y = 2.8;
227  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
228  pub_path.publish(path);
229 
230  tf2::Transform goal;
231  tf2::fromMsg(path.poses.back().pose, goal);
232 
233  ros::Rate wait(10);
234  while (ros::ok())
235  {
236  pubMapLocal();
237  ros::spinOnce();
238  wait.sleep();
239 
241  try
242  {
243  const ros::Time now = ros::Time::now();
244  geometry_msgs::TransformStamped trans_tmp =
245  tfbuf_.lookupTransform("map", "base_link", now, ros::Duration(0.5));
246  tf2::fromMsg(trans_tmp, trans);
247  }
248  catch (tf2::TransformException& e)
249  {
250  std::cerr << e.what() << std::endl;
251  continue;
252  }
253 
254  auto goal_rel = trans.inverse() * goal;
255  if (goal_rel.getOrigin().length() < 0.2 &&
256  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2)
257  {
258  std::cerr << "Navagation success." << std::endl;
259  ros::Duration(2.0).sleep();
260  return;
261  }
262 
263  for (int x = -2; x <= 2; ++x)
264  {
265  for (int y = -1; y <= 1; ++y)
266  {
267  const tf2::Vector3 pos =
268  trans * tf2::Vector3(x * map_->info.resolution, y * map_->info.resolution, 0);
269  const int map_x = pos.x() / map_->info.resolution;
270  const int map_y = pos.y() / map_->info.resolution;
271  const size_t addr = map_x + map_y * map_->info.width;
272  ASSERT_LT(addr, map_->data.size());
273  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
274  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
275  ASSERT_GE(map_x, 0);
276  ASSERT_GE(map_y, 0);
277  ASSERT_NE(map_->data[addr], 100);
278  ASSERT_NE(map_local_->data[addr], 100);
279  }
280  }
281  }
282  ASSERT_TRUE(false);
283 }
284 
285 TEST_F(Navigate, GlobalPlan)
286 {
287  ros::ServiceClient srv_plan =
288  nh_.serviceClient<nav_msgs::GetPlanRequest, nav_msgs::GetPlanResponse>(
289  "/planner_3d/make_plan");
290 
291  ros::spinOnce();
292  ASSERT_TRUE(static_cast<bool>(map_));
293 
294  nav_msgs::GetPlanRequest req;
295  nav_msgs::GetPlanResponse res;
296 
297  req.tolerance = 0.0;
298  req.start.header.frame_id = "map";
299  req.start.pose.position.x = 1.95;
300  req.start.pose.position.y = 0.45;
301  req.start.pose.orientation =
302  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), 3.14));
303 
304  req.goal.header.frame_id = "map";
305  req.goal.pose.position.x = 1.25;
306  req.goal.pose.position.y = 2.15;
307  req.goal.pose.orientation =
308  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
309  // Planning failes as (12, 21, 0) is in rock.
310  ASSERT_FALSE(srv_plan.call(req, res));
311 
312  // Goal grid is moved to (12, 22, 0).
313  req.tolerance = 0.1;
314  ASSERT_TRUE(srv_plan.call(req, res));
315  EXPECT_NEAR(1.25, res.plan.poses.back().pose.position.x, 1.0e-5);
316  EXPECT_NEAR(2.25, res.plan.poses.back().pose.position.y, 1.0e-5);
317 
318  // Goal grid is moved to (12, 23, 0). This is because cost of (12, 22, 0) is larger than 50.
319  req.tolerance = 0.2f;
320  ASSERT_TRUE(srv_plan.call(req, res));
321  EXPECT_NEAR(1.25, res.plan.poses.back().pose.position.x, 1.0e-5);
322  EXPECT_NEAR(2.35, res.plan.poses.back().pose.position.y, 1.0e-5);
323 
324  req.tolerance = 0.0;
325  req.goal.header.frame_id = "map";
326  req.goal.pose.position.x = 1.85;
327  req.goal.pose.position.y = 2.75;
328  req.goal.pose.orientation =
329  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -1.57));
330  ASSERT_TRUE(srv_plan.call(req, res));
331 
332  EXPECT_NEAR(req.start.pose.position.x, res.plan.poses.front().pose.position.x, 1.0e-5);
333  EXPECT_NEAR(req.start.pose.position.y, res.plan.poses.front().pose.position.y, 1.0e-5);
334  EXPECT_NEAR(req.goal.pose.position.x, res.plan.poses.back().pose.position.x, 1.0e-5);
335  EXPECT_NEAR(req.goal.pose.position.y, res.plan.poses.back().pose.position.y, 1.0e-5);
336 
337  for (const geometry_msgs::PoseStamped& p : res.plan.poses)
338  {
339  const int map_x = p.pose.position.x / map_->info.resolution;
340  const int map_y = p.pose.position.y / map_->info.resolution;
341  const size_t addr = map_x + map_y * map_->info.width;
342  ASSERT_LT(addr, map_->data.size());
343  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
344  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
345  ASSERT_GE(map_x, 0);
346  ASSERT_GE(map_y, 0);
347  ASSERT_NE(map_->data[addr], 100);
348  }
349 }
350 
351 int main(int argc, char** argv)
352 {
353  testing::InitGoogleTest(&argc, argv);
354  ros::init(argc, argv, "test_navigate");
355 
356  return RUN_ALL_TESTS();
357 }
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
void publish(const boost::shared_ptr< M > &message) const
void cbMap(const nav_msgs::OccupancyGrid::ConstPtr &msg)
nav_msgs::OccupancyGrid::ConstPtr map_
TEST_F(Navigate, Navigate)
Subscriber subscribe(const std::string &topic, uint32_t queue_size, void(T::*fp)(M), T *obj, const TransportHints &transport_hints=TransportHints())
void cbCostmap(const costmap_cspace_msgs::CSpace3D::ConstPtr &msg)
bool sleep() const
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const std::string &source_frame, const ros::Time &time, const ros::Duration timeout) const
ROSCPP_DECL void init(int &argc, char **argv, const std::string &name, uint32_t options=0)
bool call(MReq &req, MRes &res)
ros::Publisher pub_map_
costmap_cspace_msgs::CSpace3D::ConstPtr costmap_
TFSIMD_FORCE_INLINE const tfScalar & y() const
ros::Subscriber sub_map_local_
ros::Subscriber sub_map_
void pubMapLocal()
nav_msgs::OccupancyGrid::ConstPtr map_local_
tf2_ros::TransformListener tfl_
ros::NodeHandle nh_
ros::Subscriber sub_costmap_
ros::Publisher pub_initial_pose_
bool waitForExistence(ros::Duration timeout=ros::Duration(-1))
void fromMsg(const A &, B &b)
ROSCPP_DECL bool ok()
ros::ServiceClient srv_forget_
tf2_ros::Buffer tfbuf_
TFSIMD_FORCE_INLINE const tfScalar & x() const
Publisher advertise(const std::string &topic, uint32_t queue_size, bool latch=false)
bool sleep()
double getYaw(const A &a)
B toMsg(const A &a)
virtual void SetUp()
static Time now()
ros::Publisher pub_map_local_
ROSCPP_DECL void spinOnce()
size_t local_map_apply_cnt_
void cbMapLocal(const nav_msgs::OccupancyGrid::ConstPtr &msg)
int main(int argc, char **argv)


planner_cspace
Author(s): Atsushi Watanabe
autogenerated on Wed May 12 2021 02:20:42