Use PoseGraphInterface::TrajectoryState from libcartographer (#910)
https://github.com/googlecartographer/rfcs/pull/35 - makes use of the trajectory state in `map_builder` and `node` - adds a service to query the trajectory states - follow-up to https://github.com/googlecartographer/cartographer/pull/1214 that takes the deleted state into account in the `/finish_trajectory` service (could crash otherwise)master
							parent
							
								
									c560d05d99
								
							
						
					
					
						commit
						8bbec6bff7
					
				| 
						 | 
				
			
			@ -147,7 +147,7 @@ void MapBuilderBridge::FinishTrajectory(const int trajectory_id) {
 | 
			
		|||
  LOG(INFO) << "Finishing trajectory with ID '" << trajectory_id << "'...";
 | 
			
		||||
 | 
			
		||||
  // Make sure there is a trajectory with 'trajectory_id'.
 | 
			
		||||
  CHECK_EQ(sensor_bridges_.count(trajectory_id), 1);
 | 
			
		||||
  CHECK(GetTrajectoryStates().count(trajectory_id));
 | 
			
		||||
  map_builder_->FinishTrajectory(trajectory_id);
 | 
			
		||||
  sensor_bridges_.erase(trajectory_id);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -194,15 +194,9 @@ void MapBuilderBridge::HandleSubmapQuery(
 | 
			
		|||
  response.status.code = cartographer_ros_msgs::StatusCode::OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::set<int> MapBuilderBridge::GetFrozenTrajectoryIds() {
 | 
			
		||||
  std::set<int> frozen_trajectory_ids;
 | 
			
		||||
  const auto node_poses = map_builder_->pose_graph()->GetTrajectoryNodePoses();
 | 
			
		||||
  for (const int trajectory_id : node_poses.trajectory_ids()) {
 | 
			
		||||
    if (map_builder_->pose_graph()->IsTrajectoryFrozen(trajectory_id)) {
 | 
			
		||||
      frozen_trajectory_ids.insert(trajectory_id);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return frozen_trajectory_ids;
 | 
			
		||||
std::map<int, ::cartographer::mapping::PoseGraphInterface::TrajectoryState>
 | 
			
		||||
MapBuilderBridge::GetTrajectoryStates() {
 | 
			
		||||
  return map_builder_->pose_graph()->GetTrajectoryStates();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cartographer_ros_msgs::SubmapList MapBuilderBridge::GetSubmapList() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -24,6 +24,7 @@
 | 
			
		|||
 | 
			
		||||
#include "cartographer/common/mutex.h"
 | 
			
		||||
#include "cartographer/mapping/map_builder_interface.h"
 | 
			
		||||
#include "cartographer/mapping/pose_graph_interface.h"
 | 
			
		||||
#include "cartographer/mapping/proto/trajectory_builder_options.pb.h"
 | 
			
		||||
#include "cartographer/mapping/trajectory_builder_interface.h"
 | 
			
		||||
#include "cartographer_ros/node_options.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +78,9 @@ class MapBuilderBridge {
 | 
			
		|||
      cartographer_ros_msgs::SubmapQuery::Request& request,
 | 
			
		||||
      cartographer_ros_msgs::SubmapQuery::Response& response);
 | 
			
		||||
 | 
			
		||||
  std::set<int> GetFrozenTrajectoryIds();
 | 
			
		||||
  std::map<int /* trajectory_id */,
 | 
			
		||||
           ::cartographer::mapping::PoseGraphInterface::TrajectoryState>
 | 
			
		||||
  GetTrajectoryStates();
 | 
			
		||||
  cartographer_ros_msgs::SubmapList GetSubmapList();
 | 
			
		||||
  std::unordered_map<int, LocalTrajectoryData> GetLocalTrajectoryData()
 | 
			
		||||
      EXCLUDES(mutex_);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,6 +26,7 @@
 | 
			
		|||
#include "cartographer/common/make_unique.h"
 | 
			
		||||
#include "cartographer/common/port.h"
 | 
			
		||||
#include "cartographer/common/time.h"
 | 
			
		||||
#include "cartographer/mapping/pose_graph_interface.h"
 | 
			
		||||
#include "cartographer/mapping/proto/submap_visualization.pb.h"
 | 
			
		||||
#include "cartographer/sensor/point_cloud.h"
 | 
			
		||||
#include "cartographer/transform/rigid_transform.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +82,8 @@ template <typename MessageType>
 | 
			
		|||
namespace carto = ::cartographer;
 | 
			
		||||
 | 
			
		||||
using carto::transform::Rigid3d;
 | 
			
		||||
using TrajectoryState =
 | 
			
		||||
    ::cartographer::mapping::PoseGraphInterface::TrajectoryState;
 | 
			
		||||
 | 
			
		||||
Node::Node(
 | 
			
		||||
    const NodeOptions& node_options,
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +112,8 @@ Node::Node(
 | 
			
		|||
      kFinishTrajectoryServiceName, &Node::HandleFinishTrajectory, this));
 | 
			
		||||
  service_servers_.push_back(node_handle_.advertiseService(
 | 
			
		||||
      kWriteStateServiceName, &Node::HandleWriteState, this));
 | 
			
		||||
  service_servers_.push_back(node_handle_.advertiseService(
 | 
			
		||||
      kGetTrajectoryStatesServiceName, &Node::HandleGetTrajectoryStates, this));
 | 
			
		||||
 | 
			
		||||
  scan_matched_point_cloud_publisher_ =
 | 
			
		||||
      node_handle_.advertise<sensor_msgs::PointCloud2>(
 | 
			
		||||
| 
						 | 
				
			
			@ -341,7 +346,6 @@ int Node::AddTrajectory(const TrajectoryOptions& options,
 | 
			
		|||
  AddExtrapolator(trajectory_id, options);
 | 
			
		||||
  AddSensorSamplers(trajectory_id, options);
 | 
			
		||||
  LaunchSubscribers(options, topics, trajectory_id);
 | 
			
		||||
  is_active_trajectory_[trajectory_id] = true;
 | 
			
		||||
  for (const auto& sensor_id : expected_sensor_ids) {
 | 
			
		||||
    subscribed_topics_.insert(sensor_id.id);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -444,26 +448,26 @@ bool Node::ValidateTopicNames(
 | 
			
		|||
 | 
			
		||||
cartographer_ros_msgs::StatusResponse Node::FinishTrajectoryUnderLock(
 | 
			
		||||
    const int trajectory_id) {
 | 
			
		||||
  auto trajectory_states = map_builder_bridge_.GetTrajectoryStates();
 | 
			
		||||
 | 
			
		||||
  cartographer_ros_msgs::StatusResponse status_response;
 | 
			
		||||
 | 
			
		||||
  // First, check if we can actually finish the trajectory.
 | 
			
		||||
  if (map_builder_bridge_.GetFrozenTrajectoryIds().count(trajectory_id)) {
 | 
			
		||||
  if (!(trajectory_states.count(trajectory_id))) {
 | 
			
		||||
    const std::string error =
 | 
			
		||||
        "Trajectory " + std::to_string(trajectory_id) + " doesn't exist.";
 | 
			
		||||
    LOG(ERROR) << error;
 | 
			
		||||
    status_response.code = cartographer_ros_msgs::StatusCode::NOT_FOUND;
 | 
			
		||||
    status_response.message = error;
 | 
			
		||||
    return status_response;
 | 
			
		||||
  } else if (trajectory_states.at(trajectory_id) == TrajectoryState::FROZEN) {
 | 
			
		||||
    const std::string error =
 | 
			
		||||
        "Trajectory " + std::to_string(trajectory_id) + " is frozen.";
 | 
			
		||||
    LOG(ERROR) << error;
 | 
			
		||||
    status_response.code = cartographer_ros_msgs::StatusCode::INVALID_ARGUMENT;
 | 
			
		||||
    status_response.message = error;
 | 
			
		||||
    return status_response;
 | 
			
		||||
  }
 | 
			
		||||
  if (is_active_trajectory_.count(trajectory_id) == 0) {
 | 
			
		||||
    const std::string error =
 | 
			
		||||
        "Trajectory " + std::to_string(trajectory_id) + " is not created yet.";
 | 
			
		||||
    LOG(ERROR) << error;
 | 
			
		||||
    status_response.code = cartographer_ros_msgs::StatusCode::NOT_FOUND;
 | 
			
		||||
    status_response.message = error;
 | 
			
		||||
    return status_response;
 | 
			
		||||
  }
 | 
			
		||||
  if (!is_active_trajectory_[trajectory_id]) {
 | 
			
		||||
  } else if (trajectory_states.at(trajectory_id) == TrajectoryState::FINISHED) {
 | 
			
		||||
    const std::string error = "Trajectory " + std::to_string(trajectory_id) +
 | 
			
		||||
                              " has already been finished.";
 | 
			
		||||
    LOG(ERROR) << error;
 | 
			
		||||
| 
						 | 
				
			
			@ -471,18 +475,27 @@ cartographer_ros_msgs::StatusResponse Node::FinishTrajectoryUnderLock(
 | 
			
		|||
        cartographer_ros_msgs::StatusCode::RESOURCE_EXHAUSTED;
 | 
			
		||||
    status_response.message = error;
 | 
			
		||||
    return status_response;
 | 
			
		||||
  } else if (trajectory_states.at(trajectory_id) == TrajectoryState::DELETED) {
 | 
			
		||||
    const std::string error =
 | 
			
		||||
        "Trajectory " + std::to_string(trajectory_id) + " has been deleted.";
 | 
			
		||||
    LOG(ERROR) << error;
 | 
			
		||||
    status_response.code =
 | 
			
		||||
        cartographer_ros_msgs::StatusCode::RESOURCE_EXHAUSTED;
 | 
			
		||||
    status_response.message = error;
 | 
			
		||||
    return status_response;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Shutdown the subscribers of this trajectory.
 | 
			
		||||
  for (auto& entry : subscribers_[trajectory_id]) {
 | 
			
		||||
    entry.subscriber.shutdown();
 | 
			
		||||
    subscribed_topics_.erase(entry.topic);
 | 
			
		||||
    LOG(INFO) << "Shutdown the subscriber of [" << entry.topic << "]";
 | 
			
		||||
  // A valid case with no subscribers is e.g. if we just visualize states.
 | 
			
		||||
  if (subscribers_.count(trajectory_id)) {
 | 
			
		||||
    for (auto& entry : subscribers_[trajectory_id]) {
 | 
			
		||||
      entry.subscriber.shutdown();
 | 
			
		||||
      subscribed_topics_.erase(entry.topic);
 | 
			
		||||
      LOG(INFO) << "Shutdown the subscriber of [" << entry.topic << "]";
 | 
			
		||||
    }
 | 
			
		||||
    CHECK_EQ(subscribers_.erase(trajectory_id), 1);
 | 
			
		||||
  }
 | 
			
		||||
  CHECK_EQ(subscribers_.erase(trajectory_id), 1);
 | 
			
		||||
  CHECK(is_active_trajectory_.at(trajectory_id));
 | 
			
		||||
  map_builder_bridge_.FinishTrajectory(trajectory_id);
 | 
			
		||||
  is_active_trajectory_[trajectory_id] = false;
 | 
			
		||||
  const std::string message =
 | 
			
		||||
      "Finished trajectory " + std::to_string(trajectory_id) + ".";
 | 
			
		||||
  status_response.code = cartographer_ros_msgs::StatusCode::OK;
 | 
			
		||||
| 
						 | 
				
			
			@ -550,10 +563,41 @@ int Node::AddOfflineTrajectory(
 | 
			
		|||
      map_builder_bridge_.AddTrajectory(expected_sensor_ids, options);
 | 
			
		||||
  AddExtrapolator(trajectory_id, options);
 | 
			
		||||
  AddSensorSamplers(trajectory_id, options);
 | 
			
		||||
  is_active_trajectory_[trajectory_id] = true;
 | 
			
		||||
  return trajectory_id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Node::HandleGetTrajectoryStates(
 | 
			
		||||
    ::cartographer_ros_msgs::GetTrajectoryStates::Request& request,
 | 
			
		||||
    ::cartographer_ros_msgs::GetTrajectoryStates::Response& response) {
 | 
			
		||||
  using TrajectoryState =
 | 
			
		||||
      ::cartographer::mapping::PoseGraphInterface::TrajectoryState;
 | 
			
		||||
  carto::common::MutexLocker lock(&mutex_);
 | 
			
		||||
  response.status.code = ::cartographer_ros_msgs::StatusCode::OK;
 | 
			
		||||
  response.trajectory_states.header.stamp = ros::Time::now();
 | 
			
		||||
  for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {
 | 
			
		||||
    response.trajectory_states.trajectory_id.push_back(entry.first);
 | 
			
		||||
    switch (entry.second) {
 | 
			
		||||
      case TrajectoryState::ACTIVE:
 | 
			
		||||
        response.trajectory_states.trajectory_state.push_back(
 | 
			
		||||
            ::cartographer_ros_msgs::TrajectoryStates::ACTIVE);
 | 
			
		||||
        break;
 | 
			
		||||
      case TrajectoryState::FINISHED:
 | 
			
		||||
        response.trajectory_states.trajectory_state.push_back(
 | 
			
		||||
            ::cartographer_ros_msgs::TrajectoryStates::FINISHED);
 | 
			
		||||
        break;
 | 
			
		||||
      case TrajectoryState::FROZEN:
 | 
			
		||||
        response.trajectory_states.trajectory_state.push_back(
 | 
			
		||||
            ::cartographer_ros_msgs::TrajectoryStates::FROZEN);
 | 
			
		||||
        break;
 | 
			
		||||
      case TrajectoryState::DELETED:
 | 
			
		||||
        response.trajectory_states.trajectory_state.push_back(
 | 
			
		||||
            ::cartographer_ros_msgs::TrajectoryStates::DELETED);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool Node::HandleFinishTrajectory(
 | 
			
		||||
    ::cartographer_ros_msgs::FinishTrajectory::Request& request,
 | 
			
		||||
    ::cartographer_ros_msgs::FinishTrajectory::Response& response) {
 | 
			
		||||
| 
						 | 
				
			
			@ -578,9 +622,9 @@ bool Node::HandleWriteState(
 | 
			
		|||
 | 
			
		||||
void Node::FinishAllTrajectories() {
 | 
			
		||||
  carto::common::MutexLocker lock(&mutex_);
 | 
			
		||||
  for (auto& entry : is_active_trajectory_) {
 | 
			
		||||
    const int trajectory_id = entry.first;
 | 
			
		||||
    if (entry.second) {
 | 
			
		||||
  for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {
 | 
			
		||||
    if (entry.second == TrajectoryState::ACTIVE) {
 | 
			
		||||
      const int trajectory_id = entry.first;
 | 
			
		||||
      CHECK_EQ(FinishTrajectoryUnderLock(trajectory_id).code,
 | 
			
		||||
               cartographer_ros_msgs::StatusCode::OK);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -595,9 +639,17 @@ bool Node::FinishTrajectory(const int trajectory_id) {
 | 
			
		|||
 | 
			
		||||
void Node::RunFinalOptimization() {
 | 
			
		||||
  {
 | 
			
		||||
    carto::common::MutexLocker lock(&mutex_);
 | 
			
		||||
    for (const auto& entry : is_active_trajectory_) {
 | 
			
		||||
      CHECK(!entry.second);
 | 
			
		||||
    for (const auto& entry : map_builder_bridge_.GetTrajectoryStates()) {
 | 
			
		||||
      const int trajectory_id = entry.first;
 | 
			
		||||
      if (entry.second == TrajectoryState::ACTIVE) {
 | 
			
		||||
        LOG(WARNING)
 | 
			
		||||
            << "Can't run final optimization if there are one or more active "
 | 
			
		||||
               "trajectories. Trying to finish trajectory with ID "
 | 
			
		||||
            << std::to_string(trajectory_id) << " now.";
 | 
			
		||||
        CHECK(FinishTrajectory(trajectory_id))
 | 
			
		||||
            << "Failed to finish trajectory with ID "
 | 
			
		||||
            << std::to_string(trajectory_id) << ".";
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  // Assuming we are not adding new data anymore, the final optimization
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@
 | 
			
		|||
#include "cartographer_ros/node_options.h"
 | 
			
		||||
#include "cartographer_ros/trajectory_options.h"
 | 
			
		||||
#include "cartographer_ros_msgs/FinishTrajectory.h"
 | 
			
		||||
#include "cartographer_ros_msgs/GetTrajectoryStates.h"
 | 
			
		||||
#include "cartographer_ros_msgs/SensorTopics.h"
 | 
			
		||||
#include "cartographer_ros_msgs/StartTrajectory.h"
 | 
			
		||||
#include "cartographer_ros_msgs/StatusResponse.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -138,6 +139,10 @@ class Node {
 | 
			
		|||
      cartographer_ros_msgs::FinishTrajectory::Response& response);
 | 
			
		||||
  bool HandleWriteState(cartographer_ros_msgs::WriteState::Request& request,
 | 
			
		||||
                        cartographer_ros_msgs::WriteState::Response& response);
 | 
			
		||||
  bool HandleGetTrajectoryStates(
 | 
			
		||||
      ::cartographer_ros_msgs::GetTrajectoryStates::Request& request,
 | 
			
		||||
      ::cartographer_ros_msgs::GetTrajectoryStates::Response& response);
 | 
			
		||||
 | 
			
		||||
  // Returns the set of SensorIds expected for a trajectory.
 | 
			
		||||
  // 'SensorId::id' is the expected ROS topic name.
 | 
			
		||||
  std::set<::cartographer::mapping::TrajectoryBuilderInterface::SensorId>
 | 
			
		||||
| 
						 | 
				
			
			@ -203,7 +208,6 @@ class Node {
 | 
			
		|||
  std::unordered_map<int, TrajectorySensorSamplers> sensor_samplers_;
 | 
			
		||||
  std::unordered_map<int, std::vector<Subscriber>> subscribers_;
 | 
			
		||||
  std::unordered_set<std::string> subscribed_topics_;
 | 
			
		||||
  std::unordered_map<int, bool> is_active_trajectory_ GUARDED_BY(mutex_);
 | 
			
		||||
 | 
			
		||||
  // We have to keep the timer handles of ::ros::WallTimers around, otherwise
 | 
			
		||||
  // they do not fire.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,7 @@ constexpr char kSubmapListTopic[] = "submap_list";
 | 
			
		|||
constexpr char kSubmapQueryServiceName[] = "submap_query";
 | 
			
		||||
constexpr char kStartTrajectoryServiceName[] = "start_trajectory";
 | 
			
		||||
constexpr char kWriteStateServiceName[] = "write_state";
 | 
			
		||||
constexpr char kGetTrajectoryStatesServiceName[] = "get_trajectory_states";
 | 
			
		||||
constexpr char kTrajectoryNodeListTopic[] = "trajectory_node_list";
 | 
			
		||||
constexpr char kLandmarkPosesListTopic[] = "landmark_poses_list";
 | 
			
		||||
constexpr char kConstraintListTopic[] = "constraint_list";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,14 +34,16 @@ add_message_files(
 | 
			
		|||
    SubmapTexture.msg
 | 
			
		||||
    SensorTopics.msg
 | 
			
		||||
    TrajectoryOptions.msg
 | 
			
		||||
    TrajectoryStates.msg
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
add_service_files(
 | 
			
		||||
  FILES
 | 
			
		||||
    SubmapQuery.srv
 | 
			
		||||
    FinishTrajectory.srv
 | 
			
		||||
    GetTrajectoryStates.srv
 | 
			
		||||
    StartTrajectory.srv
 | 
			
		||||
    WriteState.srv
 | 
			
		||||
    SubmapQuery.srv
 | 
			
		||||
    WriteState.srv  
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
generate_messages(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,22 @@
 | 
			
		|||
# Copyright 2018 The Cartographer Authors
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the 'License');
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an 'AS IS' BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
 | 
			
		||||
uint8 ACTIVE = 0
 | 
			
		||||
uint8 FINISHED = 1
 | 
			
		||||
uint8 FROZEN = 2
 | 
			
		||||
uint8 DELETED = 3
 | 
			
		||||
 | 
			
		||||
std_msgs/Header header
 | 
			
		||||
int32[] trajectory_id
 | 
			
		||||
uint8[] trajectory_state
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,18 @@
 | 
			
		|||
# Copyright 2018 The Cartographer Authors
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#      http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
cartographer_ros_msgs/StatusResponse status
 | 
			
		||||
cartographer_ros_msgs/TrajectoryStates trajectory_states
 | 
			
		||||
		Loading…
	
		Reference in New Issue