【学习公司cartographer改动代码之入口差异】

学习公司cartographer改动代码之入口差异



前言

由于这段时间学习公司自研cartographer的内容,有很多内容代码与原使用方式不同,例如:本章的配置文件加载方式与原cartographer加载方式的不同,接下来我先以原cartographer的使用方式,然后再以我们自研的作对比。


一、代码入口

1.原cartographer

cartographer原代码入口主要以node_main.cc和node_grpc_main.cc,由于node_grpc_main.cc不怎么使用,这里就不再赘述了;查看文件主要先看main函数,

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);// 初始化 glog
  google::ParseCommandLineFlags(&argc, &argv, true);// 初始化 gflags

  CHECK(!FLAGS_configuration_directory.empty())
      << "-configuration_directory is missing.";
  CHECK(!FLAGS_configuration_basename.empty())
      << "-configuration_basename is missing.";

  ::ros::init(argc, argv, "cartographer_node");
  ::ros::start();

  cartographer_ros::ScopedRosLogSink ros_log_sink;//使谷歌日志记录(glog)使用ROS日志记录输出
  cartographer_ros::Run();
  ::ros::shutdown();
}

而前几行都是ros和google log使用,其中真正步入正题内容的函数就是cartographer_ros::Run()调用,进入Run()函数,具体代码作用以注释

void Run() {
  constexpr double kTfBufferCacheTimeInSeconds = 10.;
  tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};//tf变换的历史要保存10秒
  tf2_ros::TransformListener tf(tf_buffer);//负责tf接收
  NodeOptions node_options;//节点参数
  TrajectoryOptions trajectory_options;//轨迹参数
  std::tie(node_options, trajectory_options) =
      LoadOptions(FLAGS_configuration_directory, FLAGS_configuration_basename);//std::tie把node_options和trajectory_options整合成一个std::tuple,从而实现批量赋值(node_options和 trajectory_options一起赋值)

  auto map_builder =
      cartographer::mapping::CreateMapBuilder(node_options.map_builder_options);//创建一个建图指针
  Node node(node_options, std::move(map_builder), &tf_buffer,
            FLAGS_collect_metrics);//调用node主函数,这里是一个关键步骤,如果没有这一行代码,很多话题的发布,服务的调用都是不能实现的。
  if (!FLAGS_load_state_filename.empty()) {// 如果加载了pbstream文件, 就执行这个函数
    node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
  }

  if (FLAGS_start_trajectory_with_default_topics) {//默认使用topic开始轨迹,一开始是true
    node.StartTrajectoryWithDefaultTopics(trajectory_options);
  }

  ::ros::spin();

  node.FinishAllTrajectories();// 结束所有处于活动状态的轨迹
  node.RunFinalOptimization();// 当所有的轨迹结束时, 再执行一次全局优化

  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }
}

原cartographer的入口差不多就这些了,下面进行我们修改的讲解。

2.修改cartographer

最开始我以为我们的入口是node_grpc_main.cc,但后来发现也不对,我们的node_main.cc文件做了大量修改,应该就是这个部分,但是入口函数文件代码却完全不一样,以下是main函数

int main(int argc, char** argv) {
  google::InitGoogleLogging(argv[0]);
  google::ParseCommandLineFlags(&argc, &argv, true);

  //LOG(INFO) << "Peak.ding FLAGS_configuration_directory " << FLAGS_configuration_directory;
  //LOG(INFO) << "Peak.ding FLAGS_configuration_slam_basename " << FLAGS_configuration_slam_basename;
  //LOG(INFO) << "Peak.ding FLAGS_configuration_localization_basename " << FLAGS_configuration_localization_basename;

  CHECK(!FLAGS_configuration_directory.empty())
      << "-configuration_directory is missing.";
  CHECK(!FLAGS_configuration_slam_basename.empty())
      << "-FLAGS_configuration_slam_basename is missing.";
  CHECK(!FLAGS_configuration_localization_basename.empty())
      << "-FLAGS_configuration_localization_basename is missing.";

  ::ros::init(argc, argv, "cartographer_node");
  ::ros::start();

  cartographer_ros::ScopedRosLogSink ros_log_sink;
  cartographer_ros::Run();
  ::ros::shutdown();
}

其中main()函数与原cartographer的一致,我这里就不再解释,把目光转到Run()函数里面

