
做视觉SLAM有一阵子了最近用D435i在ROS2环境下把RTAB-Map完整跑通了一遍从装驱动、标定相机、调参数到手持建图一路踩了不少坑。这次直接把整个过程整理出来包括我实际用到的命令、参数和遇到问题的排查思路给准备入坑的同学省点时间。这篇东西面向的是Ubuntu 22.04 ROS2 Humble环境用的是Intel RealSense D435i深度相机核心软件栈是realsense-ros驱动和rtabmap_ros。如果你用的是ROS2 Foxy或者Ubuntu 20.04大部分内容也适用就是个别包名和依赖版本会有差异。先说结论RTAB-Map配合D435i做室内RGB-D SLAM效果相当能打。它不像激光SLAM那样依赖昂贵的雷达一个几百块的深度相机就能输出带颜色的3D点云地图而且建图过程中能看到实时回环检测对室内环境识别很准。ROS2版本下整体链路是D435i发布彩色图和深度图以及IMU数据RTAB-Map接收这些话题完成视觉里程计、回环检测和图优化最终生成3D稠密地图和2D栅格地图。整个过程学完你也能给自己的机器人加上视觉建图能力。1. 方案选型分析为什么是RTAB-Map D435i1.1 视觉SLAM方案横向对比室内3D建图的主流方案有这么几条路线纯激光、激光视觉融合、纯视觉、RGB-D视觉。纯激光SLAM比如Gmapping、Cartographer配2D雷达发展最成熟但只能输出2D栅格地图想要3D效果得用3D激光雷达那个价格直接劝退新手。视觉SLAM里ORB-SLAM系列是特征点法的代表精度高但稀疏特征图对导航不太友好而且回环检测的图优化在ROS2下集成度一般。LSD-SLAM和SVO这类直接法对光照敏感室内灯光一换就容易飘。RTAB-MapReal-Time Appearance-Based Mapping走的是RGB-D路线优势在于它同时维护一个基于视觉词袋的回环检测器和一张增量式3D占用网格图对室内环境特别友好。D435i输出对齐后的彩色图和深度图RTAB-Map直接把深度图转成点云做帧间匹配不需要额外计算视差省掉了特征点法最头疼的初始化过程。配合D435i自带的IMU还能做视觉惯性紧耦合减少纯视觉在快速运动时的漂移。另一个实际考量是社区生态。RTAB-Map在ROS1时代就有大量用户积累迁移到ROS2后核心算法没变很多老的参数调优经验可以直接移植。它还能导出多种地图格式2D栅格、3D点云、OctoMap八叉树兼容Navigation2导航栈这意味着你建完图之后可以直接接着做自主导航不用换一套工具链。1.2 D435i为什么适合入门D435i是Intel RealSense系列里性价比很高的一款深度相机。它用的是主动红外立体视觉方案室内环境下深度精度在毫米级到厘米级之间测量范围大概0.1米到10米正好覆盖室内建图的典型距离。相比ToF方案的相机D435i在纹理丰富的环境下表现更稳而且不受环境光频闪影响。它自带一个六轴IMUBMI055能输出加速度计和陀螺仪数据这对视觉SLAM是宝贵的补充信息。当然D435i也有短板。它的深度图在纯白墙面、强光直射、玻璃表面这些低纹理或反射区域会产生空洞建图时如果你快速扫过这种区域地图上会出现黑色缺口。解决办法是对深度图做时间滤波和空间滤波这部分后面实操部分细说。此外D435i的视场角大概是87°×58°不算广角建图时要注意扫过的区域有足够重叠否则帧间匹配容易丢失。选D435i还有一个很实际的原因realsense-ros驱动在ROS2 Humble下有官方维护的版本安装和编译都顺畅不需要自己折腾一堆第三方补丁。相比之下一些国产深度相机在ROS2下的驱动要么不完善要么只支持老版本ROS光环境适配就能耗费半天时间。2. 环境准备与依赖安装2.1 ROS2 Humble安装与配置如果你是全新机器建议用鱼香ROS的一键安装脚本它能同时装好ROS2、系统依赖和常用工具链比自己手动添加apt源省心很多。装完之后记得source环境把source /opt/ros/humble/setup.bash写进~/.bashrc顺手安装colcon、rosdep、turtlesim这几个基础包用于验证安装。# 验证ROS2环境 ros2 --version ros2 run turtlesim turtlesim_node如果ros2命令提示找不到基本就是没source环境或者没装完整。我遇到过一次是在Ubuntu 22.04上默认的Python版本是3.10而ROS2 Humble需要的是3.8以上如果有多个Python版本混用容易导致ament索引出错此时检查/opt/ros/humble/setup.bash文件是否存在以及echo $ROS_DISTRO是否输出humble。2.2 编译安装realsense-ros驱动D435i的ROS2驱动基于librealsense SDK先装SDK再装ROS2封装包。librealsense官方提供了apt源安装后版本大概在2.50以上然后用源码编译realsense-ros。# 安装librealsense SDK sudo apt install ros-humble-librealsense2 # 编译realsense-ros mkdir -p ~/ros2_ws/src cd ~/ros2_ws/src git clone https://github.com/IntelRealSense/realsense-ros.git -b ros2-development cd ~/ros2_ws rosdep install --from-paths src --ignore-src -r -y colcon build --symlink-install编译完成后测试相机是否正常输出话题。插上D435i运行ros2 launch realsense2_camera rs_launch.py然后另开终端看话题列表。正常的话会看到/camera/color/image_raw、/camera/depth/image_rect_raw、/camera/imu以及对应的camera_info话题。这里要注意realsense-ros发布的话题名和RTAB-Map默认订阅的话题名不直接对应后面需要用remap或参数配置对齐。2.3 安装rtabmap_ros及依赖rtabmap_ros在ROS2 Humble下有预编译包直接apt安装也可以但我建议源码编译因为在后续调整参数时能直接改源码重新编译调试。# 方式一apt安装快速 sudo apt install ros-humble-rtabmap-ros # 方式二源码编译推荐便于深度定制 cd ~/ros2_ws/src git clone https://github.com/introlab/rtabmap_ros.git -b ros2 cd ~/ros2_ws rosdep install --from-paths src --ignore-src -r -y colcon build --symlink-installrtabmap_ros依赖的rtabmap核心库如果apt版本不匹配会报一堆找不到头文件的错误所以源码编译前一定要确保rosdep把所有依赖都装齐了。我卡过的问题是从源码编译时发现rtabmap库版本是老的和rtabmap_ros的接口对不上检查发现是之前手动装过老版本卸载后重新编译就好了。2.4 相机标定与IMU标定D435i出厂自带的内参和外参存在相机固件里SDK会自动加载但不代表可以直接拿来SLAM。深度相机的内参标定在出厂时已经做过一般情况下不需要重新标定彩色图内参和深度图内参。需要注意的关键步骤是彩色图和深度图的时间戳对齐以及IMU的外参标定。彩色图与深度图对齐在realsense-ros启动参数里设置align_depth选项可以输出对齐到彩色图坐标系的深度图。RTAB-Map要求深度图和彩色图在时间和空间上对齐所以一定要开启align。IMU标定这块RTAB-Map对IMU的零偏和噪声比较敏感但D435i的IMU零偏在出厂时被标定过直接使用也能跑。如果追求更高精度可以用imu_utils工具包离线标定先录制2小时静止的IMU数据bag包然后计算零偏和随机游走。这个流程耗时长建议新手先跳过用默认参数。注意D435i的IMU数据频率是400Hz但realsense-ros默认发布200Hz。RTAB-Map内部会做降采样和滤波这个频率足够了。3. RTAB-Map核心配置与启动参数解析3.1 话题对接与launch文件编写RTAB-Map在ROS2下的标准入口是rtabmap_ros的rtabmap节点。它默认订阅的话题是rgb/image、depth/image、rgb/camera_info和realsense-ros发布的话题对不上所以要么用remap要么在launch文件里重映射话题名。我习惯用launch文件统一管理这样参数可读性好、可复用。核心launch文件示例from launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ # 相机驱动 Node( packagerealsense2_camera, executablerealsense2_camera_node, namecamera, parameters[{ align_depth: True, enable_gyro: True, enable_accel: True, rgb_camera.profile: 640x480x30, depth_module.profile: 640x480x30, imu_optical_frame_id: camera_imu_optical_frame, }], ), # RTAB-Map建图 Node( packagertabmap_ros, executablertabmap, namertabmap, parameters[{ frame_id: camera_link, subscribe_depth: True, subscribe_rgb: True, subscribe_imu: True, approx_sync: False, qos: 1, }], remappings[ (rgb/image, /camera/color/image_raw), (depth/image, /camera/aligned_depth_to_color/image_raw), (rgb/camera_info, /camera/color/camera_info), (imu, /camera/imu), ], ), # 可视化 Node( packagerviz2, executablerviz2, namerviz2, arguments[-d, /path/to/rtabmap.rviz], ), ])这个launch里有两个关键点需要注意。第一是approx_sync参数如果深度图和彩色图的时间戳不是严格同步即使开了align_depth也可能有微小偏差需要设成True使用近似时间同步。我实测下来D435i在30fps下大约每两三帧会有一帧时间戳差几个毫秒开approx_sync后建图明显更稳定。第二是qos参数realsense-ros发布的话题默认是SensorDataQoS而RTAB-Map如果没有显式指定会使用默认的ReliableQoS两者不匹配会导致收不到数据。设置qos: 1让RTAB-Map也使用SensorDataQoS。3.2 视觉里程计与回环检测参数调整RTAB-Map内部默认使用RGB-D里程计即帧间通过深度图配准估计运动。如果IMU话题接上了并且配置正确它会自动融合IMU做视觉惯性里程计此时速度估计更平滑快速转动时不容易丢。几个我实测下来对室内建图影响很大的参数Vis/MinInliers帧间匹配最少内点数量默认20。如果环境纹理少经常匹配不上可以降到10~15但降太多会导致里程计精度下降地图容易飘。RGBD/ProximityBySpace当机器人回到之前到过的位置时RTAB-Map会根据空间距离自动检测回环。这个选项默认开启室内场景建议保持。RGBD/AngularUpdate和RGBD/LinearUpdate控制每隔多少角度或位移触发一次新的关键帧。默认值是0.05弧度和0.05米对快速手持建图来说太频繁了地图冗余帧过多会拖慢处理速度。我调到0.1和0.1就是每转约6度或移动10厘米才添加一帧建图文件小了将近一半精度几乎没有变化。Mem/STMSize短期记忆窗口大小默认10。它决定回环检测的搜索范围越大越容易发现远距离回环但内存占用也越高。3.3 地图输出类型与保存路径RTAB-Map建完图后有几个出口RVIZ实时显示3D点云地图、保存到数据库.db、导出为PCD/PLY点云、导出为OctoMap八叉树地图。我的习惯是建图过程中打开RVIZ看实时效果建完之后保存数据库再从数据库导出2D栅格地图给Navigation2用。保存地图和导出点云都在rtabmap的命令行工具里操作# 保存数据库地图包含所有关键帧、回环约束、点云 ros2 run rtabmap_ros rtabmap # 建图结束后在rtabmap的GUI里点击保存或执行 ros2 service call /rtabmap/save_map rtabmap_msgs/srv/GetMap # 导出彩色点云PLY ros2 run rtabmap_ros rtabmap_export --ply map.db # 导出2D栅格地图PGM ros2 run rtabmap_ros rtabmap_export --pgm map.db重要提醒rtabmap数据库保存在你启动rtabmap节点的当前工作目录下文件名默认是~/.ros/rtabmap.db。每次重新运行都会覆盖这个文件所以建完图一定要第一时间改名备份我吃过这个亏辛苦扫了半小时的地图被下一次启动直接冲掉了。4. 实操建图全流程实录4.1 启动相机与检查话题先把相机驱动跑起来确认数据正常。ros2 launch realsense2_camera rs_launch.py在另一个终端用ros2 topic hz检查各话题帧率ros2 topic hz /camera/color/image_raw ros2 topic hz /camera/aligned_depth_to_color/image_raw ros2 topic hz /camera/imu三个话题都稳定在30HzIMU大约200Hz才算正常。如果深度话题频率掉到十几赫兹多半是USB带宽不够换一个USB 3.0口或者降低分辨率到640x48015fps。4.2 启动RTAB-Map开始建图确认话题正常后启动建图launchros2 launch ~/ros2_ws/src/my_rtabmap_launch/launch/rtabmap_d435i.launch.py第一次启动会加载词袋数据库用于回环检测大概等几秒钟。RVIZ窗口打开后会看到3D视图里开始出现彩色的点云。手持相机建图时有几个动作要领先在一个固定位置静止几秒钟让里程计初始化稳定转动相机时要慢、要稳避免快速甩动造成运动模糊扫过的区域要有重叠尤其在转角处要多停留一下让回环检测有机会建立闭环最后回到起点再绕一圈形成闭环这样地图的全局一致性会明显提升建图过程中观察RVIZ如果发现点云逐渐漂移比如两面墙出现重影说明里程计在某个时刻丢失了要退回上一个位置重新扫描让地图重新对齐。RTAB-Map有时候会自动修正回环后的地图点云会出现一个轻微的“跳动”这是正常的。4.3 建图结束与地图保存当你觉得地图覆盖够了先在rtabmap GUI里点击暂停处理然后保存数据库。我习惯再开一个终端手动输出地图mkdir -p ~/maps cd ~/maps mv ~/.ros/rtabmap.db my_room.db ros2 run rtabmap_ros rtabmap_export --ply my_room.db这样会生成my_room.ply用MeshLab或CloudCompare打开就能看到彩色的3D点云。如果要给Navigation2用还需要导出2D栅格地图这一步RTAB-Map做得比较巧妙它会从3D地图投影生成2D占据栅格。导出的PGM文件配合YAML文件就是标准的ROS2地图格式放到nav2的map_server里直接能用。4.4 生成八叉树地图可选如果想让导航在3D空间中避障可以导出OctoMap。RTAB-Map在保存数据库时如果设置了Grid/3D参数数据库里就包含了八叉树地图。另外也可以在rtabmap GUI的“文件-导出-OctoMap”里直接导出.bt文件后续用octomap_server发布占用网格话题。这里要注意OctoMap的体素分辨率设置。默认5厘米在室内足够如果你要做机械臂避障需要更精细可以设成2厘米。分辨率越高内存占用越大一个100平米的房间2厘米分辨率可能轻松占用几个GB内存自己权衡。5. 常见问题与排查技巧实录5.1 IMU话题数据异常如果你发现RTAB-Map的VIO视觉惯性里程计没生效看日志里有没有报IMU数据频率为0或协方差全是0的警告。realsense-ros默认发布的IMU消息协方差是0RTAB-Map一收到协方差为0的IMU数据就把它们全部丢弃相当于IMU完全没起作用。解决办法有两个。一是在launch文件里给相机节点设置unite_imu_method为2即把陀螺仪和加速度计合并成一个话题同时手动填充协方差矩阵二是像我在launch示例里那样在realsense-ros的参数里加imu_optical_frame_id: camera_imu_optical_frame并确保launch里指定的IMU话题名正确。这里容易卡住很多人但其实RTAB-Map官方文档对IMU话题的协方差要求写得很清楚就是老版本realsense-ros的默认值不满足。5.2 TF树缺失导致无法建图RTAB-Map运行时会要求camera_link到map的TF树完整。如果你直接自己写launch而不使用rtabmap自带的launch很容易漏掉静态坐标变换。错误表现是rtabmap节点启动后一直等待TFRVIZ里报“No transform from [camera_link] to [map]”。解决办法是在launch里加上static_transform_publisherNode( packagetf2_ros, executablestatic_transform_publisher, arguments[0, 0, 0, 0, 0, 0, map, camera_link], )这里如果相机装在一个机器人底盘上map到camera_link之间还要加上odom和底盘到相机的外参变换静态TF发布器要对应加多个。5.3 建图过程中RVIZ崩溃或卡顿RTAB-Map的3D地图由无数个点云块组成随着地图增大RVIZ渲染开销急剧上升。手持建图15分钟后点云块数量可能上万低配置电脑直接卡成PPT。解决办法是把RVIZ里的Map渲染线程数降低或者在rtabmap中开启Cloud/VoxelSize滤波比如设成0.02这样地图显示的点云被体素化数量能减少一半以上对视觉判断影响不大。5.4 回环检测失效地图越走越偏如果你发现回环一直检测不到检查两个地方。第一是Vis/MinInliers是否太低导致误匹配反而让回环没法通过验证第二是RGBD/ProximityBySpace是否开启如果它在走回头路时能检测到空间距离很近即使外观差异大也能触发回环验证。另一个常见原因是相机启动时的视野里纹理太少或者刚启动还没建好深度模型导致第一帧关键帧质量太差后面所有帧都在跟这个“坏帧”匹配。解决思路很简单重新启动建图启动后先不急着动让系统看到丰富的纹理和环境结构比如墙角、桌椅确认RVIZ里点云正常后再开始移动。5.5 地图漂移量化评估怎么判断建图质量一个好用的指标是看rtabmap GUI右下角的Error和Time两个数值。Error表示最近关键帧的重投影误差正常应该小于1。如果长时间大于1.5说明很多帧匹配的不好地图局部精度会差。另一个指标是回环数量一张100平米的室内地图如果回环少于5个说明你的扫描路线太稀或者重叠太少全局一致性可能有问题。我自己的判断标准是建完图后导出的2D栅格地图墙线和角落是否清晰锐利。如果是模糊的一团不用看指标也知道前面几步有问题。6. 进阶优化与个人心得6.1 从建图到导航的一体化衔接RTAB-Map建完图后可以无缝对接Navigation2做自主导航。2D栅格地图加载进map_server然后启动Nav2AMCL定位模块可以用RTAB-Map导出的地图做初始定位。这一步做完之后你就有了一个真正能自主移动的视觉导航机器人成本比激光方案低不少。如果你想更进一步可以研究RTAB-Map的Map/BundleAdjustment参数这是全局BA的开关。默认是True但在线运行时会消耗CPU。对实时性要求高的场景可以关掉等建图结束后再离线执行一次全局BA效果更可控。6.2 我的几个实用建议最后还是按老规矩分享几条我自己反复踩坑后沉淀下来的经验。第一D435i的USB线一定要用原装的或者质量好的USB 3.0线。我换过一根普通充电线结果深度图帧率直接掉到8Hz一开始还以为是驱动问题排查了半小时才发现是线的问题。第二建图过程中不要中途去调RVIZ的显示参数频繁刷新点云会抢占CPU影响RTAB-Map的处理实时性。第三数据库文件一定要养成随手备份的习惯哪怕你打包个zip也行丢了真没法找回来。RTAB-Map这套方案我测试下来在20平米左右的室内房间手持扫描10分钟生成的地图误差在10厘米以内回环闭合后基本看不出重影。对于自己动手做机器人、想低成本获得3D感知能力的玩家来说这套组合是目前性价比最高的选择之一。后续我打算在这套基础上加一个机械臂结合D435i的深度信息做抓取到时候再写一篇实战记录。