做过机器人或无人车项目的人基本都绕不开Cartographer。作为Google开源的一套激光SLAM方案它最让我佩服的一点是一套代码同时支持2D与3D建图还自带子图回环检测不用像早期gmapping那样依赖高质量里程计才能把走廊闭成一个环。我最早用它是给实验室一台两轮差速小车做楼道建图只用了一颗RPLIDAR A1跑完一圈回来地图没有断开楼道走廊被回环检测完整闭合那次之后我基本告别了其他2D方案。这篇文章就把我这段“从安装到实战”的整个过程完整记录下来。内容包括环境准备与三种安装方式的选择、跑通官方2D/3D demo、把自己机器人上的雷达和IMU接进来需要改哪些参数、以及从建图切换到纯定位模式时容易踩的坑。不管是第一次装Cartographer的新手还是已经在调参、想提升建图质量的开发者都能在里面找到对得上号的内容。1. 项目思路拆解为什么要用Cartographer落地2D/3D建图与定位1.1 Cartographer到底解决了什么问题要理解Cartographer得先从SLAM的痛点说起。传统的2D激光SLAM比如gmapping靠的是粒子滤波加逐帧匹配里程计精度不好时地图很容易漂移走长走廊尤其明显。而Cartographer的核心思路不是“逐帧硬怼”而是先构建局部子图submap再通过回环检测把当前帧放回全局位姿图中做整体优化。换句话说它把“建图”当成一个带反馈闭环的优化问题而不是单纯地“扫描鬼影”。在实际工程里这意味着哪怕是楼梯间、长走廊这种特征弱到极点的环境只要最终绕一圈回来回环检测都会把累积误差收敛掉地图不会裂成两半。这个特点让Cartographer非常适合楼道清扫机器人、园区配送车、室内AGV这类有“反复经过同一区域”特征的场景。1.2 原生支持2D和3D一套代码两种建图模式Cartographer的软件架构对2D和3D是统一的。2D模式把激光雷达的Scan消息在二维平面做匹配主要输出机器人在地图平面上的位姿x、y、yaw3D模式则接受三维点云匹配发生在三维空间输出完整的六自由度位姿包含roll、pitch、yaw和x、y、z。对开发者来说这意味着你学一次Cartographer的配置和代码结构就能同时搞定2D和3D项目。但硬件差别就大了2D建图基本只需要一个2D激光雷达即便没有IMU和高质量里程计也能跑起来只是效果差点3D建图则几乎离不开3D激光雷达和性能足够的CPUIMU在3D模式下基本属于必选件因为纯靠3D雷达点云在剧烈抖动场景下是无法稳定估计姿态的。ROS上使用Cartographer时这套架构通过cartographer_ros封装成节点外部传感器话题、TF树、map_frame、odom_frame这些是标准ROS概念理解了坐标系之间的传递关系后面配置launch文件就会轻松很多。1.3 核心名词submap、回环检测、位姿图优化到底在讲什么很多刚接触Cartographer的人一上来就被submap、回环检测、位姿图这三个词绕晕。我用生活中能找到的例子解释一下。你可以把submap想象成一张一张的“拼图块”。机器人每走一小段Cartographer就把这附近的扫描帧融合成一块局部拼图块新到达的激光帧先跟最近的拼图块匹配确定自己在哪。回环检测就是“拼图能不能对上”当机器人绕了一圈看到曾经去过的地方它会把眼前这帧和很久之前的那块拼图块再比较一次如果对上了就说明“闭环形成了”整个地图里的拼图块位置会被一次性微调让所有块拼得严丝合缝。位姿图优化就是这个“一次性微调”的过程它维护着一张图图的节点是机器人位姿图的边是相邻约束和闭环约束通过最小化所有边的误差来更新全部节点位置。理解了这三个词你再去看Cartographer的源码结构就不慌了。它最核心的模块也就这三块前端scan-to-submap匹配对应cartographer/mapping/internal/2d/scan_matching后端回环和位姿图优化对应cartographer/mapping/internal/optimization再加上一个处理多传感器融合的节点模板。2. 环境准备与安装从系统到编译一步步来2.1 系统版本与ROS版本选择我这边长期用的是Ubuntu 20.04 ROS Noetic的组合这也是ROS1生态里维护最活跃、第三方软件包最全的搭配。Cartographer官方支持ROS1和ROS2Noetic上下载依赖最省心。如果你正在用Ubuntu 22.04推荐直接上ROS2 Humble一套代码编译也能通过但第三方工具链适配程度不如Noetic成熟建议新手先从Noetic起步。关于ROS安装这里想说一个重要细节如果你只是要跑Cartographer完全不必安装庞大的ros-noetic-desktop-full那个包会把RViz、模拟器、大量GUI工具一起装进来体积大且容易和已有环境冲突。我一般只装ros-noetic-ros-base然后再补装rviz和slam所需的依赖就够了。2.2 安装方式对比二进制、源码、官方release怎么选Cartographer的安装有两条主流路线apt直接装二进制包或者从源码编译。还有一条官方release的路线比较适合想固定版本做产品化部署的团队。安装方式优点不足适用场景apt二进制快几分钟装完版本通常滞后不方便改代码只要个能跑的demo快速验证源码编译可随时改核心代码参数解释全第一次编译要20分钟以上学习源码、二次开发、调优官方release版本清晰发布周期明确需要自己处理依赖产品化依赖版本管理如果只是把Cartographer当作黑盒工具验证一个想法apt安装足够但我强烈建议有机会源码编译一次因为后面遇到“地图建出来不对劲”的问题时很大概率需要改源码才看得明白原因这个过程是二进制包无法提供的。2.3 源码编译详细步骤下面是我在Noetic上完整跑通过的操作。第一步先把系统依赖装齐sudo apt update sudo apt install -y cmake g git google-mock libboost-all-dev \ libcairo2-dev libeigen3-dev libgflags-dev libgoogle-glog-dev \ liblua5.3-dev libprotobuf-dev libsuitesparse-dev libwebp-dev \ ninja-build protobuf-compiler python3-sphinx python3-wstool \ python3-rosdep python3-catkin-tools然后创建工作空间用wstool拉取cartographer_ros及其全部依赖mkdir -p cartographer_ws/src cd cartographer_ws wstool init src wstool merge -t src https://raw.githubusercontent.com/cartographer-project/cartographer_ros/master/cartographer_ros.rosinstall wstool update -t src接着是rosdep安装ROS依赖sudo rosdep init rosdep update rosdep install --from-paths src --ignore-src --rosdistronoetic -y最后进入编译这里推荐用ninja加速catkin_make_isolated --use-ninja source devel_isolated/setup.bash编译过程中最容易出问题的点是protobuf版本冲突如果系统里之前装过其他ROS包建议先sudo apt purge libprotobuf-dev protobuf-compiler再统一重装版本合适的依赖。2.4 关于“无法定位软件包”和鱼香ROS一键安装的排障搜索记录里频繁出现的“无法定位软件包 ros-noetic-desktop-full”我这两年反复遇到过原因九成是ROS软件源没有正确加入或者没有执行sudo apt update。还有一部分是系统版本和ROS版本对不上比如在Ubuntu 22.04上试图装ros-noetic开头的包源里根本没有对应条目自然会提示无法定位。解决思路很简单先去确认系统版本再按官方步骤添加对应ROS源最后一定执行sudo apt update。不想手动折腾的话可以用鱼香ROS脚本执行wget http://fishros.com/install -O fishros . fishros选择“一键安装ROS”它会自动检测系统版本、配置源并安装你选定的完整或基础版本。这套脚本我帮同事装过很多次对国内网络环境做了不少优化比手动配源省事很多。3. 2D建图实战从官方demo到自己机器人3.1 先跑通官方demobackpack_2d数据集建议第一次运行不要直接接自己的传感器先用官方数据集把流程跑通。Cartographer官方提供了德国博物馆的双肩包2D数据集下载之后一条命令就能跑wget https://storage.googleapis.com/cartographer-public-data/bags/backpack_2d/cartographer_paper_deutsches_museum.bag roslaunch cartographer_ros demo_backpack_2d.launch \ bag_filename:~/cartographer_paper_deutsches_museum.bag启动后你会看到RViz窗口里逐渐出现一张越来越大的地图。这里要重点观察两个指标地图闭合时走廊两侧是否对齐以及当前估算的轨迹线有没有明显的突变。如果官方demo跑出来地图都对不齐那多数是环境问题比如RViz没收到TF、use_sim_time没开。确认rosbag play的启动节点和launch文件的时钟同步正常这个坑在后面单独讲。3.2 把Cartographer装进自己的机器人launch文件和lua参数配置跑通官方demo之后真正的工程问题是把自己的雷达数据接进Cartographer。一个典型的点云话题是/scanlaunch文件里要做两件事启动cartographer_node并传入配置目录和配置文件名同时启动占用栅格地图的发布节点。我习惯的配置结构是这样cartographer_node启动时通过-configuration_directory指定lua配置文件所在目录用-configuration_basename指定具体文件。lua文件里最常用的几个参数如下参数作用我常用的经验值map_frame地图固定坐标系maptracking_frame传感器或底盘坐标系base_linkodom_frame里程计坐标系odomprovide_odom_frame是否由Cartographer发布odomfalse有自己的里程计use_imu_data是否使用IMUtrue有IMU时强烈建议开num_range_data匹配窗口内扫描帧数90-150之间rangefinder_sampling_ratio激光数据降采样1.0global_sampling_ratio全局优化采样的数据比例0.003左右一个容易忽略的点是launch文件里remap fromscan to你的实际话题一定要确认很多跑不起来的情况都是因为话题没对上。IMU数据同理如果IMU话题是/imu/data_raw也需要remap到Cartographer期望的话题名。3.3 2D建图调优实战经验搭好框架之后真正决定地图质量的是参数调节和传感器硬件表现。这里分享几条实战经验。第一激光雷达的安装位置一定要正安装倾斜会导致Cartographer输出大量“叉腰图”。如果雷达装在车头下方和底盘x轴有固定角度偏转务必在URDF或TF里补上这个外参千万不要在lua里硬调数据。第二机器人运动速度对建图影响极大。Cartographer的回环检测需要足够多的特征重叠如果机器人跑得太快扫描帧之间重叠太少前端匹配很容易崩表现出来就是轨迹线突然跳一大截地图出现重影。我一般把线速度限制在0.3 m/s左右角速度0.5 rad/s以内建图成功率会高很多。第三如果有轮式里程计一定要用上。Cartographer可以只用雷达建图但融合里程计后前端匹配的初值会准很多复杂地形斜坡、颠簸的表现也更好。我遇到过很多次只用雷达建图时楼道拐角多转了几圈地图就歪了把轮式里程计接进TF之后问题直接消失。4. 3D建图实战从点云接入到参数优化4.1 3D建图与2D建图的本质区别3D建图和2D建图相比核心区别在传感器的数据维度和计算量上。2D模式接收单线激光每个扫描周期只有几百个点3D模式接收多线激光雷达如16线、32线生成的PointCloud2点云一帧就是几万到几十万个点匹配、子图构建和回环计算量成倍上涨。因此3D建图对CPU的性能要求非常直接。我实测过六核i7处理器跑16线雷达关闭回环检测时CPU占用在80%左右打开回环检测直接吃满。在实际项目中3D建图通常不会用于实时运行的所有环节多数时候是先离线采集数据包再用离线模式重建地图这样能把大段的建图计算放到后台慢慢算不争抢在线控制周期。4.2 跑通官方3D demoCartographer官方也提供了3D建图的数据集与2D流程几乎一致。下载数据后使用对应3D launch文件wget https://storage.googleapis.com/cartographer-public-data/bags/backpack_3d/with-intensities/b3-2016-04-05-14-14-00.bag roslaunch cartographer_ros demo_backpack_3d.launch \ bag_filename:~/b3-2016-04-05-14-14-00.bag运行后RViz里会出现三维地图包括地面和墙面的点云信息。3D建图对数据集的时间戳同步要求极高如果bag里的激光和IMU时间戳没有同步地图会出现严重的分层和错位这几乎是我看到3D建图失败的头号原因。所以3D模式特别强调数据采集时的传感器时间一致性。4.3 接入自己的3D激光雷达和IMU在自己的机器人上跑3D建图除了话题的remap还要在lua文件里多设置几个和传感器模式相关的参数。以使用16线机械雷达为例配置里最重要的是use_online_point_cloud true表示Cartographer直接使用订阅到的在线点云话题如果用的是点云话题和IMU话题还需要确认sensor_frame_2d和tracking_frame的统一。坐标系的坑在3D模式里尤其多。很多3D雷达的安装方式是倒装或者倾斜导致雷达坐标系和机器人底盘坐标系有旋转关系。如果在URDF里没有正确描述这个外参Cartographer会把地面当成墙面建出一个“宇宙飞船视角”的地图。我的建议是先单独用RViz查看雷达点云显示确认地面点确实位于雷达坐标系下方且水平再接Cartographer。另外3D模式下的IMU外参也非常关键。IMU的安装角度和雷达有偏差时点云畸变矫正会失败表现是平移时地图边缘出现大面积“开花”噪点。我通常的做法是先用机械方式保证IMU z轴朝上再在TF里标定一次外参这样后续调参工作量会小很多。4.4 3D建图最有价值的优化点如果你的CPU比较紧张可以把回环检测的loop_closure_min_score从默认值上调减少低置信度回环对全局优化的干扰也能降低CPU消耗。另外把global_sampling_ratio从默认的0.001调到0.003回环精度会明显改善但计算量也相应增加。离线建图和在线建图的选择也是经验问题。室外果园、矿场这类GPS不稳定、场景遮挡严重的环境我更推荐先采集rosbag然后用cartographer_offline_node离线建图一边建一边调参效果远比在线实时建图稳定。这里贴一个离线建图的简易命令roslaunch cartographer_ros offline_backpack_3d.launch \ bag_filenames:~/my_bag/raw.bag5. 定位模式从建图到纯定位有哪些坑5.1 保存地图pbstream和pgm/yaml到底差在哪建图完成之后保存地图有两种常见格式。一种是占栅格地图通过map_server的map_saver保存成pgm/yaml这种格式主要服务于move_base路径规划不包含任何位姿约束信息。另一种是Cartographer特有的pbstream格式它保存的是位姿图、子图和传感器外参等完整状态纯定位模式必须用它。保存pbstream的方式是调用服务rosservice call /finish_trajectory 0 rosservice call /write_state {filename: /tmp/my_map.pbstream}注意调用顺序是先finish_trajectory再write_state直接write_state也能存但轨迹没有正常结束之后定位时可能会多出一些无效约束。5.2 纯定位模式的正确打开方式Cartographer的定位模式很多人会误以为跟手机上的“定位权限”一样给个位置服务就能定。实际上机器人领域说的纯定位是给定一张已经建好的pbstream地图通过实时激光数据和IMU数据来连续地求解机器人在地图中的位姿完全不依赖GPS。在launch文件里把cartographer_node的启动参数里加入load_state_filename指定pbstream路径同时把map的话题发布出来就可以进入定位模式。但这里有一个特别容易踩的坑Cartographer定位时需要你给它一个初始位姿估计相当于告诉它“我一开始大概在哪、朝哪个方向”。如果初始位姿给错纯定位会一直在地图里找不到自己输出位姿乱跳。通常我会在RViz里用2D Pose Estimate工具点击地图上机器人实际所在位置并拖一个朝向给Cartographer一个靠谱的初值。5.3 全局重定位与自动初始化老版本Cartographer只能做带初始位姿的局部定位环境变化或个人视觉失联后需要人工重新初始化这在很多自动化场景里很痛。新版Cartographer 3.0之后引入了全局定位能力官方提供了一个Warthog演示可以通过cartographer_ros的localize模式让机器人在地图中自动寻找自己位置。全局定位的原理其实还是靠回环检测把当前帧尝试匹配到之前所有历史子图中对比相似度最高的候选位置。这个过程计算量不小但对我们使用者来说进步是明显的至少在园区车自动唤醒、系统重启后自动回到工作位这类场景里不再需要人工给初始位姿了。如果你的Cartographer版本较老想要全局定位建议升级到3.x分支。6. 常见问题排查与避坑速查6.1 安装与编译卡住的经典问题编译时提示protocol buffer版本冲突几乎人手一份。处理方法是先统一apt版本删掉手工编译的protobuf再sudo apt --reinstall install libprotobuf-dev protobuf-compiler。编译到一半因为内存不足导致ninja崩溃可以在catkin_make_isolated前设置export MAKEFLAGS-j2限制并行任务数效果立竿见影。如果还是崩就检查swap空间给系统加4 GB swap基本就稳了。6.2 运行时TF、话题与时间同步报错Cartographer运行时报错里最高频的是Lookup would require extrapolation into the past这类TF相关错误含义是某个坐标系在TF树中还不存在或者时间戳超前了。解决办法是检查launch文件是否设置了use_sim_time回放bag时必须开否则bag里的传感器时间戳会早于当前系统时间TF直接查不到。真实机器人场景则要保证雷达驱动节点发布了正确的TF并且TF时间与传感器时间一致。另一个高频报错是No map frame received说明cartographer_node没有输出map坐标系最常见的触发原因是雷达话题没对上前端匹配不到足够多有效的点云帧。检查rostopic echo /scan确认有数据持续输出再看lua配置里rangefinder_sampling_ratio是否为0这参数一旦误设为0话题数据全被丢弃地图自然不会有输出。6.3 性能与建图质量常见问题建图过程中CPU打满常见原因有两个一是雷达频率太高没有降采样二是有多帧点云在后台触发大量回环计算。建议先把num_range_data加大到150左右减少前端匹配频次再把loop_closure_min_score调高到0.65以上过滤低质量回环。地图出现双层墙壁或者地图漂移九成是传感器外参没标定好。特别是雷达安装左右倾斜或者IMU和底盘朝向不一致时地图会“一层一层”地叠起来。解决方法是重新检查URDF和TF外参并用ROS自带的tf静态发布再发布一次测试外参逐项排除。我这边把三个建图最常见的报错整理成了一张表直接按表操作就能省下大量排查时间。报错/现象原因快速定位方法地图重影/双层墙外参不对或里程计漂移单独看TF树检查外参Lookup would require extrapolationTF时间戳不同步bag播放时开启use_sim_timeNo map frame received雷达话题没对上/采样率为0echo话题确认数据CPU打满回环检测频繁/点云量大调整global_sampling_ratio最后再说点我的实际体会Cartographer虽然文档不如某些商业方案齐全但它的核心优势是架构统一、代码开源、研发生态活跃。如果你打算长期在机器人感知方向深耕一定不要只停在run通demo的层面。花点时间把cartographer_ros的源码结构过一遍特别是前端scan_to_submap匹配和后端位姿图优化两个模块理解它为什么在回环检测时用分支定界法加速搜索这些知识在你后面迁移到其他传感器平台或者自研SLAM时价值会成倍放大。我个人在做完几次2D/3D项目之后最大的体会是Cartographer对传感器硬件的要求其实没有传说中那么苛刻真正决定上限的是你对参数和坐标系的把控能力。先把坐标系捋清楚再把参数一个个弄明白绝大多数建图问题都能在半小时内定位到原因。如果你手头正好有雷达和一台Ubuntu机器别光看去下一个bag跑一遍。跑通之后再换上自己的硬件试一次你一定会在第一次闭合地图的瞬间觉得所有的配置和调试都值了。