void Run() {
  constexpr double kTfBufferCacheTimeInSeconds = 10.;
  tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
  tf2_ros::TransformListener tf(tf_buffer);
  NodeOptions node_options;
  //node_options = LoadNodeOptions(FLAGS_configuration_directory,
  //                 FLAGS_configuration_basename_server);
  
  TrajectoryOptions trajectory_options;
  std::tie(node_options, trajectory_options) =
       LoadOptions(FLAGS_configuration_directory,
                   FLAGS_configuration_basename_server);
  
  //auto map_builder = absl::make_unique<cartographer::mapping::MapBuilder>(
  //    node_options.map_builder_options);
  
  cartographer::cloud::proto::MapBuilderServerOptions
      map_builder_server_options =
          cartographer::cloud::LoadMapBuilderServerOptions(
              FLAGS_configuration_directory,
              FLAGS_configuration_basename_server);

  //LOG(INFO) << "Peak.ding map_builder";
  auto map_builder =
      std::make_shared<cartographer::mapping::MapBuilder>(  // okagv default is
                                                            // absl::make_unique
         const_cast<::cartographer::mapping::proto::MapBuilderOptions&>(map_builder_server_options.map_builder_options()));

  std::unique_ptr<cartographer::cloud::MapBuilderServerInterface>
      map_builder_server =
          absl::make_unique<cartographer::cloud::MapBuilderServer>(
              map_builder_server_options, map_builder);

  //LOG(INFO) << "Peak.ding map_builder_server start";
  map_builder_server->Start();
  
  //Node node(node_options, std::move(map_builder), &tf_buffer, FLAGS_collect_metrics);
  //LOG(INFO) << "Peak.ding Node start";
  Node node(node_options, map_builder, &tf_buffer, FLAGS_collect_metrics);

  /*          
  if (!FLAGS_load_state_filename.empty()) {
    node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
  }
  */
  //LOG(INFO) << "Peak.ding root_file_directory " << node_options.root_file_directory;

  if (!FLAGS_configuration_directory.empty()) {
    node.SetConfigurationParam(FLAGS_configuration_directory,
                               FLAGS_configuration_basename_server);
  }

  if (!node_options.root_file_directory.empty()) {
    //node.LoadMaps(node_options.root_file_directory + "/map");
  }

  if (FLAGS_start_trajectory_with_default_topics) {
    // node.StartTrajectoryWithDefaultTopics(trajectory_options);
  }

  //ros::AsyncSpinner async_spinner(2);
  //async_spinner.start();
  //::ros::waitForShutdown();

  //ros::MultiThreadedSpinner spinner(2);
  //spinner.spin();
  //LOG(INFO) << "Peak.ding Ros Start";
  ::ros::spin();
  map_builder_server->Shutdown();
  node.FinishAllTrajectories();
  node.RunFinalOptimization();

  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }
}

因这部分与原cartographer不同,这里我就一点一点的解释

  cartographer::cloud::proto::MapBuilderServerOptions
      map_builder_server_options =
          cartographer::cloud::LoadMapBuilderServerOptions(
              FLAGS_configuration_directory,
              FLAGS_configuration_basename_server);

上面这部分代码是为了获得map_builder_server_options,作用是为了后面的map_builder和map_builder_server,代码如下:

  auto map_builder =
      std::make_shared<cartographer::mapping::MapBuilder>(  // okagv default is
                                                            // absl::make_unique
         const_cast<::cartographer::mapping::proto::MapBuilderOptions&>(map_builder_server_options.map_builder_options()));

  std::unique_ptr<cartographer::cloud::MapBuilderServerInterface>
      map_builder_server =
          absl::make_unique<cartographer::cloud::MapBuilderServer>(
              map_builder_server_options, map_builder);

这里的map_builder_server在原cartographer中是没有的,这里主要是使用来启动服务线程,代码如下:

map_builder_server->Start();

下面这一句代码与原cartographer相同,不再讲述

Node node(node_options, map_builder, &tf_buffer, FLAGS_collect_metrics);

下面这一句代码是我们所改动部分Run()函数的核心,作用是将FLAGS_configuration_directory和FLAGS_configuration_basename_server两个变量传给node中

