00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <occupancy_grid_utils/ray_tracer.h>
00031 #include <occupancy_grid_utils/exceptions.h>
00032 #include <occupancy_grid_utils/shortest_path.h>
00033 #include <occupancy_grid_utils/combine_grids.h>
00034 #include <gtest/gtest.h>
00035 #include <tf/transform_datatypes.h>
00036 #include <ros/ros.h>
00037 #include <iostream>
00038 #include <boost/foreach.hpp>
00039 #include <boost/bind.hpp>
00040 #include <boost/assign.hpp>
00041 #include <cmath>
00042
00043 namespace gm=geometry_msgs;
00044 namespace nm=nav_msgs;
00045 namespace gu=occupancy_grid_utils;
00046
00047 using std::ostream;
00048 using gu::Cell;
00049 using std::abs;
00050 using boost::bind;
00051 using std::vector;
00052 using boost::assign::operator+=;
00053 using std::operator<<;
00054
00055 const double PI = 3.14159265;
00056
00057 const double TOL=1e-6;
00058
00059
00060 typedef vector<Cell> Path;
00061 typedef boost::shared_ptr<nm::OccupancyGrid> GridPtr;
00062 typedef boost::shared_ptr<nm::OccupancyGrid const> GridConstPtr;
00063
00064 bool samePath (const Path& p1, const Path& p2)
00065 {
00066 if (p1.size()!=p2.size())
00067 return false;
00068 for (unsigned i=0; i<p1.size(); i++)
00069 if (!(p1[i]==p2[i]))
00070 return false;
00071 return true;
00072 }
00073
00074 bool approxEqual (const double x, const double y)
00075 {
00076 return abs(x-y)<TOL;
00077 }
00078
00079
00080
00081 namespace geometry_msgs
00082 {
00083
00084 bool operator== (const Polygon& p1, const Polygon& p2)
00085 {
00086 if (p1.points.size() != p2.points.size())
00087 return false;
00088 for (unsigned i=0; i<p1.points.size(); i++)
00089 if (!approxEqual(p1.points[i].x, p2.points[i].x) ||
00090 !approxEqual(p1.points[i].y, p2.points[i].y) ||
00091 !approxEqual(p1.points[i].z, p2.points[i].z))
00092 return false;
00093 return true;
00094 }
00095
00096 }
00097
00098
00099 template <class T>
00100 ostream& operator<< (ostream& str, const vector<T>& s)
00101 {
00102 str << "(";
00103 BOOST_FOREACH (const T& x, s)
00104 str << x << " ";
00105 str << ")";
00106 return str;
00107 }
00108
00109
00110
00111 ostream& operator<< (ostream& str, const gm::Point& p)
00112 {
00113 str << "(" << p.x << ", " << p.y << ")";
00114 return str;
00115 }
00116
00117 gm::Point makePoint (const double x, const double y)
00118 {
00119 gm::Point p;
00120 p.x = x;
00121 p.y = y;
00122 return p;
00123 }
00124
00125 gm::Point32 makePoint32 (const double x, const double y)
00126 {
00127 gm::Point32 p;
00128 p.x = x;
00129 p.y = y;
00130 return p;
00131 }
00132
00133 double norm (const double x, const double y)
00134 {
00135 return sqrt(x*x + y*y);
00136 }
00137
00138 struct CloseTo
00139 {
00140 CloseTo(double res) : res(res) {}
00141 bool operator() (const gm::Point& p, const gm::Point& p2)
00142 {
00143 const double dx=p2.x-p.x;
00144 const double dy=p2.y-p.y;
00145 return norm(dx, dy) < res*sqrt(2);
00146 }
00147 const double res;
00148 };
00149
00150 double angle (const double x1, const double y1, const double x2, const double y2)
00151 {
00152 const double ip = x1*x2 + y1*y2;
00153 return acos(ip/(norm(x1, y1)*norm(x2, y2)));
00154 }
00155
00156 gm::Pose makePose (const double x, const double y, const double theta)
00157 {
00158 gm::Pose p;
00159 p.position.x = x;
00160 p.position.y = y;
00161 p.orientation = tf::createQuaternionMsgFromYaw(theta);
00162 return p;
00163 }
00164
00165
00166
00167
00168 TEST(GridUtils, CoordinateConversions)
00169 {
00170
00171 nm::MapMetaData info;
00172 info.resolution = 0.1;
00173 info.origin = makePose(2, -1, PI/4);
00174 info.height = 50;
00175 info.width = 80;
00176
00177
00178 EXPECT_EQ (804u, gu::cellIndex(info, Cell(4, 10)));
00179 EXPECT_EQ (Cell(8, 1), gu::pointCell(info, makePoint(2.5, -0.35)));
00180 EXPECT_EQ (88u, gu::pointIndex(info, makePoint(2.5, -0.35)));
00181 EXPECT_EQ (Cell(-8, 7), gu::pointCell(info, makePoint(1, -1)));
00182 EXPECT_THROW (gu::pointIndex(info, makePoint(1, -1)), gu::CellOutOfBoundsException);
00183 EXPECT_THROW (gu::cellIndex(info, Cell(100, 100)), gu::CellOutOfBoundsException);
00184
00185
00186 const double side=sqrt(2)/2;
00187 gm::Polygon expected;
00188 expected.points.resize(4);
00189 expected.points[0].x = 2 + .1*side;
00190 expected.points[0].y = -1 + .3*side;
00191 expected.points[1].x = 2;
00192 expected.points[1].y = -1 + .4*side;
00193 expected.points[2].x = 2 + .1*side;
00194 expected.points[2].y = -1 + .5*side;
00195 expected.points[3].x = 2 + .2*side;
00196 expected.points[3].y = -1 + .4*side;
00197 EXPECT_EQ(expected, cellPolygon(info, Cell(2, 1)));
00198
00199
00200 }
00201
00202
00203 gm::Point lastPoint (const nm::MapMetaData& info, const gu::RayTraceIterRange& r)
00204 {
00205 boost::optional<gm::Point> last_point;
00206 BOOST_FOREACH (const Cell& c, r) {
00207 last_point = gu::cellCenter(info, c);
00208 }
00209 ROS_ASSERT(last_point);
00210 return *last_point;
00211 }
00212
00213 TEST(GridUtils, RayTrace)
00214 {
00215 const double res=0.2;
00216 nm::MapMetaData info;
00217 info.resolution = res;
00218 info.origin = makePose(3, 1, PI/2);
00219 info.height = 100;
00220 info.width = 50;
00221
00222 gu::RayTraceIterRange r = gu::rayTrace(info, makePoint(1, 2), makePoint(2, 5));
00223 gm::Point last_point = lastPoint(info, r);
00224
00225 EXPECT_PRED2 (CloseTo(res), makePoint(2, 5), last_point);
00226 EXPECT_PRED2 (CloseTo(res), makePoint(1, 2), gu::cellCenter(info, *(r.first)));
00227 EXPECT_EQ (16, std::distance(r.first, r.second));
00228
00229
00230 nm::MapMetaData info2=info;
00231 info2.origin = makePose(1, 3, PI/-2);
00232 info.height = 50;
00233 info.width = 100;
00234
00235 gu::RayTraceIterRange r2 = gu::rayTrace(info2, makePoint(2, 1), makePoint(5, 2));
00236 gm::Point lp2 = lastPoint(info2, r2);
00237
00238 EXPECT_PRED2 (CloseTo(res), makePoint(5, 2), lp2);
00239 EXPECT_PRED2 (CloseTo(res), makePoint(2, 1), gu::cellCenter(info2, *(r2.first)));
00240 EXPECT_EQ (16, std::distance(r2.first, r2.second));
00241
00242
00243
00244 EXPECT_THROW(gu::rayTrace(info, makePoint(1, 2), makePoint(4, 2)), gu::PointOutOfBoundsException);
00245 gu::RayTraceIterRange r3 = gu::rayTrace(info, makePoint(1, 2), makePoint(4, 2), true);
00246 gm::Point lp3 = lastPoint(info, r3);
00247 EXPECT_PRED2 (CloseTo(res), makePoint(3, 2), lp3);
00248 EXPECT_PRED2 (CloseTo(res), makePoint(1, 2), gu::cellCenter(info, *(r.first)));
00249 EXPECT_EQ (10, std::distance(r3.first, r3.second));
00250 EXPECT_THROW(gu::rayTrace(info, makePoint(4, 2), makePoint(1, 2), true), gu::PointOutOfBoundsException);
00251 }
00252
00253
00254 TEST(GridUtils, GridOverlay)
00255 {
00256
00257 const double res=10.0;
00258 nm::MapMetaData info;
00259 info.resolution = res;
00260 info.origin = makePose(0, 0, 0);
00261 info.height = 4;
00262 info.width = 4;
00263
00264
00265 typedef vector<gm::Point32> PointVec;
00266 typedef boost::shared_ptr<gu::LocalizedCloud const> CloudConstPtr;
00267 typedef boost::shared_ptr<gu::LocalizedCloud> CloudPtr;
00268
00269 CloudPtr c1(new gu::LocalizedCloud());
00270 c1->sensor_pose = makePose(.001, .001, 0.0);
00271 c1->header.frame_id = "foo";
00272 c1->cloud.points = PointVec(1);
00273 CloudPtr c2(new gu::LocalizedCloud());
00274 c2->sensor_pose = makePose(25.0, 5.0, PI/2);
00275 c2->header.frame_id = "foo";
00276 c2->cloud.points = PointVec(4);
00277 c1->cloud.points[0] = makePoint32(20, 20);
00278 c2->cloud.points[0] = makePoint32(20, 0);
00279 c2->cloud.points[1] = makePoint32(45, 35);
00280 c2->cloud.points[2] = makePoint32(0, 1000);
00281 c2->cloud.points[3] = makePoint32(-1, -1);
00282
00283
00284 nm::OccupancyGrid fake_grid;
00285 fake_grid.info = info;
00286 gu::OverlayClouds o1 = gu::createCloudOverlay(fake_grid, "foo", .5, 200);
00287 gu::addCloud(&o1, c1);
00288 gu::addCloud(&o1, c2);
00289 GridConstPtr grid = gu::getGrid(o1);
00290 EXPECT_EQ (gu::UNKNOWN, grid->data[0]);
00291 EXPECT_EQ (gu::UNKNOWN, grid->data[1]);
00292 EXPECT_EQ (gu::UNOCCUPIED, grid->data[2]);
00293 EXPECT_EQ (gu::UNKNOWN, grid->data[3]);
00294 EXPECT_EQ (gu::UNKNOWN, grid->data[4]);
00295 EXPECT_EQ (gu::UNOCCUPIED, grid->data[5]);
00296 EXPECT_EQ (gu::UNKNOWN, grid->data[6]);
00297 EXPECT_EQ (gu::UNKNOWN, grid->data[9]);
00298 EXPECT_EQ (gu::OCCUPIED, grid->data[10]);
00299
00300
00301 gu::OverlayClouds o2 = gu::createCloudOverlay(fake_grid, "foo", .1, 2000);
00302 gu::addCloud(&o2, c2);
00303 gu::addCloud(&o2, c1);
00304 GridConstPtr grid2 = gu::getGrid(o2);
00305 EXPECT_EQ (gu::UNOCCUPIED, grid2->data[0]);
00306 EXPECT_EQ (gu::UNKNOWN, grid2->data[1]);
00307 EXPECT_EQ (gu::OCCUPIED, grid2->data[2]);
00308 EXPECT_EQ (gu::UNKNOWN, grid2->data[3]);
00309 EXPECT_EQ (gu::UNKNOWN, grid2->data[4]);
00310 EXPECT_EQ (gu::UNOCCUPIED, grid2->data[5]);
00311 EXPECT_EQ (gu::UNKNOWN, grid2->data[6]);
00312 EXPECT_EQ (gu::UNKNOWN, grid2->data[9]);
00313 EXPECT_EQ (gu::OCCUPIED, grid2->data[10]);
00314 EXPECT_EQ (gu::UNKNOWN, grid2->data[15]);
00315
00316
00317 gu::addCloud(&o1, c1);
00318 GridConstPtr grid3 = gu::getGrid(o1);
00319 EXPECT_EQ (gu::UNOCCUPIED, grid3->data[0]);
00320 EXPECT_EQ (gu::UNKNOWN, grid3->data[1]);
00321 EXPECT_EQ (gu::UNOCCUPIED, grid3->data[2]);
00322
00323
00324 gu::removeCloud(&o1, c1);
00325 GridConstPtr grid4 = gu::getGrid(o1);
00326 for (unsigned i=0; i<16; i++)
00327 EXPECT_EQ (grid->data[i], grid4->data[i]);
00328
00329
00330 EXPECT_EQ(grid4->header.frame_id, "foo");
00331 }
00332
00333
00334
00335
00336 void setOccupied (nm::OccupancyGrid* g, const unsigned x, const unsigned y)
00337 {
00338 g->data[gu::cellIndex(g->info, Cell(x, y))] = gu::OCCUPIED;;
00339 }
00340
00341 TEST(GridUtils, ShortestPath)
00342 {
00343 nm::MapMetaData info;
00344 info.resolution = 0.1;
00345 info.origin = makePose(0, 0, 0);
00346 info.height = 5;
00347 info.width = 5;
00348
00349 GridPtr grid(new nm::OccupancyGrid());
00350 grid->info = info;
00351 grid->data.resize(25);
00352 setOccupied(grid.get(), 1, 3);
00353 setOccupied(grid.get(), 2, 2);
00354 setOccupied(grid.get(), 2, 1);
00355 setOccupied(grid.get(), 3, 2);
00356 setOccupied(grid.get(), 3, 3);
00357 setOccupied(grid.get(), 3, 4);
00358 setOccupied(grid.get(), 4, 2);
00359
00360 Cell c1(0, 1);
00361 Cell c2(1, 4);
00362 Cell c3(2, 3);
00363 Cell c4(3, 1);
00364 Cell c5(4, 2);
00365 Cell c6(4, 3);
00366
00367 const gu::ResultPtr res=singleSourceShortestPaths(*grid, c1);
00368
00369 EXPECT_PRED2(approxEqual, 2+sqrt(2), *distance(res, c2));
00370 EXPECT_PRED2(approxEqual, 2*sqrt(2), *distance(res, c3));
00371 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res, c4));
00372 EXPECT_TRUE(!distance(res, c5));
00373 EXPECT_TRUE(!distance(res, c6));
00374
00375 Path p12, p13, p14;
00376 p12 += Cell(0, 1), Cell(0, 2), Cell(0, 3), Cell(1, 4);
00377 p13 += Cell(0, 1), Cell(1, 2), Cell(2, 3);
00378 p14 += Cell(0, 1), Cell(1, 1), Cell(2, 0), Cell(3, 1);
00379
00380 EXPECT_PRED2(samePath, p12, *extractPath(res, c2));
00381 EXPECT_PRED2(samePath, p13, *extractPath(res, c3));
00382 EXPECT_PRED2(samePath, p14, *extractPath(res, c4));
00383 EXPECT_TRUE(!extractPath(res, c5));
00384 EXPECT_TRUE(!extractPath(res, c6));
00385
00386
00387 const gu::NavigationFunction msg = gu::shortestPathResultToMessage(res);
00388 const gu::ResultPtr res2 = gu::shortestPathResultFromMessage(msg);
00389
00390 EXPECT_PRED2(approxEqual, 2+sqrt(2), *distance(res2, c2));
00391 EXPECT_PRED2(approxEqual, 2*sqrt(2), *distance(res2, c3));
00392 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res2, c4));
00393 EXPECT_TRUE(!distance(res2, c5));
00394 EXPECT_TRUE(!distance(res2, c6));
00395
00396 EXPECT_PRED2(samePath, p12, *extractPath(res2, c2));
00397 EXPECT_PRED2(samePath, p13, *extractPath(res2, c3));
00398 EXPECT_PRED2(samePath, p14, *extractPath(res2, c4));
00399 EXPECT_TRUE(!extractPath(res2, c5));
00400 EXPECT_TRUE(!extractPath(res2, c6));
00401
00402
00403
00404 const gu::TerminationCondition t1(4);
00405 const gu::ResultPtr res3 = gu::singleSourceShortestPaths(*grid, c4, t1);
00406 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res3, c1));
00407 EXPECT_TRUE(!distance(res3, c2));
00408 EXPECT_TRUE(!distance(res3, c3));
00409
00410 gu::Cells goals;
00411 goals += c3, c6;
00412 const gu::TerminationCondition t2(goals);
00413 const gu::ResultPtr res4 = gu::singleSourceShortestPaths(*grid, c4, t2);
00414 EXPECT_PRED2(approxEqual, 1+3*sqrt(2), *distance(res4, c3));
00415 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res4, c1));
00416 EXPECT_TRUE(!distance(res4, c6));
00417
00418 const gu::TerminationCondition t3(goals, 4);
00419 const gu::ResultPtr res5 = gu::singleSourceShortestPaths(*grid, c4, t3);
00420 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res5, c1));
00421 EXPECT_TRUE(!distance(res5, c2));
00422 EXPECT_TRUE(!distance(res5, c3));
00423
00424
00425
00426 boost::optional<gu::AStarResult> res6 = shortestPath(*grid, c1, c4);
00427 EXPECT_EQ (res6->second, 5);
00428
00429 boost::optional<gu::AStarResult> res7 = shortestPath(*grid, c1, c1);
00430 EXPECT_EQ (res7->second, 0);
00431
00432 boost::optional<gu::AStarResult> res8 = shortestPath(*grid, c1, c5);
00433 EXPECT_TRUE (!res8);
00434
00435 boost::optional<gu::AStarResult> res9 = shortestPath(*grid, c1, c6);
00436 EXPECT_TRUE (!res9);
00437
00438 }
00439
00440 int val (const nm::OccupancyGrid& g, const gu::coord_t x, const gu::coord_t y)
00441 {
00442 const gu::Cell c(x, y);
00443 ROS_ASSERT (gu::withinBounds(g.info, c));
00444 return g.data[cellIndex(g.info, c)];
00445 }
00446
00447 void setVal (nm::OccupancyGrid* g, const gu::coord_t x, const gu::coord_t y, const int v)
00448 {
00449 const gu::Cell c(x, y);
00450 ROS_ASSERT (gu::withinBounds(g->info, c));
00451 g->data[cellIndex(g->info, c)]=v;
00452 }
00453
00454 TEST(GridUtils, InflateObstacles)
00455 {
00456 GridPtr g(new nm::OccupancyGrid());
00457 g->info.origin = makePose(0, 0, 0);
00458 g->info.resolution = 0.5;
00459 g->info.width = 10;
00460 g->info.height = 11;
00461 g->data.resize(g->info.width*g->info.height);
00462 fill(g->data.begin(), g->data.end(), -1);
00463
00464 setVal(g.get(), 2, 2, 0);
00465 for (unsigned x=7; x<10; x++) {
00466 for (unsigned y=7; y<11; y++) {
00467 setVal(g.get(), x, y, 0);
00468 }
00469 }
00470
00471 setVal(g.get(), 3, 6, 50);
00472 setVal(g.get(), 3, 7, 100);
00473 setVal(g.get(), 3, 4, 0);
00474
00475 GridPtr g2 = gu::inflateObstacles(*g, 1.2, false);
00476
00477 EXPECT_EQ (100, val(*g2, 3, 6));
00478 EXPECT_EQ (50, val(*g2, 3, 4));
00479 EXPECT_EQ (100, val(*g2, 3, 5));
00480 EXPECT_EQ (-1, val(*g2, 1, 3));
00481 EXPECT_EQ (-1, val(*g2, 7, 7));
00482 EXPECT_EQ (0, val(*g2, 9, 9));
00483
00484
00485 }
00486
00487
00488 TEST(GridUtils, CombineGrids)
00489 {
00490 GridPtr g1(new nm::OccupancyGrid());
00491 GridPtr g2(new nm::OccupancyGrid());
00492
00493 g1->info.origin = makePose(2, 1, 0);
00494 g2->info.origin = makePose(3, 0, PI/4);
00495 g1->info.resolution = 0.5;
00496 g2->info.resolution = 0.5;
00497 g1->info.height = 4;
00498 g1->info.width = 6;
00499 g2->info.height = 4;
00500 g2->info.width = 4;
00501 g1->data.resize(24);
00502 g2->data.resize(16);
00503
00504 fill(g1->data.begin(), g1->data.end(), -1);
00505 fill(g2->data.begin(), g2->data.end(), -1);
00506
00507 setVal(g2.get(), 1, 0, 0);
00508 setVal(g2.get(), 0, 3, 50);
00509 setVal(g2.get(), 2, 0, 42);
00510 setVal(g2.get(), 0, 2, 11);
00511 setVal(g2.get(), 3, 2, 0);
00512 setVal(g2.get(), 3, 0, 110);
00513
00514 setVal(g1.get(), 5, 3, 100);
00515 setVal(g1.get(), 3, 0, 24);
00516 setVal(g1.get(), 0, 0, 66);
00517 setVal(g1.get(), 4, 0, 90);
00518
00519
00520
00521 vector<GridConstPtr> grids;
00522 grids += g1, g2;
00523 GridPtr combined = gu::combineGrids(grids, g1->info.resolution/2.0);
00524
00525 EXPECT_PRED2(approxEqual, 3-sqrt(2), combined->info.origin.position.x);
00526 EXPECT_PRED2(approxEqual, 0, combined->info.origin.position.y);
00527 EXPECT_EQ(0.25, combined->info.resolution);
00528 EXPECT_EQ(round((2+sqrt(2))/0.25), combined->info.width);
00529 EXPECT_EQ(12u, combined->info.height);
00530
00531
00532
00533 EXPECT_EQ(-1, val(*combined, 11, 2));
00534 EXPECT_EQ(-1, val(*combined, 2, 0));
00535 EXPECT_EQ(-1, val(*combined, 5, 0));
00536 EXPECT_EQ(0, val(*combined, 7, 2));
00537 EXPECT_EQ(66, val(*combined, 1, 6));
00538 EXPECT_EQ(100, val(*combined, 11, 11));
00539 EXPECT_EQ(-1, val(*combined, 2, 11));
00540 EXPECT_EQ(11, val(*combined, 3, 2));
00541 EXPECT_EQ(42, val(*combined, 7, 4));
00542 EXPECT_EQ(66, val(*combined, 2, 4));
00543 EXPECT_EQ(0, val(*combined, 6, 8));
00544 EXPECT_EQ(90, val(*combined, 10, 5));
00545
00546
00547 }
00548
00549 int main (int argc, char** argv)
00550 {
00551 testing::InitGoogleTest(&argc, argv);
00552 return RUN_ALL_TESTS();
00553 }