place_profile_conversions.cpp
Go to the documentation of this file.
00001 #include <lama_common/place_profile_conversions.h>
00002 
00003 #define OCCUPIED_THRESHOLD 60 // 0 = free, 100 = occupied.
00004 #define COSTMAP_DISCRETISATION_COUNT 360 // Number of points for the costmap discretisation to obtain a PlaceProfile.
00005 
00006 namespace lama_common
00007 {
00008 
00011 geometry_msgs::Polygon placeProfileToPolygon(const PlaceProfile& profile)
00012 {
00013   geometry_msgs::Polygon polygon;
00014 
00015   polygon.points.reserve(profile.polygon.points.size());
00016 
00017   for (size_t i = 0; i < polygon.points.size(); ++i)
00018   {
00019     polygon.points.push_back(polygon.points[i]);
00020   }
00021 
00022   return polygon;
00023 }
00024 
00027 sensor_msgs::PointCloud placeProfileToPointCloud(const PlaceProfile& profile)
00028 {
00029   sensor_msgs::PointCloud cloud;
00030   cloud.header = profile.header;
00031   for (size_t i = 0; i < profile.polygon.points.size(); ++i)
00032   {
00033     cloud.points.push_back(profile.polygon.points[i]);
00034   }
00035   return cloud;
00036 }
00037 
00043 PlaceProfile laserScanToPlaceProfile(const sensor_msgs::LaserScan& scan, double range_cutoff)
00044 {
00045   // Use LaserProjection for its caching of sine and cosine values.
00046   static laser_geometry::LaserProjection projector;
00047 
00048   PlaceProfile profile;
00049   profile.header = scan.header;
00050   size_t size = scan.ranges.size();
00051   profile.polygon.points.reserve(size);
00052 
00053   vector<bool> in_range(size, false);
00054   for (size_t i = 0; i < size; ++i)
00055   {
00056     if (scan.ranges[i] < range_cutoff)
00057     {
00058       in_range[i] = true;
00059     }
00060   }
00061 
00062   const double greater_than_longest = *(std::max_element(scan.ranges.begin(), scan.ranges.end())) + 1;
00063   sensor_msgs::PointCloud cloud;
00064   projector.projectLaser(scan, cloud, greater_than_longest, laser_geometry::channel_option::None);
00065 
00066   if (scan.ranges.size() != cloud.points.size())
00067   {
00068     // projectLaser removes beams smaller than scan.min_range from the
00069     // resulting cloud but we need that the point count is the same. We achieve
00070     // this by modifying a copy of scan.
00071     sensor_msgs::LaserScan copy_of_scan = scan;
00072     const double smaller_than_shortest = *(std::min_element(scan.ranges.begin(), scan.ranges.end())) - 1;
00073     copy_of_scan.range_min = smaller_than_shortest;
00074     projector.projectLaser(copy_of_scan, cloud, greater_than_longest, laser_geometry::channel_option::None);
00075   }
00076 
00077   int idx_start;
00078   int idx_end;
00079   int idx_increment;
00080   if (scan.angle_increment > 0)
00081   {
00082     idx_start = 0;
00083     idx_end = size;
00084     idx_increment = 1;
00085   }
00086   else
00087   {
00088     idx_start = size - 1;
00089     idx_end = -1;
00090     idx_increment = -1;
00091   }
00092   for (int i = idx_start; i != idx_end; i += idx_increment)
00093   {
00094     if (in_range[i] && in_range[circular_index(i + idx_increment, size)])
00095     {
00096       // point i and next point are included.
00097       profile.polygon.points.push_back(cloud.points[i]);
00098     }
00099     else if (in_range[i] && !in_range[circular_index(i + idx_increment, size)])
00100     {
00101       // point i is included, next point (which will not be part of
00102       // profile.polygon) is excluded.
00103       profile.polygon.points.push_back(cloud.points[i]);
00104       profile.exclude_segments.push_back(profile.polygon.points.size() - 1);
00105     }
00106   }
00107   
00108   if (!profile.exclude_segments.empty() && (profile.exclude_segments.front() == -1))
00109   {
00110     // If segment between last point and point 0 is excluded.
00111     // This happens if the first range(s) is(are) out of range.
00112     profile.exclude_segments[0] = profile.polygon.points.size() - 1;
00113   }
00114 
00115   normalizePlaceProfile(profile);
00116   return profile;
00117 }
00118 
00121 inline bool pointOccupied(const nav_msgs::OccupancyGrid& map, const int index)
00122 {
00123   return (map.data[index] > OCCUPIED_THRESHOLD);
00124 }
00125 
00128 inline bool pointUnknown(const nav_msgs::OccupancyGrid& map, const int index)
00129 {
00130   return (map.data[index] == -1);
00131 }
00132 
00150 bool firstNonFree(const nav_msgs::OccupancyGrid& map, double angle, double range_cutoff, geometry_msgs::Point32& point)
00151 {
00152   static map_ray_caster::MapRayCaster ray_caster;
00153   const vector<size_t>& ray = ray_caster.getRayCastToMapBorder(angle, map.info.height, map.info.width);
00154 
00155   // range_cutoff in pixel length.
00156   // Initialize to a large value, so that the if condition afterwards will be
00157   // always false in the case that range_cuttoff is set to 0.
00158   size_t pixel_range = ray.size();
00159   double cos_angle;
00160   double sin_angle;
00161   if (std::abs(range_cutoff) > 1e-10)
00162   {
00163     cos_angle = std::cos(angle);
00164     sin_angle = std::sin(angle);
00165     pixel_range = lround(range_cutoff * std::max(std::abs(cos_angle), std::abs(sin_angle)) / map.info.resolution);
00166   }
00167 
00168   for (size_t i = 0; i < ray.size(); ++i)
00169   {
00170     size_t idx = ray[i];
00171 
00172     if (i >= pixel_range)
00173     {
00174       point.x = range_cutoff * cos_angle;
00175       point.y = range_cutoff * sin_angle;
00176       return false;
00177     }
00178     if (pointOccupied(map, idx))
00179     {
00180       map_ray_caster::indexToReal(map, idx, point);
00181       return true;
00182     }
00183     if (pointUnknown(map, idx))
00184     {
00185       map_ray_caster::indexToReal(map, idx, point);
00186       return false;
00187     }
00188   }
00189   map_ray_caster::indexToReal(map, ray.back(), point);
00190   return false;
00191 }
00192 
00197 PlaceProfile costmapToPlaceProfile(const nav_msgs::OccupancyGrid& map, double range_cutoff)
00198 {
00199   PlaceProfile profile;
00200   profile.header = map.header;
00201   profile.polygon.points.reserve(COSTMAP_DISCRETISATION_COUNT);
00202 
00203   // angle_min should be slightly greater than M_PI to be sure that the
00204   // pixel on the bottom half of the map is chosen if map height is even.
00205   const double angle_min = -M_PI + 1e-6;
00206   const double resolution = 2 * M_PI / COSTMAP_DISCRETISATION_COUNT;
00207   geometry_msgs::Point32 last_point;
00208   // last_point should be different than any point in the map.
00209   last_point.x = map.info.width * map.info.resolution;
00210   geometry_msgs::Point32 this_point;
00211   geometry_msgs::Point32 next_point;
00212   bool this_in_range = firstNonFree(map, angle_min, range_cutoff, this_point);
00213   double next_angle = angle_min + resolution;
00214   for (size_t i = 0; i < COSTMAP_DISCRETISATION_COUNT; ++i)
00215   {
00216     bool next_in_range = firstNonFree(map, next_angle, range_cutoff, next_point);
00217     if (this_in_range)
00218     {
00219       if ((this_point.x != last_point.x) || (this_point.y != last_point.y))
00220       {
00221         profile.polygon.points.push_back(this_point);
00222       }
00223     }
00224     else if (next_in_range)
00225     {
00226       profile.exclude_segments.push_back(profile.polygon.points.size() - 1);
00227     }
00228     next_angle += resolution;
00229     last_point = this_point;
00230     this_point = next_point;
00231     this_in_range = next_in_range;
00232   }
00233   if (!profile.exclude_segments.empty() && (profile.exclude_segments.front() == -1))
00234   {
00235     // If segment between last point and point 0 is excluded.
00236     // This happens if the first point(s) is(are) non-free.
00237     profile.exclude_segments[0] = profile.polygon.points.size() - 1;
00238   }
00239 
00240   return profile;
00241 }
00242 
00243 } // namespace lama_common
00244 


lama_common
Author(s): Gaël Ecorchard , Karel Košnar , Vojtěch Vonásek
autogenerated on Thu Jun 6 2019 22:02:03