if (!FLAGS_configuration_directory.empty()) {
    node.SetConfigurationParam(FLAGS_configuration_directory,
                               FLAGS_configuration_basename_server);
  }

接下来的部分代码差别就在于map_builder_server->Shutdown(),这句话的意思就是关闭map_builder_server的线程服务;其它部分与原cartographer一致,到这里我们的入口程序讲完了,但很关键的一些东西完全还没有说呢。

二、本章重点之配置文件读取

与之前一样分两部分阐述

1.原cartographer创建轨迹

上一节我有一句代码没有解释,但它却是原cartographer入口的核心

node.StartTrajectoryWithDefaultTopics(trajectory_options);

通过转到定义可以看到在node.cc文件中的这个函数

void Node::StartTrajectoryWithDefaultTopics(const TrajectoryOptions& options) {
  absl::MutexLock lock(&mutex_);//上🔓
  CHECK(ValidateTrajectoryOptions(options));//检查
  AddTrajectory(options);//核心语句
}

其中核心语句是AddTrajectory(options);原代码也在node.cc中

int Node::AddTrajectory(const TrajectoryOptions& options) {  
              const
              std::set<cartographer::mapping::TrajectoryBuilderInterface::SensorId>
                    expected_sensor_ids = ComputeExpectedSensorIds(options);//传感器id,有些同一类多个传感器   const int
              trajectory_id =
                    map_builder_bridge_.AddTrajectory(expected_sensor_ids,
       options);//通过map_builder_bridge_添加轨迹  
              AddExtrapolator(trajectory_id, options);//添加外推器  
              AddSensorSamplers(trajectory_id, options);//采样器  
              LaunchSubscribers(options, trajectory_id);// 订阅话题与注册回调函数  
              wall_timers_.push_back(node_handle_.createWallTimer(
                    ::ros::WallDuration(kTopicMismatchCheckDelaySec),
                    &Node::MaybeWarnAboutTopicMismatch, this, /*oneshot=*/true));   for (const auto& sensor_id :
              expected_sensor_ids) {
                  subscribed_topics_.insert(sensor_id.id);   }   return trajectory_id; }

其中核心语句是,由于篇幅太大这里只详细解释第一种
1、ComputeExpectedSensorIds(options)确定传感器topic,如果有多个相同类型的传感器,则使用话题_数字的方式
2、AddTrajectory(expected_sensor_ids, options)增加轨迹
3、AddExtrapolator(trajectory_id, options)配置外推周期
4、AddSensorSamplers(trajectory_id, options)配置采样器采样率
5、LaunchSubscribers(options, trajectory_id)订阅话题与注册回调

ComputeExpectedSensorIds函数

std::set<cartographer::mapping::TrajectoryBuilderInterface::SensorId>
Node::ComputeExpectedSensorIds(const TrajectoryOptions& options) const {
  using SensorId = cartographer::mapping::TrajectoryBuilderInterface::SensorId;
  using SensorType = SensorId::SensorType;
  std::set<SensorId> expected_topics;
  // Subscribe to all laser scan, multi echo laser scan, and point cloud topics.
  for (const std::string& topic :
       ComputeRepeatedTopicNames(kLaserScanTopic, options.num_laser_scans)) {
    expected_topics.insert(SensorId{SensorType::RANGE, topic});
  }
  for (const std::string& topic : ComputeRepeatedTopicNames(
           kMultiEchoLaserScanTopic, options.num_multi_echo_laser_scans)) {
    expected_topics.insert(SensorId{SensorType::RANGE, topic});
  }
  for (const std::string& topic :
       ComputeRepeatedTopicNames(kPointCloud2Topic, options.num_point_clouds)) {
    expected_topics.insert(SensorId{SensorType::RANGE, topic});
  }
  // For 2D SLAM, subscribe to the IMU if we expect it. For 3D SLAM, the IMU is
  // required.
  if (node_options_.map_builder_options.use_trajectory_builder_3d() ||
      (node_options_.map_builder_options.use_trajectory_builder_2d() &&
       options.trajectory_builder_options.trajectory_builder_2d_options()
           .use_imu_data())) {
    expected_topics.insert(SensorId{SensorType::IMU, kImuTopic});
  }
  // Odometry is optional.
  if (options.use_odometry) {
    expected_topics.insert(SensorId{SensorType::ODOMETRY, kOdometryTopic});
  }
  // NavSatFix is optional.
  if (options.use_nav_sat) {
    expected_topics.insert(
        SensorId{SensorType::FIXED_FRAME_POSE, kNavSatFixTopic});
  }
  // Landmark is optional.
  if (options.use_landmarks) {
    expected_topics.insert(SensorId{SensorType::LANDMARK, kLandmarkTopic});
  }
  return expected_topics;
}

