3、地图构建基础:SLAM原理简介、Cartographer配置、建图流程与参数调优、地图保存与格式

各位同学,欢迎来到第三章。上一章我们把机器人底盘和传感器都跑通了,但机器人还是个「睁眼瞎」——它不知道自己在哪里,也不知道周围长什么样。这一章,我们就来解决这个问题。

说白了,地图构建就是让机器人一边走一边画地图,同时还得知道自己在地图上的哪个位置。这就是SLAM(Simultaneous Localization and Mapping)的核心思想。我个人习惯把SLAM理解成「先有鸡还是先有蛋」的问题:要定位就得有地图,要建图就得知道位置。嗯,这俩事儿得同时干。

核心知识点速览

  • SLAM的基本原理与数学框架
  • Cartographer的配置与启动
  • 建图流程与参数调优实战
  • 地图保存与格式转换
SLAM 核心逻辑:同时定位与建图 激光雷达 / 里程计 传感器数据输入 前端:扫描匹配 帧间位姿估计 后端:图优化 闭环检测 & 全局优化 地图 + 位姿 反馈:地图用于后续定位,定位辅助建图

3.1 SLAM 原理简介

SLAM 这个缩写,你肯定听过无数遍了。但它的本质到底是什么?我换个角度给你讲。

想象你蒙着眼睛走进一个陌生的房间,手里拿着一根棍子。你每走一步,就用棍子戳一下周围的墙壁——这就是激光雷达在测距。同时你的大脑在默默计算:我走了几步?方向变了吗?——这就是里程计。然后你根据这些信息,在脑海里拼凑出房间的轮廓,同时判断自己站在哪里。这就是SLAM。

数学上,SLAM 可以拆解成两个核心问题:

  • 前端(Front-end):处理传感器数据,做帧间匹配。比如用 ICP(迭代最近点)或 CSM(相关扫描匹配)来估计机器人相邻时刻的位姿变化。
  • 后端(Back-end):处理前端给出的约束关系,进行全局优化。最常见的方法是图优化(Graph-based SLAM),把机器人的位姿和观测都当成节点,约束当成边,然后求解一个最小二乘问题。

我在项目中遇到过最头疼的问题,就是前端匹配跑飞了。明明只转了个弯,算法却以为机器人平移了两米。后来发现是激光数据的时间戳没对齐。嗯,时间同步这事儿,后面我会专门讲。

小提示:ROS2 中常用的 SLAM 方案有 Cartographer、slam_toolbox、以及基于 ORB-SLAM3 的视觉方案。对于轮式机器人,我个人最推荐 Cartographer——它稳定、精度高,而且对传感器要求没那么苛刻。

3.2 Cartographer 配置

Cartographer 是 Google 开源的一个 2D/3D SLAM 库。它最大的特点是引入了「子图(Submap)」的概念:把地图切成一小块一小块,每块内部做局部优化,块与块之间做全局优化。这样既保证了实时性,又不会让地图漂移得太离谱。

安装 Cartographer 其实不复杂,但有几个坑。我直接给你一个我验证过的安装流程:

# 1. 安装依赖
sudo apt install -y \
  libgoogle-glog-dev libgflags-dev \
  libatlas-base-dev libeigen3-dev \
  libsuitesparse-dev

# 2. 编译 Cartographer(ROS2 版本)
cd ~/ros2_ws/src
git clone https://github.com/ros2/cartographer.git
git clone https://github.com/ros2/cartographer_ros.git

cd ~/ros2_ws
colcon build --packages-select cartographer cartographer_ros
source install/setup.bash

配置文件的路径通常在 cartographer_ros/configuration_files 下。你需要关注两个文件:

  • backpack_2d.lua:这是 Cartographer 的主配置文件,里面包含了所有可调参数。
  • backpack_2d.urdf:机器人模型文件,定义了传感器相对于机器人基座的安装位置。

我曾经犯过一个低级错误:把激光雷达的安装高度写错了,结果建出来的地图全是倾斜的。你想想看,一个倾斜的地图,导航怎么可能准?

注意:Cartographer 的配置文件是 Lua 格式,不是 YAML。别搞混了。Lua 文件里每个参数后面都要有逗号,少一个逗号就会报错,而且错误信息很不直观。

3.3 建图流程与参数调优

