入坑SLAM的都知道Cartographer是Google开源的一套激光SLAM方案2D和3D建图都能做代码质量高、社区活跃是很多机器人和自动驾驶项目里定位建图模块的首选。不过安装和跑通官网数据集这个过程对新手来说并不是一路顺畅的我见过太多人在编译、依赖、数据集下载这几个环节卡了好几天。这篇文章就按我自己实际操作的流程把Cartographer从环境准备到跑通官方demo的完整过程捋一遍每一步都给你说明为什么这么做遇到问题该怎么排查。1. 环境准备与版本选型1.1 为什么建议用Ubuntu 16.04 ROS KineticCartographer官方文档里给的推荐环境是Ubuntu 16.04 ROS Kinetic。很多朋友电脑上装的是Ubuntu 18.04或者20.04第一反应是用更新版本的ROS其实老版本环境反而是最稳的。原因很简单Cartographer发布时主要针对Kinetic做测试二进制依赖包、编译选项都是在这个组合下验证过的。用Melodic编译虽然问题也不大但你会遇到一些细微的差异比如pcl版本不同导致的API差异、glog的版本兼容性等。我实测下来如果你只是想把数据跑通、把原理搞明白直接用推荐组合可以少踩很多坑。如果你用的是18.04或20.04也不是不能装但你需要自行解决依赖版本问题后面容易在编译时卡住排查成本很高。1.2 检查系统基础依赖装ROS的时候有个基础依赖建议先确认装好否则后面编译Cartographer时会报缺头文件的错误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 \ libprotobuf-dev \ libsuitesparse-dev \ libwebp-dev \ ninja-build \ protobuf-compiler \ python-sphinx \ ros-kinetic-tf2-eigen \ libatlas-base-dev \ libsuitesparse-dev \ liblapack-dev \ libblas-dev这里有个细节libsuitesparse-dev在文档里出现了两次这不是我写重复了是因为Cartographer的编译系统对稀疏矩阵求解依赖比较敏感如果你在编译时遇到SuiteSparse相关报错装这个包基本都能解决。libboost-all-dev也是必须的Cartographer的线程池和回环检测模块用了Boost的智能指针和线程库缺了会在很早期编译阶段就报错。1.3 安装ROS及Cartographer依赖包这一步官方文档写得比较简洁实际上操作时有几个容易漏掉的点。建议用wstool管理工作空间的依赖这是ROS社区常用的工具它能把多个仓库的依赖一次性拉取下来。sudo apt-get install -y python-wstool python-rosdep ninja-build注意ninja-build这是Google常用的构建系统Cartographer的CMake项目在Ninja下编译速度比默认的Make快很多尤其你后面要反复修改源码重新编译时这个体验差异非常明显。创建工作空间并初始化mkdir -p ~/catkin_ws/src cd ~/catkin_ws catkin_make这里先跑一次空工作空间的catkin_make目的是生成devel目录和CMake缓存后面往src里放代码再编译时会顺很多。1.4 配置roscore环境如果你是第一次在这个终端窗口里运行ROS相关命令记得先source环境变量source /opt/ros/kinetic/setup.bash echo source /opt/ros/kinetic/setup.bash ~/.bashrc很多新手到这一步会忽略导致后面catkin_make时找不到rosbash等命令。source其实就是在当前shell会话里加载ROS的环境变量让系统知道ROS装在哪个目录、有哪些可执行命令这步不做后面前置准备工作基本都会报“command not found”。2. 源码安装cartographer与cartographer_ros2.1 下载源码的核心逻辑Cartographer本体是一个纯C的库独立于ROS运行cartographer_ros则是对接ROS的封装层负责把激光雷达的LaserScan或PointCloud2数据转换为Cartographer需要的数据格式同时发布子图、轨迹、位姿等话题。两者必须一起装而且版本要匹配否则接口会对接不上。cd ~/catkin_ws/src wstool init . wstool set -y src/cartographer --git https://github.com/cartographer-project/cartographer.git wstool set -y src/cartographer_ros --git https://github.com/cartographer-project/cartographer_ros.git wstool update -t src这里用wstool而不是直接git clone是因为Cartographer项目里还引用了cartographer_ros依赖的几个子模块比如cartographer_ros_msgs、ceres-solver用wstool可以统一管理这些仓库的拉取和更新。如果你直接git clone后面编译时经常会出现“找不到xxx头文件”或者“找不到cartographer_msgs消息包”的坑。如果你网络状况较差拉取GitHub仓库速度很慢可以多加几个国内镜像源后再wstool update这个不属于安装必须步骤但可以有效节省时间。2.2 安装cartographer_ros的ROS依赖源码拉下来之后使用rosdep自动解析并安装ROS相关的依赖包sudo rosdep init rosdep update cd ~/catkin_ws rosdep install --from-paths src --ignore-src --rosdistrokinetic -y这里有个高频坑rosdep init时提示ERROR: cannot download default sources list from https://raw.githubusercontent.com/...多半是网络问题。解决方法是去网上找一份别人备份好的20-default.list放进/etc/ros/rosdep/sources.list.d/然后再次rosdep update。另外注意--ignore-src参数的含义它的意思是只安装那些在src里找不到源码的依赖包也就是系统里已有的cartographer和cartographer_ros会被跳过这个参数不能删掉否则rosdep会尝试去系统源里找这两个包反而装不上。2.3 编译Cartographer本体编译部分建议先单独编译cartographer编译通过后再编译cartographer_ros这样如果后面的ROS封装层出问题比较容易定位。cd ~/catkin_ws catkin_make_isolated --install --use-ninjacatkin_make_isolated是catkin_make的隔离版本会逐个包编译并独立安装好处是某个包编译失败不会影响其他包。这里第一次编译时间会比较长我自己的机器大概15到20分钟如果你的电脑性能一般可以先去泡杯咖啡。编译过程中如果出现内存不足虚拟机常见可以用make -j2之类的参数限制并发数比如catkin_make_isolated --install --use-ninja -j2这会限制同时编译的任务数虽然慢一点但不会因为内存爆掉导致编译中断。2.4 编译期间的常见报错及处理这里挑几个我实际遇到过的报错供你参考报错1Could not find a package configuration file provided by cartographer这个一般在编译cartographer_ros时出现原因是cartographer本体还没编译出来或者编译后没有正确安装到系统路径。解决办法是先确认cartographer编译完成再检查~/.bashrc里是否加了source ~/catkin_ws/install_isolated/setup.bash然后重新打开终端再编译。报错2error: ceres::options has not been declared这是Ceres Solver版本不匹配导致的。Cartographer依赖Ceres Solver做后端图优化官方推荐用他们定制的ceres-solver版本直接在cartographer的cmake/modules目录下会通过FetchContent自动拉取。如果你系统里装了另一个版本的Ceres或者CERES_ROOT_DIR环境变量指向了错误的位置就会出这个报错。解决方法是卸载系统自带的libceres-dev重新编译时让Cartographer自己拉取内置版本。报错3Unable to find the required Boost libraries这个比较隐蔽如果你是Ubuntu 18.04或20.04系统但装了ROS Kinetic很容易遇到。因为Boost版本升级后Boost::thread和Boost::system的库文件名发生了变化。手动安装libboost-all-dev并指定-DBOOST_LIBRARYDIR可以绕过这个问题但根因还是环境组合不一致。3. 下载官网数据集3.1 数据集有哪些该选哪个Cartographer官方提供了几个测试用的ROS bag数据集在cartographer_ros的docs目录下能找到下载链接。常用的是这几个数据集内容适用场景demo_2d.bag2D激光雷达数据室内走廊环境入门首选验证2D SLAMdemo_2d_carto.bag2D激光雷达数据更复杂的室内环境2D建图细节验证demo_3d.bag3D激光雷达数据室内环境验证3D SLAMdemo_2d_loc.bag2D定位数据带预建地图纯定位功能验证如果你第一次跑只选demo_2d.bag就够了。它大概400MB左右包含激光雷达的scan话题、里程计odom话题、IMUimu话题数据完整性很好适合用来验证整个流程。有些朋友看到热词里的“poi数据集”“cwru数据集”会想顺便下下来跑其实那些是其他领域的开源数据集和Cartographer的官方demo不搭建议先别混在一起专注把手头演示跑通。3.2 数据集下载和验证官方数据集托管在谷歌云盘上下载时可能需要一些网络技巧。如果你直接下载很慢方法有两个一是用别人已经转存的国内网盘链接二是用支持断点续传的下载工具。下载完成后务必检查文件完整性否则后面播放bag时会莫名中止。md5sum demo_2d.bag和官网提供的MD5值比对不一致就重新下载。我之前就因为文件下载不完整播放到一半数据流突然中断整个程序崩溃排查了半天才发现是bag文件坏了。3.3 bag文件的基本结构如果你没接触过ROS bag简单理解就是ROS话题数据的录制文件相当于把传感器数据和时间戳打包存成了一个文件。播放bag等同于让数据按录制的顺序重新发布出来。可以用以下命令查看bag里包含的话题、消息类型和消息数量rosbag info demo_2d.bag正常情况下你应该能看到至少三个话题/scanLaserScan类型、/odomOdometry类型、/imuImu类型。这三个话题对应Cartographer建图的三要素激光数据提供观测IMU提供重力方向参考里程计提供初始位姿估计。如果bag里缺了IMU话题Cartographer的2D建图也能跑但轨迹质量会明显下降因为缺少了俯仰和翻滚方向的约束。4. 启动Cartographer建图4.1 launch文件背后的逻辑下载完bag文件后接下来就是启动建图了。Cartographer_ros提供了几个现成的launch文件使用时不用修改直接source ~/catkin_ws/install_isolated/setup.bash roslaunch cartographer_ros demo_2d.launch bag_filename:/你的路径/demo_2d.bag这里背后的逻辑是launch文件会自动启动cartographer_node核心建图节点、bag_play节点播放bag数据以及rviz可视化界面。整个过程是全自动的不需要手动录制或发布话题。这里想重点说明一下cartographer_node和cartographer_occupancy_grid_node的区别。前者负责前端匹配和后端优化输出的是子图submap和轨迹的实时位姿后者专门负责把累计的子图转换为2D占用栅格地图生成最终的地图话题map。在官方launch里两个节点都会被拉起前者是核心后者是生成地图的服务性节点。4.2 关键参数文件解析Cartographer的配置写在.lua参数文件里启动时会加载。以2D为例配置文件路径在~/catkin_ws/src/cartographer_ros/cartographer_ros/configuration_files/backpack_2d.lua里面比较关键的几个参数参数默认值作用map_framemap地图坐标系所有位姿的最终参考系tracking_framebase_link机器人本体坐标系published_frameodom里程计坐标系use_imutrue是否使用IMU数据num_range_data1每次前端匹配使用的激光帧数min_range0.3激光雷达的最小有效距离小于该值的数据会被丢弃max_range30.0激光雷达的最大有效距离超出该值的数据会被截断submaps_num_range_data90每个子图包含的激光帧数量loop_closure_min_score0.6回环检测的最低匹配得分低于该值不认为是回环这里重点讲两个参数的含义。num_range_data为1是很合理的设置它表示前端扫描匹配时一次只处理一帧激光数据。激光雷达通常频率是10Hz或20Hz每帧数据都有一定噪声一次匹配一帧能保证实时性同时对计算压力也小。如果这个值设置得太大比如8或10前端会把多帧聚合后再匹配精度会有所提升但实时性下降很多容易造成轨迹卡顿。loop_closure_min_score是回环检测的阈值。回环检测本质是判断机器人是否回到了曾经访问过的位置通过当前帧与历史子图进行相关性匹配输出一个0到1之间的得分。阈值设太低会误报回环导致地图被强行拉回错误位置造成“跳变”设太高又会漏检真实回环导致轨迹漂移无法修正。实测0.6是一个比较均衡的值室内场景下通常都能用。4.3 rviz显示配置与轨迹观察launch启动后rviz会自动加载对应的显示配置。你会在界面里看到一个不断增长的2D地图同时有两条轨迹线在推进。如果rviz里什么都看不到大概率是固定坐标系Fixed Frame设置不对。正常情况应该设置为map这样能保证所有传感器数据都在世界坐标系下显示。你可以在rviz左上角的Global Options - Fixed Frame里手动改一下。如果地图迟迟不出现检查一下demo_2d.launch中rviz配置文件路径是否正确。路径在launch文件上是写死的如果你的工作空间结构和官方不一致可能加载不到配置文件导致rviz打开但一片空白。这时可以手动加载配置File - Open Config找到~/catkin_ws/src/cartographer_ros/cartographer_ros/rviz/cartographer_demo.rviz。4.4 建图过程中的实时感受运行起来后你会看到地图从起点开始一点点向外扩展激光扫描的轮廓会随着机器人的移动逐渐拼合在一起。这个过程非常直观你能清晰地看到前端匹配的效果如果传感器标定没问题激光点会严丝合缝地贴合在已建的地图上如果传感器外参有偏差地图会变得很“糊”转角处会出现重影。跑完整个bag大约需要3到4分钟结束后终端会显示Finished playing bag但这个时候节点还没退出你可以继续在rviz里旋转查看地图细节。按CtrlC停止launch后地图数据还在内存里如果想保存成图像可以使用rosrun map_server map_saver -f ~/my_map这会在~目录下生成my_map.pgm和my_map.yaml两个文件。前者是栅格图像后者是地图的元信息。地图保存后你就可以用这个地图做后续的定位测试或者导航实验。5. 常见问题与排查技巧实录5.1 高频问题速查表问题现象可能原因排查方法建图过程中轨迹明显跳变IMU数据异常或未正确使用检查bag里的/imu话题频率是否稳定检查配置中的use_imu是否为truerviz中激光点明显错位TF树不完整或外参标定错误运行rosrun tf view_frames查看TF树结构确认是否有完整的map-odom-base_link-laser链条地图建出来有明显的重影激光雷达数据质量差或里程计漂移过大降低max_range适度提高min_range过滤掉远距离噪声和近距离反射回环检测后地图发生严重形变回环误检或参数不合适提高loop_closure_min_score检查constraint_builder的采样参数播放bag时系统时间不正常bag文件录制时的时钟比当前系统时钟落后在启动launch前手动设置use_sim_time为true确保节点使用bag中的仿真时间内存占用过高导致卡死回环检测计算量过大调整optimize_every_n_nodes为较大值减少后端优化频率5.2 一个容易被忽略但很关键的时间问题在使用bag回放时必须确保use_sim_time参数为true否则会出现一个很诡异的现象地图一直在建但建的非常慢而且位置会逐渐偏离真实轨迹。原因是bag文件里记录的是当初录制时的时间戳而你的系统当前时间可能比bag录制晚了半年甚至一年。Cartographer的代码里有用到时间差的逻辑如果时间差过大某些缓存机制会认为数据过期而丢弃或者传感器数据被错误地按时间顺序排序导致前端匹配完全错乱。启动launch时demo_2d.launch默认设置了use_sim_time为true但你需要注意确保没有别的配置把它覆盖了。如果发现bag里的时间戳和系统时间差异很大可以手动查看echo $ROS_HOME这个环境变量会告诉你bag的录制时间信息如果它和你系统时钟差太多问题基本就出在use_sim_time上。5.3 实际调试中的一个典型案例我调试时遇到过这样一个情况2D建图过程里机器人已经明显走了一圈回到了起点地图却始终没有发生回环修正走廊位置有大约20厘米的偏移。排查步骤是这样的先看/scan话题的雷达数据发现室内环境下雷达能扫到的最远距离也就8米左右而配置里的max_range是30米。距离雷达30米外的数据全是噪声点这些点参与匹配后把匹配得分拉低导致回环检测得分一直低于阈值。解决办法是把max_range从30米改成10米把远距离无效测量滤除然后重新跑了一遍bag回环很快就被检测到了地图偏移也修正过来了。这说明一个问题官方配置是给官方数据设计的场景做的你换了自己的环境后一定要根据实际环境重新调参而不是拿着默认参数直接跑。传感器的量程、环境的特征分布都会直接影响匹配效果。5.4 编译后出现的问题排查有些朋友编译、启动都没问题但运行过程中cartographer_node会突然报一个Check failed: ...之类的错误然后退出。这类问题大多是传感器数据异常导致的比如激光雷达丢帧导致前端匹配时特征不足或者IMU数据中存在NaN值。排查思路是先把传感器数据可视化确认数据流的稳定性rostopic hz /scan rostopic hz /imu rostopic echo /imu/data | head如果hz显示的话题频率严重低于bag设定频率说明消息队列有延迟如果rostopic echo显示nan说明数据本身有问题需要在配置里加一点数据滤波或跳过机制。5.5 数据包下载相关问题虽然官方数据集在谷歌云盘但国内网络环境下载体验很差。我的经验是别死磕一个源多找几个第三方转存渠道或者用支持断点续传的工具。下载完成后优先级最高的检测步骤是算md5因为bag文件是二进制的任何字节的缺失都可能导致播放中断到时候你花在排查上的时间远比下载的时间多。6. 跑通之后的进阶思考6.1 从“跑通”到“读懂”的跨越把官方demo跑通只是迈进了Cartographer的门。这个过程真正的价值在于让你理解一套SLAM系统的数据流传感器原始数据进入前端scan matching模块得到当前帧的位姿估计插入到后端的子图中后端通过回环检测和全局优化不断修正累计误差最终输出一个一致的地图。如果你接下来想深入源码我建议按照数据流的方向去读代码不要先从主逻辑入手那样很容易迷路。可以先从cartographer/mapping/internal/2d/scan_matching/下的相关匹配器源码开始理解ICP和相关性匹配的区别再看pose_graph里的约束构建和优化最后看cartographer_ros里的数据转换层理解ROS坐标和内部坐标的变换逻辑。6.2 切换到自己数据集时需要注意什么跑完官方数据后很多人迫不及待地想用自己的激光雷达数据建图。这里有几个实际经验值得参考第一雷达TF必须正确。Cartographer要求雷达数据的坐标系和base_link之间有明确的TF变换否则会输出“Extrapolation failed”之类的错误。建议先用tf_monitor和rviz把TF树display出来确认坐标关系没问题再开始建图。第二IMU的安装方向和噪声特性会影响建图质量。如果你的机器人是差速底盘IMU安装不正建图时地面倾斜会导致地图整体漂移。建议建图前先静止采集一段IMU数据检查偏置是否在合理范围内。第三雷达数据如果是Info格式的需要转换成sensor_msgs/LaserScan。如果你的雷达驱动直接输出PointCloud2Cartographer也能处理但需要配置对应的适配器。7. 我对Cartographer安装调试的一些体会最后分享一点个人经验。Cartographer的安装过程其实并不复杂核心就三步环境准备、源码编译、数据回放。难点不在安装本身而是在各种环境细节的匹配上比如依赖版本、网络状况、系统时钟、参数阈值。把这些问题逐个解决了你对这个系统的理解会明显上一个台阶。我建议读者第一次跑通之后先别着急换数据集而是把几种官方launch2D、2D带定位、3D都跑一遍观察它们配置的差异理解为什么3D需要更复杂的点云处理为什么定位模式需要预先建图和重定位参数。把这些搞透了再去碰自主采集的数据。有句话我对所有做SLAM的朋友都会讲建图不只是“让雷达转一圈出一张图”而是要理解每一帧数据是怎么被匹配、被优化的。这个理解到位了后面做什么都顺。