test_grid_utils.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
00008  *     * Redistributions of source code must retain the above copyright
00009  *       notice, this list of conditions and the following disclaimer.
00010  *     * Redistributions in binary form must reproduce the above copyright
00011  *       notice, this list of conditions and the following disclaimer in the
00012  *       documentation and/or other materials provided with the distribution.
00013  *     * Neither the name of the Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       this software without specific prior written permission.
00016  *
00017  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
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 <occupancy_grid_utils/geometry.h>
00035 #include <gtest/gtest.h>
00036 #include <tf/transform_datatypes.h>
00037 #include <ros/ros.h>
00038 #include <iostream>
00039 #include <boost/foreach.hpp>
00040 #include <boost/bind.hpp>
00041 #include <boost/assign.hpp>
00042 #include <cmath>
00043 
00044 namespace gm=geometry_msgs;
00045 namespace nm=nav_msgs;
00046 namespace gu=occupancy_grid_utils;
00047 namespace sm=sensor_msgs;
00048 
00049 using std::ostream;
00050 using gu::Cell;
00051 using std::abs;
00052 using boost::bind;
00053 using std::vector;
00054 using std::set;
00055 using boost::assign::operator+=;
00056 using std::operator<<;
00057 
00058 const double PI = 3.14159265;
00059 
00060 const double TOL=1e-6;
00061 
00062 
00063 typedef vector<Cell> Path;
00064 typedef set<Cell> Cells;
00065 typedef boost::shared_ptr<nm::OccupancyGrid> GridPtr;
00066 typedef boost::shared_ptr<nm::OccupancyGrid const> GridConstPtr;
00067 
00068 bool samePath (const Path& p1, const Path& p2)
00069 {
00070   if (p1.size()!=p2.size())
00071     return false;
00072   for (unsigned i=0; i<p1.size(); i++)
00073     if (!(p1[i]==p2[i]))
00074       return false;
00075   return true;
00076 }
00077 
00078 void setOccupied (nm::OccupancyGrid* g, const unsigned x, const unsigned y)
00079 {
00080   g->data[gu::cellIndex(g->info, Cell(x, y))] = gu::OCCUPIED;;
00081 }
00082 
00083 void setOccupied (GridPtr g, const unsigned x, const unsigned y)
00084 {
00085   setOccupied(g.get(), x, y);
00086 }
00087 
00088 template <class T>
00089 bool equalSets (const set<T>& s1, const set<T>& s2)
00090 {
00091   BOOST_FOREACH (const T& x, s1) 
00092   {
00093     if (s2.find(x)==s2.end())
00094       return false;
00095   }
00096   BOOST_FOREACH (const T& x, s2)
00097   {
00098     if (s1.find(x)==s1.end())
00099       return false;
00100   }
00101   return true;
00102 }
00103 
00104 bool approxEqual (const double x, const double y)
00105 {
00106   return abs(x-y)<TOL;
00107 }
00108 
00109 
00110 namespace geometry_msgs
00111 {
00112 
00113 bool operator== (const Polygon& p1, const Polygon& p2)
00114 {
00115   if (p1.points.size() != p2.points.size())
00116     return false;
00117   for (unsigned i=0; i<p1.points.size(); i++) 
00118     if (!approxEqual(p1.points[i].x, p2.points[i].x) ||
00119         !approxEqual(p1.points[i].y, p2.points[i].y) || 
00120         !approxEqual(p1.points[i].z, p2.points[i].z))
00121       return false;
00122   return true;  
00123 }
00124 
00125 } // namespace geometry_msgs
00126 
00127 
00128 template <class T>
00129 ostream& operator<< (ostream& str, const vector<T>& s)
00130 {
00131   str << "(";
00132   BOOST_FOREACH (const T& x, s) 
00133     str << x << " ";
00134   str << ")";
00135   return str;
00136 }
00137 
00138 template <class T>
00139 ostream& operator<< (ostream& str, const set<T>& s)
00140 {
00141   str << "{";
00142   std::ostream_iterator<T> iter(str, ", ");
00143   copy(s.begin(), s.end(), iter);
00144   str << "}";
00145   return str;
00146 }
00147 
00148 
00149 ostream& operator<< (ostream& str, const gm::Point& p)
00150 {
00151   str << "(" << p.x << ", " << p.y << ")";
00152   return str;
00153 }
00154 
00155 gm::Point makePoint (const double x, const double y)
00156 {
00157   gm::Point p;
00158   p.x = x;
00159   p.y = y;
00160   return p;
00161 }
00162 
00163 gm::Point32 makePoint32 (const double x, const double y)
00164 {
00165   gm::Point32 p;
00166   p.x = x;
00167   p.y = y;
00168   return p;
00169 }
00170 
00171 double norm (const double x, const double y)
00172 {
00173   return sqrt(x*x + y*y);
00174 }
00175 
00176 struct CloseTo
00177 {
00178   CloseTo(double res) : res(res) {}
00179   bool operator() (const gm::Point& p, const gm::Point& p2) 
00180   {
00181     const double dx=p2.x-p.x;
00182     const double dy=p2.y-p.y;
00183     return norm(dx, dy) < res*sqrt(2);
00184   }
00185   const double res;
00186 };
00187 
00188 double angle (const double x1, const double y1, const double x2, const double y2)
00189 {
00190   const double ip = x1*x2 + y1*y2;
00191   return acos(ip/(norm(x1, y1)*norm(x2, y2)));
00192 }
00193 
00194 gm::Pose makePose (const double x, const double y, const double theta)
00195 {
00196   gm::Pose p;
00197   p.position.x = x;
00198   p.position.y = y;
00199   p.orientation = tf::createQuaternionMsgFromYaw(theta);
00200   return p;
00201 }
00202 
00203 float dist (const gu::DistanceField& d, int x, int y)
00204 {
00205   return d[gu::Cell(x,y)];
00206 }
00207 
00208 
00209 TEST(GridUtils, CoordinateConversions)
00210 {
00211   // Set up info for a map at (2, -1) that is rotated 45 degrees, with resolution 0.1
00212   nm::MapMetaData info;
00213   info.resolution = 0.1;
00214   info.origin = makePose(2, -1, PI/4);
00215   info.height = 50;
00216   info.width = 80;
00217 
00218   // Check conversions
00219   EXPECT_EQ (804u, gu::cellIndex(info, Cell(4, 10)));
00220   EXPECT_EQ (Cell(8, 1), gu::pointCell(info, makePoint(2.5, -0.35)));
00221   EXPECT_EQ (88u, gu::pointIndex(info, makePoint(2.5, -0.35)));
00222   EXPECT_EQ (Cell(-8, 7), gu::pointCell(info, makePoint(1, -1)));
00223   EXPECT_THROW (gu::pointIndex(info, makePoint(1, -1)), gu::CellOutOfBoundsException);
00224   EXPECT_THROW (gu::cellIndex(info, Cell(100, 100)), gu::CellOutOfBoundsException);
00225 
00226   // Cell polygon 
00227   const double side=sqrt(2)/2;
00228   gm::Polygon expected;
00229   expected.points.resize(4);
00230   expected.points[0].x = 2 + .1*side;
00231   expected.points[0].y = -1 + .3*side;
00232   expected.points[1].x = 2;
00233   expected.points[1].y = -1 + .4*side;
00234   expected.points[2].x = 2 + .1*side;
00235   expected.points[2].y = -1 + .5*side;
00236   expected.points[3].x = 2 + .2*side;
00237   expected.points[3].y = -1 + .4*side;
00238   EXPECT_EQ(expected, cellPolygon(info, Cell(2, 1)));
00239   
00240   
00241 }
00242 
00243 
00244   
00245 TEST(GridUtils, DistanceField)
00246 {
00247   nm::OccupancyGrid g;
00248   g.info.height=5;
00249   g.info.width=6;
00250   g.data.resize(30);
00251   g.info.resolution=0.5;
00252   setOccupied(&g, 3, 1);
00253   setOccupied(&g, 0, 4);
00254   setOccupied(&g, 4, 3);
00255   
00256   gu::DistanceField d = gu::distanceField(g);
00257   EXPECT_EQ(2, dist(d, 0, 0));
00258   EXPECT_EQ(1.5, dist(d, 1, 0));
00259   EXPECT_EQ(1, dist(d, 2, 0));
00260   EXPECT_EQ(.5, dist(d, 3, 0));
00261   EXPECT_EQ(1, dist(d, 4, 0));
00262   EXPECT_EQ(0, dist(d, 3, 1));
00263   EXPECT_EQ(0, dist(d, 0, 4));
00264   EXPECT_EQ(1.5, dist(d, 1, 2));
00265   EXPECT_EQ(1, dist(d, 1, 3));
00266   EXPECT_EQ(1, dist(d, 2, 2));
00267   EXPECT_EQ(1, dist(d, 2, 3));
00268   EXPECT_EQ(.5, dist(d, 4, 4));
00269 
00270 }
00271 gm::Point lastPoint (const nm::MapMetaData& info, const gu::RayTraceIterRange& r)
00272 {
00273   boost::optional<gm::Point> last_point;
00274   BOOST_FOREACH (const Cell& c, r) {
00275     last_point = gu::cellCenter(info, c);
00276   }
00277   ROS_ASSERT(last_point);
00278   return *last_point;
00279 }
00280 
00281 TEST(GridUtils, RayTrace)
00282 {
00283   const double res=0.2;
00284   nm::MapMetaData info;
00285   info.resolution = res;
00286   info.origin = makePose(3, 1, PI/2);
00287   info.height = 100;
00288   info.width = 50;
00289   
00290   gu::RayTraceIterRange r = gu::rayTrace(info, makePoint(1, 2), makePoint(2, 5));
00291   gm::Point last_point = lastPoint(info, r);
00292 
00293   EXPECT_PRED2 (CloseTo(res), makePoint(2, 5), last_point);
00294   EXPECT_PRED2 (CloseTo(res), makePoint(1, 2), gu::cellCenter(info, *(r.first)));
00295   EXPECT_EQ (16, std::distance(r.first, r.second));
00296 
00297   // Try again with x and y flipped
00298   nm::MapMetaData info2=info;
00299   info2.origin = makePose(1, 3, PI/-2);
00300   info.height = 50;
00301   info.width = 100;
00302 
00303   gu::RayTraceIterRange r2 = gu::rayTrace(info2, makePoint(2, 1), makePoint(5, 2));
00304   gm::Point lp2 = lastPoint(info2, r2);
00305   
00306   EXPECT_PRED2 (CloseTo(res), makePoint(5, 2), lp2);
00307   EXPECT_PRED2 (CloseTo(res), makePoint(2, 1), gu::cellCenter(info2, *(r2.first)));
00308   EXPECT_EQ (16, std::distance(r2.first, r2.second));
00309 
00310 
00311   // Out of bounds points
00312   EXPECT_THROW(gu::rayTrace(info, makePoint(1, 2), makePoint(4, 2)), gu::PointOutOfBoundsException);
00313   gu::RayTraceIterRange r3 = gu::rayTrace(info, makePoint(1, 2), makePoint(4, 2), true);
00314   gm::Point lp3 = lastPoint(info, r3);
00315   EXPECT_PRED2 (CloseTo(res), makePoint(3, 2), lp3);
00316   EXPECT_PRED2 (CloseTo(res), makePoint(1, 2), gu::cellCenter(info, *(r.first)));
00317   EXPECT_EQ (10, std::distance(r3.first, r3.second));
00318   EXPECT_THROW(gu::rayTrace(info, makePoint(4, 2), makePoint(1, 2), true), gu::PointOutOfBoundsException);
00319 }
00320 
00321 
00322 TEST(GridUtils, GridOverlay)
00323 {
00324   // Grid at origin with resolution 0.1
00325   const double res=10.0;
00326   nm::MapMetaData info;
00327   info.resolution = res;
00328   info.origin = makePose(0, 0, 0);
00329   info.height = 4;
00330   info.width = 4;
00331   
00332   // Create clouds
00333   typedef vector<gm::Point32> PointVec;
00334   typedef boost::shared_ptr<gu::LocalizedCloud const> CloudConstPtr;
00335   typedef boost::shared_ptr<gu::LocalizedCloud> CloudPtr;
00336 
00337   CloudPtr c1(new gu::LocalizedCloud());
00338   c1->sensor_pose = makePose(.001, .001, 0.0);
00339   c1->header.frame_id = "foo";
00340   c1->cloud.points = PointVec(1);
00341   CloudPtr c2(new gu::LocalizedCloud());
00342   c2->sensor_pose = makePose(25.0, 5.0, PI/2);
00343   c2->header.frame_id = "foo";
00344   c2->cloud.points = PointVec(4);
00345   c1->cloud.points[0] = makePoint32(20, 20);
00346   c2->cloud.points[0] = makePoint32(20, 0);
00347   c2->cloud.points[1] = makePoint32(45, 35);
00348   c2->cloud.points[2] = makePoint32(0, 1000);
00349   c2->cloud.points[3] = makePoint32(-1, -1);
00350 
00351   // Overlay two clouds
00352   nm::OccupancyGrid fake_grid;
00353   fake_grid.info = info;
00354   gu::OverlayClouds o1 = gu::createCloudOverlay(fake_grid, "foo", .5, 200);
00355   gu::addCloud(&o1, c1);
00356   gu::addCloud(&o1, c2);
00357   GridConstPtr grid = gu::getGrid(o1);
00358   EXPECT_EQ (gu::UNKNOWN, grid->data[0]);
00359   EXPECT_EQ (gu::UNKNOWN, grid->data[1]);
00360   EXPECT_EQ (gu::UNOCCUPIED, grid->data[2]);
00361   EXPECT_EQ (gu::UNKNOWN, grid->data[3]);
00362   EXPECT_EQ (gu::UNKNOWN, grid->data[4]);
00363   EXPECT_EQ (gu::UNOCCUPIED, grid->data[5]);
00364   EXPECT_EQ (gu::UNKNOWN, grid->data[6]);
00365   EXPECT_EQ (gu::UNKNOWN, grid->data[9]);
00366   EXPECT_EQ (gu::OCCUPIED, grid->data[10]);
00367 
00368   // Vary parameters
00369   gu::OverlayClouds o2 = gu::createCloudOverlay(fake_grid, "foo", .1, 2000);
00370   gu::addCloud(&o2, c2);
00371   gu::addCloud(&o2, c1);
00372   GridConstPtr grid2 = gu::getGrid(o2);
00373   EXPECT_EQ (gu::UNOCCUPIED, grid2->data[0]);
00374   EXPECT_EQ (gu::UNKNOWN, grid2->data[1]);
00375   EXPECT_EQ (gu::OCCUPIED, grid2->data[2]);
00376   EXPECT_EQ (gu::UNKNOWN, grid2->data[3]);
00377   EXPECT_EQ (gu::UNKNOWN, grid2->data[4]);
00378   EXPECT_EQ (gu::UNOCCUPIED, grid2->data[5]);
00379   EXPECT_EQ (gu::UNKNOWN, grid2->data[6]);
00380   EXPECT_EQ (gu::UNKNOWN, grid2->data[9]);
00381   EXPECT_EQ (gu::OCCUPIED, grid2->data[10]);
00382   EXPECT_EQ (gu::UNKNOWN, grid2->data[15]);
00383 
00384   // Add another cloud
00385   gu::addCloud(&o1, c1);
00386   GridConstPtr grid3 = gu::getGrid(o1);
00387   EXPECT_EQ (gu::UNOCCUPIED, grid3->data[0]);
00388   EXPECT_EQ (gu::UNKNOWN, grid3->data[1]);
00389   EXPECT_EQ (gu::UNOCCUPIED, grid3->data[2]);
00390 
00391   // Subtract the cloud we just added and check that it's the same as grid 1
00392   gu::removeCloud(&o1, c1);
00393   GridConstPtr grid4 = gu::getGrid(o1);
00394   for (unsigned i=0; i<16; i++)
00395     EXPECT_EQ (grid->data[i], grid4->data[i]);
00396 
00397   // Check header
00398   EXPECT_EQ(grid4->header.frame_id, "foo");
00399 }
00400 
00401 
00402 TEST(GridUtils, SimulateScan)
00403 {
00404   nm::MapMetaData info;
00405   info.resolution = 0.2;
00406   info.origin = makePose(1, 1, 0);
00407   info.height = 20;
00408   info.width = 20;
00409 
00410   GridPtr grid(new nm::OccupancyGrid());
00411   grid->info = info;
00412   grid->data.resize(400);
00413   setOccupied(grid, 1, 6);
00414   setOccupied(grid, 3, 4);
00415   setOccupied(grid, 3, 7);
00416   setOccupied(grid, 4, 4);
00417   setOccupied(grid, 4, 6);
00418   setOccupied(grid, 5, 4);
00419   setOccupied(grid, 7, 8);
00420 
00421   sm::LaserScan scan_info;
00422   scan_info.angle_min = -PI/2;
00423   scan_info.angle_max = PI/2;
00424   scan_info.angle_increment = PI/4;
00425   scan_info.range_max = 1.0;
00426 
00427   const gm::Pose sensor_pose = makePose(1.7, 1.9, PI/2);
00428 
00429   sm::LaserScan::ConstPtr scan =
00430     gu::simulateRangeScan(*grid, sensor_pose, scan_info);
00431 
00432   ASSERT_EQ(scan->ranges.size(), 5);
00433   EXPECT_FLOAT_EQ(scan->ranges[0], 0.2);
00434   EXPECT_TRUE(scan->ranges[1] > 1.0);
00435   EXPECT_FLOAT_EQ(scan->ranges[2], 0.6);
00436   EXPECT_FLOAT_EQ(scan->ranges[3], 0.4*sqrt(2));
00437   EXPECT_TRUE(scan->ranges[4] > 1.0);
00438 }
00439 
00440 
00441 TEST(GridUtils, ShortestPath)
00442 {
00443   nm::MapMetaData info;
00444   info.resolution = 0.1;
00445   info.origin = makePose(0, 0, 0);
00446   info.height = 5;
00447   info.width = 5;
00448 
00449   GridPtr grid(new nm::OccupancyGrid());
00450   grid->info = info;
00451   grid->data.resize(25);
00452   setOccupied(grid, 1, 3);
00453   setOccupied(grid, 2, 2);
00454   setOccupied(grid, 2, 1);
00455   setOccupied(grid, 3, 2);
00456   setOccupied(grid, 3, 3);
00457   setOccupied(grid, 3, 4);
00458   setOccupied(grid, 4, 2);
00459 
00460   Cell c1(0, 1);
00461   Cell c2(1, 4);
00462   Cell c3(2, 3);
00463   Cell c4(3, 1);
00464   Cell c5(4, 2);
00465   Cell c6(4, 3);
00466 
00467   const gu::ResultPtr res=singleSourceShortestPaths(*grid, c1);
00468   
00469   EXPECT_PRED2(approxEqual, 0.1*(2+sqrt(2)), *distanceTo(res, c2));
00470   EXPECT_PRED2(approxEqual, 0.1*(2*sqrt(2)), *distanceTo(res, c3));
00471   EXPECT_PRED2(approxEqual, 0.1*(1+2*sqrt(2)), *distanceTo(res, c4));
00472   EXPECT_TRUE(!distanceTo(res, c5));
00473   EXPECT_TRUE(!distanceTo(res, c6));
00474 
00475   Path p12, p13, p14;
00476   p12 += Cell(0, 1), Cell(0, 2), Cell(0, 3), Cell(1, 4);
00477   p13 += Cell(0, 1), Cell(1, 2), Cell(2, 3);
00478   p14 += Cell(0, 1), Cell(1, 1), Cell(2, 0), Cell(3, 1);
00479   
00480   EXPECT_PRED2(samePath, p12, *extractPath(res, c2));
00481   EXPECT_PRED2(samePath, p13, *extractPath(res, c3));
00482   EXPECT_PRED2(samePath, p14, *extractPath(res, c4));
00483   EXPECT_TRUE(!extractPath(res, c5));
00484   EXPECT_TRUE(!extractPath(res, c6));
00485 
00486   // Conversion to and from ros messages
00487   const gu::NavigationFunction msg = gu::shortestPathResultToMessage(res);
00488   const gu::ResultPtr res2 = gu::shortestPathResultFromMessage(msg);
00489   
00490   EXPECT_PRED2(approxEqual, 0.1*(2+sqrt(2)), *distanceTo(res2, c2));
00491   EXPECT_PRED2(approxEqual, 0.1*(2*sqrt(2)), *distanceTo(res2, c3));
00492   EXPECT_PRED2(approxEqual, 0.1*(1+2*sqrt(2)), *distanceTo(res2, c4));
00493   EXPECT_TRUE(!distanceTo(res2, c5));
00494   EXPECT_TRUE(!distanceTo(res2, c6));
00495 
00496   EXPECT_PRED2(samePath, p12, *extractPath(res2, c2));
00497   EXPECT_PRED2(samePath, p13, *extractPath(res2, c3));
00498   EXPECT_PRED2(samePath, p14, *extractPath(res2, c4));
00499   EXPECT_TRUE(!extractPath(res2, c5));
00500   EXPECT_TRUE(!extractPath(res2, c6));
00501 
00502 
00503   // Termination conditions
00504   const gu::TerminationCondition t1(0.4, false);
00505   const gu::ResultPtr res3 = gu::singleSourceShortestPaths(*grid, c4, t1);
00506   EXPECT_PRED2(approxEqual, 0.1*(1+2*sqrt(2)), *distanceTo(res3, c1));
00507   EXPECT_TRUE(!distanceTo(res3, c2));
00508   EXPECT_TRUE(!distanceTo(res3, c3));
00509 
00510   gu::Cells goals;
00511   goals += c3, c6;
00512   const gu::TerminationCondition t2(goals);
00513   const gu::ResultPtr res4 = gu::singleSourceShortestPaths(*grid, c4, t2);
00514   EXPECT_PRED2(approxEqual, 0.1*(1+3*sqrt(2)), *distanceTo(res4, c3));
00515   EXPECT_PRED2(approxEqual, 0.1*(1+2*sqrt(2)), *distanceTo(res4, c1));
00516   EXPECT_TRUE(!distanceTo(res4, c6));
00517 
00518   const gu::TerminationCondition t3(goals, 0.4, false);
00519   const gu::ResultPtr res5 = gu::singleSourceShortestPaths(*grid, c4, t3);
00520   EXPECT_PRED2(approxEqual, 0.1*(1+2*sqrt(2)), *distanceTo(res5, c1));
00521   EXPECT_TRUE(!distanceTo(res5, c2));
00522   EXPECT_TRUE(!distanceTo(res5, c3));
00523 
00524 
00525   // A*
00526   boost::optional<gu::AStarResult> res6 = shortestPathAStar(*grid, c1, c4);
00527   EXPECT_FLOAT_EQ (res6->second, 0.5);
00528   EXPECT_EQ(6u, res6->first.size());
00529 
00530   boost::optional<gu::AStarResult> res7 = shortestPathAStar(*grid, c1, c1);
00531   EXPECT_FLOAT_EQ (res7->second, 0);
00532   EXPECT_EQ(1u, res7->first.size());
00533 
00534   boost::optional<gu::AStarResult> res8 = shortestPathAStar(*grid, c1, c5);
00535   EXPECT_TRUE (!res8);
00536 
00537   boost::optional<gu::AStarResult> res9 = shortestPathAStar(*grid, c1, c6);
00538   EXPECT_TRUE (!res9);
00539   
00540 }
00541 
00542 int val (const nm::OccupancyGrid& g, const gu::coord_t x, const gu::coord_t y)
00543 {
00544   const gu::Cell c(x, y);
00545   ROS_ASSERT (gu::withinBounds(g.info, c));
00546   return g.data[cellIndex(g.info, c)];
00547 }
00548 
00549 void setVal (nm::OccupancyGrid* g, const gu::coord_t x, const gu::coord_t y, const int v)
00550 {
00551   const gu::Cell c(x, y);
00552   ROS_ASSERT (gu::withinBounds(g->info, c));
00553   g->data[cellIndex(g->info, c)]=v;
00554 }
00555 
00556 
00557 TEST(GridUtils, InflateObstacles)
00558 {
00559   GridPtr g(new nm::OccupancyGrid());
00560   g->info.origin = makePose(0, 0, 0);
00561   g->info.resolution = 0.5;
00562   g->info.width = 10;
00563   g->info.height = 11;
00564   g->data.resize(g->info.width*g->info.height);
00565   fill(g->data.begin(), g->data.end(), -1);
00566   
00567   setVal(g.get(), 2, 2, 0);
00568   for (unsigned x=7; x<10; x++) {
00569     for (unsigned y=7; y<11; y++) {
00570       setVal(g.get(), x, y, 0);
00571     }
00572   }
00573     
00574   setVal(g.get(), 3, 6, 50);
00575   setVal(g.get(), 3, 7, 100);
00576   setVal(g.get(), 3, 4, 0);
00577 
00578   GridPtr g2 = gu::inflateObstacles(*g, 0.7, false);
00579 
00580   EXPECT_EQ (100, val(*g2, 3, 6));
00581   EXPECT_EQ (50, val(*g2, 3, 4));
00582   EXPECT_EQ (100, val(*g2, 3, 5));
00583   EXPECT_EQ (-1, val(*g2, 1, 3));
00584   EXPECT_EQ (-1, val(*g2, 7, 7));
00585   EXPECT_EQ (0, val(*g2, 9, 9));
00586   
00587 
00588 }
00589 
00590 bool pred (const Cell& c)
00591 {
00592   if (c.x==2 || c.y==2)
00593     return false;
00594   if (((c.x==1) || (c.x==3)) &&
00595       ((c.y==1) || (c.y==3)))
00596     return false;
00597   return true;
00598 }
00599 
00600 bool not_pred (const Cell& c)
00601 {
00602   return !pred(c);
00603 }
00604 
00605 TEST(GridUtils, Geometry)
00606 {
00607   nm::MapMetaData info;
00608   info.origin = makePose(.1,.1,0);
00609   info.resolution = .10;
00610   info.height = 5;
00611   info.width = 5;
00612 
00613   gm::Polygon poly;
00614   poly.points.push_back(makePoint32(.41, .61));
00615   poly.points.push_back(makePoint32(.43, .45));
00616   poly.points.push_back(makePoint32(.51, .41));
00617   poly.points.push_back(makePoint32(.8, .46));
00618   poly.points.push_back(makePoint32(.78, .63));
00619   
00620   const Cells cells = gu::cellsInConvexPolygon(info, poly);
00621   Cells expected;
00622   expected.insert(Cell(4,4));
00623   expected.insert(Cell(4,3));
00624   expected.insert(Cell(3,4));
00625   expected.insert(Cell(3,3));
00626   EXPECT_EQ(expected, cells);
00627   
00628 
00629   Cells cells2 = gu::tileCells(info, 0.2, pred);
00630   Cells expected2;
00631   expected2.insert(Cell(0,0));
00632   expected2.insert(Cell(0,3));
00633   expected2.insert(Cell(3,0));
00634   expected2.insert(Cell(3,4));
00635   EXPECT_EQ(expected2, cells2);
00636   
00637   Cells cells3 = gu::tileCells(info, 0.2, not_pred);
00638   Cells expected3;
00639   expected3.insert(Cell(0,2));
00640   expected3.insert(Cell(2,0));
00641   expected3.insert(Cell(2,3));
00642   expected3.insert(Cell(4,2));
00643   EXPECT_EQ(expected3, cells3);
00644 }
00645 
00646 
00647 TEST(GridUtils, CombineGrids)
00648 {
00649   GridPtr g1(new nm::OccupancyGrid());
00650   GridPtr g2(new nm::OccupancyGrid());
00651   
00652   g1->info.origin = makePose(2, 1, 0);
00653   g2->info.origin = makePose(3, 0, PI/4);
00654   g1->info.resolution = 0.5;
00655   g2->info.resolution = 0.5;
00656   g1->info.height = 4;
00657   g1->info.width = 6;
00658   g2->info.height = 4;
00659   g2->info.width = 4;
00660   g1->data.resize(24);
00661   g2->data.resize(16);
00662 
00663   fill(g1->data.begin(), g1->data.end(), -1);
00664   fill(g2->data.begin(), g2->data.end(), -1);
00665 
00666   setVal(g2.get(), 1, 0, 0);
00667   setVal(g2.get(), 0, 3, 50);
00668   setVal(g2.get(), 2, 0, 42);
00669   setVal(g2.get(), 0, 2, 11);
00670   setVal(g2.get(), 3, 2, 0);
00671   setVal(g2.get(), 3, 0, 110);
00672   
00673   setVal(g1.get(), 5, 3, 100);
00674   setVal(g1.get(), 3, 0, 24);
00675   setVal(g1.get(), 0, 0, 66);
00676   setVal(g1.get(), 4, 0, 90);
00677 
00678 
00679   
00680   vector<GridConstPtr> grids;
00681   grids += g1, g2;
00682   GridPtr combined = gu::combineGrids(grids, g1->info.resolution/2.0);
00683   
00684   EXPECT_PRED2(approxEqual, 3-sqrt(2), combined->info.origin.position.x);
00685   EXPECT_PRED2(approxEqual, 0, combined->info.origin.position.y);
00686   EXPECT_EQ(0.25, combined->info.resolution);
00687   EXPECT_EQ(round((2+sqrt(2))/0.25), combined->info.width);
00688   EXPECT_EQ(12u, combined->info.height);
00689 
00690   // Note there are rounding issues that mean that some of the values below
00691   // could theoretically go either way
00692   EXPECT_EQ(-1, val(*combined, 11, 2));
00693   EXPECT_EQ(-1, val(*combined, 2, 0));
00694   EXPECT_EQ(-1, val(*combined, 5, 0));
00695   EXPECT_EQ(0, val(*combined, 7, 2));
00696   EXPECT_EQ(66, val(*combined, 1, 6));
00697   EXPECT_EQ(100, val(*combined, 11, 11));
00698   EXPECT_EQ(-1, val(*combined, 2, 11));
00699   EXPECT_EQ(11, val(*combined, 3, 2));
00700   EXPECT_EQ(42, val(*combined, 7, 4));
00701   EXPECT_EQ(66, val(*combined, 2, 4));
00702   EXPECT_EQ(0, val(*combined, 6, 8));
00703   EXPECT_EQ(90, val(*combined, 10, 5));
00704   
00705   
00706 }
00707 
00708 
00709 
00710 
00711 
00712 int main (int argc, char** argv)
00713 {
00714   ros::Time::init();
00715   testing::InitGoogleTest(&argc, argv);
00716   return RUN_ALL_TESTS();
00717 }


occupancy_grid_utils
Author(s): Bhaskara Marthi
autogenerated on Thu Dec 12 2013 13:17:54