好了,配置搞定,我们来跑建图。启动命令其实就两行:

# 启动 Cartographer 节点
ros2 launch cartographer_ros cartographer.launch.py \
  configuration_directory:=/path/to/config \
  configuration_basename:=backpack_2d.lua

# 启动 RViz 查看建图过程
ros2 run rviz2 rviz2 -d /path/to/cartographer_ros/configuration_files/demo_2d.rviz

启动之后,你会看到 RViz 里出现一个不断扩展的地图。这时候你需要手动控制机器人移动,或者用键盘/手柄遥控它走一圈。我个人习惯先让机器人沿着房间边缘走一圈,再走一些「之」字形路线,把内部区域也覆盖到。

建图过程中,你可能会遇到几个常见问题:

  • 地图漂移:机器人走了一圈回来,地图对不上了。这通常是闭环检测没生效。检查 POSE_GRAPH.optimize_every_n_nodes 参数,我一般设成 3-5。
  • 地图模糊:边缘不清晰,像没对上焦的照片。调大 TRAJECTORY_BUILDER_2D.submaps.resolution,默认是 0.05 米,可以改成 0.025。
  • CPU 占用过高:机器人跑着跑着就卡死了。降低 TRAJECTORY_BUILDER_2D.real_time_correlative_scan_matcher.linear_search_window,从 0.1 改成 0.05。

这里我整理了一份参数调优速查表,是我在实际项目中反复试出来的:

参数路径 默认值 推荐值 说明
TRAJECTORY_BUILDER_2D.submaps.resolution 0.05 0.025 地图分辨率(米/像素),越小越精细
TRAJECTORY_BUILDER_2D.submaps.num_range_data 90 60 每个子图包含的激光帧数,越小闭环越快
POSE_GRAPH.optimize_every_n_nodes 0 3 每 N 个节点做一次全局优化,0 表示禁用
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.translation_weight 10.0 15.0 平移权重,增大可减少抖动
TRAJECTORY_BUILDER_2D.ceres_scan_matcher.rotation_weight 40.0 50.0 旋转权重,增大可减少角度漂移

调优口诀:分辨率高地图清,子图小闭环快。平移权重压抖动,旋转权重稳方向。优化间隔别太大,三到五帧刚刚好。

3.4 地图保存与格式

建图完成后,你需要把地图保存下来。Cartographer 自带了一个保存工具:

# 保存地图(包含 .pgm 和 .yaml 两个文件)
ros2 run cartographer_ros cartographer_save_map \
  -map_filename /home/user/maps/my_map

这条命令会生成两个文件:

  • my_map.pgm:灰度图,白色表示可通行区域,黑色表示障碍物,灰色表示未知区域。
  • my_map.yaml:元数据文件,包含分辨率、原点坐标、占用阈值等信息。

YAML 文件的内容大概长这样:

image: my_map.pgm
resolution: 0.050000
origin: [-10.0, -10.0, 0.0]
negate: 0
occupied_thresh: 0.65
free_thresh: 0.196

这里有个细节要注意:occupied_thresh 和 free_thresh 决定了地图的「软硬」程度。阈值设高了,地图会变得很「保守」,稍微有点噪声就认为是障碍物;设低了,又可能把真正的障碍物当成空地。我一般用默认值,除非建图环境特别复杂。

小技巧:如果你想把 Cartographer 的地图转成 ROS2 导航栈需要的格式,直接用 map_server 加载就行。命令是 ros2 run nav2_map_server map_server --ros-args -p yaml_filename:=/path/to/my_map.yaml。

地图格式这块,我再多说两句。除了 PGM,你可能会遇到 PNG、BMP 甚至 JPEG 格式的地图。但 ROS2 导航栈只认 PGM + YAML 的组合。如果你从别的地方拿到了 PNG 地图,可以用 ImageMagick 转一下:

convert input.png -colorspace Gray output.pgm

我曾经接手过一个项目,同事用 PNG 格式保存了地图,结果导航栈死活加载不了。折腾了半天才发现是格式问题。所以,老老实实用 PGM,别整花活。

好了,这一章的内容就到这里。地图建好了,下一章我们就要开始让机器人真正地「导航」了。记住,地图质量直接决定导航效果,别急着往下走,先把参数调好。