map_builder.cpp
Go to the documentation of this file.
00001 
00004 #include <cmath>
00005 #include <fstream>
00006 #include <math.h>  // for round(), std::round() is since C++11.
00007 
00008 #include <local_map/map_builder.h>
00009 
00010 namespace local_map
00011 {
00012 
00013 const double g_default_p_occupied_when_laser = 0.9;
00014 const double g_default_p_occupied_when_no_laser = 0.3;
00015 const double g_default_large_log_odds = 100;
00016 const double g_default_max_log_odds_for_belief = 20;
00017 
00020 std::string getWorldFrame(const tf::Transformer& tf_transformer, const std::string& child)
00021 {
00022   std::string last_parent = child;
00023   std::string parent;
00024   bool has_parent = tf_transformer.getParent(child, ros::Time(0), parent);
00025   while (has_parent)
00026   {
00027     last_parent = parent;
00028     has_parent = tf_transformer.getParent(parent, ros::Time(0), parent);
00029   }
00030   return last_parent;
00031 }
00032 
00042 double angleFromQuaternion(const tf::Quaternion& q)
00043 {
00044   if (std::fabs(q.x()) > 1e-5 || std::fabs(q.y()) > 1e-5)
00045   {
00046     tf::Vector3 axis = q.getAxis();
00047     ROS_WARN("Laser frame rotation is not around the z-axis (axis = [%f, %f, %f], just pretending it is",
00048         axis.x(), axis.y(), axis.z());
00049   }
00050   return 2 * std::atan2(q.z(), q.w());
00051 }
00052 
00065 template <typename T>
00066 void moveAndCopyImage(int fill, int dx, int dy, unsigned int ncol, vector<T>& map)
00067 {
00068   if (dx == 0 && dy == 0)
00069   {
00070     return;
00071   }
00072 
00073   const unsigned int nrow = map.size() / ncol;
00074   int row_start = 0;
00075   int row_end = nrow;
00076   int row_increment = 1;
00077   if (dy < 0)
00078   {
00079     row_start = nrow - 1;
00080     row_end = -1;
00081     row_increment = -1;
00082   }
00083   int col_start = 0;
00084   int col_steps = ncol;
00085   int col_increment = 1;
00086   if (dx < 0)
00087   {
00088     col_start = ncol - 1;
00089     col_steps = -ncol;
00090     col_increment = -1;
00091   }
00092   for (int new_row = row_start; new_row != row_end; new_row += row_increment)
00093   {
00094     const int new_idx_start = offsetFromRowColNoRangeCheck(new_row, col_start, ncol);
00095     const int row = new_row + dy;  // row in old map, can be outside old map
00096     int idx = offsetFromRowColNoRangeCheck(row, col_start + dx, ncol);
00097     const int min_idx = std::max(0, offsetFromRowColNoRangeCheck(row, 0, ncol));
00098     const int max_idx = std::min(static_cast<int>(map.size()) - 1, offsetFromRowColNoRangeCheck(row, ncol - 1, ncol));
00099     const int new_idx_end = new_idx_start + col_steps;
00100     for (int new_idx = new_idx_start; new_idx != new_idx_end;)
00101     {
00102       if (min_idx <= idx && idx <= max_idx)
00103       {
00104         map[new_idx] = map[idx];
00105       }
00106       else
00107       {
00108         map[new_idx] = fill;
00109       }
00110       new_idx += col_increment;
00111       idx += col_increment;
00112     }
00113   }
00114 }
00115 
00116 MapBuilder::MapBuilder(int width, int height, double resolution) :
00117   angle_resolution_(M_PI / 720),
00118   p_occupied_when_laser_(g_default_p_occupied_when_laser),
00119   p_occupied_when_no_laser_(g_default_p_occupied_when_no_laser),
00120   large_log_odds_(g_default_large_log_odds),
00121   max_log_odds_for_belief_(g_default_max_log_odds_for_belief),
00122   has_frame_id_(false)
00123 {
00124   map_frame_id_ = ros::this_node::getName() + "/local_map";
00125   map_.header.frame_id = map_frame_id_;
00126   map_.info.width = width;
00127   map_.info.height = height;
00128   map_.info.resolution = resolution;
00129   map_.info.origin.position.x = -static_cast<double>(width) / 2 * resolution;
00130   map_.info.origin.position.y = -static_cast<double>(height) / 2 * resolution;
00131   map_.info.origin.orientation.w = 1.0;
00132   map_.data.assign(width * height, -1);  // Fill with "unknown" occupancy.
00133   // log_odds = log(occupancy / (1 - occupancy); prefill with
00134   // occupancy = 0.5, equiprobability between occupied and free.
00135   log_odds_.assign(width * height, 0);
00136 
00137   ros::NodeHandle private_nh("~");
00138   private_nh.getParam("angle_resolution", angle_resolution_);
00139   private_nh.getParam("p_occupied_when_laser", p_occupied_when_laser_);
00140   if (p_occupied_when_laser_ <=0 || p_occupied_when_laser_ >= 1)
00141   {
00142     ROS_ERROR_STREAM("Parameter "<< private_nh.getNamespace() <<
00143         "/p_occupied_when_laser must be within ]0, 1[, setting to default (" <<
00144         g_default_p_occupied_when_laser << ")");
00145     p_occupied_when_laser_ = g_default_p_occupied_when_laser;
00146   }
00147   private_nh.getParam("p_occupied_when_no_laser", p_occupied_when_no_laser_);
00148   if (p_occupied_when_no_laser_ <=0 || p_occupied_when_no_laser_ >= 1)
00149   {
00150     ROS_ERROR_STREAM("Parameter "<< private_nh.getNamespace() <<
00151         "/p_occupied_when_no_laser must be within ]0, 1[, setting to default (" <<
00152         g_default_p_occupied_when_no_laser << ")");
00153     p_occupied_when_no_laser_ = g_default_p_occupied_when_no_laser;
00154   }
00155   private_nh.getParam("large_log_odds", large_log_odds_);
00156   if (large_log_odds_ <=0)
00157   {
00158     ROS_ERROR_STREAM("Parameter "<< private_nh.getNamespace() << "/large_log_odds must be positive, setting to default (" <<
00159         g_default_large_log_odds << ")");
00160     large_log_odds_ = g_default_large_log_odds;
00161   }
00162   private_nh.getParam("max_log_odds_for_belief", max_log_odds_for_belief_);
00163   try
00164   {
00165     std::exp(max_log_odds_for_belief_);
00166   }
00167   catch (std::exception)
00168   {
00169     ROS_ERROR_STREAM("Parameter "<< private_nh.getNamespace() << "/max_log_odds_for_belief too large, setting to default (" <<
00170         g_default_max_log_odds_for_belief << ")");
00171     max_log_odds_for_belief_ = g_default_max_log_odds_for_belief;
00172   }
00173 
00174 
00175   // Fill in the lookup cache.
00176   const double angle_start = -M_PI;
00177   const double angle_end = angle_start + 2 * M_PI - 1e-6;
00178   for (double a = angle_start; a <= angle_end; a += angle_resolution_)
00179   {
00180     ray_caster_.getRayCastToMapBorder(a, height, width, 0.9 * angle_resolution_);
00181   }
00182 }
00183 
00192 void MapBuilder::updatePointOccupancy(bool occupied, size_t idx, vector<int8_t>& occupancy, vector<double>& log_odds) const
00193 {
00194   if (idx >= occupancy.size())
00195   {
00196     return;
00197   }
00198 
00199   if (occupancy.size() != log_odds.size())
00200   {
00201     ROS_ERROR("occupancy and count do not have the same number of elements");
00202     return;
00203   }
00204 
00205   // Update log_odds.
00206   double p;  // Probability of being occupied knowing current measurement.
00207   if (occupied)
00208   {
00209     p = p_occupied_when_laser_;
00210   }
00211   else
00212   {
00213     p = p_occupied_when_no_laser_;
00214   }
00215   // Original formula: Table 4.2, "Probabilistics robotics", Thrun et al., 2005:
00216   // log_odds[idx] = log_odds[idx] +
00217   //     std::log(p * (1 - p_occupancy) / (1 - p) / p_occupancy);
00218   // With p_occupancy = 0.5, this simplifies to:
00219   log_odds[idx] += std::log(p / (1 - p));
00220   if (log_odds[idx] < -large_log_odds_)
00221   {
00222     log_odds[idx] = -large_log_odds_;
00223   }
00224   else if(log_odds[idx] > large_log_odds_)
00225   {
00226     log_odds[idx] = large_log_odds_;
00227   }
00228   // Update occupancy.
00229   if (log_odds[idx] < -max_log_odds_for_belief_)
00230   {
00231     occupancy[idx] = 0;
00232   }
00233   else if (log_odds[idx] > max_log_odds_for_belief_)
00234   {
00235     occupancy[idx] = 100;
00236   }
00237   else
00238   {
00239     occupancy[idx] = static_cast<int8_t>(lround((1 - 1 / (1 + std::exp(log_odds[idx]))) * 100));
00240   }
00241 }
00242 
00247 void MapBuilder::grow(const sensor_msgs::LaserScan& scan)
00248 {
00249   if (!has_frame_id_)
00250   {
00251     // Wait for a parent.
00252     std::string parent;
00253     bool has_parent = tf_listerner_.getParent(scan.header.frame_id, ros::Time(0), parent);
00254     if (!has_parent)
00255     {
00256       ROS_DEBUG_STREAM("Frame " << scan.header.frame_id << " has no parent");
00257       return;
00258     }
00259     world_frame_id_ = getWorldFrame(tf_listerner_, scan.header.frame_id);
00260     ROS_INFO_STREAM("Found world frame " << world_frame_id_);
00261     has_frame_id_ = true;
00262 
00263     // Initialize saved positions.
00264     tf::StampedTransform transform;
00265     try
00266     {
00267       tf_listerner_.waitForTransform(world_frame_id_, scan.header.frame_id,
00268           scan.header.stamp, ros::Duration(1.0));
00269       tf_listerner_.lookupTransform(world_frame_id_, scan.header.frame_id,
00270           scan.header.stamp, transform);
00271     }
00272     catch (tf::TransformException ex)
00273     {
00274       ROS_ERROR("%s", ex.what());
00275       has_frame_id_ = false;
00276     }
00277     xinit_ = transform.getOrigin().x();
00278     yinit_ = transform.getOrigin().y();
00279     last_xmap_ = lround(xinit_ / map_.info.resolution);
00280     last_ymap_ = lround(yinit_ / map_.info.resolution);
00281 
00282     // Send a map frame with identity transform.
00283     tf::Transform map_transform;
00284     map_transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
00285     map_transform.setRotation(tf::Quaternion(1, 0, 0, 0));
00286     tr_broadcaster_.sendTransform(tf::StampedTransform(map_transform,
00287           scan.header.stamp, scan.header.frame_id, map_frame_id_));
00288   }
00289 
00290   // Get the displacement.
00291   tf::StampedTransform new_tr;
00292   try
00293   {
00294     tf_listerner_.waitForTransform(world_frame_id_, scan.header.frame_id,
00295         scan.header.stamp, ros::Duration(0.2));
00296     tf_listerner_.lookupTransform(world_frame_id_, scan.header.frame_id,
00297         scan.header.stamp, new_tr);
00298   }
00299   catch (tf::TransformException ex)
00300   {
00301     ROS_ERROR("%s", ex.what());
00302     return;
00303   }
00304 
00305   // Map position relative to initialization.
00306   const double x = new_tr.getOrigin().x() - xinit_;
00307   const double y = new_tr.getOrigin().y() - yinit_;
00308   const double theta = angleFromQuaternion(new_tr.getRotation());
00309 
00310   // Get the pixel displacement of the map.
00311   const long int xmap = lround(x / map_.info.resolution);
00312   const long int ymap = lround(y / map_.info.resolution);
00313   const long int map_dx = xmap - last_xmap_;
00314   const long int map_dy = ymap - last_ymap_;
00315 
00316   // Update the map
00317   const bool move = updateMap(scan, map_dx, map_dy, theta);
00318   if (move)
00319   {
00320     //ROS_INFO("Displacement: %ld, %ld pixels", map_dx, map_dy);
00321     // Record the position only if the map moves.
00322     last_xmap_ = xmap;
00323     last_ymap_ = ymap;
00324   }
00325 
00326   // Update the map frame, so that it's oriented like frame named "world_frame_id_".
00327   tf::Transform map_transform;
00328   map_transform.setOrigin(tf::Vector3(0.0, 0.0, 0.0));
00329   tf::Quaternion q;
00330   q.setRPY(0, 0, -theta);
00331   map_transform.setRotation(q);
00332   tr_broadcaster_.sendTransform(tf::StampedTransform(map_transform, scan.header.stamp, scan.header.frame_id, map_frame_id_));
00333 }
00334 
00335 bool MapBuilder::updateMap(const sensor_msgs::LaserScan& scan, long int dx, long int dy, double theta)
00336 {
00337   const bool has_moved = (dx != 0 || dy != 0);
00338   const int ncol = map_.info.width;
00339   if (has_moved)
00340   {
00341     // Move the map and log_odds_.
00342     moveAndCopyImage(-1, dx, dy, ncol, map_.data);
00343     moveAndCopyImage(0, dx, dy, ncol, log_odds_);
00344   }
00345 
00346   // Update occupancy.
00347   for (size_t i = 0; i < scan.ranges.size(); ++i)
00348   {
00349     const double angle = angles::normalize_angle(scan.angle_min + i * scan.angle_increment + theta);
00350     vector<size_t> pts;
00351     const bool obstacle_in_map = getRayCastToObstacle(map_, angle, scan.ranges[i], pts);
00352     if (pts.empty())
00353     {
00354       continue;
00355     }
00356     if (obstacle_in_map)
00357     {
00358       // The last point is the point with obstacle.
00359       const size_t last_pt = pts.back();
00360       updatePointOccupancy(true, last_pt, map_.data, log_odds_);
00361       pts.pop_back();
00362     }
00363     // The remaining points are in free space.
00364     updatePointsOccupancy(false, pts, map_.data, log_odds_);
00365   }
00366   return has_moved;
00367 }
00368 
00379 bool MapBuilder::getRayCastToObstacle(const nav_msgs::OccupancyGrid& map, double angle, double range, vector<size_t>& raycast)
00380 {
00381   // Do not consider a 0-length range.
00382   if (range < 1e-10)
00383   {
00384     raycast.clear();
00385     return false;
00386   }
00387 
00388   const vector<size_t>& ray_to_map_border = ray_caster_.getRayCastToMapBorder(angle,
00389       map.info.height, map.info.width, 1.1 * angle_resolution_);
00390   // range in pixel length. The ray length in pixels corresponds to the number
00391   // of pixels in the bresenham algorithm.
00392   const size_t pixel_range = lround(range * max(abs(std::cos(angle)), abs(std::sin(angle))) / map.info.resolution);
00393   size_t raycast_size;
00394   bool obstacle_in_map = pixel_range < ray_to_map_border.size();
00395   if (obstacle_in_map)
00396   {
00397     raycast_size = pixel_range;
00398   }
00399   else
00400   {
00401     raycast_size = ray_to_map_border.size();
00402   }
00403   raycast.clear();
00404   raycast.reserve(raycast_size);
00405   for (size_t i = 0; i < raycast_size; ++i)
00406   {
00407     raycast.push_back(ray_to_map_border[i]);
00408   }
00409 
00410   return obstacle_in_map;
00411 }
00412 
00413 bool MapBuilder::saveMap(const std::string& name) const
00414 {
00415   std::string filename;
00416   if (name.empty())
00417   {
00418     const ros::Time time = ros::Time::now();
00419     const int sec = time.sec;
00420     const int nsec = time.nsec;
00421 
00422     std::stringstream sname;
00423     sname << "map_";
00424     sname << std::setw(5) << std::setfill('0') << sec;
00425     sname << std::setw(0) << "_";
00426     sname << std::setw(9) << std::setfill('0') << nsec;
00427     sname << std::setw(0) << ".txt";
00428     filename = sname.str();
00429   }
00430   else
00431   {
00432     filename = name;
00433   }
00434 
00435   std::ofstream ofs;
00436   ofs.open(filename.c_str());
00437   if (!ofs.is_open())
00438   {
00439     ROS_ERROR("Cannot open %s", filename.c_str());
00440     return false;
00441   }
00442 
00443   for (size_t i = 0; i < map_.data.size(); ++i)
00444   {
00445     ofs << static_cast<int>(map_.data[i]);
00446     if ((i % map_.info.width) == (map_.info.width - 1))
00447     {
00448       ofs << "\n";
00449     }
00450     else
00451     {
00452       ofs << ",";
00453     }
00454   }
00455   ofs.close();
00456   return true;
00457 }
00458 
00459 } // namespace local_map
00460 


local_map
Author(s): Gaƫl Ecorchard
autogenerated on Thu Jun 6 2019 22:02:09