C++Node类Cartographer开始轨迹的处理深度详解

yizhihongxing

"C++Node类Cartographer开始轨迹的处理深度详解"是关于使用C++ Node类库Cartographer中开始轨迹处理的详细攻略。

在Cartographer中,开始轨迹处理包括以下几个步骤:

1.创建一个Cartographer运行时环境

需要使用Cartographer的前提是已经在计算机上安装了Cartographer运行时环境,可以在命令行中输入"cartographer_version"命令检测是否已经安装成功。如果未安装,可以参考Cartographer官方文档进行安装。

2.准备地图、传感器数据和配置文件

在运行Cartographer之前需要准备一个地图、传感器数据和一个配置文件。地图可以是一个CAD或地理信息系统(GIS)文件格式,传感器数据可以是来自GPS、激光雷达或相机的信息,配置文件必须包含Cartographer的参数设置。

3.创建Node类

使用C++实现的Cartographer库是基于ROS(Robot Operating System)系统的,因此需要创建一个Node类来调用Cartographer的API进行数据处理,实现的代码如下:

#include "cartographer_ros/node.h"

namespace cartographer_ros {

class Node {
 public:
  Node(const NodeOptions& node_options,
       std::unique_ptr<cartographer_ros::MapBuilderBridge> map_builder_bridge);
  ~Node();

  Node(const Node&) = delete;
  Node& operator=(const Node&) = delete;

  void Initialize();

  void RunForever();

 private:
  const NodeOptions node_options_;
  std::unique_ptr<cartographer_ros::MapBuilderBridge> map_builder_bridge_;
  std::unique_ptr<cartographer_ros::TimedRosMessageFilter<sensor_msgs::LaserScan>> laser_scan_filter_;
  std::unique_ptr<tf2_ros::TransformBroadcaster> transform_broadcaster_;
  std::unique_ptr<tf2_ros::StaticTransformBroadcaster> static_transform_broadcaster_;

  // ... other member variables
};

}  // namespace cartographer_ros

在上面的代码中,我们定义了一个名为Node的类,用于调用Cartographer库中的API进行数据处理。在类中定义了一些成员变量和成员函数。

4.开始轨迹处理

接下来,在C++实现的Cartographer库中开始轨迹处理的代码如下:

