1. 从零上手 cartographer:这套建图方案到底解决什么问题
如果你正在折腾移动机器人、AGV 小车或者手持激光建图设备,大概率绕不开 cartographer 这个名字。它是 Google 开源的一套实时 SLAM(同步定位与建图)系统,核心能力是让机器人在未知环境里一边走一边画地图,同时知道自己在地图上的位置。我第一次接触它是在一个室内巡检小车的项目上,当时用别的方案建出来的图要么重影严重,要么回环之后整张图直接错位,换成 cartographer 之后,同样的走廊场景,闭环精度肉眼可见地稳了一大截。
这篇文章要讲的是 cartographer 的完整安装过程。别小看“安装”这两个字,我见过太多人卡在依赖编译、版本冲突、protobuf 报错这些环节上,折腾两三天都跑不起来一个 demo。所以我会把整个流程拆开,从环境准备、依赖安装、源码编译到跑通官方数据集,每一步都讲清楚为什么这么做,以及容易踩的坑在哪。适合的读者是:有一定 Linux 基础、用过 ROS、想快速把 cartographer 跑起来做建图验证的开发者。哪怕你之前没编译过大型 C++ 工程,跟着走也能搞定。
需要提前说明的是,cartographer 的安装方式主要有两种:一种是直接装二进制包(apt 安装),另一种是从源码编译。前者快,但版本受限、不好改代码;后者慢,但可控性强、方便二次开发。这篇文章以源码编译为主线,因为实际项目里你迟早要改它的配置甚至源码,早点熟悉编译流程没坏处。同时我也会给出二进制安装的快捷路径,供只想快速验证的朋友参考。
在正式开始之前,先明确一下整体思路。cartographer 本身是一个 C++ 库,它依赖一大堆第三方组件,比如protobuf、ceres solver、eigen、gflags、glog、lua等等。这些依赖的版本匹配非常关键,尤其是 protobuf,版本不对会直接导致编译失败或者运行时崩溃。所以整个安装的核心逻辑就是:先把依赖的版本对齐,再编译主库,最后编译 ROS 接口层。理解了这条主线,后面每一步你都知道自己在干什么,而不是无脑复制命令。
2. 安装前的环境准备与依赖梳理
2.1 系统与 ROS 版本的选择逻辑
cartographer 对系统环境是有要求的,不是随便一个 Linux 都能跑。目前主流的选择是Ubuntu 18.04 + ROS Melodic或者Ubuntu 20.04 + ROS Noetic。这两个组合是社区验证最充分的,遇到问题也最容易搜到答案。如果你用的是更新的系统,比如 Ubuntu 22.04,ROS 版本对应的是 Humble,cartographer 的支持相对没那么成熟,可能会遇到一些依赖包找不到的情况。
我个人的建议是:如果你只是学习验证,直接用 Ubuntu 20.04 + Noetic,这是目前最省心的组合。为什么?因为 Noetic 是 ROS1 的最后一个长期支持版本,Python3 环境,很多老教程里的 Python2 问题都不存在了。而 Melodic 虽然也稳定,但它是 Python2 的,现在装一些工具链会有点别扭。
选好系统之后,ROS 的安装这里不展开,假设你已经装好了。验证方法很简单,开一个终端敲roscore,能正常启动就说明 ROS 环境没问题。如果这一步都报错,那先回去把 ROS 装好,别急着上 cartographer。
2.2 核心依赖清单与版本陷阱
这是整个安装过程中最容易翻车的地方,我单独拎出来讲。cartographer 的依赖分两类:一类是系统级的库,通过 apt 安装;另一类是需要从源码编译的,主要是 protobuf 和 abseil(新版本需要)。
先看系统级依赖,下面这些是必须的:
sudo apt-get update sudo apt-get install -y \ cmake \ g++ \ git \ google-mock \ libboost-all-dev \ libcairo2-dev \ libeigen3-dev \ libgflags-dev \ libgoogle-glog-dev \ liblua5.2-dev \ libsuitesparse-dev \ libwebp-dev \ ninja-build \ python3-sphinx \ libatlas-base-dev这些包各自的作用我简单说一下,方便你理解而不是死记。libeigen3-dev是做矩阵运算的,SLAM 里大量的位姿计算都靠它;libceres相关的(这里通过 suitesparse 间接引入)是非线性优化库,cartographer 的后端优化核心;liblua5.2-dev是因为 cartographer 的配置文件用的是 Lua 脚本;ninja-build是一个比 make 更快的构建工具,编译大型工程能省不少时间。
注意:
liblua5.2-dev这个版本号很重要,必须是 5.2,装成 5.3 或别的版本可能编译不过。这是很多人忽略的细节。
然后是版本陷阱的重灾区——protobuf。cartographer 对 protobuf 的版本相当敏感。老版本的 cartographer 需要 protobuf 3.6 左右,新版本(比如配合 Noetic 的)通常用系统自带的就行。但如果你系统里已经装了别的版本的 protobuf,就可能冲突。判断方法:
protoc --version如果输出是libprotoc 3.x.x,记下这个版本号。cartographer 编译时会检查这个。如果版本太新或太旧导致报错,最稳妥的办法是从源码编译一个指定版本的 protobuf 并安装到独立目录,然后编译 cartographer 时通过CMAKE_PREFIX_PATH指向它。这个操作稍微复杂,我在第 4 节的排查部分会详细讲。
2.3 工作空间的创建与规划
依赖理清楚之后,建工作空间。ROS 的惯例是建一个 catkin 工作空间:
mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src这里有个经验:不要把 cartographer 直接放在系统目录或者随便一个文件夹里编译,一定要放在 catkin 工作空间的src下,因为它的 ROS 接口层是 catkin 包,需要 catkin 的构建系统来管理。放在别处虽然理论上也能编,但环境变量和包路径会搞得你头大。
工作空间建好后,先初始化一次:
cd ~/catkin_ws catkin_make source devel/setup.bash这一步是为了确保基础环境是通的。如果catkin_make就报错,说明 ROS 环境本身有问题,先解决它。
3. 源码编译全流程与关键环节实现
3.1 拉取源码与目录结构说明
cartographer 的源码分两个仓库:一个是核心库cartographer,一个是 ROS 接口cartographer_ros。两个都要拉,而且必须放在同一个工作空间的 src 下,因为后者依赖前者。
cd ~/catkin_ws/src git clone https://github.com/cartographer-project/cartographer.git git clone https://github.com/cartographer-project/cartographer_ros.git拉下来之后,先别急着编译。看一下目录结构,心里有个数:
cartographer/下面是核心算法,包括mapping、sensor、common等模块,还有configuration_files目录放 Lua 配置。cartographer_ros/下面是 ROS 的封装,包括cartographer_ros节点、cartographer_ros_msgs消息定义、cartographer_rviz可视化插件。
理解这个结构的意义在于:当你后面要改配置(比如调整建图分辨率、传感器话题名),你知道该去哪个目录找文件。比如改传感器配置,就在cartographer_ros/configuration_files里。
提示:git clone 如果太慢,可以考虑用国内的镜像源,或者提前把仓库下载成压缩包再解压。这一步纯粹是网络问题,跟技术无关,但确实卡人。
3.2 依赖自动安装脚本的使用与取舍
cartographer 官方提供了一个依赖安装脚本,在cartographer/scripts/install_dependencies.sh。这个脚本会自动帮你装一堆依赖,包括从源码编译 abseil、protobuf 等。用起来很方便:
cd ~/catkin_ws/src/cartographer sudo bash scripts/install_dependencies.sh但我得提醒你,这个脚本不是万能的。它有几个问题:第一,它会往系统目录里装东西,可能和你已有的环境冲突;第二,它编译的依赖版本是固定的,如果你的系统比较特殊,可能编不过;第三,它跑起来时间很长,中途失败的话不好排查。
我的建议是:第一次装可以先用这个脚本试试,如果顺利跑完,那恭喜你省事了。如果中途报错,别慌,把报错信息记下来,然后回到手动安装依赖的路子上。手动装虽然麻烦,但每一步都可控,出了问题知道去哪查。
脚本跑完之后,验证一下关键依赖是否就位:
# 检查 abseil 是否安装 ls /usr/local/lib | grep absl # 检查 protobuf protoc --version如果这些都有输出,说明依赖基本 OK。
3.3 编译参数的选择与优化
依赖搞定,开始编译。回到工作空间根目录:
cd ~/catkin_ws catkin_make_isolated --install --use-ninja这里解释一下这几个参数。catkin_make_isolated是隔离编译,每个包单独编译,避免包之间的依赖干扰,cartographer 官方推荐用这个。--install表示编译完安装到install目录,方便后续 source。--use-ninja是启用 ninja 构建工具,比默认的 make 快很多,尤其是 cartographer 这种大工程,能省一半以上的时间。
如果你机器核多,可以加-j参数指定并行数,比如-j8。但注意别开太大,内存不够会直接 OOM 崩掉。一般来说,每核配 2GB 内存比较稳妥,8 核机器开-j4到-j6比较安全。
编译过程可能要十几分钟到半小时不等,取决于机器性能。这期间你可以去干点别的,但别关终端,因为可能有报错需要你处理。
编译成功的标志是最后输出[100%]之类的完成信息,没有 error。如果有 error,往下看第 4 节的排查。
3.4 环境变量配置与验证
编译安装完成后,需要 source 环境:
source ~/catkin_ws/install_isolated/setup.bash注意路径是install_isolated,不是devel,因为用了--install参数。这个区别很多人搞混,source 错了路径会导致找不到包。
为了方便,把这行加到~/.bashrc里,省得每次开终端都要 source:
echo "source ~/catkin_ws/install_isolated/setup.bash" >> ~/.bashrc source ~/.bashrc然后验证安装是否成功:
roslaunch cartographer_ros demo_backpack_2d.launch如果能看到 RViz 启动,并且有地图在逐渐生成,那就说明装好了。这个 demo 用的是官方提供的 2D 背包数据集,不需要真实硬件,是验证安装的最佳方式。
4. 常见报错排查与避坑经验实录
4.1 编译期典型错误与解决思路
安装 cartographer 最折磨人的就是各种编译报错。我把最常见的几类整理成表,方便你对照排查。
| 报错关键词 | 根本原因 | 解决方向 |
|---|---|---|
protobuf version mismatch | protobuf 版本与 cartographer 要求不符 | 源码编译指定版本 protobuf,用 CMAKE_PREFIX_PATH 指向 |
undefined reference to absl | abseil 库没装或路径不对 | 重新跑 install_dependencies.sh 或手动编译 abseil |
Could not find Lua | lua 版本不对或没装 | 确认装的是 liblua5.2-dev |
ceres not found | ceres solver 缺失 | 装 libceres-dev 或从源码编译 |
out of memory | 并行编译数太多 | 降低 -j 参数,比如改成 -j2 |
拿 protobuf 版本冲突举例,这是最典型的。假设你系统里是 protobuf 3.21,但 cartographer 需要 3.6,编译时就会报版本不匹配。解决办法是编译一个 3.6 版本装到独立目录:
cd ~ git clone https://github.com/protocolbuffers/protobuf.git cd protobuf git checkout v3.6.1 ./autogen.sh ./configure --prefix=/usr/local/protobuf-3.6.1 make -j4 sudo make install然后在编译 cartographer 时指定路径:
catkin_make_isolated --install --use-ninja \ -DCMAKE_PREFIX_PATH=/usr/local/protobuf-3.6.1这样就能让 cartographer 用你指定的 protobuf 版本,而不受系统版本影响。
4.2 运行期问题与调试技巧
编译过了不代表就能跑,运行期的问题同样不少。最常见的是话题名对不上。cartographer 默认订阅的话题是/scan、/odom、/imu这些,但你的机器人发布的话题可能叫别的名字。这时候需要改 Lua 配置文件,把话题名改成你实际的。
配置文件在cartographer_ros/configuration_files目录下,比如backpack_2d.lua。打开它,找到tracking_frame、published_frame、num_laser_scans这些参数,按你的实际情况改。改完不需要重新编译,直接重新 launch 就行,因为 Lua 是运行时读取的。
另一个常见问题是建图漂移严重。这通常不是安装问题,而是参数没调好。比如TRAJECTORY_BUILDER_2D.linear_velocity_penalty这个参数,默认值可能不适合你的场景。如果机器人走得慢,可以适当调小;走得快就调大。这类调参没有标准答案,得根据实际数据反复试。
提示:调试建图效果时,建议先用官方数据集跑通,确认软件本身没问题,再上真实硬件。这样能把“软件问题”和“数据问题”分开,排查效率高很多。
4.3 我踩过的三个真实坑
第一个坑是磁盘空间不足。cartographer 编译过程中会产生大量中间文件,加上依赖库,整个工作空间可能占十几个 G。我有次在虚拟机里装,磁盘只分了 20G,编到一半直接满了,报了一堆莫名其妙的错。后来扩到 50G 才顺利编完。所以装之前先df -h看一眼磁盘,留足空间。
第二个坑是多版本 ROS 冲突。我机器上同时装了 Melodic 和 Noetic,环境变量混在一起,导致编译时找不到正确的包。解决办法是在~/.bashrc里只 source 一个 ROS 版本,别同时 source 两个。切换版本时手动改,虽然麻烦但不会出错。
第三个坑是ninja 版本太老。有些系统的 ninja 版本比较旧,编译 cartographer 时会报不支持的语法。解决办法是升级 ninja,或者干脆不用--use-ninja,退回默认的 make。慢是慢点,但稳。
5. 二进制快速安装与源码方案对比
5.1 apt 安装的适用场景
不是所有人都需要源码编译。如果你的目的只是快速验证功能,或者你的项目不需要改 cartographer 源码,那直接用 apt 装二进制包是最省事的:
sudo apt-get install ros-noetic-cartographer \ ros-noetic-cartographer-ros两条命令,几分钟搞定,不用折腾依赖和编译。装完直接roslaunch就能跑 demo。对于做算法验证、教学演示的场景,这种方式性价比最高。
但二进制安装的局限也很明显:版本固定,你没法选;不能改源码,遇到需要定制的地方只能干瞪眼;依赖也是打包好的,如果和你项目里其他库冲突,很难调。所以它适合“用”,不适合“改”。
5.2 两种方案的选择建议
到底选哪种,我总结了一个简单的判断标准:
- 只是学习、跑 demo、做课程作业 → 二进制安装
- 要做二次开发、改算法、集成到自己的系统 → 源码编译
- 硬件资源紧张、不想折腾 → 二进制安装
- 需要特定版本、特定功能 → 源码编译
我个人的习惯是:先用二进制装一个跑通,确认功能符合预期,再上源码编译做定制。这样能快速建立信心,也能提前发现一些环境问题,避免在源码编译时被这些基础问题干扰。
5.3 安装后的目录结构与文件说明
不管你用哪种方式装,装完之后了解一下文件分布是有好处的。源码编译的话,核心文件在~/catkin_ws/install_isolated下,包括:
lib/放编译出来的库文件share/cartographer_ros/放 launch 文件和配置include/放头文件,二次开发时要用
二进制安装的话,文件分散在/opt/ros/noetic下,结构类似。知道这些文件在哪,当你需要改配置、加自定义传感器、或者排查找不到文件的问题时,就能快速定位。
6. 建图效果验证与参数微调入门
6.1 用官方数据集做基准测试
装完之后,怎么判断“装好了”而不是“装了个能启动但建图稀烂的版本”?答案是用官方数据集做基准测试。cartographer 官方提供了几个经典数据集,比如 2D 的背包数据、3D 的博物馆数据。跑这些数据的好处是:数据质量有保证,建图效果有参考,能排除硬件和数据的干扰。
跑 2D 数据集的命令前面提过:
roslaunch cartographer_ros demo_backpack_2d.launch跑起来之后,观察 RViz 里的地图。正常情况下,地图应该是清晰的、墙壁是直的、回环处没有明显错位。如果地图糊成一团,或者走着走着就飘了,那说明有问题,可能是参数不对,也可能是依赖版本有微妙的不兼容。
6.2 关键参数的含义与调整方向
cartographer 的参数非常多,新手容易看花眼。我挑几个最影响建图效果的讲:
TRAJECTORY_BUILDER_2D.min_range和max_range是激光的有效距离范围。设太小会丢数据,设太大会引入噪声。一般按你激光雷达的实际参数设,比如雷达标称 12 米,就设max_range = 12。
TRAJECTORY_BUILDER_2D.submaps.num_range_data控制每个子图用多少帧激光数据。值越大,子图越“厚实”,但计算量也越大。默认 90 左右,室内场景够用。
POSE_GRAPH.optimize_every_n_nodes控制回环优化的频率。值越小优化越频繁,建图越准但越吃 CPU。资源紧张的话可以调大。
这些参数都在 Lua 配置文件里,改完重新 launch 生效。调参是个细活,建议一次只改一个参数,改完看效果,这样才能知道是哪个参数起了作用。
6.3 从 demo 到真实硬件的过渡要点
demo 跑通了,接下来就是上真实硬件。这一步的坑主要在两个地方:传感器标定和话题对接。
传感器标定包括激光雷达和 IMU 的外参(相对位置和朝向)。cartographer 需要知道 IMU 和激光雷达之间的变换关系,这个通过static_transform_publisher或者 URDF 来配置。如果外参不准,建图会系统性偏移。
话题对接就是前面说的,把 cartographer 订阅的话题改成你实际发布的话题。这个在 Lua 配置里改,改完确认rostopic list里能看到对应话题,并且有数据在发布。
提示:上真实硬件前,先用
rosbag record录一段数据,然后用rosbag play回放来调试。这样比直接上硬件调试方便得多,数据可以反复用,参数可以慢慢调。
7. 二次开发准备与长期维护建议
7.1 源码结构与二次开发入口
如果你打算基于 cartographer 做二次开发,得先熟悉它的源码结构。核心算法在cartographer/cartographer/mapping下,local_trajectory_builder负责前端,pose_graph负责后端优化。传感器数据处理在sensor目录,配置解析在common目录。
二次开发最常见的需求是加自定义传感器或者改建图策略。加传感器的话,需要在sensor目录下加数据结构和处理逻辑,然后在trajectory_builder里接入。改建图策略的话,主要动local_trajectory_builder_2d.cc和pose_graph相关的文件。
改完源码后,重新编译只需要编对应的包,不用全量重编:
catkin_make_isolated --install --use-ninja --pkg cartographer这样能省不少时间。
7.2 版本管理与升级注意事项
cartographer 更新比较频繁,升级时要注意配置文件的兼容性。新版本可能改了参数名或者默认值,直接覆盖升级可能导致原来的配置失效。我的做法是:升级前先备份configuration_files目录,升级后对比新旧配置,把改动合并进去。
另外,git 管理源码时,建议锁定一个稳定的 commit,不要盲目追最新。新版本可能有未修复的 bug,生产环境用稳定版更靠谱。可以在git checkout时指定一个 tag 或者 commit hash。
7.3 性能优化与资源占用控制
cartographer 比较吃资源,尤其是 3D 建图。如果跑在嵌入式设备上,得做优化。几个方向:
降低激光数据的降采样率,减少每帧处理的点数;调大optimize_every_n_nodes,减少后端优化频率;关闭不必要的可视化,RViz 很吃 GPU;用 release 模式编译,别用 debug。
我实测下来,在树莓派这类设备上跑 2D 建图是可行的,但 3D 基本没戏,算力不够。所以选方案时要先评估硬件能力,别硬上。
最后分享一个我自己的习惯:每次装完 cartographer,我都会把整个流程和遇到的报错记成一个文档,包括用的系统版本、依赖版本、编译命令、报错信息和解决办法。下次再装或者帮别人排查时,直接翻文档,效率高很多。这个习惯看起来笨,但真的省时间。