天天看點

cartographer_rviz submap地圖繪制

cartographer寫了一個插件,其中的類函數是SubmapsDisplay,這個類中有2個比較重要的函數,一個是processMessage,用來接收每個子圖之間的位置關系,一個是update,用來更新每個子圖的紋理

SubmapsDisplay::processMessage():

void SubmapsDisplay::processMessage(
    const ::cartographer_ros_msgs::SubmapList::ConstPtr& msg) {
  absl::MutexLock locker(&mutex_);
  map_frame_ = absl::make_unique<std::string>(msg->header.frame_id);
  // In case Cartographer node is relaunched, destroy trajectories from the
  // previous instance.
  for (const ::cartographer_ros_msgs::SubmapEntry& submap_entry : msg->submap) {
    const size_t trajectory_id = submap_entry.trajectory_id;
    if (trajectories_.count(trajectory_id) == 0) {
      continue;
    }
    const auto& trajectory_submaps = trajectories_[trajectory_id]->submaps;
    const auto it = trajectory_submaps.find(submap_entry.submap_index);
    if (it != trajectory_submaps.end() &&
        it->second->version() > submap_entry.submap_version) {
      // Versions should only increase unless Cartographer restarted.
      trajectories_.clear();
      break;
    }
  }
  using ::cartographer::mapping::SubmapId;
  std::set<SubmapId> listed_submaps;
  std::set<int> listed_trajectories;
  for (const ::cartographer_ros_msgs::SubmapEntry& submap_entry : msg->submap) {
    const SubmapId id{submap_entry.trajectory_id, submap_entry.submap_index};
    listed_submaps.insert(id);
    listed_trajectories.insert(submap_entry.trajectory_id);
    if (trajectories_.count(id.trajectory_id) == 0) {
      trajectories_.insert(std::make_pair(
          id.trajectory_id,
          absl::make_unique<Trajectory>(
              absl::make_unique<::rviz::BoolProperty>(
                  QString("Trajectory %1").arg(id.trajectory_id),
                  visibility_all_enabled_->getBool(),
                  QString(
                      "List of all submaps in Trajectory %1. The checkbox "
                      "controls whether all submaps in this trajectory should "
                      "be displayed or not.")
                      .arg(id.trajectory_id),
                  trajectories_category_),
              pose_markers_all_enabled_->getBool())));
    }
    auto& trajectory_visibility = trajectories_[id.trajectory_id]->visibility;
    auto& trajectory_submaps = trajectories_[id.trajectory_id]->submaps;
    auto& pose_markers_visibility =
        trajectories_[id.trajectory_id]->pose_markers_visibility;
    if (trajectory_submaps.count(id.submap_index) == 0) {
      // TODO(ojura): Add RViz properties for adjusting submap pose axes
      constexpr float kSubmapPoseAxesLength = 0.3f;
      constexpr float kSubmapPoseAxesRadius = 0.06f;
      trajectory_submaps.emplace(
          id.submap_index,
          absl::make_unique<DrawableSubmap>(
              id, context_, map_node_, trajectory_visibility.get(),
              trajectory_visibility->getBool(),
              pose_markers_visibility->getBool(), kSubmapPoseAxesLength,
              kSubmapPoseAxesRadius));
      trajectory_submaps.at(id.submap_index)
          ->SetSliceVisibility(0, slice_high_resolution_enabled_->getBool());
      trajectory_submaps.at(id.submap_index)
          ->SetSliceVisibility(1, slice_low_resolution_enabled_->getBool());
    }
    trajectory_submaps.at(id.submap_index)->Update(msg->header, submap_entry);
  }
  // Remove all deleted trajectories not mentioned in the SubmapList.
  for (auto it = trajectories_.begin(); it != trajectories_.end();) {
    if (listed_trajectories.count(it->first) == 0) {
      it = trajectories_.erase(it);
    } else {
      ++it;
    }
  }
  // Remove all submaps not mentioned in the SubmapList.
  for (const auto& trajectory_by_id : trajectories_) {
    const int trajectory_id = trajectory_by_id.first;
    auto& trajectory_submaps = trajectory_by_id.second->submaps;
    for (auto it = trajectory_submaps.begin();
         it != trajectory_submaps.end();) {
      if (listed_submaps.count(
              SubmapId{static_cast<int>(trajectory_id), it->first}) == 0) {
        it = trajectory_submaps.erase(it);
      } else {
        ++it;
      }
    }
  }
}
           

processMessage函數中,這句話就是更新的子圖的位置

繼續跟進這個Update,它把子圖的位置指派給了submap_node_,submap_node_是繪圖類orge的一個圖元。