namespace cartographer_ros {

void Node::StartTrajectoryWithDefaultTopics(const TrajectoryOptions& trajectory_options) {
  // 选择初始化滤波器
  carto::mapping::TrajectoryBuilder::PoseEstimate pose_estimate;
  if (trajectory_options.use_initial_pose) {
    pose_estimate =
        carto::mapping::PoseGraphInterface::GetLatestPoseFromTrajectoryOrZero(
            map_builder_bridge_->GetPoseGrapnInterface(),
            trajectory_options.initial_trajectory_pose_estimate.trajectory_id);
  }
  if (!carto::common::FromRosMessage(trajectory_options.imu_sampling_ratio, &pose_estimate.constant_data->imu_sampling_ratio)) {
    LOG(ERROR) << "Invalid imu_sampling_ratio: " << trajectory_options.imu_sampling_ratio;
    return;
  }

  // 开始轨迹
  const int trajectory_id = map_builder_bridge_->AddTrajectoryBuilder(
      std::unique_ptr<carto::mapping::TrajectoryBuilder>(new carto::mapping::TrajectoryBuilder(
          trajectory_options.trajectory_builder_options,
          map_builder_bridge_->sparse_pose_graph()->GetTrajectoryNodeOptions(
              trajectory_options.trajectory_builder_options.trajectory_builder_3d_options().min_range(),
              trajectory_options.trajectory_builder_options.trajectory_builder_3d_options().max_range(),
              carto::mapping::kImuLinearAccelerationNoiseModel)),
          trajectory_id));
  carto::mapping::TrajectoryBuilderInterface* const trajectory_builder =
      map_builder_bridge_->GetTrajectoryBuilderById(trajectory_id);

  //选择订阅传感器数据
  const std::unordered_set<
      ::cartographer::mapping::TrajectoryBuilderInterface::SensorId,
      ::cartographer::mapping::TrajectoryBuilderInterface::SensorIdHash>
      expected_sensor_ids = trajectory_builder->expected_sensor_ids();

  int message_period_rounding_error_ns = 0;
  if (ContainsKey(expected_sensor_ids, kLaserScanTopic)) {
    // 订阅激光雷达数据
    laser_scan_filter_.reset(new cartographer_ros::TimedRosMessageFilter<sensor_msgs::LaserScan>(
        node_handle_, kLaserScanTopic, message_queue_size_,
        ::ros::Duration(0.2 * pose_estimate.constant_data->imu_gravity_time_constant()),
        ::ros::Duration(kMessageSubscriptionsTimeoutSec),
        [this, trajectory_id](const std::deque<cartographer_ros::TimedMessage<sensor_msgs::LaserScan>>& laser_msg_buffer,
                              const cartographer_ros::Time earliest_timestamp) {
          HandleLaserScanMessage(trajectory_id, laser_msg_buffer, earliest_timestamp);
        }));

    // 准备激光雷达数据处理相关的参数
    carto::sensor::LaserFan3D laser_fan_3d;
    const auto process_laser_scan = [this, trajectory_id, &laser_fan_3d](
                                         const std::string& sensor_id,
                                         const ::cartographer::common::Time time,
                                         Eigen::Vector3f* const origin,
                                         ::cartographer::sensor::PointCloudWithIntensities* const ranges) {
      cartographer_ros_msgs::SensorTopics msg;
      cartographer_ros::SensorBridge(sensor_id, msg).ToRosMessage(time, laser_fan_3d);
      this->HandleRangefinderMessage(trajectory_id, msg, laser_fan_3d.origin, laser_fan_3d.point_cloud,
                                     laser_fan_3d.time);
    };

    // 订阅激光雷达数据以及tf transform
    laser_scan_filter_->connect();
    laser_scan_subscriber_ = node_handle_->subscribe(
        kLaserScanTopic, kSubscriberQueueSize,
        [this, process_laser_scan](const sensor_msgs::LaserScan::ConstPtr& msg) {
          ::cartographer::common::Time timestamp = FromRos(msg->header.stamp);
          // 处理激光雷达数据
          std::deque<cartographer_ros::TimedMessage<sensor_msgs::LaserScan>> timed_msg;
          timed_msg.emplace_back(timestamp, msg);
          laser_scan_filter_->on_new_message(std::move(timed_msg), timestamp);
        });

    if (trajectory_options.use_nav_sat) {
      // 订阅GPS数据
      gps_subscriber_ = node_handle_->subscribe<sensor_msgs::NavSatFix>(
          kGpsFixTopic, kSubscriberQueueSize,
          [this, trajectory_id](const sensor_msgs::NavSatFix::ConstPtr& msg) {
            HandleNavSatFixMessage(trajectory_id, *msg);
          });
    }
  }
}

上面的代码中,我们实现了开始轨迹处理的核心逻辑,包括从传感器订阅数据、处理、转换、建图等操作流程。

示例1:假设我们有一个地图文件"example_map.pgm"和一个传感器数据文件"laser_data.bag",想要使用Cartographer建立地图。我们可以运行以下命令:

roslaunch cartographer_ros offline_backpack_2d.launch bag_filenames:=/path/to/laser_data.bag bag_topics:=/scan,config_file:=/path/to/config.lua map_filestem:=example_map

然后,在rviz中打开Cartographer的配置文件"configuration_files/backpack_2d.lua",选择主题"/map"和"/path",可以看到通过Cartographer创建的地图。

示例2:使用IMU数据和GPS数据进行建筑物内部地图绘制,配置文件如下:

include "cartographer_ros/configuration_files/backpack_3d.lua"

options = {
  sensor_bridge_options = {
    proteins = {
      imu = {  
        tracked_frame = "imu",
        published_frame = "imu",
        lookup_table = "config_imu.lua" 
      }
    },
    navsat_translator_options = {
      tracking_frame = "gps",
    }
  }
}

return options

使用以下命令开始地图绘制:

roslaunch cartographer_ros backpack_3d.launch bag_filenames:="/path/to/laser_data.bag" bag_topics:="/velodyne_points,/imu/um7,/gps/filtered_imu" configuration_directory:=/path/to/configuration_files

在rviz中打开主题"/map",可以看到通过Cartographer创建的建筑物地图。

以上就是关于使用C++ Node类库Cartographer中开始轨迹处理的详细攻略,希望能对大家有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++Node类Cartographer开始轨迹的处理深度详解 - Python技术站

(0)
上一篇 2023年6月26日
下一篇 2023年6月26日

相关文章

