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文件路径正确,且当前用户对输出目录有写权限。