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.pose = makePose(.001, .001, 0.0);
00271 c1->sensor_pose.header.frame_id = "foo";
00272 c1->cloud.points = PointVec(1);
00273 CloudPtr c2(new gu::LocalizedCloud());
00274 c2->sensor_pose.pose = makePose(25.0, 5.0, PI/2);
00275 c2->sensor_pose.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 gu::OverlayClouds o1 = gu::createCloudOverlay(info, "foo", .5, 200);
00285 gu::addCloud(&o1, c1);
00286 gu::addCloud(&o1, c2);
00287 GridConstPtr grid = gu::getGrid(o1);
00288 EXPECT_EQ (gu::UNKNOWN, grid->data[0]);
00289 EXPECT_EQ (gu::UNKNOWN, grid->data[1]);
00290 EXPECT_EQ (gu::UNOCCUPIED, grid->data[2]);
00291 EXPECT_EQ (gu::UNKNOWN, grid->data[3]);
00292 EXPECT_EQ (gu::UNKNOWN, grid->data[4]);
00293 EXPECT_EQ (gu::UNOCCUPIED, grid->data[5]);
00294 EXPECT_EQ (gu::UNKNOWN, grid->data[6]);
00295 EXPECT_EQ (gu::UNKNOWN, grid->data[9]);
00296 EXPECT_EQ (gu::OCCUPIED, grid->data[10]);
00297
00298
00299 gu::OverlayClouds o2 = gu::createCloudOverlay(info, "foo", .1, 2000);
00300 gu::addCloud(&o2, c2);
00301 gu::addCloud(&o2, c1);
00302 GridConstPtr grid2 = gu::getGrid(o2);
00303 EXPECT_EQ (gu::UNOCCUPIED, grid2->data[0]);
00304 EXPECT_EQ (gu::UNKNOWN, grid2->data[1]);
00305 EXPECT_EQ (gu::OCCUPIED, grid2->data[2]);
00306 EXPECT_EQ (gu::UNKNOWN, grid2->data[3]);
00307 EXPECT_EQ (gu::UNKNOWN, grid2->data[4]);
00308 EXPECT_EQ (gu::UNOCCUPIED, grid2->data[5]);
00309 EXPECT_EQ (gu::UNKNOWN, grid2->data[6]);
00310 EXPECT_EQ (gu::UNKNOWN, grid2->data[9]);
00311 EXPECT_EQ (gu::OCCUPIED, grid2->data[10]);
00312 EXPECT_EQ (gu::UNKNOWN, grid2->data[15]);
00313
00314
00315 gu::addCloud(&o1, c1);
00316 GridConstPtr grid3 = gu::getGrid(o1);
00317 EXPECT_EQ (gu::UNOCCUPIED, grid3->data[0]);
00318 EXPECT_EQ (gu::UNKNOWN, grid3->data[1]);
00319 EXPECT_EQ (gu::UNOCCUPIED, grid3->data[2]);
00320
00321
00322 gu::removeCloud(&o1, c1);
00323 GridConstPtr grid4 = gu::getGrid(o1);
00324 for (unsigned i=0; i<16; i++)
00325 EXPECT_EQ (grid->data[i], grid4->data[i]);
00326
00327
00328 EXPECT_EQ(grid4->header.frame_id, "foo");
00329 }
00330
00331
00332
00333
00334 void setOccupied (nm::OccupancyGrid* g, const unsigned x, const unsigned y)
00335 {
00336 g->data[gu::cellIndex(g->info, Cell(x, y))] = gu::OCCUPIED;;
00337 }
00338
00339 TEST(GridUtils, ShortestPath)
00340 {
00341 nm::MapMetaData info;
00342 info.resolution = 0.1;
00343 info.origin = makePose(0, 0, 0);
00344 info.height = 5;
00345 info.width = 5;
00346
00347 GridPtr grid(new nm::OccupancyGrid());
00348 grid->info = info;
00349 grid->data.resize(25);
00350 setOccupied(grid.get(), 1, 3);
00351 setOccupied(grid.get(), 2, 2);
00352 setOccupied(grid.get(), 2, 1);
00353 setOccupied(grid.get(), 3, 2);
00354 setOccupied(grid.get(), 3, 3);
00355 setOccupied(grid.get(), 3, 4);
00356 setOccupied(grid.get(), 4, 2);
00357
00358 Cell c1(0, 1);
00359 Cell c2(1, 4);
00360 Cell c3(2, 3);
00361 Cell c4(3, 1);
00362 Cell c5(4, 2);
00363 Cell c6(4, 3);
00364
00365 const gu::ResultPtr res=singleSourceShortestPaths(*grid, c1);
00366
00367 EXPECT_PRED2(approxEqual, 2+sqrt(2), *distance(res, c2));
00368 EXPECT_PRED2(approxEqual, 2*sqrt(2), *distance(res, c3));
00369 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res, c4));
00370 EXPECT_TRUE(!distance(res, c5));
00371 EXPECT_TRUE(!distance(res, c6));
00372
00373 Path p12, p13, p14;
00374 p12 += Cell(0, 1), Cell(0, 2), Cell(0, 3), Cell(1, 4);
00375 p13 += Cell(0, 1), Cell(1, 2), Cell(2, 3);
00376 p14 += Cell(0, 1), Cell(1, 1), Cell(2, 0), Cell(3, 1);
00377
00378 EXPECT_PRED2(samePath, p12, *extractPath(res, c2));
00379 EXPECT_PRED2(samePath, p13, *extractPath(res, c3));
00380 EXPECT_PRED2(samePath, p14, *extractPath(res, c4));
00381 EXPECT_TRUE(!extractPath(res, c5));
00382 EXPECT_TRUE(!extractPath(res, c6));
00383
00384
00385 const gu::NavigationFunction msg = gu::shortestPathResultToMessage(res);
00386 const gu::ResultPtr res2 = gu::shortestPathResultFromMessage(msg);
00387
00388 EXPECT_PRED2(approxEqual, 2+sqrt(2), *distance(res2, c2));
00389 EXPECT_PRED2(approxEqual, 2*sqrt(2), *distance(res2, c3));
00390 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res2, c4));
00391 EXPECT_TRUE(!distance(res2, c5));
00392 EXPECT_TRUE(!distance(res2, c6));
00393
00394 EXPECT_PRED2(samePath, p12, *extractPath(res2, c2));
00395 EXPECT_PRED2(samePath, p13, *extractPath(res2, c3));
00396 EXPECT_PRED2(samePath, p14, *extractPath(res2, c4));
00397 EXPECT_TRUE(!extractPath(res2, c5));
00398 EXPECT_TRUE(!extractPath(res2, c6));
00399
00400
00401
00402 const gu::TerminationCondition t1(4);
00403 const gu::ResultPtr res3 = gu::singleSourceShortestPaths(*grid, c4, t1);
00404 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res3, c1));
00405 EXPECT_TRUE(!distance(res3, c2));
00406 EXPECT_TRUE(!distance(res3, c3));
00407
00408 gu::Cells goals;
00409 goals += c3, c6;
00410 const gu::TerminationCondition t2(goals);
00411 const gu::ResultPtr res4 = gu::singleSourceShortestPaths(*grid, c4, t2);
00412 EXPECT_PRED2(approxEqual, 1+3*sqrt(2), *distance(res4, c3));
00413 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res4, c1));
00414 EXPECT_TRUE(!distance(res4, c6));
00415
00416 const gu::TerminationCondition t3(goals, 4);
00417 const gu::ResultPtr res5 = gu::singleSourceShortestPaths(*grid, c4, t3);
00418 EXPECT_PRED2(approxEqual, 1+2*sqrt(2), *distance(res5, c1));
00419 EXPECT_TRUE(!distance(res5, c2));
00420 EXPECT_TRUE(!distance(res5, c3));
00421
00422
00423
00424 boost::optional<gu::AStarResult> res6 = shortestPath(*grid, c1, c4);
00425 EXPECT_EQ (res6->second, 5);
00426
00427 boost::optional<gu::AStarResult> res7 = shortestPath(*grid, c1, c1);
00428 EXPECT_EQ (res7->second, 0);
00429
00430 boost::optional<gu::AStarResult> res8 = shortestPath(*grid, c1, c5);
00431 EXPECT_TRUE (!res8);
00432
00433 boost::optional<gu::AStarResult> res9 = shortestPath(*grid, c1, c6);
00434 EXPECT_TRUE (!res9);
00435
00436 }
00437
00438 int val (const nm::OccupancyGrid& g, const gu::coord_t x, const gu::coord_t y)
00439 {
00440 const gu::Cell c(x, y);
00441 ROS_ASSERT (gu::withinBounds(g.info, c));
00442 return g.data[cellIndex(g.info, c)];
00443 }
00444
00445 void setVal (nm::OccupancyGrid* g, const gu::coord_t x, const gu::coord_t y, const int v)
00446 {
00447 const gu::Cell c(x, y);
00448 ROS_ASSERT (gu::withinBounds(g->info, c));
00449 g->data[cellIndex(g->info, c)]=v;
00450 }
00451
00452 TEST(GridUtils, InflateObstacles)
00453 {
00454 GridPtr g(new nm::OccupancyGrid());
00455 g->info.origin = makePose(0, 0, 0);
00456 g->info.resolution = 0.5;
00457 g->info.width = 10;
00458 g->info.height = 11;
00459 g->data.resize(g->info.width*g->info.height);
00460 fill(g->data.begin(), g->data.end(), -1);
00461
00462 setVal(g.get(), 2, 2, 0);
00463 setVal(g.get(), 8, 8, 0);
00464 setVal(g.get(), 3, 6, 50);
00465 setVal(g.get(), 3, 7, 100);
00466 setVal(g.get(), 3, 4, 0);
00467
00468 GridPtr g2 = gu::inflateObstacles(*g, 1.2);
00469
00470 EXPECT_EQ (-1, val(*g2, 9, 9));
00471 EXPECT_EQ (0, val(*g2, 8, 8));
00472 EXPECT_EQ (100, val(*g2, 3, 6));
00473 EXPECT_EQ (50, val(*g2, 3, 3));
00474 EXPECT_EQ (100, val(*g2, 3, 4));
00475 EXPECT_EQ (-1, val(*g2, 1, 3));
00476
00477
00478 }
00479
00480
00481 TEST(GridUtils, CombineGrids)
00482 {
00483 GridPtr g1(new nm::OccupancyGrid());
00484 GridPtr g2(new nm::OccupancyGrid());
00485
00486 g1->info.origin = makePose(2, 1, 0);
00487 g2->info.origin = makePose(3, 0, PI/4);
00488 g1->info.resolution = 0.5;
00489 g2->info.resolution = 0.5;
00490 g1->info.height = 4;
00491 g1->info.width = 6;
00492 g2->info.height = 4;
00493 g2->info.width = 4;
00494 g1->data.resize(24);
00495 g2->data.resize(16);
00496
00497 fill(g1->data.begin(), g1->data.end(), -1);
00498 fill(g2->data.begin(), g2->data.end(), -1);
00499
00500 setVal(g2.get(), 1, 0, 0);
00501 setVal(g2.get(), 0, 3, 50);
00502 setVal(g2.get(), 2, 0, 42);
00503 setVal(g2.get(), 0, 2, 11);
00504 setVal(g2.get(), 3, 2, 0);
00505 setVal(g2.get(), 3, 0, 110);
00506
00507 setVal(g1.get(), 5, 3, 100);
00508 setVal(g1.get(), 3, 0, 24);
00509 setVal(g1.get(), 0, 0, 66);
00510 setVal(g1.get(), 4, 0, 90);
00511
00512
00513
00514 vector<GridConstPtr> grids;
00515 grids += g1, g2;
00516 GridPtr combined = gu::combineGrids(grids, g1->info.resolution/2.0);
00517
00518 EXPECT_PRED2(approxEqual, 3-sqrt(2), combined->info.origin.position.x);
00519 EXPECT_PRED2(approxEqual, 0, combined->info.origin.position.y);
00520 EXPECT_EQ(0.25, combined->info.resolution);
00521 EXPECT_EQ(round((2+sqrt(2))/0.25), combined->info.width);
00522 EXPECT_EQ(12u, combined->info.height);
00523
00524
00525
00526 EXPECT_EQ(-1, val(*combined, 11, 2));
00527 EXPECT_EQ(-1, val(*combined, 2, 0));
00528 EXPECT_EQ(-1, val(*combined, 5, 0));
00529 EXPECT_EQ(0, val(*combined, 7, 2));
00530 EXPECT_EQ(66, val(*combined, 1, 6));
00531 EXPECT_EQ(100, val(*combined, 11, 11));
00532 EXPECT_EQ(-1, val(*combined, 2, 11));
00533 EXPECT_EQ(11, val(*combined, 3, 2));
00534 EXPECT_EQ(42, val(*combined, 7, 4));
00535 EXPECT_EQ(66, val(*combined, 2, 4));
00536 EXPECT_EQ(0, val(*combined, 6, 8));
00537 EXPECT_EQ(90, val(*combined, 10, 5));
00538
00539
00540 }
00541
00542 int main (int argc, char** argv)
00543 {
00544 testing::InitGoogleTest(&argc, argv);
00545 return RUN_ALL_TESTS();
00546 }