手头有一台吃灰的老电脑,一块奥比中光Astra系列的深度相机,再配上Ubuntu 16.04和ROS Kinetic这个远古组合,想把RGBD-SLAM v2完整跑通——听起来简单,实际上我前前后后折腾了一周多,踩的坑加起来能绕实验室一圈。这篇东西不是官方案例复读,是我自己从白纸到建图成功全过程的记录,包含驱动安装、依赖编译、话题对接、内参调试等每一个环节的具体操作和报错处理,给同样需要在这套老环境上跑RGBD-SLAM v2的朋友做个参考。
1. 环境“老三项”:Ubuntu 16.04 配 Kinetic 时的版本匹配暗坑
1.1 为什么这个组合还在被大量使用
RGBD-SLAM v2是很多SLAM入门者绕不开的框架,它支持用RGB-D相机实时构建八叉树地图,代码逻辑清晰,非常适合学习图优化和回环检测。但这套代码从诞生到现在已经很多年没有大更新了,官方README里写的还是Ubuntu 14.04 + ROS Indigo。很多人手里只有Ubuntu 16.04的机器,加上ROS Kinetic是16.04对应的官方版本,于是“Ubuntu 16.04 + Kinetic + RGBD-SLAM v2”就成了一个半官方、半民间的主流搭配。Kinetic自带的OpenCV 3.3.1、PCL 1.8、Eigen 3.2等库版本,和RGBD-SLAM v2的部分源码有兼容缝隙,这就是编译时各种莫名其妙的报错来源。
1.2 先把rosdep和catkin的环境理顺
我建议在一台刚装好的Ubuntu 16.04上,先把ROS Kinetic完整装好,再用rosdep把所有系统依赖解决掉,最后创建一个全新的catkin工作空间专门给RGBD-SLAM v2用,不要和日常开发的工作空间混在一起。这样做的好处是,即使编译失败搞坏了某些配置,也不会影响你其他项目。
mkdir -p ~/rgbdslam_ws/src cd ~/rgbdslam_ws catkin_make很多人第一次编译RGBD-SLAM v2时会发现,默认的Python是2.7,而ROS Kinetic工具链完全基于Python 2.7,所以不需要刻意切换Python 3。如果系统里同时装了多个Python版本,注意在编译时不要用python3开头的命令去执行catkin工具,否则会报一堆环境变量错误。
1.3 编译内存和swap的调整
RGBD-SLAM v2的依赖里,g2o和OctoMap的编译非常吃内存。我在编译时经历过编译进程直接被系统kill掉的情况,查看dmesg才发现是OOM Killer。如果你用的老电脑内存只有8GB甚至更少,建议在编译前增大swap空间,或者编译时用catkin_make -j2限制并行任务数,不要上来就-j8。实测下来,-j2虽然慢,但是稳定,不会在最后阶段崩掉。
2. 奥比中光相机驱动安装与ROS数据流打通
2.1 驱动安装不是装完就完事
奥比中光的Astra系列相机在Linux下走的协议基本是OpenNI 2,所以驱动安装的核心是获取厂商提供的OpenNI 2 SDK和ROS关联包。现在网上能搜到的安装方式有两种:一种是下载官方SDK包后运行里面的install.sh,另一种是用apt直接安装第三方维护的驱动包。我测试下来,官方SDK的方式对Astra Pro的兼容性最好,但需要注意以下几点:
- 安装完成后必须重新插拔USB,并确认
lsusb能看到设备; - 老主板上尽量把相机插在USB 3.0接口,Astra对USB带宽要求不低,插在USB 2.0上偶尔会出现帧率不稳;
- 某些内核版本下需要手动配置udev规则,否则
/dev/bus/usb权限不足,ROS节点起来后一直报DeviceOpen failed。
2.2 启动ROS节点后的话题检查
在Ubuntu 16.04上,奥比中光的ROS驱动通常是astra_camera或者ob_camera这一类包名。启动方式一般是:
roslaunch astra_camera astrapro.launch启动成功后,我建议立刻用rostopic list确认相机话题是否正常发布。正常情况下你会看到类似这样的几个核心话题:
/camera/rgb/image_raw:彩色图像/camera/depth/image_raw:深度图像/camera/depth/camera_info:深度相机内参/camera/rgb/camera_info:彩色相机内参
这时不要急着进SLAM,先用rqt_image_view分别订阅彩色图像和深度图像,确认两个画面都正常显示且帧率稳定。我在这一步就发现了一个经典问题:深度图视野里有一大片黑色的无数据区域,后来排查发现是相机红外发射器被灰尘遮挡,手动清洁后恢复正常。这种硬件层面的问题如果不提前排除,后面SLAM跑起来会一直提示特征点不足。
2.3 RGBD-SLAM v2对话题命名的要求
RGBD-SLAM v2默认订阅的是/camera/rgb/image_raw和/camera/depth/image_raw,并且要求图像分辨率一致。如果你用的奥比中光相机型号在驱动里默认把彩色图像和深度图像设成不同分辨率(比如彩色是1280x960,深度是640x480),就必须在launch文件里把彩色图像分辨率降为和深度图像一致,或者在RGBD-SLAM中重新映射话题。我自己是在相机的launch文件中直接设定color_width:=640、color_height:=480,使得彩色图和深度图都是640x480,省去了后面很多麻烦。
3. RGBD-SLAM v2编译:依赖库、源码改动与完整构建顺序
3.1 依赖清点
RGBD-SLAM v2的核心依赖是OpenCV、PCL、Eigen、g2o、OctoMap,以及ROS的cv_bridge、tf、image_transport等。Ubuntu 16.04 + Kinetic自带的环境里,OpenCV是3.3.1,PCL是1.8,Eigen是3.2.x,g2o则没有系统包,需要从源码编译。
这里有一个比较隐蔽的问题:RGBD-SLAM v2源码仓库里往往自带了一个g2o的副本,这是为了在旧系统上免去安装g2o的麻烦。但这个自带g2o的版本比较老,在某些编译环境下会和新版Eigen起冲突,报一堆模板类相关的错误。我最终使用的是系统Eigen 3.2自带版本,配合源码仓库中g2o的cmake/modules/FindG2O.cmake来查找依赖。如果你也遇到g2o编译问题,建议优先检查Eigen版本,不要一上来就改源码。
3.2 修改源码中的OpenCV适配
Kinetic的cv_bridge默认链接的是OpenCV 3.x,而RGBD-SLAM v2部分代码是OpenCV 2.x风格,集中表现为两类问题:
CV_LOAD_IMAGE_UNCHANGED这类宏在新版OpenCV中改名;cv::SIFT、cv::SURF等特征检测器需要引用opencv_features2d和opencv_nonfree模块,但源码的CMakeLists里可能没有显式链接。
我的做法是在RGBD-SLAM v2的CMakeLists.txt里,把所有OpenCV相关查找语句统一改为:
find_package(OpenCV 3 REQUIRED COMPONENTS core highgui imgproc imgcodecs features2d xfeatures2d) include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES})然后在需要的地方把代码里的CV_LOAD_IMAGE_UNCHANGED改成cv::IMREAD_UNCHANGED。注意,如果你的相机驱动和ROS节点里有其他模块也依赖OpenCV,尽量让所有模块使用同一个OpenCV版本,避免链接时出现符号冲突。这一点我吃过亏,后来在同一个工作空间里重新编译了所有依赖包才解决。
3.3 完整构建顺序
我最终的编译顺序是:
- 编译工作空间基础环境:
catkin_make生成setup.bash; - 单独编译g2o(如果源码自带版本可用,也可以跳过);
- 将RGBD-SLAM v2源码放入
src目录; - 执行
catkin_make -j2,逐条解决报错; source devel/setup.bash后,用rospack find rgbdslam验证包路径正确。
下面是编译过程中我遇到的高频错误和处理方法,整理成表供你对照:
| 报错特征 | 可能原因 | 处理方式 |
|---|---|---|
找不到g2o相关头文件 | 没有安装g2o,或G2O_DIR未设置 | 编译g2o后export G2O_DIR=/path/to/g2o |
Eigen模板编译报错,提示hypot或Index | Eigen版本过新或过旧 | 确认系统Eigen版本为3.2.x,必要时头文件路径优先指向系统Eigen |
cv_bridge链接失败,提示opencv2/core找不到 | OpenCV 2/3头文件混用 | 统一用find_package(OpenCV 3 ...),编译后检查ldd |
SIFT/SURF未声明 | 未链接opencv_features2d等模块 | CMakeLists中显式增加组件 |
ros::init相关错误 | 没有链接roscpp库 | 检查target_link_libraries是否包含roscpp |
如果编译到一半出现“undefined reference”类错误,大部分是某个库没有链接完整,用grep在CMakeLists里搜索对应的库名,逐一补上即可。
3.4 运行前设置环境变量
编译完成后,还有一步很多人会漏掉——RGBD-SLAM v2默认需要加载词汇文件来完成回环检测,这个词汇文件通常在包的data目录下,文件名为ORBvocab.dict之类的。如果运行时提示找不到词汇文件,需要在launch文件或环境变量里显式指定路径:
<param name="vocab_path" value="/home/你的用户名/rgbdslam_ws/src/rgbdslam_v2/data/ORBvocab.dict" />我在第一次运行时就是没设置这个参数,程序启动后直接段错误退出,查了很久才发现是词汇文件没加载。
4. 启动前必须确认的相机内参与深度对齐问题
4.1 camera_info里的内参为什么可能是全零
奥比中光相机驱动的话题里,/camera/rgb/camera_info和/camera/depth/camera_info理论上会发布内参矩阵,但有的固件版本或驱动版本身不会填充这些数据,导致SLAM拿到的内参全是0。这会造成一个很隐蔽的现象:程序不报错,图像也能显示,但SLAM跑起来后特征点三角化完全不对,地图直接乱掉。
要判断内参是否正确,可以在启动SLAM前执行:
rostopic echo /camera/rgb/camera_info -n1看输出里K矩阵是否是一组非零的数值。如果全是0,有两个办法:
- 在相机驱动的launch里添加参数强制加载相机标定文件;
- 自己用棋盘格标定,把结果写到
camera_info发布端。
对于Astra这种出厂自带内参的相机,我更推荐直接找驱动里的标定文件,把它们填进去,不要自己重新标定,因为深度相机的内参和红外镜头相关,自己标定误差反而大。
4.2 深度对齐:RGB和Depth的像素对应关系
RGBD-SLAM v2要求彩色图和深度图严格对齐,也就是彩色图上的某个像素(x, y),在深度图上的同一像素位置必须对应同一个空间点。Astra驱动通常提供一个“深度对齐到彩色”的开关参数,类似depth_registration或align_depth。
如果没有开启这个对齐,RGB和Depth的视角会存在一个小偏移,SLAM构建出的点云会出现重影和拖尾,甚至回环检测完全失效。我在运行时就发现图像上棋盘格边缘的深度图出现了约5个像素的偏移,开启深度对齐后恢复正常。
检查方式很简单:在rqt_image_view里订阅深度话题,但把显示模式调成“按彩色图叠加”或直接用两个视图对比桌角、墙面边缘的位置是否吻合。如果不吻合,回到相机驱动的launch文件里找对齐参数并打开。
4.3 TF树检查
RGBD-SLAM v2运行时需要依赖TF树来完成坐标变换,最关键的坐标系关系是/camera_link(相机坐标系)和/camera_depth_optical_frame(深度图光学坐标系)之间的变换。如果TF丢失,SLAM启动后会提示“wait for transform”之类的错误,图像话题正常但地图始终不更新。
我建议在启动SLAM前先只运行相机驱动,然后执行:
rosrun tf view_frames生成的frames.pdf中检查TF树的根节点和叶子节点是否完整。如果发现相机相关坐标系没有发布,多半是驱动里的URDF模型没有加载。奥比中光驱动一般会提供一个robot_description参数或者URDF文件,在launch里加上即可。
5. 跑起来只是开始:rviz、TF与特征匹配实测排错
5.1 第一帧图像的地图验证
当所有准备完成后,就可以启动RGBD-SLAM v2了。我通常使用的启动方式是:
roslaunch rgbdslam rgbdslam.launch然后在rviz中添加PointCloud2显示,话题选择/rgbdslam/cloud或类似的话题。如果你看到点云开始缓慢累积,说明SLAM已经正常接收图像、深度和TF了。如果一帧都没有,下一步用rqt_graph查看节点之间的数据流,确认rgbdslam节点是否真的订阅到了图像话题。
这里有个小技巧:RGBD-SLAM v2启动后不要立刻大幅度移动相机,先在原地停留几秒,让它初始化第一帧关键帧。我因为上来就快速转头,导致初始帧没有足够的特征点,后面整个轨迹都飘了。正确操作是缓慢平移相机,保证场景里有足够的纹理和纵深变化,比如桌子上的书本、墙角的边界。
5.2 特征点不足与匹配失败
运行过程中最常见的问题是特征点匹配太少,导致位姿估计断开。屏幕上会不停提示帧率很低或者cannot find enough features之类的内容。除了清理相机镜头污渍、调节曝光之外,还有一个参数值得关注:RGBD-SLAM v2在launch里通常会开放feature_extractor_type、min_matches等参数,可以适当降低最小匹配数。
我在室内普通环境下,把这些参数调整成了下面这样:
<param name="min_matches" value="20" /> <param name="feature_extractor_type" value="orb" />注意不要调太低,否则误匹配会直线上升,地图精度下降得厉害。如果你使用ORB特征仍然匹配不够,可以尝试feature_extractor_type改为sift,SIFT对光照和视角变化鲁棒性更强,代价是计算量更大,老机器跑起来会掉帧。
5.3 TF断链和漂移
运行几分钟后,我遇到过一段很典型的问题:rviz里的点云突然不再更新,TF树显示的相机坐标系在来回跳变,地图开始明显漂移。这种情况通常是视觉里程计已经失配,但优化器仍然在尝试使用错误的约束。
排查时先看rqt_tf_tree或rosrun tf tf_monitor的输出,确认变换频率是否稳定。如果变换频率忽高忽低,问题大概率出在图像帧率或深度图噪声上。我最终通过降低octuple地图分辨率,同时把相机的帧率从30FPS降到15FPS,缓解了大部分漂移。降低帧率听起来反直觉,但实际上减少了相邻帧的重复匹配,反而提高了位姿估计的稳定性。
5.4 保存建图结果
如果你最终建图成功,记得把地图保存下来。RGBD-SLAM v2的交互方式主要是通过服务调用或命令行完成,通常可以保存为PCD格式的点云文件。我使用的保存方式是在另一个终端里调用保存服务,并指定输出路径:
rosservice call /rgbdslam/save_pcd "filename: '/home/你的用户名/map.pcd'"保存成功后可以用pcl_viewer map.pcd快速查看建图结果。
我在实际建图中发现,RGBD-SLAM v2对室内纹理丰富的环境表现最好,纯白墙面加空旷走廊这种场景很容易崩。如果你主要跑室内结构化环境,这个框架完全够用;但如果你的场景很空旷,建议把深度相机斜向下倾斜一些,让地面纹理参与特征匹配,效果会好很多。
最后再分享一个小经验:整套流程跑通后,我强烈建议把相机驱动的launch文件、RGBD-SLAM v2的launch文件、你改动过的CMakeLists都备份到一个固定的配置目录里。这个组合太老了,每次换机器、换系统都要重新折腾一遍,有了一套自己的配置文档,至少能省下一半的时间。希望这篇记录能给正在同一坑里的你一点帮助。