这段代码实现的功能就是,创建一个set类expected_topics,向其中推送各种传感器的SensorId,其详情这里就不阐述了。

2.修改的cartographer创建轨迹

修改后的cartographer创建轨迹却与原cartographer不一样,上面部分的阐述中主要是从node_main.cc的node.StartTrajectoryWithDefaultTopics(trajectory_options);作为调用函数,而我们自有的却是被注释掉了的,这里我花了很长时间才搞明白。这里再把那部分代码发出来看看

void Run() {
  constexpr double kTfBufferCacheTimeInSeconds = 10.;
  tf2_ros::Buffer tf_buffer{::ros::Duration(kTfBufferCacheTimeInSeconds)};
  tf2_ros::TransformListener tf(tf_buffer);
  NodeOptions node_options;
  //node_options = LoadNodeOptions(FLAGS_configuration_directory,
  //                 FLAGS_configuration_basename_server);
  
  TrajectoryOptions trajectory_options;
  std::tie(node_options, trajectory_options) =
       LoadOptions(FLAGS_configuration_directory,
                   FLAGS_configuration_basename_server);
  
  //auto map_builder = absl::make_unique<cartographer::mapping::MapBuilder>(
  //    node_options.map_builder_options);
  
  cartographer::cloud::proto::MapBuilderServerOptions
      map_builder_server_options =
          cartographer::cloud::LoadMapBuilderServerOptions(
              FLAGS_configuration_directory,
              FLAGS_configuration_basename_server);

  //LOG(INFO) << "Peak.ding map_builder";
  auto map_builder =
      std::make_shared<cartographer::mapping::MapBuilder>(  // okagv default is
                                                            // absl::make_unique
         const_cast<::cartographer::mapping::proto::MapBuilderOptions&>(map_builder_server_options.map_builder_options()));

  std::unique_ptr<cartographer::cloud::MapBuilderServerInterface>
      map_builder_server =
          absl::make_unique<cartographer::cloud::MapBuilderServer>(
              map_builder_server_options, map_builder);

  //LOG(INFO) << "Peak.ding map_builder_server start";
  map_builder_server->Start();
  
  //Node node(node_options, std::move(map_builder), &tf_buffer, FLAGS_collect_metrics);
  //LOG(INFO) << "Peak.ding Node start";
  Node node(node_options, map_builder, &tf_buffer, FLAGS_collect_metrics);

  /*          
  if (!FLAGS_load_state_filename.empty()) {
    node.LoadState(FLAGS_load_state_filename, FLAGS_load_frozen_state);
  }
  */
  //LOG(INFO) << "Peak.ding root_file_directory " << node_options.root_file_directory;

  if (!FLAGS_configuration_directory.empty()) {
    node.SetConfigurationParam(FLAGS_configuration_directory,
                               FLAGS_configuration_basename_server);
  }

  if (!node_options.root_file_directory.empty()) {
    //node.LoadMaps(node_options.root_file_directory + "/map");
  }

  if (FLAGS_start_trajectory_with_default_topics) {
    // node.StartTrajectoryWithDefaultTopics(trajectory_options);
  }

  //ros::AsyncSpinner async_spinner(2);
  //async_spinner.start();
  //::ros::waitForShutdown();

  //ros::MultiThreadedSpinner spinner(2);
  //spinner.spin();
  //LOG(INFO) << "Peak.ding Ros Start";
  ::ros::spin();
  map_builder_server->Shutdown();
  node.FinishAllTrajectories();
  node.RunFinalOptimization();

  if (!FLAGS_save_state_filename.empty()) {
    node.SerializeState(FLAGS_save_state_filename,
                        true /* include_unfinished_submaps */);
  }
}

注释掉了,那肯定就是其它地方作为入口了,这里我们把入口放在了服务调用里面去了,而服务调用就在Node函数中,调用代码如下

Node node(node_options, map_builder, &tf_buffer, FLAGS_collect_metrics);

这里之前有介绍过,这里把node函数展示出来

Node::Node(
    const NodeOptions& node_options,
    std::shared_ptr<cartographer::mapping::MapBuilderInterface> map_builder,
    tf2_ros::Buffer* const tf_buffer, const bool collect_metrics)
    : node_options_(node_options),
      map_builder_bridge_(node_options_, 
      map_builder, //std::move(map_builder),
      tf_buffer) {
  absl::MutexLock lock(&mutex_);
  if (collect_metrics) {
    metrics_registry_ = absl::make_unique<metrics::FamilyFactory>();
    carto::metrics::RegisterAllMetrics(metrics_registry_.get());
  }

  submap_list_publisher_ =
      node_handle_.advertise<::cartographer_ros_msgs::SubmapList>(
          kSubmapListTopic, kLatestOnlyPublisherQueueSize);
  trajectory_node_list_publisher_ =
      node_handle_.advertise<::visualization_msgs::MarkerArray>(
          kTrajectoryNodeListTopic, kLatestOnlyPublisherQueueSize);
  landmark_poses_list_publisher_ =
      node_handle_.advertise<::visualization_msgs::MarkerArray>(
          kLandmarkPosesListTopic, kLatestOnlyPublisherQueueSize);
  constraint_list_publisher_ =
      node_handle_.advertise<::visualization_msgs::MarkerArray>(
          kConstraintListTopic, kLatestOnlyPublisherQueueSize);
  service_servers_.push_back(node_handle_.advertiseService(
      kSubmapQueryServiceName, &Node::HandleSubmapQuery, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kTrajectoryQueryServiceName, &Node::HandleTrajectoryQuery, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kStartTrajectoryServiceName, &Node::HandleStartTrajectory, this));
  service_servers_.push_back(node_handle_.advertiseService(
      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));
  service_servers_.push_back(node_handle_.advertiseService(
      kReadMetricsServiceName, &Node::HandleReadMetrics, this));

  // okagv
  service_servers_.push_back(node_handle_.advertiseService(
      kLoadTrajectoryServiceName, &Node::HandleLoadTrajectory, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kDeleteTrajectoryServiceName, &Node::HandleDeleteTrajectory, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kLocalizeTrajectoryServiceName, &Node::HandleLocalizeTrajectory, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kSetTrajectoryStatesServiceName, &Node::HandleSetTrajectoryStates, this));
  service_servers_.push_back(node_handle_.advertiseService(
      kUpdateStateServiceName, &Node::HandleUpdateState, this));


  scan_matched_point_cloud_publisher_ =
      node_handle_.advertise<sensor_msgs::PointCloud2>(
          kScanMatchedPointCloudTopic, kLatestOnlyPublisherQueueSize);

  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(node_options_.submap_publish_period_sec),
      &Node::PublishSubmapList, this));
  if (node_options_.pose_publish_period_sec > 0) {
    publish_local_trajectory_data_timer_ = node_handle_.createTimer(
        ::ros::Duration(node_options_.pose_publish_period_sec),
        &Node::PublishLocalTrajectoryData, this);
  }
  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(node_options_.trajectory_publish_period_sec),
      &Node::PublishTrajectoryNodeList, this));
  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(node_options_.trajectory_publish_period_sec),
      &Node::PublishLandmarkPosesList, this));
  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(kConstraintPublishPeriodSec),
      &Node::PublishConstraintList, this));

  // okagv
  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(kConstraintPublishPeriodSec),
       &Node::CheckAndRunOKagvOrder, this));
 
  // okagv
  initial_pose_subscriber_ = node_handle_.subscribe(
      "/initialpose", 1, &Node::HandleInitialPoseMessage, this);

  // okagv
  current_pose_publisher_ =
      node_handle_.advertise<::cartographer_ros_msgs::RobotPose>(
          kRobotPoseTopic, kLatestOnlyPublisherQueueSize);

  timer_thread_ = new std::thread(boost::bind(&Node::StartRelocalizeRobot, this));

  //okagv
  service_servers_.push_back(node_handle_.advertiseService(
      kLaserScanQualityServiceName, &Node::HandleScanQualityQuery, this));

  // okagv
  rosbag_start_recorder_ =
      node_handle_.advertise<::cartographer_ros_msgs::Rosbag>(
          kRosbagStartRecordTopic, kLatestOnlyPublisherQueueSize);

  // okagv
  rosbag_stop_recorder_ =
      node_handle_.advertise<std_msgs::String>(
          kRosbagStopRecordTopic, kLatestOnlyPublisherQueueSize);
}

