00001
00004 #include <cmath>
00005 #include <fstream>
00006 #include <math.h>
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;
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);
00133
00134
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
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
00206 double p;
00207 if (occupied)
00208 {
00209 p = p_occupied_when_laser_;
00210 }
00211 else
00212 {
00213 p = p_occupied_when_no_laser_;
00214 }
00215
00216
00217
00218
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
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
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
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
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
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
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
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
00317 const bool move = updateMap(scan, map_dx, map_dy, theta);
00318 if (move)
00319 {
00320
00321
00322 last_xmap_ = xmap;
00323 last_ymap_ = ymap;
00324 }
00325
00326
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
00342 moveAndCopyImage(-1, dx, dy, ncol, map_.data);
00343 moveAndCopyImage(0, dx, dy, ncol, log_odds_);
00344 }
00345
00346
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
00359 const size_t last_pt = pts.back();
00360 updatePointOccupancy(true, last_pt, map_.data, log_odds_);
00361 pts.pop_back();
00362 }
00363
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
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
00391
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 }
00460