test_costmap_3d.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2016-2019, 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 <vector>
35 
36 #include <ros/ros.h>
37 
39 #include <nav_msgs/OccupancyGrid.h>
40 
41 #include <gtest/gtest.h>
42 
43 const std::string footprint_str(
44  "<value><array><data>"
45  " <value><array><data>"
46  " <value><double>1.5</double></value>"
47  " <value><double>0.0</double></value>"
48  " </data></array></value>"
49  " <value><array><data>"
50  " <value><double>-0.5</double></value>"
51  " <value><double>-0.5</double></value>"
52  " </data></array></value>"
53  " <value><array><data>"
54  " <value><double>-0.5</double></value>"
55  " <value><double>0.5</double></value>"
56  " </data></array></value>"
57  "</data></array></value>");
58 // This footprint with resolution of 0.1 means:
59 // 0 0 0
60 // 1 1 0 <--x
61 // 0 0 0
62 const char temp_dir[4][2] =
63  {
64  // x, y which must be occupied in the template
65  -1, 0,
66  0, -1,
67  1, 0,
68  0, 1
69  };
70 
71 TEST(Costmap3dLayerFootprint, CSpaceTemplate)
72 {
74 
75  // Settings: 4 angular grids, no expand/spread
76  cm.setAngleResolution(4);
77  cm.setExpansion(0.0, 0.0);
79 
80  // Set example footprint
81  int footprint_offset = 0;
82  XmlRpc::XmlRpcValue footprint_xml;
83  ASSERT_TRUE(footprint_xml.fromXml(footprint_str, &footprint_offset));
84  cm.setFootprint(costmap_cspace::Polygon(footprint_xml));
85 
86  // Check local footprint
87  const costmap_cspace::Polygon polygon = cm.getFootprint();
88  ASSERT_EQ(3u + 1u, polygon.v.size());
89  ASSERT_EQ(1.5, polygon.v[0][0]);
90  ASSERT_EQ(0.0, polygon.v[0][1]);
91  ASSERT_EQ(-0.5, polygon.v[1][0]);
92  ASSERT_EQ(-0.5, polygon.v[1][1]);
93  ASSERT_EQ(-0.5, polygon.v[2][0]);
94  ASSERT_EQ(0.5, polygon.v[2][1]);
95  // Last point is same as the first point
96  ASSERT_EQ(1.5, polygon.v[3][0]);
97  ASSERT_EQ(0.0, polygon.v[3][1]);
98  ASSERT_EQ(1.5, cm.getFootprintRadius());
99 
100  // Generate CSpace pattern around the robot
101  costmap_cspace_msgs::MapMetaData3D map_info;
102  map_info.width = 3;
103  map_info.height = 3;
104  map_info.angle = 4;
105  map_info.linear_resolution = 1.0;
106  map_info.angular_resolution = M_PI / 2.0;
107  map_info.origin.orientation.w = 1.0;
108 
109  cm.setMapMetaData(map_info);
110 
111  ASSERT_EQ(static_cast<int>(std::ceil(1.5 / 1.0)), cm.getRangeMax());
112 
113  const costmap_cspace::CSpace3Cache& temp = cm.getTemplate();
114  // Check template size
115  int x, y, a;
116  int cx, cy, ca;
117  temp.getSize(x, y, a);
118  temp.getCenter(cx, cy, ca);
119  ASSERT_EQ(2 * 2 + 1, x);
120  ASSERT_EQ(2 * 2 + 1, y);
121  ASSERT_EQ(4, a);
122  ASSERT_EQ(2, cx);
123  ASSERT_EQ(2, cy);
124  ASSERT_EQ(0, ca);
125 
126  // Check generated template
127  for (int k = -ca; k < a - ca; ++k)
128  {
129  for (int j = -cy; j < y - cy; ++j)
130  {
131  for (int i = -cx; i < x - cx; ++i)
132  {
133  if (i == 0 && j == 0)
134  {
135  ASSERT_EQ(100, temp.e(i, j, k));
136  }
137  else if (i == temp_dir[k + ca][0] && j == temp_dir[k + ca][1])
138  {
139  ASSERT_EQ(100, temp.e(i, j, k));
140  }
141  else
142  {
143  ASSERT_EQ(0, temp.e(i, j, k));
144  }
145  }
146  }
147  }
148 }
149 
150 TEST(Costmap3dLayerPlain, CSpaceTemplate)
151 {
153 
154  // Settings: 4 angular grids, no expand/spread
155  cm.setAngleResolution(4);
156  cm.setExpansion(0.0, 0.0);
158 
159  // Generate CSpace pattern around the robot
160  costmap_cspace_msgs::MapMetaData3D map_info;
161  map_info.width = 1;
162  map_info.height = 1;
163  map_info.angle = 4;
164  map_info.linear_resolution = 1.0;
165  map_info.angular_resolution = M_PI / 2.0;
166  map_info.origin.orientation.w = 1.0;
167 
168  cm.setMapMetaData(map_info);
169 
170  ASSERT_EQ(0, cm.getRangeMax());
171 
172  const costmap_cspace::CSpace3Cache& temp = cm.getTemplate();
173  // Check template size
174  int x, y, a;
175  int cx, cy, ca;
176  temp.getSize(x, y, a);
177  temp.getCenter(cx, cy, ca);
178  ASSERT_EQ(1, x);
179  ASSERT_EQ(1, y);
180  ASSERT_EQ(4, a);
181  ASSERT_EQ(0, cx);
182  ASSERT_EQ(0, cy);
183  ASSERT_EQ(0, ca);
184 
185  // Check generated template
186  for (int k = -ca; k < a - ca; ++k)
187  {
188  ASSERT_EQ(100, temp.e(0, 0, k));
189  }
190 }
191 
192 TEST(Costmap3dLayerFootprint, CSpaceGenerate)
193 {
195 
196  // Set example footprint
197  int footprint_offset = 0;
198  XmlRpc::XmlRpcValue footprint_xml;
199  footprint_xml.fromXml(footprint_str, &footprint_offset);
200  cm.setFootprint(costmap_cspace::Polygon(footprint_xml));
201 
202  // Settings: 4 angular grids, no expand/spread
203  cm.setAngleResolution(4);
204  cm.setExpansion(0.0, 0.0);
206 
207  // Generate sample map
208  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
209  map->info.width = 7;
210  map->info.height = 7;
211  map->info.resolution = 1.0;
212  map->info.origin.orientation.w = 1.0;
213  map->data.resize(map->info.width * map->info.height);
214 
215  // Apply empty map
216  cm.setBaseMap(map);
217 
218  for (int k = 0; k < cm.getAngularGrid(); ++k)
219  {
220  for (size_t j = 0; j < map->info.height; ++j)
221  {
222  for (size_t i = 0; i < map->info.width; ++i)
223  {
224  const int cost = cm.getMapOverlay()->getCost(i, j, k);
225  // All grid must be unknown at initialization
226  ASSERT_EQ(0, cost);
227  }
228  }
229  }
230 
231  for (auto& g : map->data)
232  {
233  g = 100;
234  }
235  cm.setBaseMap(map);
236  for (int k = 0; k < cm.getAngularGrid(); ++k)
237  {
238  for (size_t j = 0; j < map->info.height; ++j)
239  {
240  for (size_t i = 0; i < map->info.width; ++i)
241  {
242  const int cost = cm.getMapOverlay()->getCost(i, j, k);
243  // All grid must be 100
244  ASSERT_EQ(100, cost);
245  }
246  }
247  }
248 
249  // C shape wall in the map
250  for (auto& g : map->data)
251  {
252  g = 0;
253  }
254  for (size_t i = map->info.width / 2 - 2; i < map->info.width / 2 + 2; ++i)
255  {
256  map->data[i + (map->info.height / 2 - 2) * map->info.width] = 100;
257  map->data[i + (map->info.height / 2 + 2) * map->info.width] = 100;
258  }
259  for (size_t i = map->info.height / 2 - 2; i < map->info.height / 2 + 2; ++i)
260  {
261  map->data[(map->info.width / 2 - 2) + i * map->info.width] = 100;
262  }
263  cm.setBaseMap(map);
264  for (int k = 0; k < cm.getAngularGrid(); ++k)
265  {
266  for (size_t j = 0; j < map->info.height; ++j)
267  {
268  for (size_t i = 0; i < map->info.width; ++i)
269  {
270  const int cost = cm.getMapOverlay()->getCost(i, j, k);
271 
272  // Offset according to the template shape
273  int cost_offset = 0;
274  const int i_offset = static_cast<int>(i) - temp_dir[k][0];
275  const int j_offset = static_cast<int>(j) - temp_dir[k][1];
276  if (static_cast<size_t>(i_offset) < map->info.width &&
277  static_cast<size_t>(j_offset) < map->info.height)
278  {
279  cost_offset = map->data[i_offset + j_offset * map->info.width];
280  }
281  if (map->data[i + j * map->info.width] == 100 || cost_offset == 100)
282  {
283  ASSERT_EQ(100, cost);
284  }
285  else
286  {
287  ASSERT_EQ(0, cost);
288  }
289  }
290  }
291  }
292 }
293 
294 TEST(Costmap3dLayerFootprint, CSpaceExpandSpread)
295 {
297 
298  // Set example footprint
299  int footprint_offset = 0;
300  XmlRpc::XmlRpcValue footprint_xml;
301  footprint_xml.fromXml(footprint_str, &footprint_offset);
302  cm.setFootprint(costmap_cspace::Polygon(footprint_xml));
303 
304  // Settings: 4 angular grids, expand 1.0, spread 2.0
305  const float expand = 1.0;
306  const float spread = 2.0;
307  cm.setAngleResolution(4);
308  cm.setExpansion(expand, spread);
310 
311  // Generate sample map
312  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
313  map->info.width = 9;
314  map->info.height = 9;
315  map->info.resolution = 1.0;
316  map->info.origin.orientation.w = 1.0;
317  map->data.resize(map->info.width * map->info.height);
318 
319  const int max_cost = 80;
320  map->data[map->info.width / 2 + (map->info.height / 2) * map->info.width] = max_cost;
321  map->data[map->info.width / 2 + 1 + (map->info.height / 2) * map->info.width] = -1;
322 
323  cm.setBaseMap(map);
324  for (int k = 0; k < cm.getAngularGrid(); ++k)
325  {
326  const int i_center = map->info.width / 2;
327  const int j_center = map->info.height / 2;
328  const int i_center2 = map->info.width / 2 + temp_dir[k][0];
329  const int j_center2 = map->info.height / 2 + temp_dir[k][1];
330 
331  for (size_t j = 0; j < map->info.height; ++j)
332  {
333  for (size_t i = 0; i < map->info.width; ++i)
334  {
335  const int cost = cm.getMapOverlay()->getCost(i, j, k);
336  const float dist1 = std::hypot(static_cast<int>(i) - i_center, static_cast<int>(j) - j_center);
337  const float dist2 = std::hypot(static_cast<int>(i) - i_center2, static_cast<int>(j) - j_center2);
338  const float dist = std::min(dist1, dist2);
339 
340  if (i == static_cast<size_t>(i_center + 1) &&
341  j == static_cast<size_t>(j_center))
342  {
343  // Unknown cell must be unknown
344  EXPECT_EQ(-1, cost);
345  }
346  else if (dist <= expand)
347  {
348  // Inside expand range must be max_cost
349  EXPECT_EQ(max_cost, cost);
350  }
351  else if (dist <= expand + spread)
352  {
353  // Between expand and spread must be intermidiate value
354  EXPECT_NE(0, cost);
355  EXPECT_NE(100, cost);
356  }
357  else if (dist > expand + spread + 1)
358  {
359  // Outside must be zero
360  // Since the template is calculated by the precised footprint not by the grid,
361  // tolerance of test (+1) is needed.
362  EXPECT_EQ(0, cost);
363  }
364  }
365  }
366  }
367 }
368 
369 TEST(Costmap3dLayerFootprint, CSpaceOverwrite)
370 {
373 
374  // Set example footprint
375  int footprint_offset = 0;
376  XmlRpc::XmlRpcValue footprint_xml;
377  footprint_xml.fromXml(footprint_str, &footprint_offset);
378  costmap_cspace::Polygon footprint(footprint_xml);
379  cm_ref.setFootprint(footprint);
380  cm_base.setFootprint(footprint);
381 
382  // Settings: 4 angular grids, no expand/spread
385  cm->setExpansion(0.0, 0.0);
386  cm->setFootprint(footprint);
389  cm_over->setExpansion(0.0, 0.0);
390  cm_over->setFootprint(footprint);
391  auto cm_output = cms.addLayer<costmap_cspace::Costmap3dLayerOutput>();
392 
393  cm_ref.setAngleResolution(4);
394  cm_ref.setExpansion(0.0, 0.0);
396  cm_base.setAngleResolution(4);
397  cm_base.setExpansion(0.0, 0.0);
399 
400  // Generate two sample maps
401  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
402  map->info.width = 9;
403  map->info.height = 9;
404  map->info.resolution = 1.0;
405  map->info.origin.orientation.w = 1.0;
406  map->data.resize(map->info.width * map->info.height);
407 
408  nav_msgs::OccupancyGrid::Ptr map2(new nav_msgs::OccupancyGrid);
409  *map2 = *map;
410 
411  const int num_points_base_map = 2;
412  const int points_base_map[num_points_base_map][2] =
413  {
414  2, 3,
415  4, 4
416  };
417  for (int i = 0; i < num_points_base_map; ++i)
418  {
419  map->data[points_base_map[i][0] + points_base_map[i][1] * map->info.width] = 100;
420  }
421 
422  const int num_points_local_map = 3;
423  const int points_local_map[num_points_local_map][2] =
424  {
425  3, 4,
426  5, 3,
427  4, 4
428  };
429  for (int i = 0; i < num_points_local_map; ++i)
430  {
431  map2->data[points_local_map[i][0] + points_local_map[i][1] * map->info.width] = 100;
432  }
433 
434  // Apply base map
435  cm->setBaseMap(map);
436 
437  // Overlay local map
438  costmap_cspace_msgs::CSpace3DUpdate::Ptr updated(new costmap_cspace_msgs::CSpace3DUpdate);
439  auto cb = [&updated](
441  const costmap_cspace_msgs::CSpace3DUpdate::Ptr& update) -> bool
442  {
443  updated = update;
444  return true;
445  };
446  cm_output->setHandler(cb);
447  cm_over->processMapOverlay(map2);
448 
449  // In this case, updated map must have same size as the base map. Check it.
450  ASSERT_EQ(0u, updated->x);
451  ASSERT_EQ(0u, updated->y);
452  ASSERT_EQ(0u, updated->yaw);
453  ASSERT_EQ(map->info.width, updated->width);
454  ASSERT_EQ(map->info.height, updated->height);
455  ASSERT_EQ(static_cast<size_t>(cm_over->getAngularGrid()), updated->angle);
456 
457  // Generate reference local and base cspace map
458  cm_ref.setBaseMap(map2);
459  cm_base.setBaseMap(map);
460 
461  // Compare to confirm MAX mode
462  // note: boundary of the local map is not completely overwritten for keeping the spread effect.
463  for (int k = 0; k < cm_over->getAngularGrid(); ++k)
464  {
465  for (size_t j = cm_over->getRangeMax(); j < map->info.height - cm_over->getRangeMax(); ++j)
466  {
467  for (size_t i = cm_over->getRangeMax(); i < map->info.width - cm_over->getRangeMax(); ++i)
468  {
469  const size_t addr = ((k * map->info.height + j) * map->info.width) + i;
470  ROS_ASSERT(addr < updated->data.size());
471  const int cost = updated->data[addr];
472  const int cost_ref = cm_ref.getMapOverlay()->getCost(i, j, k);
473 
474  ASSERT_EQ(cost_ref, cost);
475  }
476  }
477  }
478  // Set MAX mode and check
479  cm_over->setAngleResolution(4);
480  cm_over->setExpansion(0.0, 0.0);
481  cm_over->setOverlayMode(costmap_cspace::MapOverlayMode::MAX);
482  costmap_cspace_msgs::CSpace3DUpdate::Ptr updated_max(new costmap_cspace_msgs::CSpace3DUpdate);
483  auto cb_max = [&updated_max](
485  const costmap_cspace_msgs::CSpace3DUpdate::Ptr& update) -> bool
486  {
487  updated_max = update;
488  return true;
489  };
490  cm_output->setHandler(cb_max);
491  cm_over->processMapOverlay(map2);
492 
493  ASSERT_EQ(0u, updated_max->x);
494  ASSERT_EQ(0u, updated_max->y);
495  ASSERT_EQ(0u, updated_max->yaw);
496  ASSERT_EQ(map->info.width, updated_max->width);
497  ASSERT_EQ(map->info.height, updated_max->height);
498  ASSERT_EQ(static_cast<size_t>(cm_over->getAngularGrid()), updated_max->angle);
499 
500  for (int k = 0; k < cm_over->getAngularGrid(); ++k)
501  {
502  for (int j = cm_over->getRangeMax(); j < static_cast<int>(map->info.height) - cm_over->getRangeMax(); ++j)
503  {
504  for (int i = cm_over->getRangeMax(); i < static_cast<int>(map->info.width) - cm_over->getRangeMax(); ++i)
505  {
506  const size_t addr = ((k * map->info.height + j) * map->info.width) + i;
507  ROS_ASSERT(addr < updated_max->data.size());
508  const int cost = updated_max->data[addr];
509  const int cost_ref = cm_ref.getMapOverlay()->getCost(i, j, k);
510  const int cost_base = cm_base.getMapOverlay()->getCost(i, j, k);
511  const int cost_max = std::max(cost_ref, cost_base);
512 
513  ASSERT_EQ(cost_max, cost);
514  }
515  }
516  }
517 }
518 
519 TEST(Costmap3dLayerFootprint, CSpaceOverlayMove)
520 {
521  // Set example footprint
522  int footprint_offset = 0;
523  XmlRpc::XmlRpcValue footprint_xml;
524  footprint_xml.fromXml(footprint_str, &footprint_offset);
525  costmap_cspace::Polygon footprint(footprint_xml);
526 
527  // Settings: 4 angular grids, no expand/spread
530  cm->setExpansion(0.0, 0.0);
531  cm->setFootprint(footprint);
534  cm_over->setExpansion(0.0, 0.0);
535  cm_over->setFootprint(footprint);
536 
537  // Generate sample map
538  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
539  map->info.width = 5;
540  map->info.height = 5;
541  map->info.resolution = 1.0;
542  map->info.origin.orientation.w = 1.0;
543  map->data.resize(map->info.width * map->info.height);
544 
545  const int max_cost = 100;
546  map->data[map->info.width / 2 + (map->info.height / 2) * map->info.width] = max_cost;
547  cm->setBaseMap(map);
548 
549  // Generate local sample map
550  nav_msgs::OccupancyGrid::Ptr map2(new nav_msgs::OccupancyGrid);
551  *map2 = *map;
552 
553  for (int xp = -1; xp <= 1; ++xp)
554  {
555  for (int yp = -1; yp <= 1; ++yp)
556  {
557  map2->info.origin.position.x = map2->info.resolution * xp;
558  map2->info.origin.position.y = map2->info.resolution * yp;
559  cm_over->processMapOverlay(map2);
560  for (int k = 0; k < cm_over->getAngularGrid(); ++k)
561  {
562  const size_t i_center = map->info.width / 2;
563  const size_t j_center = map->info.height / 2;
564  const size_t i_center2 = map->info.width / 2 + temp_dir[k][0];
565  const size_t j_center2 = map->info.height / 2 + temp_dir[k][1];
566 
567  for (size_t j = 0; j < map->info.height; ++j)
568  {
569  for (size_t i = 0; i < map->info.width; ++i)
570  {
571  const int cost = cm_over->getMapOverlay()->getCost(i, j, k);
572 
573  if ((i == i_center && j == j_center) ||
574  (i == i_center2 && j == j_center2) ||
575  (i == i_center + xp && j == j_center + yp) ||
576  (i == i_center2 + xp && j == j_center2 + yp))
577  {
578  ASSERT_EQ(max_cost, cost);
579  }
580  else
581  {
582  ASSERT_EQ(0, cost);
583  }
584  }
585  }
586  }
587  }
588  }
589 }
590 
591 TEST(Costmap3dLayerOutput, CSpaceOutOfBoundary)
592 {
593  struct TestData
594  {
595  struct Input
596  {
597  float x;
598  float y;
599  };
600  struct Expected
601  {
602  unsigned int x;
603  unsigned int y;
604  unsigned int yaw;
605  unsigned int width;
606  unsigned int height;
607  unsigned int angle;
608  };
609  std::string name;
610  Input input;
611  bool valid;
612  Expected expected;
613  };
614  const TestData dataset[] =
615  {
616  { "inside0", { 0.0, 0.0 }, true, { 0u, 0u, 0u, 2u, 2u, 4u } },
617  { "inside1", { 1.0, 0.0 }, true, { 1u, 0u, 0u, 2u, 2u, 4u } },
618  { "half-outside x0", { -1.0, 0.0 }, true, { 0u, 0u, 0u, 1u, 2u, 4u } },
619  { "half-outside x1", { 3.0, 0.0 }, true, { 3u, 0u, 0u, 1u, 2u, 4u } },
620  { "half-outside y0", { 0.0, -1.0 }, true, { 0u, 0u, 0u, 2u, 1u, 4u } },
621  { "half-outside y1", { 0.0, 3.0 }, true, { 0u, 3u, 0u, 2u, 1u, 4u } },
622  { "half-outside xy0", { -1.0, -1.0 }, true, { 0u, 0u, 0u, 1u, 1u, 4u } },
623  { "half-outside xy1", { 3.0, -1.0 }, true, { 3u, 0u, 0u, 1u, 1u, 4u } },
624  { "half-outside xy2", { 3.0, 3.0 }, true, { 3u, 3u, 0u, 1u, 1u, 4u } },
625  { "half-outside xy3", { -1.0, 3.0 }, true, { 0u, 3u, 0u, 1u, 1u, 4u } },
626  { "boundary x0", { -2.0, 0.0 }, false },
627  { "boundary x1", { 4, 0.0 }, false },
628  { "boundary y0", { 0, -2.0 }, false },
629  { "boundary y1", { 0, 4.0 }, false },
630  { "boundary xy0", { -2.0, -2.0 }, false },
631  { "boundary xy1", { 4.0, -2.0 }, false },
632  { "boundary xy2", { 4.0, 4.0 }, false },
633  { "boundary xy3", { -2.0, 4.0 }, false },
634  { "outside x0", { -3.0, 0.0 }, false },
635  { "outside x1", { 5, 0.0 }, false },
636  { "outside y0", { 0, -3.0 }, false },
637  { "outside y1", { 0, 5.0 }, false },
638  { "outside xy0", { -3.0, -3.0 }, false },
639  { "outside xy1", { 5.0, -3.0 }, false },
640  { "outside xy2", { 5.0, 5.0 }, false },
641  { "outside xy3", { -3.0, 5.0 }, false },
642  };
643 
644  for (auto& d : dataset)
645  {
646  const std::string test_name = "Case [" + d.name + "]";
647  // Settings: 4 angular grids
651  auto cm_over = cms.addLayer<costmap_cspace::Costmap3dLayerPlain>(
653  auto cm_output = cms.addLayer<costmap_cspace::Costmap3dLayerOutput>();
654 
655  // Generate two sample maps
656  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
657  map->info.width = 4;
658  map->info.height = 4;
659  map->info.resolution = 1.0;
660  map->info.origin.orientation.w = 1.0;
661  map->data.resize(map->info.width * map->info.height);
662 
663  nav_msgs::OccupancyGrid::Ptr map2(new nav_msgs::OccupancyGrid);
664  map2->info.width = 2;
665  map2->info.height = 2;
666  map2->info.resolution = 1.0;
667  map2->info.origin.orientation.w = 1.0;
668  map2->info.origin.position.x = d.input.x;
669  map2->info.origin.position.y = d.input.y;
670  map2->data.resize(map->info.width * map->info.height);
671 
672  // Apply base map
673  cm->setBaseMap(map);
674 
675  // Overlay local map
676  costmap_cspace_msgs::CSpace3DUpdate::Ptr updated;
677  auto cb = [&updated](
679  const costmap_cspace_msgs::CSpace3DUpdate::Ptr& update) -> bool
680  {
681  updated = update;
682  return true;
683  };
684  cm_output->setHandler(cb);
685  // First pass of the processing contains parent layer updates
686  cm_over->processMapOverlay(map2);
687 
688  if (d.valid)
689  {
690  ASSERT_TRUE(static_cast<bool>(updated)) << test_name;
691  EXPECT_EQ(0u, updated->x) << test_name;
692  EXPECT_EQ(0u, updated->y) << test_name;
693  EXPECT_EQ(0u, updated->yaw) << test_name;
694  EXPECT_EQ(map->info.width, updated->width) << test_name;
695  EXPECT_EQ(map->info.height, updated->height) << test_name;
696  EXPECT_EQ(4u, updated->angle) << test_name;
697  }
698  else
699  {
700  EXPECT_FALSE(static_cast<bool>(updated)) << test_name;
701  }
702 
703  // Second pass has only local updates
704  cm_over->processMapOverlay(map2);
705 
706  if (d.valid)
707  {
708  ASSERT_TRUE(static_cast<bool>(updated)) << test_name;
709  EXPECT_EQ(d.expected.x, updated->x) << test_name;
710  EXPECT_EQ(d.expected.y, updated->y) << test_name;
711  EXPECT_EQ(d.expected.yaw, updated->yaw) << test_name;
712  EXPECT_EQ(d.expected.width, updated->width) << test_name;
713  EXPECT_EQ(d.expected.height, updated->height) << test_name;
714  EXPECT_EQ(d.expected.angle, updated->angle) << test_name;
715  }
716  else
717  {
718  EXPECT_FALSE(static_cast<bool>(updated)) << test_name;
719  }
720  }
721 }
722 
723 TEST(Costmap3dLayerFootprint, CSpaceKeepUnknown)
724 {
725  // Set example footprint
726  int footprint_offset = 0;
727  XmlRpc::XmlRpcValue footprint_xml;
728  footprint_xml.fromXml(footprint_str, &footprint_offset);
729  costmap_cspace::Polygon footprint(footprint_xml);
730 
731  const size_t unknown_x = 3;
732  const size_t unknown_y = 4;
733  const size_t width = 6;
734  const size_t height = 5;
735  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
736  map->info.width = width;
737  map->info.height = height;
738  map->info.resolution = 1.0;
739  map->info.origin.orientation.w = 1.0;
740  map->data = std::vector<int8_t>(width * height, 0);
741  map->data[2 + width * 3] = 100;
742  map->data[3 + width * 3] = -1;
743 
744  nav_msgs::OccupancyGrid::Ptr map_overlay(new nav_msgs::OccupancyGrid);
745  map_overlay->info.width = width;
746  map_overlay->info.height = height;
747  map_overlay->info.resolution = 1.0;
748  map_overlay->info.origin.orientation.w = 1.0;
749  map_overlay->data = std::vector<int8_t>(width * height, 0);
750  map_overlay->data[2 + width * 4] = 100;
751  map_overlay->data[4 + width * 4] = -1;
752 
753  map->data[unknown_x + width * unknown_y] = -1;
754  map_overlay->data[unknown_x + width * unknown_y] = -1;
755 
758  cm_base1->setExpansion(0.0, 2.0);
759  cm_base1->setFootprint(footprint);
760  auto cm_normal = cms1.addLayer<costmap_cspace::Costmap3dLayerFootprint>(
762  cm_normal->setExpansion(0.0, 2.0);
763  cm_normal->setFootprint(footprint);
764  cm_normal->setKeepUnknown(false);
765  cm_base1->setBaseMap(map);
766  cm_normal->processMapOverlay(map_overlay);
767 
770  cm_base2->setExpansion(0.0, 2.0);
771  cm_base2->setFootprint(footprint);
772  auto cm_keep_uknown = cms2.addLayer<costmap_cspace::Costmap3dLayerFootprint>(
774  cm_keep_uknown->setExpansion(0.0, 2.0);
775  cm_keep_uknown->setFootprint(footprint);
776  cm_keep_uknown->setKeepUnknown(true);
777  cm_base2->setBaseMap(map);
778  cm_keep_uknown->processMapOverlay(map_overlay);
779 
780  const costmap_cspace::CSpace3DMsg::Ptr normal_result = cm_normal->getMapOverlay();
781  const costmap_cspace::CSpace3DMsg::Ptr keep_unknown_result = cm_keep_uknown->getMapOverlay();
782  for (size_t yaw = 0; yaw < normal_result->info.angle; ++yaw)
783  {
784  for (size_t y = 0; y < normal_result->info.height; ++y)
785  {
786  for (size_t x = 0; x < normal_result->info.width; ++x)
787  {
788  if ((x == unknown_x) && (y == unknown_y))
789  {
790  EXPECT_GT(normal_result->getCost(x, y, yaw), 0);
791  EXPECT_EQ(static_cast<int>(keep_unknown_result->getCost(x, y, yaw)), -1);
792  }
793  else
794  {
795  EXPECT_EQ(normal_result->getCost(x, y, yaw), keep_unknown_result->getCost(x, y, yaw))
796  << " x:" << x << " y:" << y << " yaw:" << yaw;
797  }
798  }
799  }
800  }
801 }
802 
803 TEST(Costmap3dLayerFootprint, Costmap3dLayerPlain)
804 {
805  costmap_cspace::Polygon footprint;
806  footprint.v.resize(3);
807  for (auto& p : footprint.v)
808  {
809  p[0] = p[1] = 0.0;
810  }
811 
812  const size_t unknown_x = 3;
813  const size_t unknown_y = 4;
814  const size_t width = 6;
815  const size_t height = 5;
816  nav_msgs::OccupancyGrid::Ptr map(new nav_msgs::OccupancyGrid);
817  map->info.width = width;
818  map->info.height = height;
819  map->info.resolution = 1.0;
820  map->info.origin.orientation.w = 1.0;
821  map->data = std::vector<int8_t>(width * height, 0);
822  map->data[2 + width * 3] = 100;
823  map->data[3 + width * 3] = -1;
824 
825  nav_msgs::OccupancyGrid::Ptr map_overlay(new nav_msgs::OccupancyGrid);
826  map_overlay->info.width = width;
827  map_overlay->info.height = height;
828  map_overlay->info.resolution = 1.0;
829  map_overlay->info.origin.orientation.w = 1.0;
830  map_overlay->data = std::vector<int8_t>(width * height, 0);
831  map_overlay->data[2 + width * 4] = 100;
832  map_overlay->data[4 + width * 4] = -1;
833 
834  map->data[unknown_x + width * unknown_y] = -1;
835  map_overlay->data[unknown_x + width * unknown_y] = -1;
836 
839  cm_base1->setExpansion(0.0, 2.0);
840  cm_base1->setFootprint(footprint);
841  auto cm_normal = cms1.addLayer<costmap_cspace::Costmap3dLayerFootprint>(
843  cm_normal->setExpansion(0.0, 2.0);
844  cm_normal->setFootprint(footprint);
845  cm_normal->setKeepUnknown(false);
846  cm_base1->setBaseMap(map);
847  cm_normal->processMapOverlay(map_overlay);
848 
850  auto cm_base2 = cms2.addRootLayer<costmap_cspace::Costmap3dLayerPlain>();
851  cm_base2->setExpansion(0.0, 2.0);
852  auto cm_plain = cms2.addLayer<costmap_cspace::Costmap3dLayerPlain>(
854  cm_plain->setExpansion(0.0, 2.0);
855  cm_plain->setKeepUnknown(false);
856  cm_base2->setBaseMap(map);
857  cm_plain->processMapOverlay(map_overlay);
858 
859  const costmap_cspace::CSpace3DMsg::Ptr normal_result = cm_normal->getMapOverlay();
860  const costmap_cspace::CSpace3DMsg::Ptr plain_result = cm_plain->getMapOverlay();
861  for (size_t yaw = 0; yaw < normal_result->info.angle; ++yaw)
862  {
863  for (size_t y = 0; y < normal_result->info.height; ++y)
864  {
865  for (size_t x = 0; x < normal_result->info.width; ++x)
866  {
867  EXPECT_EQ(normal_result->getCost(x, y, yaw), plain_result->getCost(x, y, yaw))
868  << " x:" << x << " y:" << y << " yaw:" << yaw;
869  EXPECT_EQ(plain_result->getCost(x, y, yaw), plain_result->getCost(x, y, 0))
870  << " x:" << x << " y:" << y << " yaw:" << yaw;
871  }
872  }
873  }
874 }
875 int main(int argc, char** argv)
876 {
877  testing::InitGoogleTest(&argc, argv);
878 
879  return RUN_ALL_TESTS();
880 }
d
void setMapMetaData(const costmap_cspace_msgs::MapMetaData3D &info)
Definition: footprint.h:129
const char temp_dir[4][2]
void setExpansion(const float linear_expand, const float linear_spread)
Definition: footprint.h:91
const std::string footprint_str("<value><array><data>"" <value><array><data>"" <value><double>1.5</double></value>"" <value><double>0.0</double></value>"" </data></array></value>"" <value><array><data>"" <value><double>-0.5</double></value>"" <value><double>-0.5</double></value>"" </data></array></value>"" <value><array><data>"" <value><double>-0.5</double></value>"" <value><double>0.5</double></value>"" </data></array></value>""</data></array></value>")
Input
const CSpace3Cache & getTemplate() const
Definition: footprint.h:125
void setOverlayMode(const MapOverlayMode overlay_mode)
Definition: base.h:288
TFSIMD_FORCE_INLINE const tfScalar & y() const
std::vector< Vec > v
Definition: polygon.h:93
TFSIMD_FORCE_INLINE tfScalar angle(const Quaternion &q1, const Quaternion &q2)
void update(const std::string &key, const XmlRpc::XmlRpcValue &v)
void setBaseMap(const nav_msgs::OccupancyGrid::ConstPtr &base_map)
Definition: base.h:299
T::Ptr addLayer(const MapOverlayMode overlay_mode=MapOverlayMode::MAX)
Definition: costmap_3d.h:79
TFSIMD_FORCE_INLINE const tfScalar & x() const
void setAngleResolution(const int ang_resolution)
Definition: base.h:283
void processMapOverlay(const nav_msgs::OccupancyGrid::ConstPtr &msg)
Definition: base.h:349
int main(int argc, char **argv)
bool fromXml(std::string const &valueXml, int *offset)
std::shared_ptr< CSpace3DMsg > Ptr
Definition: base.h:52
TEST(Costmap3dLayerFootprint, CSpaceTemplate)
#define ROS_ASSERT(cond)
void setFootprint(const Polygon footprint)
Definition: footprint.h:103
CSpace3DMsg::Ptr getMapOverlay()
Definition: base.h:378


costmap_cspace
Author(s): Atsushi Watanabe
autogenerated on Wed May 12 2021 02:20:29