4. 基于bag文件的离线建图

任务目标

本案例将完成基于bag文件的Cartographer离线建图闭环:录制包含2D激光雷达与里程计数据的rosbag,使用Cartographer离线处理该bag生成栅格地图,并保存为PGM/YAML格式的地图文件。最终通过地图文件与可视化结果验证建图效果。

环境与依赖

  • Ubuntu 20.04 + ROS Noetic(或Ubuntu 18.04 + ROS Melodic)
  • 已编译安装Cartographer与cartographer_ros(建议使用源码编译方式)
  • 已安装ros-${ROS_DISTRO}-rosbag、ros-${ROS_DISTRO}-tf2-ros等标准ROS包
  • 用于录制bag的传感器驱动:本案例使用仿真环境或真实机器人,需保证发布以下话题:/scan(sensor_msgs/LaserScan)、/odom(nav_msgs/Odometry),以及TF树(map→odom→base_footprint→laser)

实现步骤

1. 录制bag文件

启动机器人传感器驱动后,录制包含激光、里程计和TF的bag。使用以下命令录制10秒数据(按需调整时长):

rosbag record -O offline_mapping.bag \
  /scan \
  /odom \
  /tf \
  /tf_static

录制完成后,用rosbag info offline_mapping.bag确认话题与消息数量正常。

2. 配置Cartographer离线建图参数

创建离线建图所需的lua配置文件。以下为最小可用配置(保存为offline_mapping.lua):

include "cartographer_ros/backpack_2d.lua"

TRAJECTORY_BUILDER_2D.min_range = 0.1
TRAJECTORY_BUILDER_2D.max_range = 30.
TRAJECTORY_BUILDER_2D.missing_data_ray_length = 5.
TRAJECTORY_BUILDER_2D.use_imu_data = false
TRAJECTORY_BUILDER_2D.use_online_correlative_scan_matching = true
TRAJECTORY_BUILDER_2D.min_score = 0.5

MAP_BUILDER.use_trajectory_builder_2d = true

return {
  map_builder = MAP_BUILDER,
  trajectory_builder = TRAJECTORY_BUILDER_2D,
  map_frame = "map",
  tracking_frame = "base_footprint",
  published_frame = "odom",
  odom_frame = "odom",
  provide_odom_frame = true,
  publish_frame_projected_to_2d = false,
  use_odometry = true,
  num_laser_scans = 1,
  num_multi_echo_laser_scans = 0,
  num_subdivisions_per_laser_scan = 1,
  num_point_clouds = 0,
  lookup_transform_timeout_sec = 0.2,
  submap_publish_period_sec = 0.3,
  pose_publish_period_sec = 5e-3,
  trajectory_builder_2d = TRAJECTORY_BUILDER_2D,
}

注意:若你的TF树中laser坐标系名称不是laser,需在cartographer_ros启动文件中通过num_laser_scans对应的topic映射指定实际坐标系。本配置假设激光话题为/scan,且TF中base_footprint→laser已正确发布。

3. 编写离线建图启动文件

创建offline_mapping.launch,使用cartographer_ros的离线节点处理bag:

<launch>
  <node name="cartographer_offline_node" pkg="cartographer_ros"
        type="cartographer_offline_node" output="screen">
    <param name="configuration_directory"
           value="$(find your_package)/config"/>
    <param name="configuration_basename"
           value="offline_mapping.lua"/>
    <remap from="scan" to="/scan"/>
    <remap from="odom" to="/odom"/>
  </node>
</launch>

将上述文件放入你的ROS包中,并确保lua文件位于config目录下。

4. 执行离线建图

# 终端1:启动离线建图(等待bag播放)
roslaunch your_package offline_mapping.launch

# 终端2:播放bag(注意循环播放,确保建图完整)
rosbag play -r 0.5 --clock offline_mapping.bag

建图过程中,Cartographer会实时输出子图与轨迹信息。等待bag播放完毕后,在终端1按Ctrl+C终止节点,Cartographer会自动保存pbstream文件(默认路径为当前目录的map.pbstream)。

5. 生成地图文件(PGM/YAML)

使用cartographer_pbstream_to_ros_map工具将pbstream转换为ROS地图格式:

# 生成map.pgm与map.yaml
rosrun cartographer_ros cartographer_pbstream_to_ros_map \
  -pbstream_filename=map.pbstream \
  -map_filestem=map

执行后当前目录出现map.pgm与map.yaml,即为最终栅格地图。

验证方法与判定标准

  • bag数据有效性:rosbag info显示/scan与/odom消息数量大于100,且TF话题存在。
  • 建图过程无致命错误:离线节点运行期间无FATAL或ERROR输出,正常输出I...级别日志。
  • 地图文件生成:map.pgm与map.yaml存在且非空,map.yaml中分辨率与原点参数合理。
  • 可视化验证:在RViz中加载map.pgm(通过map_server),观察地图是否呈现清晰的墙体轮廓,无严重重影或断裂。若使用仿真环境,地图应与仿真场景布局一致。

常见问题

  • TF缺失:若报错Could not find transform,检查bag中是否录制了/tf与/tf_static,且坐标系名称与lua配置一致。
  • 建图质量差:尝试降低rosbag play -r速率(如0.3),或调整lua中min_score与max_range参数。
  • 地图保存失败:确认cartographer_pbstream_to_ros_map与pbstream文件路径正确,且当前用户对输出目录有写权限。