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 <algorithm>
31 #include <cmath>
32 #include <cstddef>
33 #include <string>
34 #include <unistd.h>
35 #include <vector>
36 
37 #include <ros/ros.h>
38 
39 #include <costmap_cspace_msgs/CSpace3D.h>
40 #include <nav_msgs/GetPlan.h>
41 #include <nav_msgs/OccupancyGrid.h>
42 #include <nav_msgs/Path.h>
43 #include <planner_cspace_msgs/PlannerStatus.h>
44 #include <std_msgs/Empty.h>
45 #include <std_srvs/Empty.h>
46 #include <tf2/utils.h>
49 #include <trajectory_tracker_msgs/PathWithVelocity.h>
50 
52 
53 #include <gtest/gtest.h>
54 
55 class Navigate : public ::testing::Test
56 {
57 protected:
62  nav_msgs::OccupancyGrid::ConstPtr map_;
63  nav_msgs::OccupancyGrid::Ptr map_local_;
64  planner_cspace_msgs::PlannerStatus::ConstPtr planner_status_;
65  costmap_cspace_msgs::CSpace3D::ConstPtr costmap_;
66  nav_msgs::Path::ConstPtr path_;
67  trajectory_tracker_msgs::PathWithVelocity::ConstPtr path_vel_;
80  std::vector<tf2::Stamped<tf2::Transform>> traj_;
81  std::string test_scope_;
82 
84  : pnh_("~")
85  , tfl_(tfbuf_)
87  {
88  sub_map_ = nh_.subscribe("map_global", 1, &Navigate::cbMap, this);
89  sub_map_local_ = nh_.subscribe("map_local", 1, &Navigate::cbMapLocal, this);
90  sub_costmap_ = nh_.subscribe("costmap", 1, &Navigate::cbCostmap, this);
92  "/planner_3d/status", 10, &Navigate::cbStatus, this);
93  sub_path_ = nh_.subscribe("path", 1, &Navigate::cbPath, this);
94  sub_path_vel_ = nh_.subscribe("path_velocity", 1, &Navigate::cbPathVel, this);
95  srv_forget_ =
96  nh_.serviceClient<std_srvs::EmptyRequest, std_srvs::EmptyResponse>(
97  "forget_planning_cost");
98  pub_map_ = nh_.advertise<nav_msgs::OccupancyGrid>("map", 1, true);
99  pub_map_local_ = nh_.advertise<nav_msgs::OccupancyGrid>("overlay", 1, true);
101  nh_.advertise<geometry_msgs::PoseWithCovarianceStamped>("initialpose", 1, true);
102  pub_patrol_nodes_ = nh_.advertise<nav_msgs::Path>("patrol_nodes", 1, true);
103  }
104 
105  virtual void SetUp()
106  {
107  test_scope_ =
108  "[" + std::to_string(getpid()) + "/" +
109  ::testing::UnitTest::GetInstance()->current_test_info()->name() + "] ";
110 
111  ros::Rate rate(10.0);
112 
113  const ros::Time deadline = ros::Time::now() + ros::Duration(15.0);
114  while (ros::ok())
115  {
116  rate.sleep();
117  ASSERT_LT(ros::Time::now(), deadline)
118  << test_scope_ << "Initialization timeout: "
119  << "sub_map:" << sub_map_.getNumPublishers() << " "
120  << "sub_map_local:" << sub_map_local_.getNumPublishers() << " "
121  << "sub_costmap:" << sub_costmap_.getNumPublishers() << " "
122  << "sub_status:" << sub_status_.getNumPublishers() << " "
123  << "pub_map:" << pub_map_.getNumSubscribers() << " "
124  << "pub_map_local:" << pub_map_local_.getNumSubscribers() << " "
125  << "pub_initial_pose:" << pub_initial_pose_.getNumSubscribers() << " "
126  << "pub_patrol_nodes:" << pub_patrol_nodes_.getNumSubscribers() << " ";
127  if (sub_map_.getNumPublishers() > 0 &&
131  pub_map_.getNumSubscribers() > 0 &&
135  {
136  break;
137  }
138  }
139  ASSERT_TRUE(srv_forget_.waitForExistence(ros::Duration(10.0)));
140 
141  geometry_msgs::PoseWithCovarianceStamped pose;
142  pose.header.frame_id = "map";
143  pose.pose.pose.position.x = 2.5;
144  pose.pose.pose.position.y = 0.45;
145  pose.pose.pose.orientation.z = 1.0;
147 
148  while (ros::ok())
149  {
150  ros::spinOnce();
151  rate.sleep();
152  const ros::Time now = ros::Time::now();
153  ASSERT_LT(now, deadline) << test_scope_ << "Initial transform timeout";
154  if (tfbuf_.canTransform("map", "base_link", now, ros::Duration(0.5)))
155  {
156  break;
157  }
158  }
159 
160  while (ros::ok() && !map_)
161  {
162  ros::spinOnce();
163  rate.sleep();
164  ASSERT_LT(ros::Time::now(), deadline) << test_scope_ << "Initial map timeout";
165  }
167  std::cerr << test_scope_ << ros::Time::now() << " Map applied." << std::endl;
168 
169  while (ros::ok() && !map_local_)
170  {
171  ros::spinOnce();
172  rate.sleep();
173  ASSERT_LT(ros::Time::now(), deadline) << test_scope_ << "Initial local map timeout";
174  }
175 
176  while (ros::ok() && !costmap_)
177  {
178  ros::spinOnce();
179  rate.sleep();
180  ASSERT_LT(ros::Time::now(), deadline) << test_scope_ << "Initial costmap timeout";
181  }
182 
183  std_srvs::EmptyRequest req;
184  std_srvs::EmptyResponse res;
185  srv_forget_.call(req, res);
186 
187  ros::Duration(1.0).sleep();
188  }
189  void cbCostmap(const costmap_cspace_msgs::CSpace3D::ConstPtr& msg)
190  {
191  costmap_ = msg;
192  std::cerr << test_scope_ << msg->header.stamp << " Costmap received." << std::endl;
193  }
194  void cbMap(const nav_msgs::OccupancyGrid::ConstPtr& msg)
195  {
196  map_ = msg;
197  std::cerr << test_scope_ << msg->header.stamp << " Map received." << std::endl;
198  }
199  void cbMapLocal(const nav_msgs::OccupancyGrid::ConstPtr& msg)
200  {
201  if (map_local_)
202  {
203  return;
204  }
205  map_local_.reset(new nav_msgs::OccupancyGrid(*msg));
206  std::cerr << test_scope_ << msg->header.stamp << " Local map received." << std::endl;
207  }
208  void cbStatus(const planner_cspace_msgs::PlannerStatus::ConstPtr& msg)
209  {
210  if (!planner_status_ || planner_status_->status != msg->status || planner_status_->error != msg->error)
211  {
212  std::cerr << test_scope_ << msg->header.stamp << " Status updated." << msg << std::endl;
213  }
215  }
216  void cbPath(const nav_msgs::Path::ConstPtr& msg)
217  {
218  if (!path_ || path_->poses.size() != msg->poses.size())
219  {
220  if (msg->poses.size() == 0)
221  {
222  std::cerr << test_scope_ << msg->header.stamp << " Path updated. (empty)" << std::endl;
223  }
224  else
225  {
226  std::cerr
227  << test_scope_ << msg->header.stamp << " Path updated." << std::endl
228  << msg->poses.front().pose.position.x << ", " << msg->poses.front().pose.position.y << std::endl
229  << msg->poses.back().pose.position.x << ", " << msg->poses.back().pose.position.y << std::endl;
230  }
231  path_ = msg;
232  }
233  }
234  void cbPathVel(const trajectory_tracker_msgs::PathWithVelocity::ConstPtr& msg)
235  {
236  if (!path_vel_ || path_vel_->poses.size() != msg->poses.size())
237  {
238  if (msg->poses.size() == 0)
239  {
240  std::cerr << test_scope_ << msg->header.stamp << " PathWithVelocity updated. (empty)" << std::endl;
241  }
242  else
243  {
244  std::cerr
245  << test_scope_ << msg->header.stamp << " PathWithVelocity updated." << std::endl
246  << msg->poses.front().pose.position.x << ", " << msg->poses.front().pose.position.y << std::endl
247  << msg->poses.back().pose.position.x << ", " << msg->poses.back().pose.position.y << std::endl;
248  }
249  path_vel_ = msg;
250  }
251  }
252  void pubMapLocal()
253  {
254  if (!map_local_)
255  {
256  return;
257  }
259  if ((local_map_apply_cnt_++) % 30 == 0)
260  {
261  int num_occupied = 0;
262  for (const auto& c : map_local_->data)
263  {
264  if (c == 100)
265  {
266  num_occupied++;
267  }
268  }
269  std::cerr << test_scope_ << " Local map applied. occupied grids:" << num_occupied << std::endl;
270  }
271  }
273  {
274  geometry_msgs::TransformStamped trans_tmp =
275  tfbuf_.lookupTransform("map", "base_link", now, ros::Duration(0.5));
277  tf2::fromMsg(trans_tmp, trans);
278  traj_.push_back(trans);
279  return trans;
280  }
282  {
283  double x_prev(0), y_prev(0);
284  tf2::Quaternion rot_prev(0, 0, 0, 1);
285 
286  std::cerr << test_scope_ << traj_.size() << " points recorded" << std::endl;
287 
288  for (const auto& t : traj_)
289  {
290  const double x = t.getOrigin().getX();
291  const double y = t.getOrigin().getY();
292  const tf2::Quaternion rot = t.getRotation();
293  const double yaw_diff = rot.angleShortestPath(rot_prev);
294  if (std::abs(x - x_prev) >= 0 || std::abs(y - y_prev) >= 0 || std::abs(yaw_diff) >= 0)
295  {
296  x_prev = x;
297  y_prev = y;
298  rot_prev = rot;
299  std::cerr << t.stamp_ << " " << x << " " << y << " " << tf2::getYaw(rot) << std::endl;
300  }
301  }
302  }
303 
304  void waitForPlannerStatus(const std::string& name, const int expected_error)
305  {
306  ros::spinOnce();
307  ASSERT_TRUE(static_cast<bool>(map_));
308  ASSERT_TRUE(static_cast<bool>(map_local_));
309  pubMapLocal();
310  ros::Duration(0.2).sleep();
311 
312  ros::Rate wait(10);
313  ros::Time deadline = ros::Time::now() + ros::Duration(10);
314  while (ros::ok())
315  {
316  pubMapLocal();
317  ros::spinOnce();
318  wait.sleep();
319 
320  const ros::Time now = ros::Time::now();
321 
322  if (now > deadline)
323  {
325  FAIL()
326  << test_scope_ << "/" << name << ": Navigation timeout." << std::endl
327  << "now: " << now << std::endl
328  << "status: " << planner_status_ << " (expected: " << expected_error << ")";
329  }
330 
331  if (planner_status_->error == expected_error)
332  {
333  return;
334  }
335  }
336  }
337 };
338 
340 {
341  ros::spinOnce();
342  ASSERT_TRUE(static_cast<bool>(map_));
343 
344  nav_msgs::Path path;
345  path.poses.resize(2);
346  path.header.frame_id = "map";
347  path.poses[0].header.frame_id = path.header.frame_id;
348  path.poses[0].pose.position.x = 1.7;
349  path.poses[0].pose.position.y = 2.8;
350  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
351  path.poses[1].header.frame_id = path.header.frame_id;
352  path.poses[1].pose.position.x = 1.9;
353  path.poses[1].pose.position.y = 2.8;
354  path.poses[1].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -1.57));
355  pub_patrol_nodes_.publish(path);
356 
357  tf2::Transform goal;
358  tf2::fromMsg(path.poses.back().pose, goal);
359 
360  ros::Rate wait(10);
361  const ros::Time deadline = ros::Time::now() + ros::Duration(60);
362  while (ros::ok())
363  {
364  ros::spinOnce();
365  wait.sleep();
366 
367  const ros::Time now = ros::Time::now();
368 
369  if (now > deadline)
370  {
371  dumpRobotTrajectory();
372  FAIL()
373  << test_scope_ << "Navigation timeout." << std::endl
374  << "now: " << now << std::endl
375  << "status: " << planner_status_;
376  break;
377  }
378 
380  try
381  {
382  trans = lookupRobotTrans(now);
383  }
384  catch (tf2::TransformException& e)
385  {
386  std::cerr << test_scope_ << e.what() << std::endl;
387  continue;
388  }
389 
390  auto goal_rel = trans.inverse() * goal;
391  if (goal_rel.getOrigin().length() < 0.2 &&
392  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2)
393  {
394  std::cerr << test_scope_ << "Navigation success." << std::endl;
395  ros::Duration(2.0).sleep();
396  return;
397  }
398 
399  for (int x = -2; x <= 2; ++x)
400  {
401  for (int y = -1; y <= 1; ++y)
402  {
403  const tf2::Vector3 pos =
404  trans * tf2::Vector3(x * map_->info.resolution, y * map_->info.resolution, 0);
405  const int map_x = pos.x() / map_->info.resolution;
406  const int map_y = pos.y() / map_->info.resolution;
407  const size_t addr = map_x + map_y * map_->info.width;
408  ASSERT_LT(addr, map_->data.size());
409  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
410  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
411  ASSERT_GE(map_x, 0);
412  ASSERT_GE(map_y, 0);
413  ASSERT_NE(map_->data[addr], 100);
414  }
415  }
416  }
417  ASSERT_TRUE(false);
418 }
419 
420 TEST_F(Navigate, NavigateWithLocalMap)
421 {
422  ros::spinOnce();
423  ASSERT_TRUE(static_cast<bool>(map_));
424  ASSERT_TRUE(static_cast<bool>(map_local_));
425  pubMapLocal();
426  ros::Duration(0.2).sleep();
427 
428  nav_msgs::Path path;
429  path.poses.resize(1);
430  path.header.frame_id = "map";
431  path.poses[0].header.frame_id = path.header.frame_id;
432  path.poses[0].pose.position.x = 1.7;
433  path.poses[0].pose.position.y = 2.8;
434  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
435  pub_patrol_nodes_.publish(path);
436 
437  tf2::Transform goal;
438  tf2::fromMsg(path.poses.back().pose, goal);
439 
440  ros::Rate wait(10);
441  const ros::Time deadline = ros::Time::now() + ros::Duration(60);
442  while (ros::ok())
443  {
444  pubMapLocal();
445  ros::spinOnce();
446  wait.sleep();
447 
448  const ros::Time now = ros::Time::now();
449 
450  if (now > deadline)
451  {
452  dumpRobotTrajectory();
453  FAIL()
454  << test_scope_ << "Navigation timeout." << std::endl
455  << "now: " << now << std::endl
456  << "status: " << planner_status_;
457  break;
458  }
459 
461  try
462  {
463  trans = lookupRobotTrans(now);
464  }
465  catch (tf2::TransformException& e)
466  {
467  std::cerr << test_scope_ << e.what() << std::endl;
468  continue;
469  }
470 
471  auto goal_rel = trans.inverse() * goal;
472  if (goal_rel.getOrigin().length() < 0.2 &&
473  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2)
474  {
475  std::cerr << test_scope_ << "Navagation success." << std::endl;
476  ros::Duration(2.0).sleep();
477  return;
478  }
479 
480  for (int x = -2; x <= 2; ++x)
481  {
482  for (int y = -1; y <= 1; ++y)
483  {
484  const tf2::Vector3 pos =
485  trans * tf2::Vector3(x * map_->info.resolution, y * map_->info.resolution, 0);
486  const int map_x = pos.x() / map_->info.resolution;
487  const int map_y = pos.y() / map_->info.resolution;
488  const size_t addr = map_x + map_y * map_->info.width;
489  ASSERT_LT(addr, map_->data.size());
490  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
491  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
492  ASSERT_GE(map_x, 0);
493  ASSERT_GE(map_y, 0);
494  ASSERT_NE(map_->data[addr], 100);
495  ASSERT_NE(map_local_->data[addr], 100);
496  }
497  }
498  }
499  ASSERT_TRUE(false);
500 }
501 
502 TEST_F(Navigate, GlobalPlan)
503 {
504  ros::ServiceClient srv_plan =
505  nh_.serviceClient<nav_msgs::GetPlanRequest, nav_msgs::GetPlanResponse>(
506  "/planner_3d/make_plan");
507 
508  ros::spinOnce();
509  ASSERT_TRUE(static_cast<bool>(map_));
510 
511  nav_msgs::GetPlanRequest req;
512  nav_msgs::GetPlanResponse res;
513 
514  req.tolerance = 0.0;
515  req.start.header.frame_id = "map";
516  req.start.pose.position.x = 1.95;
517  req.start.pose.position.y = 0.45;
518  req.start.pose.orientation =
519  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), 3.14));
520 
521  req.goal.header.frame_id = "map";
522  req.goal.pose.position.x = 1.25;
523  req.goal.pose.position.y = 2.15;
524  req.goal.pose.orientation =
525  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -3.14));
526  // Planning failes as (12, 21, 0) is in rock.
527  ASSERT_FALSE(srv_plan.call(req, res));
528 
529  // Goal grid is moved to (12, 22, 0).
530  req.tolerance = 0.1;
531  ASSERT_TRUE(srv_plan.call(req, res));
532  EXPECT_NEAR(1.25, res.plan.poses.back().pose.position.x, 1.0e-5);
533  EXPECT_NEAR(2.25, res.plan.poses.back().pose.position.y, 1.0e-5);
534 
535  // Goal grid is moved to (12, 23, 0). This is because cost of (12, 22, 0) is larger than 50.
536  req.tolerance = 0.2f;
537  ASSERT_TRUE(srv_plan.call(req, res));
538  EXPECT_NEAR(1.25, res.plan.poses.back().pose.position.x, 1.0e-5);
539  EXPECT_NEAR(2.35, res.plan.poses.back().pose.position.y, 1.0e-5);
540 
541  req.tolerance = 0.0;
542  req.goal.header.frame_id = "map";
543  req.goal.pose.position.x = 1.85;
544  req.goal.pose.position.y = 2.75;
545  req.goal.pose.orientation =
546  tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), -1.57));
547  ASSERT_TRUE(srv_plan.call(req, res));
548 
549  EXPECT_NEAR(req.start.pose.position.x, res.plan.poses.front().pose.position.x, 1.0e-5);
550  EXPECT_NEAR(req.start.pose.position.y, res.plan.poses.front().pose.position.y, 1.0e-5);
551  EXPECT_NEAR(req.goal.pose.position.x, res.plan.poses.back().pose.position.x, 1.0e-5);
552  EXPECT_NEAR(req.goal.pose.position.y, res.plan.poses.back().pose.position.y, 1.0e-5);
553 
554  for (const geometry_msgs::PoseStamped& p : res.plan.poses)
555  {
556  const int map_x = p.pose.position.x / map_->info.resolution;
557  const int map_y = p.pose.position.y / map_->info.resolution;
558  const size_t addr = map_x + map_y * map_->info.width;
559  ASSERT_LT(addr, map_->data.size());
560  ASSERT_LT(map_x, static_cast<int>(map_->info.width));
561  ASSERT_LT(map_y, static_cast<int>(map_->info.height));
562  ASSERT_GE(map_x, 0);
563  ASSERT_GE(map_y, 0);
564  ASSERT_NE(map_->data[addr], 100);
565  }
566 }
567 
568 TEST_F(Navigate, RobotIsInRockOnSetGoal)
569 {
570  ros::Publisher pub_path = nh_.advertise<nav_msgs::Path>("patrol_nodes", 1, true);
571 
572  ros::spinOnce();
573  ASSERT_TRUE(static_cast<bool>(map_));
574  ASSERT_TRUE(static_cast<bool>(map_local_));
575  pubMapLocal();
576  ros::Duration(0.2).sleep();
577 
578  nav_msgs::Path path;
579  path.poses.resize(1);
580  path.header.frame_id = "map";
581  path.poses[0].header.frame_id = path.header.frame_id;
582  path.poses[0].pose.position.x = 1.19;
583  path.poses[0].pose.position.y = 1.90;
584  path.poses[0].pose.orientation.w = 1.0;
585  pub_path.publish(path);
586 
587  tf2::Transform goal;
588  tf2::fromMsg(path.poses.back().pose, goal);
589 
590  ros::Rate wait(10);
591  const ros::Time deadline = ros::Time::now() + ros::Duration(10);
592  while (ros::ok())
593  {
594  pubMapLocal();
595  ros::spinOnce();
596  wait.sleep();
597 
598  const ros::Time now = ros::Time::now();
599 
600  if (now > deadline)
601  {
602  dumpRobotTrajectory();
603  FAIL()
604  << test_scope_ << "Navigation timeout." << std::endl
605  << "now: " << now << std::endl
606  << "status: " << planner_status_;
607  break;
608  }
609 
610  if (planner_status_->error == planner_cspace_msgs::PlannerStatus::PATH_NOT_FOUND)
611  {
612  return;
613  }
614  }
615  ASSERT_TRUE(false);
616 }
617 
618 TEST_F(Navigate, GoalIsInRockRecovered)
619 {
620  if (pnh_.param("enable_crowd_mode", false))
621  {
622  GTEST_SKIP() << "enable_crowd_mode is set";
623  }
624 
625  ros::spinOnce();
626  ASSERT_TRUE(static_cast<bool>(map_));
627  ASSERT_TRUE(static_cast<bool>(map_local_));
628 
629  for (int x = 10; x <= 16; ++x)
630  {
631  for (int y = 22; y <= 26; ++y)
632  {
633  const int pos = x + y * map_local_->info.width;
634  map_local_->data[pos] = 100;
635  }
636  }
637  pubMapLocal();
638 
639  nav_msgs::Path path;
640  path.poses.resize(1);
641  path.header.frame_id = "map";
642  path.poses[0].header.frame_id = path.header.frame_id;
643  path.poses[0].pose.position.x = 1.25;
644  path.poses[0].pose.position.y = 2.55;
645  path.poses[0].pose.orientation.w = 1.0;
646  pub_patrol_nodes_.publish(path);
647 
648  waitForPlannerStatus("Got stuck", planner_cspace_msgs::PlannerStatus::PATH_NOT_FOUND);
649  ASSERT_FALSE(::testing::Test::HasFailure());
650 
651  for (int x = 10; x <= 16; ++x)
652  {
653  for (int y = 22; y <= 26; ++y)
654  {
655  const int pos = x + y * map_local_->info.width;
656  map_local_->data[pos] = 0;
657  }
658  }
659  waitForPlannerStatus("Stuck recovered", planner_cspace_msgs::PlannerStatus::GOING_WELL);
660 }
661 
662 TEST_F(Navigate, RobotIsInRockOnRecovered)
663 {
664  ros::spinOnce();
665  ASSERT_TRUE(static_cast<bool>(map_));
666  ASSERT_TRUE(static_cast<bool>(map_local_));
667 
668  for (int x = 21; x <= 28; ++x)
669  {
670  for (int y = 2; y <= 8; ++y)
671  {
672  const int pos = x + y * map_local_->info.width;
673  map_local_->data[pos] = 100;
674  }
675  }
676  pubMapLocal();
677 
678  nav_msgs::Path path;
679  path.poses.resize(1);
680  path.header.frame_id = "map";
681  path.poses[0].header.frame_id = path.header.frame_id;
682  path.poses[0].pose.position.x = 1.25;
683  path.poses[0].pose.position.y = 2.55;
684  path.poses[0].pose.orientation.w = 1.0;
685  pub_patrol_nodes_.publish(path);
686 
687  waitForPlannerStatus("Got stuck", planner_cspace_msgs::PlannerStatus::IN_ROCK);
688  ASSERT_FALSE(::testing::Test::HasFailure());
689 
690  for (int x = 21; x <= 28; ++x)
691  {
692  for (int y = 2; y <= 8; ++y)
693  {
694  const int pos = x + y * map_local_->info.width;
695  map_local_->data[pos] = 0;
696  }
697  }
698  waitForPlannerStatus("Stuck recovered", planner_cspace_msgs::PlannerStatus::GOING_WELL);
699 }
700 
701 TEST_F(Navigate, RobotIsInCrowd)
702 {
703  if (!pnh_.param("enable_crowd_mode", false))
704  {
705  GTEST_SKIP() << "enable_crowd_mode is not set";
706  }
707 
708  ros::spinOnce();
709  ASSERT_TRUE(static_cast<bool>(map_));
710  ASSERT_TRUE(static_cast<bool>(map_local_));
711 
712  nav_msgs::Path path;
713  path.poses.resize(1);
714  path.header.frame_id = "map";
715  path.poses[0].header.frame_id = path.header.frame_id;
716  path.poses[0].pose.position.x = 1.6;
717  path.poses[0].pose.position.y = 2.2;
718  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), 1.57));
719  pub_patrol_nodes_.publish(path);
720 
721  tf2::Transform goal;
722  tf2::fromMsg(path.poses.back().pose, goal);
723 
724  ros::Rate wait(10);
725  const ros::Time deadline = ros::Time::now() + ros::Duration(60);
726  while (ros::ok())
727  {
728  pubMapLocal();
729  ros::spinOnce();
730  wait.sleep();
731 
732  const ros::Time now = ros::Time::now();
733 
734  if (now > deadline)
735  {
736  dumpRobotTrajectory();
737  FAIL()
738  << test_scope_ << "Navigation timeout." << std::endl
739  << "now: " << now << std::endl
740  << "status: " << planner_status_;
741  break;
742  }
743 
745  try
746  {
747  trans = lookupRobotTrans(now);
748  }
749  catch (tf2::TransformException& e)
750  {
751  std::cerr << test_scope_ << e.what() << std::endl;
752  continue;
753  }
754 
755  const auto goal_rel = trans.inverse() * goal;
756  if (goal_rel.getOrigin().length() < 0.2 &&
757  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2 &&
758  planner_status_->status == planner_cspace_msgs::PlannerStatus::DONE)
759  {
760  std::cerr << test_scope_ << "Navigation success." << std::endl;
761  ros::Duration(2.0).sleep();
762  return;
763  }
764 
765  const size_t rx = trans.getOrigin().x() / map_->info.resolution;
766  const size_t ry = trans.getOrigin().y() / map_->info.resolution;
767  const size_t data_size = map_local_->data.size();
768  map_local_->data.clear();
769  map_local_->data.resize(data_size, 0);
770  const int bs = 6;
771  for (int i = -bs; i <= bs; ++i)
772  {
773  map_local_->data[std::min((rx + i) + (ry - bs) * map_local_->info.width, data_size - 1)] = 100;
774  map_local_->data[std::min((rx + i) + (ry + bs) * map_local_->info.width, data_size - 1)] = 100;
775  map_local_->data[std::min((rx - bs) + (ry + i) * map_local_->info.width, data_size - 1)] = 100;
776  map_local_->data[std::min((rx + bs) + (ry + i) * map_local_->info.width, data_size - 1)] = 100;
777  }
778  }
779 }
780 
781 TEST_F(Navigate, ForceTemporaryEscape)
782 {
783  if (!pnh_.param("enable_crowd_mode", false))
784  {
785  GTEST_SKIP() << "enable_crowd_mode is not set";
786  }
787 
788  ros::Publisher pub_trigger = nh_.advertise<std_msgs::Empty>("/planner_3d/temporary_escape", 1);
789 
790  ros::spinOnce();
791  ASSERT_TRUE(static_cast<bool>(map_));
792  ASSERT_TRUE(static_cast<bool>(map_local_));
793 
794  nav_msgs::Path path;
795  path.poses.resize(1);
796  path.header.frame_id = "map";
797  path.poses[0].header.frame_id = path.header.frame_id;
798  path.poses[0].pose.position.x = 1.6;
799  path.poses[0].pose.position.y = 2.2;
800  path.poses[0].pose.orientation = tf2::toMsg(tf2::Quaternion(tf2::Vector3(0.0, 0.0, 1.0), 1.57));
801  pub_patrol_nodes_.publish(path);
802 
803  tf2::Transform goal;
804  tf2::fromMsg(path.poses.back().pose, goal);
805 
806  ros::Rate wait(2);
807  const ros::Time deadline = ros::Time::now() + ros::Duration(60);
808  while (ros::ok())
809  {
810  const size_t data_size = map_local_->data.size();
811  map_local_->data.clear();
812  map_local_->data.resize(data_size, 0);
813  pubMapLocal();
814 
815  std_msgs::Empty msg;
816  pub_trigger.publish(msg);
817 
818  ros::spinOnce();
819  wait.sleep();
820 
821  const ros::Time now = ros::Time::now();
822 
823  if (now > deadline)
824  {
825  dumpRobotTrajectory();
826  FAIL()
827  << test_scope_ << "Navigation timeout." << std::endl
828  << "now: " << now << std::endl
829  << "status: " << planner_status_;
830  break;
831  }
832 
834  try
835  {
836  trans = lookupRobotTrans(now);
837  }
838  catch (tf2::TransformException& e)
839  {
840  std::cerr << test_scope_ << e.what() << std::endl;
841  continue;
842  }
843 
844  const auto goal_rel = trans.inverse() * goal;
845  if (goal_rel.getOrigin().length() < 0.2 &&
846  std::abs(tf2::getYaw(goal_rel.getRotation())) < 0.2 &&
847  planner_status_->status == planner_cspace_msgs::PlannerStatus::DONE)
848  {
849  std::cerr << test_scope_ << "Navigation success." << std::endl;
850  ros::Duration(2.0).sleep();
851  return;
852  }
853  }
854 }
855 
856 int main(int argc, char** argv)
857 {
858  testing::InitGoogleTest(&argc, argv);
859  ros::init(argc, argv, "test_navigate");
860 
861  return RUN_ALL_TESTS();
862 }
Navigate::test_scope_
std::string test_scope_
Definition: test_navigate.cpp:81
Navigate::path_vel_
trajectory_tracker_msgs::PathWithVelocity::ConstPtr path_vel_
Definition: test_navigate.cpp:67
ros::ServiceClient::waitForExistence
bool waitForExistence(ros::Duration timeout=ros::Duration(-1))
TEST_F
TEST_F(Navigate, Navigate)
Definition: test_navigate.cpp:339
msg
msg
ros::Publisher
planner_status.h
ros::init
ROSCPP_DECL void init(const M_string &remappings, const std::string &name, uint32_t options=0)
tf2::getYaw
double getYaw(const A &a)
tf2::fromMsg
void fromMsg(const A &, B &b)
utils.h
ros.h
Navigate::sub_map_local_
ros::Subscriber sub_map_local_
Definition: test_navigate.cpp:69
ros::NodeHandle::serviceClient
ServiceClient serviceClient(const std::string &service_name, bool persistent=false, const M_string &header_values=M_string())
Navigate::pub_map_local_
ros::Publisher pub_map_local_
Definition: test_navigate.cpp:76
ros::spinOnce
ROSCPP_DECL void spinOnce()
tf2::Stamped
wait
void wait(int seconds)
Navigate::Navigate
Navigate()
Definition: test_navigate.cpp:83
Navigate::lookupRobotTrans
tf2::Stamped< tf2::Transform > lookupRobotTrans(const ros::Time &now)
Definition: test_navigate.cpp:272
Navigate::sub_path_
ros::Subscriber sub_path_
Definition: test_navigate.cpp:72
ros::Publisher::publish
void publish(const boost::shared_ptr< M > &message) const
ros::NodeHandle::advertise
Publisher advertise(AdvertiseOptions &ops)
ros::ok
ROSCPP_DECL bool ok()
main
int main(int argc, char **argv)
Definition: test_navigate.cpp:856
ros::Subscriber::getNumPublishers
uint32_t getNumPublishers() const
tf2_ros::TransformListener
Navigate::cbPathVel
void cbPathVel(const trajectory_tracker_msgs::PathWithVelocity::ConstPtr &msg)
Definition: test_navigate.cpp:234
tf2_ros::Buffer::canTransform
virtual bool canTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration timeout, std::string *errstr=NULL) const
Navigate::local_map_apply_cnt_
size_t local_map_apply_cnt_
Definition: test_navigate.cpp:79
Navigate::map_
nav_msgs::OccupancyGrid::ConstPtr map_
Definition: test_navigate.cpp:62
Navigate::planner_status_
planner_cspace_msgs::PlannerStatus::ConstPtr planner_status_
Definition: test_navigate.cpp:64
Navigate::pub_patrol_nodes_
ros::Publisher pub_patrol_nodes_
Definition: test_navigate.cpp:78
Navigate::sub_path_vel_
ros::Subscriber sub_path_vel_
Definition: test_navigate.cpp:73
Navigate::pnh_
ros::NodeHandle pnh_
Definition: test_navigate.cpp:58
Navigate::tfbuf_
tf2_ros::Buffer tfbuf_
Definition: test_navigate.cpp:60
Navigate::dumpRobotTrajectory
void dumpRobotTrajectory()
Definition: test_navigate.cpp:281
Navigate::sub_status_
ros::Subscriber sub_status_
Definition: test_navigate.cpp:71
Navigate::waitForPlannerStatus
void waitForPlannerStatus(const std::string &name, const int expected_error)
Definition: test_navigate.cpp:304
tf2::Transform
ros::ServiceClient
Navigate::sub_costmap_
ros::Subscriber sub_costmap_
Definition: test_navigate.cpp:70
Navigate::cbMapLocal
void cbMapLocal(const nav_msgs::OccupancyGrid::ConstPtr &msg)
Definition: test_navigate.cpp:199
tf2_ros::Buffer
Navigate::cbStatus
void cbStatus(const planner_cspace_msgs::PlannerStatus::ConstPtr &msg)
Definition: test_navigate.cpp:208
ros::NodeHandle::subscribe
Subscriber subscribe(const std::string &topic, uint32_t queue_size, const boost::function< void(C)> &callback, const VoidConstPtr &tracked_object=VoidConstPtr(), const TransportHints &transport_hints=TransportHints())
Navigate::costmap_
costmap_cspace_msgs::CSpace3D::ConstPtr costmap_
Definition: test_navigate.cpp:65
ros::Rate::sleep
bool sleep()
Navigate::cbCostmap
void cbCostmap(const costmap_cspace_msgs::CSpace3D::ConstPtr &msg)
Definition: test_navigate.cpp:189
Navigate
Definition: test_navigate.cpp:55
transform_listener.h
Navigate::SetUp
virtual void SetUp()
Definition: test_navigate.cpp:105
Navigate::cbPath
void cbPath(const nav_msgs::Path::ConstPtr &msg)
Definition: test_navigate.cpp:216
ros::Time
Navigate::pub_initial_pose_
ros::Publisher pub_initial_pose_
Definition: test_navigate.cpp:77
Navigate::tfl_
tf2_ros::TransformListener tfl_
Definition: test_navigate.cpp:61
Navigate::pub_map_
ros::Publisher pub_map_
Definition: test_navigate.cpp:75
ros::ServiceClient::call
bool call(const MReq &req, MRes &resp, const std::string &service_md5sum)
Navigate::map_local_
nav_msgs::OccupancyGrid::Ptr map_local_
Definition: test_navigate.cpp:63
tf2::Quaternion
tf2_geometry_msgs.h
tf2::toMsg
B toMsg(const A &a)
ros::Rate
Navigate::traj_
std::vector< tf2::Stamped< tf2::Transform > > traj_
Definition: test_navigate.cpp:80
ros::Duration::sleep
bool sleep() const
ros::Publisher::getNumSubscribers
uint32_t getNumSubscribers() const
Navigate::cbMap
void cbMap(const nav_msgs::OccupancyGrid::ConstPtr &msg)
Definition: test_navigate.cpp:194
Navigate::srv_forget_
ros::ServiceClient srv_forget_
Definition: test_navigate.cpp:74
tf2::TransformException
ros::Duration
tf2::Quaternion::angleShortestPath
tf2Scalar angleShortestPath(const Quaternion &q) const
Navigate::path_
nav_msgs::Path::ConstPtr path_
Definition: test_navigate.cpp:66
t
geometry_msgs::TransformStamped t
Navigate::sub_map_
ros::Subscriber sub_map_
Definition: test_navigate.cpp:68
tf2_ros::Buffer::lookupTransform
virtual geometry_msgs::TransformStamped lookupTransform(const std::string &target_frame, const ros::Time &target_time, const std::string &source_frame, const ros::Time &source_time, const std::string &fixed_frame, const ros::Duration timeout) const
Navigate::nh_
ros::NodeHandle nh_
Definition: test_navigate.cpp:59
Navigate::pubMapLocal
void pubMapLocal()
Definition: test_navigate.cpp:252
ros::NodeHandle
ros::Subscriber
ros::Time::now
static Time now()


planner_cspace
Author(s): Atsushi Watanabe
autogenerated on Fri May 16 2025 02:15:23