其中服务调用在如下代码之中

  service_servers_.push_back(node_handle_.advertiseService(
      kStartTrajectoryServiceName, &Node::HandleStartTrajectory, this));

这里面的HandleStartTrajectory是一个ros的服务处理函数,看函数在node.cc文件中

bool Node::HandleStartTrajectory(
    ::cartographer_ros_msgs::StartTrajectory::Request& request,
    ::cartographer_ros_msgs::StartTrajectory::Response& response) {
    //absl::MutexLock lock(&mutex_);
  if (is_service_done == true) {
    is_service_done = false;
    //
    absl::SleepFor(absl::Seconds(0.1));
  } else {
    response.status.code = cartographer_ros_msgs::StatusCode::UNIMPLEMENTED;
    response.status.message = "another service is running, please wait";
    // is_service_done = f;
    // current_trajectory_type = TrajectoryType::IDLE;
    return true;
  }

  switch (current_trajectory_type) {
    case TrajectoryType::SLAM:
      LOG(INFO) << "HandleStartTrajectory SLAM";

        // finish and delete current trajectory_id;
        if (!FinishAndDeleteTrajectory(last_trajectory_id_name)) {
          response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
          response.status.message =
              "last trajectory is in slam mode, and remove it fail";
          return true;
        }
      /*
      if (request.use_initial_pose == true) {
        // finish and delete current trajectory_id;
        if (!FinishAndDeleteTrajectory(last_trajectory_id_name)) {
          response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
          response.status.message =
              "last trajectory is in slam mode, and remove it fail";
          return true;
        }
      } else {
        if (!trajectory_id_toint.count(last_trajectory_id_name)) {
          response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
          response.status.message = "no trajectory found, and finish it fail";
          return true;
        }
        if (!FinishTrajectory(trajectory_id_toint[last_trajectory_id_name])) {
          response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
          response.status.message =
              "last trajectory is in slam mode, and finish it fail";
          return true;
        }
      }
      */
      break;
    case TrajectoryType::NAVIGATION:
          LOG(INFO) << "HandleStartTrajectory NAVIGATION";
      // finish and delete current navigation trajectory_id;
      if (!FinishAndDeleteTrajectory(last_trajectory_id_name)) {
        response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
        response.status.message =
            "last trajectory is in navigation mode, and remove it fail";
        return true;
      }

      // finsih and delete current map trajectory_id
      if (!FinishAndDeleteTrajectory(last_map_trajectory_id_name)) {
        response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
        response.status.message =
            "last trajectory is in navigation mode, and remove it fail";
        return true;
      }
      break;
    case TrajectoryType::IDLE:
      LOG(INFO) << "HandleStartTrajectory IDLE";
      // finsih and delete current map trajectory_id
      LOG(INFO) << "last_map_trajectory_id_name " << last_map_trajectory_id_name;

      if (request.use_initial_pose == true) break;

      if (!last_trajectory_id_name.empty()) {
        if (!FinishAndDeleteTrajectory(last_map_trajectory_id_name)) {
          response.status.code = cartographer_ros_msgs::StatusCode::ABORTED;
          response.status.message = "has last trajectory but remove it fail";
          return true;
        }
      }

      break;
    default:
      break;
  }


  if(request.use_initial_pose == true)
  {
    request.relative_to_trajectory_id = last_trajectory_id_name;
    request.initial_pose = ToGeometryMsgPose(current_tracking_to_map);
  }

  // start new slam
  HandleStartTrajectoryDetail(request, response);

  if (response.status.code == cartographer_ros_msgs::StatusCode::OK) {
    current_trajectory_type = TrajectoryType::SLAM;
    LOG(INFO) << "Success to start Trajectory";

    cartographer_ros_msgs::Rosbag order;
    order.config = "standard";
    order.bag_name = request.trajectory_id;
    rosbag_start_recorder_.publish(order);

    OKagv_Feedback feedback;
    feedback.code = 0;
    feedback.state = OKagv_State::SUCCESS;
    map_builder_bridge_.SetOKagv_Feedback(feedback);

  } else {
    current_trajectory_type = TrajectoryType::ABORTION;
    LOG(INFO) << "Fail to start Trajectory";
    
    OKagv_Feedback feedback;
    feedback.code = 1;
    feedback.state = OKagv_State::SUCCESS;
    feedback.message = "Fail to start Trajectory";

    map_builder_bridge_.SetOKagv_Feedback(feedback);
    
  }

  absl::SleepFor(absl::Seconds(node_options_.time_delay_for_start_trajectory));
  is_service_done = true;
  return true;
}