void DrawableSubmap::Update(
    const ::std_msgs::Header& header,
    const ::cartographer_ros_msgs::SubmapEntry& metadata) {
  absl::MutexLock locker(&mutex_);
  metadata_version_ = metadata.submap_version;
  pose_ = ::cartographer_ros::ToRigid3d(metadata.pose);
  submap_node_->setPosition(ToOgre(pose_.translation()));
  submap_node_->setOrientation(ToOgre(pose_.rotation()));
  display_context_->queueRender();
  visibility_->setName(
      QString("%1.%2").arg(id_.submap_index).arg(metadata_version_));
  visibility_->setDescription(
      QString("Toggle visibility of this individual submap.<br><br>"
              "Trajectory %1, submap %2, submap version %3")
          .arg(id_.trajectory_id)
          .arg(id_.submap_index)
          .arg(metadata_version_));
}
           

繼續看這個函數,SubmapsDisplay::update():,它更新了子圖的紋理

void SubmapsDisplay::update(const float wall_dt, const float ros_dt) {
  absl::MutexLock locker(&mutex_);
  // Schedule fetching of new submap textures.
  for (const auto& trajectory_by_id : trajectories_) {
    int num_ongoing_requests = 0;
    for (const auto& submap_entry : trajectory_by_id.second->submaps) {
      if (submap_entry.second->QueryInProgress()) {
        ++num_ongoing_requests;
      }
    }
    for (auto it = trajectory_by_id.second->submaps.rbegin();
         it != trajectory_by_id.second->submaps.rend() &&
         num_ongoing_requests < kMaxOnGoingRequestsPerTrajectory;
         ++it) {
      if (it->second->MaybeFetchTexture(&client_)) {
        ++num_ongoing_requests;
      }
    }
  }
  if (map_frame_ == nullptr) {
    return;
  }
  // Update the fading by z distance.
  const ros::Time kLatest(0);
  try {
    const ::geometry_msgs::TransformStamped transform_stamped =
        tf_buffer_.lookupTransform(
            *map_frame_, tracking_frame_property_->getStdString(), kLatest);
    for (auto& trajectory_by_id : trajectories_) {
      for (auto& submap_entry : trajectory_by_id.second->submaps) {
        submap_entry.second->SetAlpha(
            transform_stamped.transform.translation.z,
            fade_out_start_distance_in_meters_->getFloat());
      }
    }
  } catch (const tf2::TransformException& ex) {
    ROS_WARN_THROTTLE(1., "Could not compute submap fading: %s", ex.what());
  }
  // Update the map frame to fixed frame transform.
  Ogre::Vector3 position;
  Ogre::Quaternion orientation;
  if (context_->getFrameManager()->getTransform(*map_frame_, kLatest, position,
                                                orientation)) {
    map_node_->setPosition(position);
    map_node_->setOrientation(orientation);
    context_->queueRender();
  }
}
           

其中比較重要的一個函數,

繼續跟進這個函數,它擷取了子圖的紋理,然後通過EMIT RequestSucceeded,讓UpdateSceneNode繼續處理接收到的子圖

bool DrawableSubmap::MaybeFetchTexture(ros::ServiceClient* const client) {
  absl::MutexLock locker(&mutex_);
  // Received metadata version can also be lower if we restarted Cartographer.
  const bool newer_version_available =
      submap_textures_ == nullptr ||
      submap_textures_->version != metadata_version_;
  const std::chrono::milliseconds now =
      std::chrono::duration_cast<std::chrono::milliseconds>(
          std::chrono::system_clock::now().time_since_epoch());
  const bool recently_queried =
      last_query_timestamp_ + kMinQueryDelayInMs > now;
  if (!newer_version_available || recently_queried || query_in_progress_) {
    return false;
  }
  query_in_progress_ = true;
  last_query_timestamp_ = now;
  rpc_request_future_ = std::async(std::launch::async, [this, client]() {
    std::unique_ptr<::cartographer::io::SubmapTextures> submap_textures =
        ::cartographer_ros::FetchSubmapTextures(id_, client);
    absl::MutexLock locker(&mutex_);
    query_in_progress_ = false;
    if (submap_textures != nullptr) {
      // We emit a signal to update in the right thread, and pass via the
      // 'submap_texture_' member to simplify the signal-slot connection
      // slightly.
      submap_textures_ = std::move(submap_textures);
      Q_EMIT RequestSucceeded();
    }
  });
  return true;
}
           

來看一下UpdateSceneNode這個函數,ogre_slices_更新紋理,其中它的成員變量slice_node_是上一步submap_node_的子圖元,他們之間沒有坐标偏移,可以認為他們是同一坐标系。

void DrawableSubmap::UpdateSceneNode() {
  absl::MutexLock locker(&mutex_);
  for (size_t slice_index = 0; slice_index < ogre_slices_.size() &&
                               slice_index < submap_textures_->textures.size();
       ++slice_index) {
    ogre_slices_[slice_index]->Update(submap_textures_->textures[slice_index]);
  }
  display_context_->queueRender();
}
           

至此我們知道了cartographer_rviz的繪制方式,就是submap_node_控制一個子圖的繪制,每個子圖有一個子圖原點,這個是submap_query中slice_pose。而每個子圖之間是有微弱旋轉的,而控制旋轉的中心也是這個slice_pose。而每個子圖之間的旋轉和平移可以通過/submap_list的pose得到。