
洞穴建图是特种机器人里面比较典型又容易被低估的任务。表面上看它和室内建图一样都是“传感器进来地图出去”实际跑过洞穴环境就会发现光照、粉尘、无线信号、地形起伏和机器人自身姿态变化每一项都会直接影响建图结果。ROS1 和 ROS2 在这个场景里都有可落地的方案但它们的定位不一样ROS1 生态里现成算法多ROS2 在分布式通信和多机器人协同上更顺手。这篇内容我会从传感器选型、ROS1/ROS2 差异、单机建图流程、实际踩坑排查这几个方向拆开讲适合正在做洞穴勘探、地下空间测量、巷道巡检以及准备把 ROS 建图往复杂环境迁移的开发者参考。1. 洞穴建图为什么先想清楚“传感器”和“工作环境”很多人在做洞穴建图时第一反应是去选 SLAM 算法而不是先看环境条件。这个顺序不太对。洞穴不是一个标准室内环境它同时包含低纹理、大尺度、上下坡、狭窄通道、扬尘和水雾等特征不同传感器组合应对这些问题的能力差距非常大。先把环境和工作方式摸清楚再决定用 2D 还是 3D用激光还是视觉用不用 IMU后续会少走很多弯路。1.1 洞穴环境到底难在哪光照、粉尘、无线、地形洞穴建图最大的难点不是“算法不够先进”而是传感器数据质量不稳定。首先是光照问题。常见洞穴内部亮度很低视觉相机如果不开补光灯帧率再高也拿不到有效纹理开灯之后又可能出现反光、过曝尤其是有水渍和潮湿岩壁的区域。基于视觉的 SLAM 在这种场景里很容易出现特征点丢失或误匹配。其次是粉尘和水雾。激光雷达对粉尘比较敏感细颗粒物会让点云里出现大量飘浮噪点近处物体的边缘会被拉花。超声波传感器在粉尘环境下误判更多。这些问题在算法层只能部分补偿传感器选型时就要尽量留出余量。再次是无线通信。洞穴内部没有稳定的 GPSWi-Fi 和 4G/5G 信号也可能穿不透岩层。这意味着建图过程中很可能无法依赖外部定位机器人必须靠自身传感器完成里程估计地面站能看到的回传数据也有限地图可能只能在机器人离线后统一导出。这个特点直接决定了系统必须具备较强的本地计算能力不能把所有数据都丢到远端处理。最后是地形起伏。洞穴不是平整走廊可能有陡坡、台阶、低矮通道、湿滑地面。普通轮式机器人容易打滑履带或足式机器人又容易带来更多位姿跳变。这些都会导致里程计漂移加剧。所以我在做洞穴建图方案时第一步永远不是写代码而是先问三个问题机器人是什么底盘能不能在洞穴地面稳定通过洞穴内部有没有供电和通信条件数据是实时回传还是回来后处理建图结果是给人看的三维模型还是要用来做导航的二维栅格地图这三个问题决定了传感器配置和算法选型方向。1.2 基于 ROS1/ROS2 的常用传感器组合与选型根据我的实际经验洞穴建图方案大致可以分成三类。第一类是低成本入门方案2D 激光雷达 轮式里程计。适合比较规整的巷道、人工洞穴、废弃矿道一类环境。地图结果是 2D 栅格地图能直接用于导航避障。传感器价格不高ROS 里的 gmapping、Cartographer 2D 模式都能跑。缺点是处理不了明显的高低起伏机器人上下坡时地图会失真。第二类是主流实用方案3D LiDAR IMU 可选视觉。适合自然洞穴、地下空间、复杂地形。地图结果是 3D 点云地图或 2.5D 栅格地图。算法可以用 LIO-SAM、FAST-LIO、Cartographer 3D 这类基于因子图的方案。IMU 在这里不是可有可无它能在点云畸变和位姿跳变明显时提供高频运动约束。第三类是无人平台或深度勘探方案多传感器融合 后端优化 回环检测。适合大范围、长时间、多机器人协同场景。除了激光和 IMU可能还会加入气压高度计、磁力计、超宽带定位标签等。这个方案工程量最大但对环境的适应性也最强。从实用角度看我更推荐第一类和第二类组合先跑通。视觉可以等激光方案稳定后再加入不要一上来就做多传感器融合因为每个传感器都会引入新的标定参数和时间同步问题。1.3 在开展建图前先确认的硬件与驱动问题传感器选完下一步不是直接启动 SLAM而是先把驱动跑通确认数据能稳定进入 ROS 话题。以 ROS1 为例激光雷达驱动会发布/scan或/points话题IMU 驱动发布/imu/data。先要确认这几个话题的频率和数据格式符合算法要求。常见问题有两个一是设备权限没配置好Linux 下普通用户读不到串口或 USB 设备二是驱动版本和 ROS 版本不匹配比如 Ubuntu 20.04 上装 ROS Noetic有些老雷达驱动需要重新编译。如果是 ROS2还需要额外注意 DDS 发现机制。不同设备或进程之间能否互相发现取决于同一个网络域、相同的ROS_DOMAIN_ID以及防火墙是否有阻隔。很多 ROS2 建图任务不是算法本身有问题而是机器人本体和地面站之间话题发现不了导致数据根本过不来。安装 ROS 环境时我一般会建议新手先确认系统版本。Ubuntu 20.04 对应 ROS1 Noetic 或 ROS2 Foxy 等版本Ubuntu 22.04 就用 ROS2 Humble 比较多。镜像和依赖安装时如果遇到网络慢可以使用国内镜像源也可以通过社区整理的一键安装脚本来减少配置成本。网上常见的“鱼香ROS一键安装”就是这类工具它解决的问题是用脚本把 ROS 安装、源配置、依赖安装串起来。用是可以用的但装完之后最好自己再确认一遍环境变量和依赖版本不要把所有事情都交给脚本。注意一键安装脚本只负责装环境不会帮你调传感器驱动也不会解决算法参数问题。装完之后最该做的是打开终端跑一遍rosversion -d或printenv ROS_DISTRO确认版本真的对得上。2. ROS1 和 ROS2 在建图任务中各自适合干什么洞穴建图不是只写一个算法节点它通常包含驱动节点、预处理节点、SLAM 节点、地图保存节点、可视化节点、可能还有地面站通信节点。ROS1 和 ROS2 在这些环节里的优劣势不一样选择时要结合团队现有代码和任务规模。2.1 ROS1 Noetic 仍是很多现成算法的主战场从现成算法数量来看ROS1 生态里能找到的建图资料最多。gmapping、hector_slam、cartographer、loam、lio-sam 等算法都有 ROS1 版本网上也有很多基于 Ubuntu 16.04 Kinetic、Ubuntu 18.04 Melodic、Ubuntu 20.04 Noetic 的教程。为什么大家都在 ROS1 里跑算法一个重要原因是传感器厂家的老驱动大多先出 ROS1 版本。比如一些激光雷达、工业相机、组合导航设备官方 SDK 在十几年前就是基于 ROS1 写的移植到 ROS2 需要改消息类型、改节点生命周期、改编译方式工作量不小。如果只是想快速验证一颗雷达能不能用于洞穴建图ROS1 确实更快。另外调试工具也更顺手。rqt_graph、rviz、rostopic hz、rosbag record这些命令行工具在 ROS1 里非常稳定很多老工程师都很熟悉。洞穴建图过程中我经常需要一边采集 bag 包一边实时看 tf 树和点云话题频率ROS1 在这套工作流里体验很好。2.2 ROS2 在分布式通信和多机器人扩展上的优势ROS2 的优势不在“跑单个 SLAM 算法”而在系统架构。它的节点发现、进程隔离、生命周期管理、参数服务都比 ROS1 更适合做长期运行的机器人系统。洞穴建图如果要做多机器人协同ROS2 的价值会明显体现出来。多个机器人可以各自建图再通过共享地图或位姿估计来融合结果。ROS2 默认使用 DDS 通信节点之间不依赖中心 master一个节点挂掉不会导致整个系统瘫痪。这个特性在洞穴内部通信不稳定、地面站需要远程监控的场景下很有意义。ROS2 还更适合容器化部署。比如在一台车载工控机上用 Docker 跑 ROS2 环境把算法、驱动、通信配置打包成镜像。现场部署时拉取镜像就能启动不容易出现依赖污染问题。网上也有讨论在 Windows 上用 Docker 跑 ROS 的做法这种方式适合做桌面端调试但真正上机器人时还是建议用 Linux 系统设备权限和实时性会更好。2.3 从 ROS1 迁移到 ROS2 时需要处理的接口差异如果你已经有 ROS1 的洞穴建图代码想迁移到 ROS2主要会碰到这几类差异。消息类型变化。ROS2 的消息定义带包名前缀比如sensor_msgs/msg/LaserScan、nav_msgs/msg/Odometry写回调函数的方式和 ROS1 也不一样。对于激光雷达和 IMU 驱动需要确认话题消息类型是否被算法包直接支持。坐标变换库变化。ROS1 里常用tfROS2 里是tf2。虽然tf2在 ROS1 里也有但 API 和调用方式不完全相同。启动文件里广播静态坐标变换的方式也不一样ROS2 更推荐用tf2_ros的静态变换发布节点。编译工具和依赖管理。ROS1 主要用catkinROS2 用colcon。包管理也变成了rosdepament。一些老驱动在 ROS2 下可能无法直接catkin_make需要先改成ament_cmake结构。运行时形态。ROS2 节点默认可以设置生命周期状态比如未配置、未激活、已激活。SLAM 节点通常需要激活后才开始处理数据。这个机制有助于工程化控制但对新手来说第一个坑往往是“节点启动了但 rviz 里没有点云”因为节点没有进入 active 状态。如果只是想快速在 ROS2 里跑通一个洞穴建图 Demo前两个差异最需要关注如果是长期开发最好提前规划好消息规范、tf 树结构和参数服务器配置。3. 洞穴建图的一次标准落地流程这一步进入实操。我的建议是先把流程拆成“单条任务”不要一上来就追求完整洞穴地图。先跑一小段数据确认输入、tf、输出、日志都正常再扩大范围。3.1 从单线激光雷达和里程计跑出第一张 2D 栅格图如果你用的是 ROS1 和 2D 激光雷达最快能出结果的流程是这样的。第一步启动雷达驱动。比如一些低成本的 2D 激光雷达节点会发布/scan话题用rostopic hz /scan查看发布频率。常见的频率在 5Hz 到 20Hz 之间。如果话题频率忽高忽低先检查 USB 供电和线缆不要急着调算法参数。第二步确认里程计来源。洞穴环境里如果没有轮式里程计可以用激光里程计替代比如在雷达扫描匹配过程中估计帧间位姿。关键是一开始就要保证/tf里有从base_link到laser的静态变换以及从odom到base_link的动态变换。第三步选择合适的 SLAM 包。gmapping 对计算资源要求低适合平整巷道Cartographer 2D 模式带有回环检测和子图优化更适合环境规模稍大的场景但参数调整更复杂。第四步保存地图。运行结束后可以使用map_server把栅格地图保存成.pgm和.yaml文件。保存成功的关键是 SLAM 节点结束时输出的地图话题还在不要急着关 rviz。我一般会先用 rosbag 录一小段数据离线跑而不是直接在真机上实时调参。原因很简单洞穴环境可能不方便重来先录数据离线复现能省不少现场时间。# 录制传感器数据示例实际话题名以你的驱动为准 rosbag record /scan /tf /odom -O cave_test.bag拿到 bag 包后回放数据运行算法确认结果稳定再上真机。3.2 用 3D LiDAR 与 IMU 通过因子图算法生成 3D 点云地图如果是自然洞穴或复杂地形2D 栅格地图往往不够用。这时候需要 3D 点云地图。推荐流程是3D 激光雷达 IMU 作为输入使用基于因子图的激光惯性 SLAM 算法。这类算法把激光点云匹配、IMU 预积分、回环检测作为因子放进一个图优化后端里位姿估计比纯激光匹配更稳。落地时先确认几个前置条件IMU 安装位置与雷达坐标系的标定参数是否准确激光点云是否包含时间戳和每个点的相对时间偏移运动过程中点云是否发生畸变算法内部是否做了去畸变处理。算法启动后第一步是观察位姿轨迹是否平滑。洞穴环境里如果轨迹出现突然跳变优先怀疑 IMU 数据异常或标定不准。第二步是观察回环闭合时地图是否对齐。如果回环后地图仍然断开说明前端的帧间匹配误差已经累积到后端无法修正的程度需要提高关键帧频率或增加回环约束。下面是一个站点式洞穴数据采集的通用步骤机器人静止 10 秒以上让 IMU 完成初始化。缓慢移动经过有明显特征的岩壁、洞口、分岔口时适当放慢速度。每隔一段距离停留 2 到 3 秒让算法积累关键帧。遇到可回环的通道时尽量绕回之前经过的区域触发回环检测。采集完成后保持机器人静止数秒再停止记录。这些行为看起来简单但比调算法参数更有效。洞穴环境里很多建图失败都不是参数原因而是采集轨迹太激进机器人一直在快速旋转和颠簸导致点云畸变严重。3.3 回环检测、点云拼接与地图后处理建图完成后直接输出的点云地图通常不能直接用。洞穴内部可能存在噪声点、重叠点、边缘不齐。回环检测只是让轨迹位姿更准并不等于点云本身干净。后处理阶段我常用的步骤是第一步对原始点云做体素滤波降采样减少数据量。洞穴环境范围大原始点云可能上千万个点直接加载会卡。第二步去除明显离群点。可以用统计滤波器把邻近点数量不足的点标为噪声。洞穴里粉尘产生的飘浮点通常会在这个步骤被剔除但要注意不要把真实边缘点也删掉。第三步手动检查地图中的关键位置。重点看洞口附近的点云是否闭合、通道宽度是否合理、岩壁细节是否模糊。如果有多个机器人或多次勘探数据还需要做点云配准拼接。常见做法是利用 ICP 系列算法把两块点云对齐到同一个坐标系。洞穴环境没有明显特征时点云配准容易陷入局部最优所以尽量依靠 SLAM 输出的位姿做初始对齐再运行 ICP 精配准而不是直接对两块大点云做全局配准。3.4 输出格式与精度评估标准洞穴建图的结果到底好不好不能用“看起来像”判断要有可验证的指标。对于 2D 栅格地图最简单的评估方式是看地图上通道宽度和实际测量的误差。用卷尺在几个关键位置量一下再在 rviz 里测量地图栅格宽度。误差在可接受范围内说明建图基本合格。对于 3D 点云地图可以看回环位置的重叠误差。如果机器人回到起点后起点区域的点云边缘偏移量小于设定阈值说明轨迹漂移可控。更严格的做法是用地面控制点或全站仪坐标来评估但这需要额外设备不是每个团队都能做。输出格式方面2D 地图常用.pgm.yaml3D 地图常用.pcd、.ply、.las。如果后续要做导航2D 栅格地图更合适如果要生成洞穴三维模型或做体积计算建议保存.ply或.las并保留点云的坐标精度和颜色信息。4. 实际踩坑与排查顺序洞穴建图项目里大多数问题不是 SLAM 原理没搞懂而是“数据不对、坐标系不对、时间不对、参数不对”。下面按我自己的排查顺序整理一遍从现象到可能原因再到处理建议。4.1 里程计漂移、点云畸变、高度线断裂这三个问题最容易出现在洞穴环境。里程计漂移的典型表现是地图中同一面岩壁被画成两层或者通道宽度从原来的 3 米慢慢变成 4 米。这种情况多半是前端匹配累计误差变大常见原因是传感器数据频率不够、机器人运动过快、激光扫描范围过窄。处理方式不是只调算法权重而是先降低速度增加特征明显的停留点必要时加入 IMU 或轮式里程计约束。点云畸变的典型表现是快速转动雷达时墙壁点云出现明显弯曲。这通常是因为运动补偿不正确或者 IMU 与激光雷达时间不同步。洞穴环境里机器人遇到转弯和颠簸畸变更容易暴露。排查时先回放 bag 包观察雷达话题频率和 IMU 时间戳是否对齐。高度线断裂的典型表现是三维地图里地面不连续同一水平面出现错层。原因往往是机器人上下坡时 IMU 重力方向估计不准或轮式里程计在打滑时输出了错误位移。洞穴里湿滑地面很常见因此要在算法里增加重力约束或者把机器人在上下坡前后的姿态变化标记为关键帧。4.2 驱动、TF 树、时间戳和坐标变换问题很多洞穴建图任务跑不起来第一报错不是算法崩溃而是坐标变换缺失。ROS1 里常见报错是 “Could not find transform from base_link to laser”。ROS2 里类似报错也会出现在节点日志里。遇到这类问题不要急着改算法参数先用tf2_echo或ros2 run tf2_ros tf2_echo查看当前 tf 树是否完整。检查顺序是在 rviz 或命令行里查看机器人模型是否加载静态变换是否发布确认odom到base_link的变换是否由里程计节点或 SLAM 节点发布确认base_link到laser的变换数值是否正确尤其是雷达安装高度和前后偏移确认时间戳是不是同一时基常见问题是雷达驱动和里程计驱动发布的数据时间相差过大。时间戳问题在 ROS2 里更隐蔽。DDS 通信本身有延迟如果节点之间没有启用同步时间不同话题的时间戳会存在偏差。洞穴建图通常不需要高精度时钟同步但如果系统里有多台计算机建议配置 NTP 或 PTP避免时间戳乱跳。4.3 资源占用与运行稳定性很多人在自己的台式机上跑通算法后把同样的配置放到机器人工控机上结果 CPU 占用 100%、内存爆满、节点频繁被杀。洞穴机器人的计算平台通常不如办公电脑这个问题很现实。在真机部署前我一般先做一轮资源评估激光雷达点云频率和数据量每次扫描的点数、是否做了降采样SLAM 算法发布的地图更新频率rviz 可视化是否消耗大量 CPU必要时可以关闭 3D 云显示。如果资源不够优先从三处入手。第一降低雷达帧率比如从 15Hz 降到 10Hz第二对点云做体素滤波下采样后再送入 SLAM第三关闭不必要的可视化窗口只在出现问题时通过 rosbag 离线分析。注意低配机器能跑通 Demo不代表能稳定跑完整段洞穴。如果现场设备有限建议先离线跑一段 20 分钟的数据观察内存和 CPU 曲线再决定是否优化。4.4 批量或长期建图任务如何避免“跑着跑着就崩”洞穴勘探往往不是单次几十秒的任务而是几十分钟甚至几小时的连续采集。长期运行下各种小问题都会被放大。我见过最多的是日志文件撑满磁盘。SLAM 节点、驱动节点、rosbag 记录都在写文件如果日志和 bag 放在同一分区空间会被瞬间打满。解决办法是提前规划数据目录把日志、bag、地图分别存储并设置磁盘空间监控。还有节点异常退出问题。ROS1 里某个驱动节点崩溃master 本身还能继续运行但 SLAM 会因为缺少输入数据而失去判断。ROS2 里可以用生命周期节点和心跳机制做健康检查但需要额外开发。实际工程中我建议用systemd或容器编排工具把每个节点管起来崩溃后自动重启同时保留核心日志。批量处理多段洞穴数据时还要格外注意输出文件命名。如果每次都生成map.pgm或map.pcd很容易覆盖前一段数据。建议按日期、洞穴编号、传感器来源命名比如cave03_20250115_lidar.pcd。这个习惯能省很多后期整理时间。5. 从单机建图到多机协同和部署平台如果洞穴规模较大单台机器人走完全程可能耗时太长而且无法覆盖多入口、多通道结构。这时候需要考虑多机器人协同建图以及更稳定的部署方式。5.1 洞穴分层勘探与多机器人分工一个实用的分工方式是一台机器人沿主通道走另一台进入支洞或低矮通道还有一台在洞口或地面站负责通信中继和全局地图汇总。多机器人建图时地图融合是关键。常见有两条路线。第一条是“各建各的最后拼接”。每台机器人先基于自身里程计坐标系建图返回地面站后通过已知位置或点云配准把各段地图拼成一张完整地图。这种方式实现简单但如果没有精确的相对位姿不同地图之间的误差会很大。第二条是“共享全局坐标实时融合”。机器人之间通过 ROS2 分布式通信交换位姿和地图数据在后端做统一优化。这种方式对通信质量要求高但地图一致性更好。洞穴内部如果信号不稳定可能需要进行多跳中继或让机器人按队列轮流上传数据避免同时抢占带宽。从 ROS1 迁移到 ROS2 时多机器人协同会轻松一些因为 ROS1 依赖 master 集中管理跨机器人的节点通信需要手动配置网络和ROS_MASTER_URI而 ROS2 通过 DDS 域发现机制就能实现同域通信。只要所有设备在同一个局域网设置相同的ROS_DOMAIN_ID节点通常可以自动发现。# 设置 ROS2 域 ID两台设备保持一致 export ROS_DOMAIN_ID55.2 ROS2 分布式通信与网络条件洞穴环境对无线网络不友好分布式建图最大的风险不是协议而是链路不稳定。ROS2 默认使用 DDS很多 DDS 实现基于 UDP 发现和组播。洞穴内部如果存在路由器隔离或防火墙规则组播可能被阻断节点之间就互相发现不了。遇到这种问题可以先做一个小实验在地面站和机器人之间用ros2 topic list查看是否能互相看到对方话题。如果看不到优先检查网段、防火墙和ROS_DOMAIN_ID不要直接怀疑算法。有些场景下会使用固定 IP 和单播配置把机器人、地面站的 IP 信息写进发现配置避免组播不稳定。这样可以降低对路由器和交换机配置的依赖但每台设备的 IP 不能随意更改。另外DDS 默认通信端口比较多某些网络环境下需要开放端口范围。具体端口取决于 DDS 实现现场部署时最好先做一轮连通性测试再执行实际建图任务。5.3 后续工程化保存地图、可视化、避障规划建图不是终点。洞穴机器人后续可能还要在已知地图里做自主导航、避障和定点巡检这就需要把地图结果接到下游模块。2D 栅格地图可以直接给nav2或 ROS1 的 move_base 使用。要确认地图坐标系、原点、分辨率正确且障碍物膨胀参数符合机器人实际尺寸。洞穴通道较窄时膨胀半径不能设太大否则机器人会找不到可通行路径但也不能设太小否则容易碰到岩壁。这个参数要通过实测取一个折中值。3D 点云地图更适合做三维可视化、洞穴体积估算和地质分析。要把点云发布到 ROS 可视化或导入专业软件通常需要先转成.ply或.las。如果点云量过大可以先做抽稀和地面分割再把剩余点云导出。长期部署时建议把整个洞穴建图流程容器化。机器人端只保留驱动和必要的中间件算法节点通过镜像发布。这样更换算法时不需要重装系统只需要替换容器镜像。地面站则负责参数配置、数据回放、地图后处理和任务调度。我自己在工程化阶段最看重的三个指标一是从启动到产出第一张稳定地图的时间二是连续运行 1 小时以上的内存和 CPU 表现三是地图可复用性——也就是同一张地图能不能在多次巡检中保持不错位。如果这三个指标都稳定洞穴建图系统才算真正进入可用状态。如果是个人学习ROS 小乌龟、仿真环境、网上公开的洞穴或隧道点云数据都可以用来练习。先用仿真把坐标变换、SLAM 原理、bag 录制回放跑熟再考虑用低成本雷达在室内走廊或地下车库里做实测。一步一步来比一开始就追求复杂传感器融合更稳妥。