这其中主要的函数在

// start new slam
HandleStartTrajectoryDetail(request, response);
这个函数也在node.cc文件中
bool Node::HandleStartTrajectoryDetail(
    ::cartographer_ros_msgs::StartTrajectory::Request& request,
    ::cartographer_ros_msgs::StartTrajectory::Response& response) {
  TrajectoryOptions trajectory_options;

  std::tie(std::ignore, trajectory_options) =
      LoadOptions(configuration_directory_, configuration_basename_);

  if (configuration_directory_.empty() || configuration_basename_.empty()) {
    response.status.message = "configuration file do not find";
    return true;
  } else {
    //LOG(INFO) << "Peak.ding check configuration_directory_ "
    //          << configuration_directory_;
    //LOG(INFO) << "Peak.ding check configuration_basename_ "
    //          << configuration_basename_;
  }

    //
  int scheduled_trajectory_id = 0;
  std::string scheduled_trajectory_id_name;

  auto it = trajectory_id_toint.find(request.trajectory_id);
  if (it == trajectory_id_toint.end()) {
    // okagv
    if (request.trajectory_type == "slam") {
      scheduled_trajectory_id = trajectory_id_toint.size();
      scheduled_trajectory_id_name = request.trajectory_id;
      if (scheduled_trajectory_id_name.empty())
        scheduled_trajectory_id_name = std::to_string(scheduled_trajectory_id);
      scheduled_trajectory_type = TrajectoryType::SLAM;
      // set slam param
      trajectory_options.trajectory_builder_options.set_pure_localization(
          false);

    } else if (request.trajectory_type == "navigation") {
      scheduled_trajectory_id = 1001;
      scheduled_trajectory_id_name = "navigation";
      scheduled_trajectory_type = TrajectoryType::NAVIGATION;
      // set navigation param
      trajectory_options.trajectory_builder_options.set_pure_localization(true);

    } else {
      response.status.message = "Failed. trajectory_type don't exist";
      return true;
    }

  } else {
    scheduled_trajectory_id = it->second;
  }

  if (request.use_initial_pose) {
    const auto pose = ToRigid3d(request.initial_pose);
    if (!pose.IsValid()) {
      response.status.message =
          "Invalid pose argument. Orientation quaternion must be normalized.";
      LOG(ERROR) << response.status.message;
      response.status.code =
          cartographer_ros_msgs::StatusCode::INVALID_ARGUMENT;
          LOG(ERROR) << "intial pose is wrong";
      return true;
    }

    // Check if the requested trajectory for the relative initial pose exists.
    if (!trajectory_id_toint.count(request.relative_to_trajectory_id)) {
      response.status.code =
          cartographer_ros_msgs::StatusCode::INVALID_ARGUMENT;
      LOG(ERROR) << "relative_to_trajectory_id is "
                 << request.relative_to_trajectory_id
                 << ", do not exist map trajectory_id";
      return true;
    }

    int relative_to_trajectory_id =
        trajectory_id_toint.at(request.relative_to_trajectory_id);
        //LOG(INFO) << "Peak.ding relative_to_trajectory_id " << relative_to_trajectory_id; 
    response.status = TrajectoryStateToStatus(
        relative_to_trajectory_id,
        {TrajectoryState::ACTIVE, TrajectoryState::FROZEN,
         TrajectoryState::FINISHED, TrajectoryState::IDLE} /* valid states */);
    if (response.status.code != cartographer_ros_msgs::StatusCode::OK) {
      LOG(ERROR) << "Can't start a trajectory with initial pose: "
                 << response.status.message;
      return true;
    }

    ::cartographer::mapping::proto::InitialTrajectoryPose
        initial_trajectory_pose;
    initial_trajectory_pose.set_to_trajectory_id(relative_to_trajectory_id);
    *initial_trajectory_pose.mutable_relative_pose() =
        cartographer::transform::ToProto(pose);
    initial_trajectory_pose.set_timestamp(cartographer::common::ToUniversal(
        ::cartographer_ros::FromRos(ros::Time(0))));
    *trajectory_options.trajectory_builder_options
         .mutable_initial_trajectory_pose() = initial_trajectory_pose;
  }

  // First, check if we can actually start the trajectory.
  cartographer_ros_msgs::StatusResponse status_response =
      TrajectoryStateToStatus(scheduled_trajectory_id, {} /* valid states */);
  if (status_response.code != cartographer_ros_msgs::StatusCode::NOT_FOUND) {
    LOG(INFO) << "Peak.ding scheduled_trajectory_id " << scheduled_trajectory_id;
    LOG(ERROR) << "Can't start trajectory that is exist ";

    response.status.code = cartographer_ros_msgs::StatusCode::ALREADY_EXISTS;
    response.status.message = "trajectory id already exist";
    return true;
  }

  if (!ValidateTrajectoryOptions(trajectory_options)) {
    response.status.message = "Invalid trajectory options.";
    LOG(ERROR) << response.status.message;
    response.status.code = cartographer_ros_msgs::StatusCode::INVALID_ARGUMENT;
  } else if (!ValidateTopicNames(trajectory_options)) {
    response.status.message = "Topics are already used by another trajectory.";
    LOG(ERROR) << response.status.message;
    response.status.code = cartographer_ros_msgs::StatusCode::INVALID_ARGUMENT;
  } else {
    response.status.message = "Success";
    // okagv
    SetTrajectoryTypeWithId(request.trajectory_type, scheduled_trajectory_id);
    response.trajectory_id =
        AddTrajectoryWithId(scheduled_trajectory_id, trajectory_options);
    response.status.code = cartographer_ros_msgs::StatusCode::OK;

    //okagv
    RegisterClientIdForTrajectory(scheduled_trajectory_id,scheduled_trajectory_id_name);
    // okagv
    last_trajectory_id = scheduled_trajectory_id;
    last_trajectory_id_name = scheduled_trajectory_id_name;
    trajectory_id_toint.emplace(last_trajectory_id_name,
                                last_trajectory_id);
    current_trajectory_type = scheduled_trajectory_type;

  }
  return true;
}

