cartographer基于grpc、ros的rviz地图显示流程

一个子图的更新显示,跨越了从cartographer_rviz->cartographer_ros->cartographer。

其中cartographer_rviz->cartographer_ros之间是ros架构的。
cartographer_ros->cartographer直接调用了cartographer开放的接口。

1-首先cartographer_rviz写了一个rviz的显示插件,即submap_display,并导出:

PLUGINLIB_EXPORT_CLASS(cartographer_rviz::SubmapsDisplay, ::rviz::Display)

submap_display.cc中的函数update用来进行地图更新的,

 // 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;
  }

重点是这个函数 it->second->MaybeFetchTexture(&client),它是来自DrawableSubmap类的函数。这个client是啥呢?我们看他的初始化,可以知道这是一个请求ros服务的客户端,请求的服务名称为”/submap_query“。

  client_ = update_nh_.serviceClient<::cartographer_ros_msgs::SubmapQuery>(
      submap_query_service_property_->getStdString());

我们继续把DrawableSubmap::MaybeFetchTexture函数展开:

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;
}

2-重点是这个函数 ::cartographer_ros::FetchSubmapTextures(id_, client),id是submap的id,client就是上面传下来的。我们继续在cartographer_ros里展开这个函数。

std::unique_ptr<::cartographer::io::SubmapTextures> FetchSubmapTextures(
    const ::cartographer::mapping::SubmapId& submap_id,
    ros::ServiceClient* client) {
    
    
  ::cartographer_ros_msgs::SubmapQuery srv;
  srv.request.trajectory_id = submap_id.trajectory_id;
  srv.request.submap_index = submap_id.submap_index;
  if (!client->call(srv) ||
      srv.response.status.code != ::cartographer_ros_msgs::StatusCode::OK) {
    
    
    return nullptr;
  }
  if (srv.response.textures.empty()) {
    
    
    return nullptr;
  }
  auto response = absl::make_unique<::cartographer::io::SubmapTextures>();
  response->version = srv.response.submap_version;
  for (const auto& texture : srv.response.textures) {
    
    
    const std::string compressed_cells(texture.cells.begin(),
                                       texture.cells.end());
    response->textures.emplace_back(::cartographer::io::SubmapTexture{
    
    
        ::cartographer::io::UnpackTextureData(compressed_cells, texture.width,
                                              texture.height),
        texture.width, texture.height, texture.resolution,
        ToRigid3d(texture.slice_pose)});
  }
  return response;
}

重点是这个函数client->call(srv),srv.responce就是我们得到的子图数据。那这个客户端请求的服务函数在哪呢?
我们在程序里搜素“submap_query“,可以找到

constexpr char kSubmapQueryServiceName[] = "submap_query";
  service_servers_.push_back(node_handle_.advertiseService(
      kSubmapQueryServiceName, &Node::HandleSubmapQuery, this));

这个服务端的函数是Node::HandleSubmapQuery,我们继续把它展开

bool Node::HandleSubmapQuery(
    ::cartographer_ros_msgs::SubmapQuery::Request& request,
    ::cartographer_ros_msgs::SubmapQuery::Response& response) {
    
    
  absl::MutexLock lock(&mutex_);
  map_builder_bridge_.HandleSubmapQuery(request, response);
  return true;
}

可以看到这个变量继续向下传递,我们继续展开 map_builder_bridge_.HandleSubmapQuery(request, response);

void MapBuilderBridge::HandleSubmapQuery(
    cartographer_ros_msgs::SubmapQuery::Request& request,
    cartographer_ros_msgs::SubmapQuery::Response& response) {
    
    
  cartographer::mapping::proto::SubmapQuery::Response response_proto;
  cartographer::mapping::SubmapId submap_id{
    
    request.trajectory_id,
                                            request.submap_index};
  const std::string error =
      map_builder_->SubmapToProto(submap_id, &response_proto);
  if (!error.empty()) {
    
    
    LOG(ERROR) << error;
    response.status.code = cartographer_ros_msgs::StatusCode::NOT_FOUND;
    response.status.message = error;
    return;
  }

  response.submap_version = response_proto.submap_version();
  for (const auto& texture_proto : response_proto.textures()) {
    
    
    response.textures.emplace_back();
    auto& texture = response.textures.back();
    texture.cells.insert(texture.cells.begin(), texture_proto.cells().begin(),
                         texture_proto.cells().end());
    texture.width = texture_proto.width();
    texture.height = texture_proto.height();
    texture.resolution = texture_proto.resolution();
    texture.slice_pose = ToGeometryMsgPose(
        cartographer::transform::ToRigid3(texture_proto.slice_pose()));
  }
  response.status.message = "Success.";
  response.status.code = cartographer_ros_msgs::StatusCode::OK;
}