  • 怎么看win10是否为9926版本?查看win10版本号的三种方法

    当你想要确定你的Windows 10操作系统是否为9926版本时,可以使用以下三种方法来查看版本号: 使用系统设置: 点击任务栏上的“开始”按钮,然后点击“设置”图标(齿轮状图标)。 在“设置”窗口中,点击“系统”选项。 在左侧导航栏中,选择“关于”选项。 在右侧窗口中,你将看到“Windows规格”部分,其中包含了你的Windows 10版本号。 示例说明…

    other 2023年8月2日
    00
  • WPF基础——Application

    WPF基础——Application 概述 WPF(Application)是一个Windows Presentation Foundation应用程序,它是WPF应用程序的起点。本文将讨论WPF(Application)的基础知识。 创建一个WPF(Application) 要创建一个WPF(Application),可以使用Visual Studio创建…

    其他 2023年3月28日
    00
  • Qt编写提示进度条的实现示例

    我们来详细讲解如何使用Qt编写提示进度条的实现示例。 步骤1:创建一个进度条 在Qt中,我们可以使用QProgressBar类来创建一个进度条。以下是创建进度条的示例代码: QProgressBar* progressBar = new QProgressBar(this); progressBar->setMinimum(0); progressBa…

    other 2023年6月26日
    00
  • apt-get更换源

    以下是关于“apt-get更换源”的完整攻略,包括定义、更换步骤、示例说明和注意事项。 定义 Linux系统中,apt-get是一个常用的软件包管理工具。默认情况下,apt-get使用官方来下载软件包。但是,时候官方源的下载速度较慢,或者某些软件包在官方源中不可用在这种情况下,可以更换apt-get的源,以便更快地下载软件或者下载到所需的软件包。 更步骤 更…

    other 2023年5月8日
    00
  • Python编程实现双链表,栈,队列及二叉树的方法示例

    Python编程实现双链表,栈,队列及二叉树是数据结构中非常重要的内容。本文将详细介绍Python实现双链表、栈、队列及二叉树的方法示例。 双链表实现方法示例 定义节点类 首先,我们需要定义一个节点类,该类包含三个属性: data:表示节点值 prev:表示前一个节点 next:表示下一个节点 class Node: def __init__(self, d…

    other 2023年6月27日
    00
  • 一篇文章带你入门java变量与类型

    以下是一个完整的攻略,带你入门Java变量与类型,包括两个示例说明。 … Java变量与类型的基本概念 在Java中,变量是用来存储数据的容器,而类型则定义了变量可以存储的数据的种类。Java中的变量可以分为基本类型和引用类型两种。 基本类型:Java提供了一组基本类型,包括整数类型(如int、long)、浮点数类型(如float、double)、字符类…

    other 2023年8月15日
    00
  • js调用打印机打印整体或部分

    当然,我可以为您提供有关“JS调用打印机打印整体或部分”的完整攻略,以下是详细说明: 什么是JS调用打印机打印整体或部分? JS调用打印机打印整体或部分是指使用JavaScript代码控制打印机打印网页内容的过程。通过JS用打印机打印整体或部分,可以实现在网页上选择需要打印的内容,或者直接打印整个网页。 JS调用打印机打印整体或部分的步骤 以下是JS用打印机…

    other 2023年5月7日
    00
  • jdbctemplate进行查询

    使用jdbctemplate进行查询 在Java开发中,使用JDBC连接数据库是常见的操作。JDBC提供了一组接口让我们来操作数据库。但是,使用JDBC的过程中需要编写大量的SQL语句和一些繁琐的操作。为了简化这些操作,Spring框架提供了JdbcTemplate类来简化JDBC的操作,下面来讲解如何使用JdbcTemplate类进行查询操作。 创建Jdb…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部