这里有很多东西我还没有细看,不过我们只需要找到与原cartographer相似的方法
其代码如下:

    response.trajectory_id =
        AddTrajectoryWithId(scheduled_trajectory_id, trajectory_options);

通过转到定义可以发现代码

int Node::AddTrajectoryWithId(const int trajectory_id,
                              const TrajectoryOptions& options) {
  const std::set<cartographer::mapping::TrajectoryBuilderInterface::SensorId>
      expected_sensor_ids = ComputeExpectedSensorIds(options);

  map_builder_bridge_.AddTrajectoryWithId(trajectory_id, expected_sensor_ids,
                                          options);

  AddExtrapolator(trajectory_id, options);
  AddSensorSamplers(trajectory_id, options);
  LaunchSubscribers(options, trajectory_id);
  wall_timers_.push_back(node_handle_.createWallTimer(
      ::ros::WallDuration(kTopicMismatchCheckDelaySec),
      &Node::MaybeWarnAboutTopicMismatch, this, /*oneshot=*/true));
  for (const auto& sensor_id : expected_sensor_ids) {
    subscribed_topics_.insert(sensor_id.id);
  }
  return trajectory_id;
}

是不是有几分熟悉,我们在上一小节就有其中的介绍

1、ComputeExpectedSensorIds(options)确定传感器topic,如果有多个相同类型的传感器,则使用话题_数字的方式
2、AddTrajectory(expected_sensor_ids, options)增加轨迹
3、AddExtrapolator(trajectory_id, options)配置外推周期
4、AddSensorSamplers(trajectory_id, options)配置采样器采样率
5、LaunchSubscribers(options, trajectory_id)订阅话题与注册回调


总结

到这里,初步相同的地方和不同的地方都介绍了很多,下一片继续围绕几个处理函数介绍。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值