3-重点是map_builder_->SubmapToProto(submap_id, &response_proto);这句话,这里的map_builder_是cartographer里MapBuilderStub类实例化的对象,我们在cartographer里看这个函数SubmapToProto:

std::string MapBuilderStub::SubmapToProto(
    const mapping::SubmapId& submap_id,
    mapping::proto::SubmapQuery::Response* submap_query_response) {
    
    
  proto::GetSubmapRequest request;
  submap_id.ToProto(request.mutable_submap_id());
  async_grpc::Client<handlers::GetSubmapSignature> client(client_channel_);
  CHECK(client.Write(request));
  submap_query_response->CopyFrom(client.response().submap_query_response());
  return client.response().error_msg();
}

可以看到这里使用async_grpc::Client类实例化的对象通过grpc调用远程的grpc服务器,最终返回的client.response().submap_query_response()赋值给submap_query_response,至此一个submap的请求传递完毕。

4-那我们自己写一个客户端请求getsubmap要怎么写呢?

第1种方法:我认为可以使用async_grpc库的Client,创建一个client_channel_,然后就像上面的例子:

我们看下我们这个要传递查询的数据在proto的那里,在这里
cartographer/mapping/proto/submap_visualization.proto。
来看下这个内容

message SubmapQuery {
    
    
  message Request {
    
    
    // Index into 'SubmapList.trajectory(trajectory_id).submap'.
    int32 submap_index = 1;
    // Index into 'TrajectoryList.trajectory'.
    int32 trajectory_id = 2;
  }

  message Response {
    
    
    // Version of the given submap, higher means newer.
    int32 submap_version = 2;

    // Texture that visualizes a grid of a submap.
    message SubmapTexture {
    
    
      // GZipped map data, in row-major order, starting with (0,0). Each cell
      // consists of two bytes: value (premultiplied by alpha) and alpha.
      bytes cells = 1;

      // Dimensions of the grid in cells.
      int32 width = 2;
      int32 height = 3;

      // Size of one cell in meters.
      double resolution = 4;

      // Pose of the resolution*width x resolution*height rectangle in the
      // submap frame.
      transform.proto.Rigid3d slice_pose = 5;
    }

    // When multiple textures are present, high resolution comes first.
    repeated SubmapTexture textures = 10;

    // Error message in response to malformed requests.
    string error_message = 8;
  }

  Request request = 1;
  Response response = 2;
}

我们再看下这个请求rpc的proto在哪里?在这里cartographer/cloud/proto/map_builder_service.proto

  // Retrieves a single submap.
  rpc GetSubmap(GetSubmapRequest) returns (GetSubmapResponse);

来看async_grpc::client的构造

  async_grpc::Client<handlers::GetSubmapSignature> client(client_channel_);

来看client_channel_的构造

    : client_channel_(::grpc::CreateChannel(
          server_address, ::grpc::InsecureChannelCredentials())),

来看GetSubmapSignature的构造

DEFINE_HANDLER_SIGNATURE(
    GetSubmapSignature, proto::GetSubmapRequest, proto::GetSubmapResponse,
    "/cartographer.cloud.proto.MapBuilderService/GetSubmap")

这里使用了DEFINE_HANDLER_SIGNATURE宏,来看看这个宏

#define DEFINE_HANDLER_SIGNATURE(traitsName, incomingType, outgoingType, \
                                 methodName)                             \
  struct traitsName {                                                    \
    using IncomingType = incomingType;                                   \
    using OutgoingType = outgoingType;                                   \
    static const char* MethodName() { return methodName; }               \
  };
第2种方法:我们知道proto的类型,使用常规的写法也可以。

至此整个流程解释完毕。

猜你喜欢

转载自blog.csdn.net/windxf/article/details/108883957