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
00031
00032
00033
00034
00035
00036
00037
00038 #include <map_merge/map_merge.h>
00039
00040 #include <thread>
00041
00042 #include <ros/console.h>
00043 #include <ros/assert.h>
00044 #include <occupancy_grid_utils/combine_grids.h>
00045 #include <occupancy_grid_utils/coordinate_conversions.h>
00046 #include <tf/transform_datatypes.h>
00047 #include <combine_grids/estimate_transform.h>
00048
00049 namespace map_merge
00050 {
00051 geometry_msgs::Pose& operator+=(geometry_msgs::Pose&,
00052 const geometry_msgs::Pose&);
00053
00054 MapMerging::MapMerging() : maps_size_(0)
00055 {
00056 ros::NodeHandle private_nh("~");
00057 std::string frame_id;
00058 std::string merged_map_topic;
00059
00060 private_nh.param("merging_rate", merging_rate_, 4.0);
00061 private_nh.param("discovery_rate", discovery_rate_, 0.05);
00062 private_nh.param("estimation_rate", estimation_rate_, 0.5);
00063 private_nh.param("known_init_poses", have_initial_poses_, true);
00064 private_nh.param("estimation_confidence", confidence_treshold_, 1.0);
00065 private_nh.param<std::string>("robot_map_topic", robot_map_topic_, "map");
00066 private_nh.param<std::string>("robot_map_updates_topic",
00067 robot_map_updates_topic_, "map_updates");
00068 private_nh.param<std::string>("robot_namespace", robot_namespace_, "");
00069 private_nh.param<std::string>("merged_map_topic", merged_map_topic, "map");
00070 private_nh.param<std::string>("world_frame", frame_id, "world");
00071
00072 merged_map_.header.frame_id = frame_id;
00073
00074
00075 merged_map_publisher_ =
00076 node_.advertise<nav_msgs::OccupancyGrid>(merged_map_topic, 50, true);
00077 }
00078
00079
00080
00081
00082 void MapMerging::topicSubscribing()
00083 {
00084 ROS_DEBUG("Robot discovery started.");
00085
00086 ros::master::V_TopicInfo topic_infos;
00087 geometry_msgs::Pose init_pose;
00088 std::string robot_name;
00089 std::string map_topic;
00090 std::string map_updates_topic;
00091
00092 ros::master::getTopics(topic_infos);
00093
00094 init_pose.orientation.w = 1;
00095
00096 for (const auto& topic : topic_infos) {
00097
00098 if (!isRobotMapTopic(topic)) {
00099 continue;
00100 }
00101
00102 robot_name = robotNameFromTopic(topic.name);
00103 if (robots_.count(robot_name)) {
00104
00105 continue;
00106 }
00107
00108 if (have_initial_poses_ && !getInitPose(robot_name, init_pose)) {
00109 ROS_WARN("Couldn't get initial position for robot [%s]\n"
00110 "did you defined parameters map_merge/init_pose_[xyz]? in robot "
00111 "namespace? If you want to run merging without known initial "
00112 "positions of robots please set `known_init_poses` parameter "
00113 "to false. See relavant documentation for details.",
00114 robot_name.c_str());
00115 continue;
00116 }
00117
00118 ROS_INFO("adding robot [%s] to system", robot_name.c_str());
00119 {
00120
00121
00122
00123
00124
00125
00126 boost::shared_lock<boost::shared_mutex> lock(merging_mutex_);
00127 maps_.emplace_front();
00128 ++maps_size_;
00129 all_grids_view_.emplace_back(maps_.front().map);
00130 }
00131
00132
00133 PosedMap& map = maps_.front();
00134 robots_.insert({robot_name, &map});
00135 map.initial_pose = init_pose;
00136
00137
00138 map_topic = ros::names::append(robot_name, robot_map_topic_);
00139 map_updates_topic =
00140 ros::names::append(robot_name, robot_map_updates_topic_);
00141 ROS_INFO("Subscribing to MAP topic: %s.", map_topic.c_str());
00142 map.map_sub = node_.subscribe<nav_msgs::OccupancyGrid>(
00143 map_topic, 50,
00144 [this, &map](const nav_msgs::OccupancyGrid::ConstPtr& msg) {
00145 fullMapUpdate(msg, map);
00146 });
00147 ROS_INFO("Subscribing to MAP updates topic: %s.",
00148 map_updates_topic.c_str());
00149 map.map_updates_sub = node_.subscribe<map_msgs::OccupancyGridUpdate>(
00150 map_updates_topic, 50,
00151 [this, &map](const map_msgs::OccupancyGridUpdate::ConstPtr& msg) {
00152 partialMapUpdate(msg, map);
00153 });
00154 }
00155 }
00156
00157
00158
00159
00160 void MapMerging::mapMerging()
00161 {
00162 grids_view_t* grids;
00163
00164 ROS_DEBUG("Map merging started.");
00165
00166
00167
00168 if (have_initial_poses_) {
00169 grids = &all_grids_view_;
00170 } else {
00171 grids = &estimated_grids_view_;
00172 }
00173
00174 {
00175
00176 std::lock_guard<boost::shared_mutex> lock(merging_mutex_);
00177 occupancy_grid_utils::combineGrids(grids->cbegin(), grids->cend(),
00178 merged_map_);
00179 }
00180
00181 ROS_DEBUG("all maps merged, publishing");
00182 ros::Time now = ros::Time::now();
00183 merged_map_.info.map_load_time = now;
00184 merged_map_.header.stamp = now;
00185 merged_map_publisher_.publish(merged_map_);
00186 }
00187
00188 void MapMerging::poseEstimation()
00189 {
00190 ROS_DEBUG("Grid pose estimation started.");
00191
00192 std::vector<geometry_msgs::Pose> transforms;
00193
00194
00195 transforms.resize(maps_size_);
00196 estimated_grids_view_.reserve(maps_size_);
00197
00198
00199
00200
00201 std::lock_guard<boost::shared_mutex> lock(merging_mutex_);
00202
00203
00204 transforms.resize(maps_size_);
00205
00206 size_t num_est_transforms = combine_grids::estimateGridTransform(
00207 all_grids_view_.cbegin(), all_grids_view_.cend(), transforms.begin(),
00208 confidence_treshold_);
00209
00210
00211
00212
00213
00214 estimated_grids_view_.clear();
00215 estimated_grids_view_.reserve(num_est_transforms);
00216 auto map_it = maps_.begin();
00217 for (auto& transform : transforms) {
00218
00219
00220 double norm =
00221 std::abs(transform.orientation.w) + std::abs(transform.orientation.x) +
00222 std::abs(transform.orientation.y) + std::abs(transform.orientation.z);
00223 if (norm < std::numeric_limits<double>::epsilon()) {
00224
00225 continue;
00226 }
00227
00228 tf::Pose init_pose_tf;
00229 tf::Pose origin_tf;
00230 tf::Pose transform_tf;
00231 tf::poseMsgToTF(map_it->initial_pose, init_pose_tf);
00232 tf::poseMsgToTF(map_it->map.info.origin, origin_tf);
00233 tf::poseMsgToTF(transform, transform_tf);
00234
00235
00236 origin_tf *= init_pose_tf.inverseTimes(transform_tf);
00237
00238
00239 tf::poseTFToMsg(origin_tf, map_it->map.info.origin);
00240
00241 map_it->initial_pose = transform;
00242
00243
00244 estimated_grids_view_.emplace_back(map_it->map);
00245
00246 ++map_it;
00247 }
00248
00249 ROS_ASSERT(estimated_grids_view_.size() == num_est_transforms);
00250 }
00251
00252 void MapMerging::fullMapUpdate(const nav_msgs::OccupancyGrid::ConstPtr& msg,
00253 PosedMap& map)
00254 {
00255 ROS_DEBUG("received full map update");
00256
00257 std::lock_guard<std::mutex> lock(map.mutex);
00258
00259
00260 boost::shared_lock<boost::shared_mutex> merging_lock(merging_mutex_);
00261
00262
00263
00264
00265
00266
00267
00268
00269 map.map = *msg;
00270
00271 ROS_DEBUG("Adjusting origin");
00272
00273 ROS_DEBUG("origin %f %f, (%f, %f, %f, %f)", map.map.info.origin.position.x,
00274 map.map.info.origin.position.y, map.map.info.origin.orientation.x,
00275 map.map.info.origin.orientation.y,
00276 map.map.info.origin.orientation.z,
00277 map.map.info.origin.orientation.w);
00278 ROS_DEBUG("init_pose %f %f, (%f, %f, %f, %f)", map.initial_pose.position.x,
00279 map.initial_pose.position.y, map.initial_pose.orientation.x,
00280 map.initial_pose.orientation.y, map.initial_pose.orientation.z,
00281 map.initial_pose.orientation.w);
00282 map.map.info.origin += map.initial_pose;
00283 ROS_DEBUG("origin %f %f, (%f, %f, %f, %f)", map.map.info.origin.position.x,
00284 map.map.info.origin.position.y, map.map.info.origin.orientation.x,
00285 map.map.info.origin.orientation.y,
00286 map.map.info.origin.orientation.z,
00287 map.map.info.origin.orientation.w);
00288 }
00289
00290 void MapMerging::partialMapUpdate(
00291 const map_msgs::OccupancyGridUpdate::ConstPtr& msg, PosedMap& map)
00292 {
00293 ROS_DEBUG("received partial map update");
00294
00295 if (msg->x < 0 || msg->y < 0) {
00296 ROS_ERROR("negative coordinates, invalid update. x: %d, y: %d", msg->x,
00297 msg->y);
00298 return;
00299 }
00300
00301 size_t x0 = static_cast<size_t>(msg->x);
00302 size_t y0 = static_cast<size_t>(msg->y);
00303 size_t xn = msg->width + x0;
00304 size_t yn = msg->height + y0;
00305
00306
00307
00308 std::lock_guard<std::mutex> lock(map.mutex);
00309
00310
00311
00312
00313
00314 size_t grid_xn = map.map.info.width;
00315 size_t grid_yn = map.map.info.height;
00316
00317 if (xn > grid_xn || x0 > grid_xn || yn > grid_yn || y0 > grid_yn) {
00318 ROS_WARN("received update doesn't fully fit into existing map, "
00319 "only part will be copied. received: [%lu, %lu], [%lu, %lu] "
00320 "map is: [0, %lu], [0, %lu]",
00321 x0, xn, y0, yn, grid_xn, grid_yn);
00322 }
00323
00324
00325 size_t i = 0;
00326 for (size_t y = y0; y < yn && y < grid_yn; ++y) {
00327 for (size_t x = x0; x < xn && x < grid_xn; ++x) {
00328 size_t idx = y * grid_xn + x;
00329 map.map.data[idx] = msg->data[i];
00330 ++i;
00331 }
00332 }
00333 }
00334
00335 std::string MapMerging::robotNameFromTopic(const std::string& topic)
00336 {
00337 return ros::names::parentNamespace(topic);
00338 }
00339
00340 geometry_msgs::Pose& operator+=(geometry_msgs::Pose& p1,
00341 const geometry_msgs::Pose& p2)
00342 {
00343 tf::Pose tf_p1, tf_p2;
00344 tf::poseMsgToTF(p1, tf_p1);
00345 tf::poseMsgToTF(p2, tf_p2);
00346
00347 tf_p1 *= tf_p2;
00348 tf::poseTFToMsg(tf_p1, p1);
00349
00350 return p1;
00351 }
00352
00353
00354 bool MapMerging::isRobotMapTopic(const ros::master::TopicInfo& topic)
00355 {
00356
00357 std::string topic_namespace = ros::names::parentNamespace(topic.name);
00358 bool is_map_topic =
00359 ros::names::append(topic_namespace, robot_map_topic_) == topic.name;
00360
00361
00362 auto pos = topic.name.find(robot_namespace_);
00363 bool contains_robot_namespace = pos != std::string::npos;
00364
00365
00366 bool is_occupancy_grid = topic.datatype == "nav_msgs/OccupancyGrid";
00367
00368
00369 bool is_our_topic = merged_map_publisher_.getTopic() == topic.name;
00370
00371 return is_occupancy_grid && !is_our_topic && contains_robot_namespace &&
00372 is_map_topic;
00373 }
00374
00375
00376
00377
00378 bool MapMerging::getInitPose(const std::string& name, geometry_msgs::Pose& pose)
00379 {
00380 std::string merging_namespace = ros::names::append(name, "map_merge");
00381 double yaw = 0.0;
00382
00383 bool success =
00384 ros::param::get(ros::names::append(merging_namespace, "init_pose_x"),
00385 pose.position.x) &&
00386 ros::param::get(ros::names::append(merging_namespace, "init_pose_y"),
00387 pose.position.y) &&
00388 ros::param::get(ros::names::append(merging_namespace, "init_pose_z"),
00389 pose.position.z) &&
00390 ros::param::get(ros::names::append(merging_namespace, "init_pose_yaw"),
00391 yaw);
00392
00393 pose.orientation = tf::createQuaternionMsgFromYaw(yaw);
00394
00395 return success;
00396 }
00397
00398
00399
00400
00401 void MapMerging::executemapMerging()
00402 {
00403 ros::Rate r(merging_rate_);
00404 while (node_.ok()) {
00405 mapMerging();
00406 r.sleep();
00407 }
00408 }
00409
00410 void MapMerging::executetopicSubscribing()
00411 {
00412 ros::Rate r(discovery_rate_);
00413 while (node_.ok()) {
00414 topicSubscribing();
00415 r.sleep();
00416 }
00417 }
00418
00419 void MapMerging::executeposeEstimation()
00420 {
00421 if (have_initial_poses_)
00422 return;
00423
00424 ros::Rate r(estimation_rate_);
00425 while (node_.ok()) {
00426 poseEstimation();
00427 r.sleep();
00428 }
00429 }
00430
00431
00432
00433
00434 void MapMerging::spin()
00435 {
00436 ros::spinOnce();
00437 std::thread merging_thr([this]() { executemapMerging(); });
00438 std::thread subscribing_thr([this]() { executetopicSubscribing(); });
00439 std::thread estimation_thr([this]() { executeposeEstimation(); });
00440 ros::spin();
00441 estimation_thr.join();
00442 merging_thr.join();
00443 subscribing_thr.join();
00444 }
00445
00446 }
00447
00448 int main(int argc, char** argv)
00449 {
00450 ros::init(argc, argv, "map_merge");
00451
00452 if (ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME,
00453 ros::console::levels::Debug)) {
00454 ros::console::notifyLoggerLevelsChanged();
00455 }
00456 map_merge::MapMerging map_merging;
00457 map_merging.spin();
00458 return 0;
00459 }