尧图精选

ROS1/ROS2下洞穴环境Cartographer建图实战与避坑指南

🕒 发布时间:2026/9/3 16:23:43 📁 来源:尧图网络
长隧道、狭窄通道、岩壁转弯再加上完全没有卫星信号——在这种环境里做机器人建图和普通室内建图的体验完全不同。很多在楼道里效果不错的方案放进洞穴场景就容易出现漂移、退化甚至地图错乱。这篇文章围绕“洞穴建图”这一主题以 ROS1 和 ROS2 两套环境为主线整理一份从环境搭建、传感器选型、Cartographer 建图配置到问题排查的完整笔记。无论你是刚接触 SLAM 的学生还是已经在做洞穴巡检、地下勘探项目的工程师都可以直接参考本文内容落地验证。文章会覆盖三块内容一是洞穴建图的难点与 SLAM 基础概念二是 ROS1 / ROS2 环境准备和建图算法选型三是基于 Cartographer 的实战配置、迁移差异、常见问题以及工程化建议。全文代码以 Ubuntu 20.04 ROS Noetic 和 Ubuntu 22.04 ROS 2 Humble 为示例实际使用时请根据你的发行版调整。1. 洞穴建图为什么比普通室内难很多1.1 洞穴环境的几个极端特征洞穴建图属于典型的“非结构化环境”任务。和普通室内走廊相比它的难点主要体现在四个方面。第一个难点是定位信息源缺失。洞穴内部没有可靠的 GNSS 信号机器人不能依赖 GPS 获取绝对位置所有位姿估计都只能靠机上传感器。对很多做 AGV、配送机器人开发的团队来说室内定位已经习惯了 UWB、反光板甚至二维码辅助而这些手段在洞穴中很难大规模部署。第二个难点是激光匹配退化。洞穴走廊往往非常狭窄同时沿走廊方向又很长。2D 激光雷达在一条长直隧道里扫描时沿隧道方向的特征非常弱匹配算法可能认为“左右约束很强前后约束很弱”于是机器人不知道自己到底往前走了多少。这就是 SLAM 里常说的退化问题也是洞穴建图最常见的漂移来源。第三个难点是环境纹理差。洞穴岩壁在视觉上通常没有太多明显的结构纹理光线也很暗普通相机很容易过曝或欠曝。如果只依赖视觉里程计很容易因为特征点不足导致跟踪丢失。第四个难点是回环少。洞穴内部往往不是规则的环状路线机器人来回走同样区域的机会不多回环检测很难触发。回环检测是消除累计误差的关键手段缺少回环就意味着后期地图很难收敛成全局一致。1.2 SLAM 建图的基本工作流程SLAMSimultaneous Localization and Mapping同步定位与建图要同时回答两个问题机器人当前在哪里周围环境长什么样常规的 2D SLAM 流程可以拆成四步。前端里程计负责根据相邻帧的激光数据、IMU 或轮式里程计估计机器人短时间内的位移和转角。由于前端只做局部估计误差会不断累积。后端优化负责维护历史位姿和观测约束并在合适时机对整体轨迹做修正减小累计误差。回环检测则是当机器人再次经过同一区域时通过特征匹配识别出“我回到了之前来过的地方”从而给后端提供一个强约束。建图则是在位姿已经修好的前提下把激光点按位姿投影到地图坐标系生成栅格地图。洞穴场景中前端的鲁棒性、回环检测的触发频率、后端的退化处理能力都会直接影响最终地图质量。1.3 为什么洞穴建图需要多传感器融合在洞穴环境里任何单一传感器都有明显的短板。轮式里程计在坑洼地面和大坡度路段容易打滑2D 激光雷达在长直隧道中沿行进方向约束不足视觉传感器在暗光环境下容易失效IMU 短时间精度高但长时间会漂移。多传感器融合的核心思路是“让不同传感器的误差特性互补”。例如激光雷达提供空间结构信息IMU 提供短时间内的姿态变化和重力方向轮式里程计提供速度参考。即便激光匹配在隧道方向退化IMU 和里程计仍然可以在短时间内维持位姿估计等雷达匹配恢复后再整体修正。1.4 ROS1 与 ROS2 如何选洞穴建图项目往往不是一台机器人单机运行而是需要“地面站 多台机器人”配合。这种情况下ROS1 和 ROS2 的选择会影响后续的整体架构。对比项ROS1以 Noetic 为例ROS2以 Humble 为例通信架构基于 Master 中心化节点管理基于 DDS 分布式发现无中心节点多机器人支持需要手动配置多 Master 或 namespace天然支持多个 domain隔离方便网络通信质量默认 TCPROS弱网场景需自行优化支持 QoS 配置可调整可靠性和延迟策略实时性较弱支持实时节点与生命周期管理社区资源经典包极多文档成熟新包越来越多主流方向适用洞穴场景适合单机建图、学习验证原型适合多机协同、长期演进型项目我的建议是如果只是学习 SLAM 原理、快速验证建图算法ROS1 Noetic 资料多、坑少是最稳妥的起点。如果目标是做多机器人洞穴协同探索或者希望长期维护一个工程直接上 ROS2 更合理毕竟 ROS1 已经停止维护。2. 环境准备与版本说明2.1 推荐环境组合洞穴建图涉及上位机、下位机、仿真环境三部分。最常见的开发配置是上位机Ubuntu 20.04 ROS Noetic或 Ubuntu 22.04 ROS 2 Humble下位机树莓派或 Jetson 系列运行传感器驱动和底层控制仿真环境Gazebo RViz用于在没有真机时验证建图管线激光雷达2D 雷达如 RPLIDAR、Neato XV-11 等或 3D 激光雷达里程计信息轮式里程计、IMU、或两者同时提供ROS 版本和 Ubuntu 版本有严格对应关系。Noetic 只能稳定运行在 Ubuntu 20.04Humble 建议使用 Ubuntu 22.04。如果你在 Windows 上折腾 ROS可以尝试 Docker 方案但传感器数据透传和 USB 设备映射会比较麻烦不建议作为洞穴真机开发的主环境。2.2 安装 ROS 的几种方式ROS 官方支持二进制包安装也就是通过 apt 直接安装。国内用户如果遇到网络慢或源更新失败可以考虑使用国内镜像源或者使用网上流传的“鱼香ROS一键安装”脚本快速部署。一键安装脚本的好处是快能把 ROS、colcon、Gazebo、常用依赖一次性装好适合新手快速搭建环境。但要注意一键脚本通常会修改~/.bashrc并安装很多额外工具如果你同时维护多个 ROS 环境建议还是手动安装保持环境纯净。这里给出一段通用的安装思路具体命令以你的 ROS 发行版为准# Ubuntu 20.04 ROS Noetic 示例 sudo sh -c echo deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main /etc/apt/sources.list.d/ros-latest.list sudo apt install curl curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | sudo apt-key add - sudo apt update sudo apt install ros-noetic-desktop-fullROS2 Humble 则使用 apt 安装ros-humble-desktopsudo apt update sudo apt install ros-humble-desktop python3-colcon-common-extensions安装完成后建议先验证基础功能是否正常。ROS1 可以启动 roscoreROS2 可以运行ros2 topic list# ROS1 roscore # ROS2 ros2 topic list如果能看到类似/rosout的话题输出说明环境基本可用。2.3 安装建图相关组件洞穴建图常用的软件包包括cartographer和cartographer_ros谷歌开源的跨 ROS1/ROS2 建图库navigation2ROS2或move_baseROS1用于后续导航规划teleop_twist_keyboard键盘控制机器人移动rviz可视化地图和 TF 关系tf2坐标变换管理slam_toolboxROS2 中另一种常用 2D 建图工具在安装 Cartographer 时如果通过 apt 安装版本不够可以考虑源码编译。源码编译的好处是可以自行切换分支、调试参数缺点是需要编译较长时间。对洞穴建图这种定制化程度较高的场景源码编译往往更合适。# ROS1 下的 cartographer 源码安装思路参考官方文档 sudo apt install python3-wstool python3-rosdep ninja-build stow mkdir -p ~/carto_ws/src cd ~/carto_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 install --from-paths src --ignore-src --rosdistronoetic catkin_make_isolated --install --use-ninjaROS2 环境下的源码编译思路类似只是把catkin_make_isolated换成colcon build。具体包名和分支建议以 Cartographer 官方仓库的 README 为准。3. 洞穴建图技术选型传感器与算法3.1 传感器组合怎么搭传感器选型取决于洞穴的类型和你想要的建图结果。如果只做 2D 平面建图隧道相对规则可以使用 2D 激光雷达 里程计 IMU。这种方案成本低、配置简单适合探洞机器人、巷道巡检小车。需要注意2D 雷达假设环境在同一个平面内遇到起伏较大的地面时建图质量会明显下降。Neato XV-11、RPLIDAR A 系列就是这一档常见的雷达。如果洞穴内部结构复杂有斜坡、岔洞、高低起伏就应该考虑 3D 激光雷达 IMU 轮式里程计的组合。3D 雷达能提供更丰富的空间结构配合 LOAM、LIO-SAM 等算法能生成 3D 点云地图。如果洞穴岩壁纹理丰富且有人工光源也可以加入相机做视觉惯性融合但这项工作量和调试成本都会明显上升。3.2 常用建图算法对比算法适用场景优点缺点Gmapping2D 室内、走廊环境经典部署简单计算量小依赖里程计回环检测弱Hector SLAM2D 无里程计场景不依赖轮式里程计在长走廊中容易漂移Cartographer2D/3D多传感器融合子图回环支持 ROS1/ROS2官方维护配置项多学习成本略高LOAM / LIO-SAM3D 雷达建图点云配准效果好适合复杂地面需要高质量 3D 雷达算力要求高ORB-SLAM3视觉 / 视觉惯性支持单目、双目、RGB-D洞穴暗光环境容易丢失slam_toolboxROS2 2D 建图与 ROS2 集成好支持二维回环主要解决 2D 场景洞穴建图场景下Cartographer 是平衡效果与工程成本的最佳选择。它同时支持 2D 和 3D 建图官方提供了 ROS1 与 ROS2 双版本支持还允许激光、IMU、里程计同时参与位姿估计。3.3 Cartographer 的核心思路Cartographer 与普通 2D SLAM 最大的不同在于引入了子图submap概念。机器人每采集一段时间数据就生成一个局部子图子图内部做帧间匹配。当新的子图和旧子图之间有足够的重叠时后端会尝试做回环闭合把组成地图的所有子图整体优化一次。这个设计对洞穴建图非常友好。一方面洞穴环境回环少但子图内匹配可以保证局部地图质量另一方面当机器人回到之前经过的岔洞时回环检测能帮助修正长时间累计的漂移。在配置 Cartographer 时重点调整的参数包括是否使用里程计、激光束数量、子图插入间隔、回环最小置信度、位姿发布频率等。后续实战部分会给出具体配置。4. 实战用 Cartographer 完成洞穴隧道建图下面进入本文的核心实操。我们以一台搭载 2D 激光雷达、轮式里程计和 IMU 的差速小车为例演示在洞穴隧道环境下的 Cartographer 建图流程。为了便于验证你可以用自己采集的洞穴 rosbag 数据也可以先搭建一个隧道 Gazebo 仿真模型按同样的流程跑通。4.1 创建功能包ROS1 中创建一个功能包用于存放配置和 launch 文件cd ~/catkin_ws/src catkin_create_pkg cave_mapping cartographer_ros roscpp rospy tf2_ros cd ~/catkin_ws catkin_make source devel/setup.bashROS2 中对应的创建方式cd ~/ros2_ws/src ros2 pkg create cave_mapping --build-type ament_cmake cd ~/ros2_ws colcon build source install/setup.bash这个功能包只负责组织洞穴场景的建图配置和启动文件并不修改 Cartographer 源码。4.2 编写 Cartographer 2D 配置文件在功能包下新建config目录创建cave_cartographer_2d.lua。下面是一份针对洞穴隧道场景的 2D 配置示例-- 文件路径cave_mapping/config/cave_cartographer_2d.lua include map_builder.lua include trajectory_builder.lua options { map_builder MAP_BUILDER, trajectory_builder TRAJECTORY_BUILDER, map_frame map, tracking_frame base_link, published_frame odom, odom_frame odom, provide_odom_frame false, publish_frame_projected_to_2d false, use_odometry true, use_nav_sat false, use_landmarks false, num_laser_scans 1, num_multi_echo_laser_scans 0, num_subdivisions_per_laser_scan 1, num_point_clouds 0, lookup_transform_timeout_sec 0.2, submap_publish_period_sec 0.3, pose_publish_period_sec 5e-3, trajectory_publish_period_sec 30e-3, } return options这里有几个关键配置需要特别说明。map_frame固定为map是世界坐标系。tracking_frame填写机器人底盘中心所在坐标系通常是base_link。published_frame和odom_frame都设置为odom表示 Cartographer 会把修正后的位姿发布到 odom再由轮式里程计预测里程计到底盘之间的变换。provide_odom_frame如果设为trueCartographer 会自己发布map - odom变换适合没有外部里程计模型的情况。use_odometry true表示利用底盘发布的里程计消息参与位姿估计这一步在洞穴弱特征环境中尤其重要。num_laser_scans 1表示使用一束 2D 激光数据。如果你的雷达话题是/scan并且没有其他激光源保持这个参数为 1 即可。pose_publish_period_sec控制位姿发布频率设置较小值可以让 RViz 中的运动更平滑同时也会增加 CPU 占用。洞穴调试阶段建议保持默认后期性能优化时再调整。4.3 编写 ROS1 launch 文件在cave_mapping/launch目录下创建cave_mapping_ros1.launchlaunch node namecartographer_node pkgcartographer_ros typecartographer_node args-configuration_directory $(find cave_mapping)/config -configuration_basename cave_cartographer_2d.lua outputscreen /node node namecartographer_occupancy_grid_node pkgcartographer_ros typecartographer_occupancy_grid_node args-resolution 0.05 outputscreen /node node namerviz pkgrviz typerviz outputscreen / /launchcartographer_node负责接收激光、里程计、IMU 数据并计算位姿。cartographer_occupancy_grid_node负责把子图数据转换成 ROS 的map_msg/msg/OccupancyGrid栅格地图数据这样在 RViz 中可以直接看到 2D 地图。-resolution 0.05表示每个像素代表 5 厘米分辨率越高地图越细腻但也会增加存储和计算压力。4.4 编写 ROS2 launch 文件在 ROS2 中launch 文件更推荐使用 Python 格式。在cave_mapping/launch目录下创建cave_mapping_ros2.launch.pyimport os from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): pkg_dir os.path.join( os.getenv(ROS2_WS, os.path.expanduser(~/ros2_ws)), src, cave_mapping ) config_dir os.path.join(pkg_dir, config) return LaunchDescription([ Node( packagecartographer_ros, executablecartographer_node, namecartographer_node, outputscreen, parameters[{ configuration_directory: config_dir, configuration_basename: cave_cartographer_2d.lua, }], ), Node( packagecartographer_ros, executablecartographer_occupancy_grid_node, namecartographer_occupancy_grid_node, parameters[{resolution: 0.05}], outputscreen, ), ])不同 ROS2 发行版和不同版本的cartographer_ros对参数传递方式略有差异。如果你的版本不支持parameters列表这种写法可以查看官方仓库中的 launch 示例把配置路径改为arguments方式传递。4.5 数据准备与运行如果已经有洞穴环境的数据包直接播放即可。ROS1 使用rosbag playROS2 使用ros2 bag play。# ROS1 播放 bag rosbag play cave_tunnel.bag # ROS2 播放 bag ros2 bag play cave_tunnel如果还没有数据可以先用 Gazebo 仿真一条简单隧道。在仿真环境中通过键盘控制机器人前进、转弯、原路返回制造一个简单回环方便观察回环闭合前后的地图变化。启动建图前确认底盘发布的话题是/scan、/odom并且 TF 关系完整。启动命令如下# ROS1 启动建图 roslaunch cave_mapping cave_mapping_ros1.launch # ROS2 启动建图 ros2 launch cave_mapping cave_mapping_ros2.launch.py然后在另一个终端启动键盘控制# ROS1 使用 teleop_twist_keyboard rosrun teleop_twist_keyboard teleop_twist_keyboard.py # ROS2 使用 teleop_twist_keyboard包名可能为 teleop_twist_keyboard ros2 run teleop_twist_keyboard teleop_twist_keyboard4.6 在 RViz 中检查建图结果启动成功后在 RViz 中添加以下显示项Map话题选择/map确认栅格地图不断更新。LaserScan话题选择/scan检查激光数据是否和地图边缘对齐。TF显示勾选map、odom、base_link、laser等坐标系。判断建图质量时重点观察两个方面。一是看地图边缘是否有明显“拖尾”或重影如果墙体线条模糊说明位姿估计存在较大误差。二是看机器人经过同一位置后地图是否发生跳变。如果跳变明显说明回环检测还没有触发或者传感器的外参标定存在问题。5. ROS1 迁移到 ROS2 的关键差异很多洞穴建图项目是从 ROS1 起步的采样了一批数据后再迁到 ROS2 做多机协同或工程化落地。这个过程中有几个差异很容易踩坑。5.1 工作空间与构建方式ROS1 使用catkin_makeROS2 使用colcon build。两者的依赖管理方式不同混合开发时不要用错命令。在同一个机器上同时维护 ROS1 和 ROS2 环境时建议用.bashrc中的函数切换环境避免两个环境的依赖互相污染。5.2 Launch 文件差异ROS1 的 launch 文件是 XML 格式ROS2 支持 XML、YAML 和 Python 三种格式官方推荐使用 Python。如果你从 ROS1 复制一个 launch 文件到 ROS2 中直接ros2 launch大概率会报格式错误。5.3 话题通信与 QoSROS2 的 DDS 通信引入了 QoS 策略。激光雷达、相机这类传感器数据通常是BestEffort策略也就是允许丢包、追求低延迟。Cartographer 节点在订阅这些话题时必须设置相匹配的 QoS否则会出现“订阅者发现不了发布者”或“能发现话题但收不到数据”的问题。洞穴环境中网络抖动明显合理设置 QoS 可以降低数据传输延迟但不要让可靠性降得太低否则关键位姿数据丢失会导致建图异常。5.4 TF 与时间同步ROS1 中TF 树由/tf和/tf_static两个话题维护。ROS2 中同样如此但 API 和坐标系广播方式变了。洞穴建图要求激光时间戳、里程计时间戳和 IMU 时间戳统一否则 Cartographer 在查找坐标变换时会抛出Lookup would require extrapolation的报错。遇到这类问题先检查传感器驱动是否启用了时间同步再检查各时间戳是否使用了同一时基。5.5 跨版本通信桥接如果暂时无法把全部节点迁移到 ROS2可以使用ros1_bridge在两个环境之间转发话题。典型场景是ROS1 端有雷达驱动ROS2 端跑 Cartographer通过桥接把/scan、/odom转发到 ROS2 域中。使用 ros1_bridge 的前提是 ROS1 的roscore和 ROS2 的 DDS 网络都在运行然后启动动态桥接节点ros2 run ros1_bridge dynamic_bridge这种方式适合短期过渡不建议在真机洞穴场景中长期使用因为增加了一层转发延迟和稳定性都会受到影响。6. 常见问题与排查思路问题现象常见原因解决思路激光雷达没有点云显示驱动未启动、串口权限不足、话题名不对检查ls /dev/ttyUSB*权限确认话题名是否为/scan建图过程中地图偏移雷达外参或 TF 树错误用rviz_tf或tf2_echo检查坐标系树Cartographer 报 TF 查询超时时间戳不同步或缺少坐标变换统一传感器时间戳补全map - odom - base_link - laser长直隧道中地图漂移激光匹配退化沿隧道方向约束不足接入 IMU / 轮式里程计观察 odom 是否参与位姿估计ROS1 / ROS2 节点无法互通未启用 ros1_bridge或 QoS 不匹配启动桥接节点检查话题 QoS 策略播放 bag 时地图卡住仿真时间未同步设置/use_sim_time为 true或用ros2 bag play --clock不同洞穴环境的坑点不完全一样但排查顺序是固定的先确认数据源再看 TF 树再检查算法配置最后才怀疑 Cartographer 本身。很多时候问题不是出在 SLAM 算法而是雷达驱动没有配置好或者里程计消息的时间戳不稳定。7. 工程化建议与最佳实践7.1 时间同步是第一优先级洞穴环境对时间同步的要求比普通室内更严格。由于没有 GPS 提供绝对时间多传感器的时钟基准必须统一。真机上建议使用硬件时间同步或者至少保证所有传感器驱动使用sensor_msgs::TimeReference机制。录制 bag 时也要保持时钟一致否则离线建图的结果和在线建图会差异很大。7.2 激光点云预处理洞穴隧道中激光雷达容易打到岩壁上的碎石、水汽和粉尘产生大量离群点。在建图前可以先对点云做降采样和离群点滤除减少无效点对匹配的干扰。ROS 中可以使用pcl_ros的VoxelGrid和StatisticalOutlierRemoval滤波器。如果使用 3D 雷达做 2D 建图可以先把 3D 点云投影成 2D 激光扫描或者只选择雷达安装高度附近的点参与 2D 匹配。这样做的好处是减少斜向岩壁对 2D 地图的干扰。7.3 先离线建图再在线建图洞穴真机测试的成本很高不建议第一次下洞就直接在线跑实时建图。更稳妥的做法是先用小车在洞穴里录制一段包含多传感器数据的 rosbag回到实验室后离线跑 Cartographer通过调整参数得到满意地图后再把参数移植到在线建图节点上。离线建图的另一个优点是速度可以变慢、参数可以反复迭代不会因为真机移动速度过快导致匹配跟不上。7.4 机器人本体与图传通信洞穴场景中上位机通常放在地面站远处下位机搭载在机器人上。ROS1 在同一网络内需要配置ROS_MASTER_URI和ROS_IP如果网络不稳定节点容易失联。ROS2 中没有了 Master但 DDS 对网络环境更敏感建议设置合理的ROS_DOMAIN_ID并在弱网环境中适当调整 QoS 策略。无论是 ROS1 还是 ROS2都不要让建图节点长期运行在无线信号不稳定的环境中。地面站只做地图可视化和数据记录车载计算单元尽量本地跑 Cartographer这样即使图传断开机器人也能继续建图等信号恢复后再把地图同步到地面站。7.5 注意安全与合规洞穴建图常用于地质勘探、矿山巡检、灾难救援等场景真机测试前务必确认现场环境和设备合法性遵守行业安全规范。尤其是在矿洞、隧道等实际生产环境中需要提前探测气体、评估塌方风险不能仅依赖机器人自主判断。机器人本身也要预留急停开关和手动接管通道。8. 总结与下一步规划这篇笔记从概念、环境、方案到实战系统梳理了 ROS1 和 ROS2 下洞穴建图的主要工作。你需要重点掌握的内容包括洞穴环境对 SLAM 的挑战、多传感器融合的必要性、Cartographer 配置思路、ROS1 与 ROS2 的迁移差异以及常见报错的排查顺序。如果接下来想继续深入建议按这样的路线推进先用 Cartographer 官方 demo 数据跑通 2D 建图流程再替换成你自己的洞穴 bag然后加入 IMU 和里程计观察长直隧道中的漂移变化接着尝试 ROS2 slam_toolbox 或 3D 方案 LIO-SAM最后再考虑多机器人协同建图。建图只是洞穴机器人的第一步后续的定位、路径规划和自主导航同样依赖高质量地图这部分可以接着学习 ROS2 Navigation2 相关技术。实际项目中优先关注的永远是传感器时间同步、TF 树完整性和回环触发频率。只要这三块不出问题Cartographer 给出的地图基本可用。如果这篇笔记对你有帮助建议收藏备用之后调参或者排查问题时可以直接对照里面的配置和表格来操作。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →