AI

RTAB-Map

Real-Time Appearance-Based Mapping

Posted by LXG on June 16, 2026

rtabmap-github

ROS2 环境检查


# source /opt/ros/humble/setup.bash

# ros2 doctor

安装 rtabmap


sudo apt update

sudo apt install \
ros-humble-rtabmap \
ros-humble-rtabmap-ros \
ros-humble-rtabmap-msgs

$ ros2 pkg list | grep rtabmap
rtabmap_conversions
rtabmap_demos
rtabmap_examples
rtabmap_launch
rtabmap_msgs
rtabmap_odom
rtabmap_python
rtabmap_ros
rtabmap_rviz_plugins
rtabmap_slam
rtabmap_sync
rtabmap_util
rtabmap_viz

rtabmap 命令

核心运行与可视化工具

  • rtabmap
    • 介绍:RTAB-Map 的官方图形界面(GUI)主程序。你可以通过它直接连接相机建图、导入数据集、查看 3D 点云拼接效果以及进行在线实时 SLAM。
  • rtabmap-console
    • 介绍:RTAB-Map 的纯命令行控制台版本(无图形界面)。适合跑在没有显示器的机器人、服务器或树莓派上,在后台默默进行建图并保存数据库。
  • rtabmap-odometryViewer
    • 介绍:纯里程计可视化工具。如果你只想单独测试视觉里程计(VO)或轮式里程计的轨迹漂移情况,而不进行后端的全局建图与回环检测,用这个工具观察最轻量。
  • rtabmap-lidar_viewer
    • 介绍:一个简易的 3D 激光雷达(LiDAR)点云数据查看器,主要用来快速预览采集到的激光点云文件。

数据集与相机驱动输入工具(包含你遇到的问题核心)

  • rtabmap-rgbd_dataset
    • 介绍重点工具。专门用来离线读取、回放和处理各大公开 RGB-D 数据集(如 TUM、EuroC、ICL-NUIM)的命令行工具。你之前遇到的 rgb_sync 命名要求,正是由这个工具的内部解析机制决定的。
  • rtabmap-rgbd_camera
    • 介绍:相机硬件测试驱动。无需打开庞大的主界面,直接在终端调用它即可快速验证你的外接硬件相机(如 Intel RealSense、Stereolabs ZED、Orbbec 等)能否被系统正常读取并输出图像。
  • rtabmap-camera
    • 介绍:普通的普通单目/普通普通相机读取工具。
  • rtabmap-kitti_dataset
    • 介绍:专门针对自动驾驶领域大名鼎鼎的 KITTI 室外双目/激光雷达数据集的专用读取与回放工具。
  • rtabmap-dataRecorder
    • 介绍:数据录制器。用于把相机捕捉到的 RGB 图像、深度图、时间戳等原始数据直接录制保存为一个特定的 .db 文件,方便后续做离线 SLAM 实验。

数据库维护与离线后期优化工具(核心亮点)

  • rtabmap-databaseViewer
    • 介绍极为常用的工具。专门用来分析建图结束后保存的 .db 数据库。你可以查看每一帧的视觉特征点匹配、检测被错误关联的回环、手动删除错误的闭环约束,甚至是导出拼好的 3D 模型。
  • rtabmap-recovery
    • 介绍数据库修复工具。当系统异常断电、闪退导致 .db 数据库文件损坏打不开时(你之前遇到的错误弹窗就是它触发的),可以用它尝试将残留的节点数据捞出来修复。
  • rtabmap-reprocess
    • 介绍离线重新处理工具。这是学术研究的神器。如果你用相同的传感器录制了一段数据,修改了提取特征点的算法(比如从 SURF 改成 ORB),可以用它在不重新录制数据的前提下,对数据库里面的数据重新跑一遍 SLAM。
  • rtabmap-globalBundleAdjustment
    • 介绍:全局光束法平差工具(GBA)。在离线状态下对已有的整个 SLAM 轨迹和点云进行高精度的非线性最小二乘优化,用于极大程度地消除长距离漂移。
  • rtabmap-detectMoreLoopClosures
    • 介绍:离线回环再检测工具。建图时有些回环可能因为计算超时漏掉了,在建图结束后运行它,可以让算法花更多的时间去数据库里死磕,强行找出更多隐藏的闭环。

数据导出与分析报告工具

  • rtabmap-export
    • 介绍:数据导出命令行版。可以把数据库里的 3D 点云无损导出为常见的 3D 模型格式(如 .ply, .pcd, .obj),供 MeshLab、CloudCompare 等软件或 3D 打印使用。
  • rtabmap-report
    • 介绍:性能报告生成器。分析数据库并输出详尽的 SLAM 统计图表(包括内存占用变化、每帧计算耗时、回环检测成功率等趋势图),非常适合写论文时拿来当实验图表。
  • rtabmap-info
    • 介绍:快速查看数据库基础信息的命令行工具,比如直接输出数据库里有多少个关键帧、有没有包含三维重构数据、文件大小等。

其他辅助小工具

  • rtabmap-calibration
    • 介绍:相机标定/校准工具。用于标定单目、双目或 RGB-D 相机的内参以及彩色镜头与红外镜头之间的外参,生成 SLAM 算法需要的 yaml 标定文件。
  • rtabmap-cleanupLocalGrids
    • 介绍:用于清理和重构栅格地图(Grid Map)的局部冗余数据。
  • rtabmap-extractObject
    • 介绍:基于 3D 边界框或点云分割,从已经建好的三维地图中强行提取出某一个特定物体的点云数据。
  • rtabmap-res_tool & rtabmap-res_tool-0.3.0
    • 介绍:资源管理与分辨率缩放的内部辅助小工具。

TUM_RGB-D 数据集

TUM RGB-D 数据集是由慕尼黑工业大学(Technical University of Munich)计算机视觉实验室发布的一个极为经典的 SLAM(同时定位与地图构建)评测基准。自2012年发布以来,它成为了评估 RGB-D SLAM(彩色+深度相机) 算法(如 ORB-SLAM2/3、RTAB-Map、ElasticFusion 等)性能的行业事实标准。

1. Freiburg 1 系列 (fr1):标准室内与基础测试

特点:使用第一代 Kinect 采集,视角较窄,深度图噪点相对较大。

压缩包文件名 场景内容 核心测试目的 / 算法难点 推荐测试阶段 / 难度
rgbd_dataset_freiburg1_xyz.zip 纯手持前后左右平移 测试视觉里程计(VO)的最基础平移计算,排除旋转干扰。 入门冒烟测试 ★☆☆☆☆
rgbd_dataset_freiburg1_rpy.zip 原地手持翻滚、俯仰、偏航(旋转) 测试算法在无平移、纯旋转下的退化表现(特征点容易跟丢)。 基础功能测试 ★★☆☆☆
rgbd_dataset_freiburg1_desk.zip 典型的办公桌环境(显示器/书本/键盘) 最经典的通用基准测试。评估算法在纹理、结构丰富环境下的综合表现。 核心性能评估 ★★★☆☆
rgbd_dataset_freiburg1_room.zip 相机在整个房间绕行一大圈回到原点 专门用来测试算法的回环检测(Loop Closure)与全局后端优化能力。 闭环能力测试 ★★★★☆
rgbd_dataset_freiburg1_floor.zip 相机对着地面移动 弱纹理、高重复性环境,测试算法在视觉特征缺失时的鲁棒性。 极端环境测试 ★★★★☆

rgbd_dataset_freiburg1_room

rgbd_dataset_freiburg1_room

2. Freiburg 2 系列 (fr2):长距离与机器人搭载

特点:使用 Asus Xtion 采集,图像质量较高,运动多由 Pioneer 先锋轮式机器人底盘驱动(Z轴稳定)。

压缩包文件名 场景内容 核心测试目的 / 算法难点 推荐测试阶段 / 难度
rgbd_dataset_freiburg2_xyz.zip 机器人底盘驱动的纯平移 Fr2 传感器下的平移基准线数据。 入门冒烟测试 ★☆☆☆☆
rgbd_dataset_freiburg2_desk.zip 办公桌环境(对照 fr1_desk) 用于对比不同传感器(Kinect vs Xtion)在相似场景下的算法表现。 核心性能评估 ★★★☆☆
rgbd_dataset_freiburg2_large_with_loop.zip 大范围工业大厅,轨迹最终闭合 评估算法在长距离运动下的累积误差消除与大范围回环能力。 对照组实验(优) ★★★★☆
rgbd_dataset_freiburg2_large_no_loop.zip 大范围工业大厅,单向推算不闭合 与上一个数据集形成完美对照,用于量化“回环检测”对算法精度的绝对贡献 对照组实验(劣) ★★★☆☆
rgbd_dataset_freiburg2_360_kidnap.zip 快速360度旋转,中途故意遮挡相机 魔鬼测试。遮挡模拟了机器人被“绑架”跟丢,测试算法的重定位功能 鲁棒性终极挑战 ★★★★★
rgbd_dataset_freiburg2_pioneer_360.zip 机器人原地360度剧烈转圈 测试算法在遭遇高角速度、画面剧烈模糊时的帧间匹配能力。 抗晃动测试 ★★★★☆
rgbd_dataset_freiburg2_pioneer_slam.zip 机器人搭载,长距离室内探索(标准版) 贴近真实的 AGV/扫地机导航场景,评估里程计在平面运动下的实用性。 机器人落地测试 ★★★☆☆
rgbd_dataset_freiburg2_pioneer_slam2.zip 机器人长距离探索(序列二) 变换了行进路线和局部障碍物,用于验证算法的泛化性。 迭代验证测试 ★★★☆☆
rgbd_dataset_freiburg2_pioneer_slam3.zip 机器人长距离探索(序列三) 进一步改变光照和运动轨迹,多用于多地图拼接或连续建图测试。 迭代验证测试 ★★★☆☆

rgbd_dataset_freiburg2_pioneer_slam

3. Freiburg 3 系列 (fr3):视觉与结构退化控制(写论文核心)

特点:严格控制环境中的“3D几何结构”和“视觉纹理”两大变量,专门暴露各类 SLAM 的物理死穴。

压缩包文件名 几何结构 (Structure) 视觉纹理 (Texture) 相机距离 / 测试目的与难点 推荐测试阶段 / 难度
fr3_nostructure_notexture_far.zip ❌ 无(平整表面) ❌ 无(白墙) 远距离。地狱场景,视觉特征点和点云配准全部失效,测试算法何时崩溃。 极限边缘测试 ★★★★★
fr3_nostructure_notexture_near_withloop.zip ❌ 无(平整表面) ❌ 无(白墙) 近距离(含回环)。特征全无,由于距离近,仅靠极其微弱的红外噪点或回环硬撑。 极限边缘测试 ★★★★★
fr3_nostructure_texture_far.zip ❌ 无(平整平面) 有(贴满海报) 远距离。平面海报,纯视觉(如 ORB)的乐园,但 3D 建图(如 ICP)由于缺乏起伏会退化。 纯视觉算法评测 ★★☆☆☆
fr3_nostructure_texture_near_withloop.zip ❌ 无(平整平面) 有(贴满海报) 近距离(含回环)。测试在完美的二维像素特征下,全局后端优化的极限对齐精度。 算法精度上限测试 ★★☆☆☆
fr3_structure_notexture_far.zip 有(白漆楼梯/方块) ❌ 无(全是纯白) 远距离。直接法/光流 SLAM 的噩梦,但却是 RGB-D/点云 SLAM 的天堂(全靠深度图配准)。 深度图配准评估 ★★★★☆
fr3_structure_notexture_near.zip 有(白漆楼梯/方块) ❌ 无(全是纯白) 近距离。眼睛看是一片白,但深度图凹凸有致。用来测试纯几何里程计(如 Point-to-Plane ICP)。 几何里程计测试 ★★★☆☆
fr3_structure_texture_far.zip 有(杂物台阶) 有(丰富色彩) 远距离。理想环境。但远距离导致第一代相机的深度数据噪点呈指数级上升,测试去噪能力。 综合抗噪测试 ★★★☆☆
fr3_structure_texture_near.zip 有(杂物台阶) 有(丰富色彩) 近距离。完美教科书环境。视觉特征无限,深度极其精准,用来跑出算法的理论最高精度。 算法上限/基准测试 ★☆☆☆☆

深度相机 Orbbec Gemini 336L

Orbbec Gemini 336L相机

Orbbec Gemini 336L 是奥比中光 Gemini 330 系列中的一款高性能双目3D相机。它最大的特点是,在 Gemini 335L 的优异性能基础上,通过过滤可见光来增强主动红外成像,从而在强光、反光等复杂光照条件下,也能输出稳定、精准的深度图像

如何打开相机

OrbbecSDK

OrbbecSDK_v2

OrbbecViewer_336L

OrbbecSDK 用途

OrbbecSDK 是奥比中光(Orbbec)官方为其旗下的 3D 深度相机(Depth Cameras) 开发的跨平台软件开发工具包(SDK)。

简单来说,它是连接奥比中光 3D 相机硬件与你的上层应用程序(如 SLAM 算法、AI 识别、三维重建等)之间的桥梁。它为开发者提供了一套统一的 API 接口,用来控制相机、流式获取数据并进行图像处理。

多模态数据流的获取与同步

  • 3D 相机通常集成了多个传感器,OrbbecSDK 可以帮助开发者同时获取并对齐这些数据:
  • 彩色图像流 (Color Stream): 获取标准的 2D RGB 视频流。
  • 深度图像流 (Depth Stream): 获取每个像素点带有距离信息的深度图(通常是 16位无符号整型)。
  • 红外图像流 (IR Stream): 获取左/右红外相机的图像,常用于暗光环境或主动结构光分析。
  • IMU 数据流 (IMU Stream): 如果相机内置了惯性测量单元,SDK 可以获取高频的加速度和角速度数据。
  • 数据同步 (Synchronization): 提供硬件级或软件级的时间戳对齐,确保彩色图、深度图和 IMU 数据在时间上完全匹配(这是做视觉 SLAM 建图的关键)。

高级图像处理与对齐

  • D2C (Depth to Color) 像素对齐: 由于深度相机和彩色相机的物理位置有偏差,SDK 支持将深度图的每个像素直接对齐到彩色图上,从而轻松获得“带有颜色和 RGB 信息的 3D 点云”。
  • 点云生成 (Point Cloud Generation): 自动将 2D 的深度图像转化为 3D 空间中的点云数据(XYZ 坐标)。
  • 滤波优化: 包含空间滤波、时间滤波、空洞填充等,能够减少深度图像中的噪点和边缘锯齿。

相机硬件控制与参数调节

开发者可以通过 SDK 动态调整相机的底层参数,以适应不同的光照或工作距离:

  • 曝光与增益: 调节 RGB 和红外相机的曝光时间、白平衡、激光发射器功率等。
  • 工作模式切换: 在“近距离高精度”与“远距离宽视野”等不同的硬件预设模式间切换。
  • 相机标定参数获取: 读取出厂内置的相机内参(Camera Intrinsic)和外参(Extrinsic Matrix),用于各类 3D 视觉计算。

深度相机数据录制

/home/lxg/software/Orbbec/OrbbecViewer_v2.8.6_202604271453_db82a04_linux_x86_64/output/RecordFile/Orbbec Gemini 336L_CPC9363000F3_20260616181547.bag

学习计划


首周目标

说清项目的 4 种运行模式和适用场景。
独立启动一次完整导航链路。
能用常见排查方法定位 1 个问题(TF/话题/参数)。
完成 1 次小参数调整并记录效果。
Day 1:建立全局认知

读 README 和顶层启动脚本,画一张自己的架构草图。
输出一页笔记:
输入传感器有哪些。
定位来源有哪些。
地图来源有哪些。
控制输出到哪里。
交付物:一张“感知-定位-规划-控制”流程图(手绘也行)。

Day 2:只做一件事,跑通基础链路

选择一个最常用模式启动。
确认核心话题是否有数据(scan、odom、map、cmd_vel)。
在可视化工具里确认坐标关系是否连通。
交付物:一份“成功启动截图 + 终端关键日志摘要”。

Day 3:理解配置文件结构

只读导航参数文件,重点看这几类参数:
控制器参数(速度、采样、评价器)。
局部/全局代价地图参数。
定位相关参数(里程计、AMCL 或融合里程计入口)。
建一个自己的参数字典:参数名、作用、调大调小现象。
交付物:10 个关键参数的个人注释表。

Day 4:定位融合专项

阅读 EKF 配置。
明确每个输入源(视觉里程计、激光里程计、IMU)分别贡献什么维度。
观察融合后里程计是否平滑、是否有跳变。
交付物:一页“融合输入与输出关系说明”。

Day 5:传感器接入专项

跑雷达和相机启动链路,确认数据稳定。
学会判定“是设备问题、时序问题,还是坐标问题”。
总结你的排查顺序(先数据,再 TF,再参数)。
交付物:一份故障排查 checklist(5-8 条)。

Day 6:小改动实战

只改 1-2 个参数(例如局部代价地图膨胀半径或控制器权重)。
保持同一路径重复测试。
记录改前改后差异(是否更平滑、是否更保守、是否更容易卡住)。
交付物:参数变更记录(变更项、原因、现象、结论)。

Day 7:做一次可复现汇报

用 5 分钟讲清楚:
项目主流程。
你调过的参数与结果。
你遇到的问题和排查方法。
交付物:简版周报(建议 1 页)。

什么是IMU

IMU(Inertial Measurement Unit,惯性测量单元)是一种用于测量物体运动状态的传感器模块,可以测量:

  • 加速度(Acceleration)
  • 角速度(Angular Velocity)
  • 有些还可以测量磁场(Magnetic Field)

建图的数据格式

RTAB-Map 建图使用的是原始传感器数据(图像、IMU、相机参数等),而 RViz 显示的是 RTAB-Map 处理后的结果数据(地图、轨迹、点云、位姿等)。

run_depth_to_rtabmap.sh


#!/usr/bin/env bash

set -euo pipefail

# 目录与环境相关配置
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_PATH="$(cd -- "$SCRIPT_DIR/../../.." && pwd)"
WORKSPACE_SETUP="$WORKSPACE_PATH/install/setup.bash"
# 是否在新终端拉起节点(1: gnome-terminal, 0: 后台 nohup)
LAUNCH_IN_NEW_TERMINAL="${LAUNCH_IN_NEW_TERMINAL:-1}"
# 话题就绪等待超时(秒)
TOPIC_WAIT_TIMEOUT_SEC="${TOPIC_WAIT_TIMEOUT_SEC:-25}"
# RTAB-Map 机体坐标系(按你的 TF 习惯可改)
RTABMAP_FRAME_ID="${RTABMAP_FRAME_ID:-camera_link}"
LOG_DIR="$WORKSPACE_PATH/logs"
# 是否加载当前工作区 overlay(install/setup.bash)
USE_WORKSPACE_OVERLAY="${USE_WORKSPACE_OVERLAY:-0}"
RTABMAP_IMU_TOPIC="${RTABMAP_IMU_TOPIC:-}"
CLEANUP_PREVIOUS_DEMO="${CLEANUP_PREVIOUS_DEMO:-1}"
# 默认关闭 IMU,先保证纯视觉链路稳定
RTABMAP_USE_IMU="${RTABMAP_USE_IMU:-0}"
RTABMAP_DATABASE_PATH="${RTABMAP_DATABASE_PATH:-$WORKSPACE_PATH/maps/learning_depth_demo.db}"
RTABMAP_DELETE_DB_ON_START="${RTABMAP_DELETE_DB_ON_START:-true}"
RTABMAP_LINEAR_UPDATE="${RTABMAP_LINEAR_UPDATE:-0.10}"
RTABMAP_ANGULAR_UPDATE="${RTABMAP_ANGULAR_UPDATE:-0.10}"
# Orbbec UVC 后端(当前默认 v4l2)
CAMERA_UVC_BACKEND="${CAMERA_UVC_BACKEND:-v4l2}"

# 安全 source:规避 set -u 导致的未定义变量报错
source_with_nounset_guard() {
  set +u
  # ROS setup scripts may access optional variables directly.
  source "$1"
  set -u
}

# 统一加载 ROS 环境,可选叠加工作区 overlay
source_demo_environment() {
  source_with_nounset_guard /opt/ros/humble/setup.bash
  if [[ "$USE_WORKSPACE_OVERLAY" == "1" ]]; then
    if [[ ! -f "$WORKSPACE_SETUP" ]]; then
      echo "未找到 $WORKSPACE_SETUP,无法启用工作区 overlay。"
      exit 1
    fi
    source_with_nounset_guard "$WORKSPACE_SETUP"
  fi
}

# 启动命令包装:优先新终端,回退到后台 nohup
launch_command() {
  local title="$1"
  local command="$2"

  if [[ "$LAUNCH_IN_NEW_TERMINAL" == "1" ]]; then
    if ! command -v gnome-terminal >/dev/null 2>&1; then
      echo "未找到 gnome-terminal,自动改为后台启动模式。"
      LAUNCH_IN_NEW_TERMINAL="0"
    fi
  fi

  if [[ "$LAUNCH_IN_NEW_TERMINAL" == "1" ]]; then
    gnome-terminal --title "$title" -- bash -lc "
      set +u
      source /opt/ros/humble/setup.bash
      if [ '$USE_WORKSPACE_OVERLAY' = '1' ]; then
        source '$WORKSPACE_SETUP'
      fi
      set -u
      $command
      exec bash
    "
  else
    echo "后台启动: $title"
    nohup bash -lc "
      set +u
      source /opt/ros/humble/setup.bash
      if [ '$USE_WORKSPACE_OVERLAY' = '1' ]; then
        source '$WORKSPACE_SETUP'
      fi
      set -u
      $command
    " >"$LOG_DIR/${title// /_}.log" 2>&1 &
  fi
}

# 等待话题出现(存在即可)
wait_for_topic() {
  local topic="$1"
  local timeout_sec="$2"
  local start_ts elapsed

  start_ts="$(date +%s)"
  while true; do
    if ros2 topic info "$topic" >/dev/null 2>&1; then
      return 0
    fi
    elapsed=$(( $(date +%s) - start_ts ))
    if [[ "$elapsed" -ge "$timeout_sec" ]]; then
      return 1
    fi
    sleep 0.5
  done
}

# 等待话题有发布者(防止只有话题名但无数据源)
wait_for_topic_publisher() {
  local topic="$1"
  local timeout_sec="$2"
  local start_ts elapsed info

  start_ts="$(date +%s)"
  while true; do
    info="$(ros2 topic info "$topic" 2>/dev/null || true)"
    if printf '%s\n' "$info" | grep -q "Publisher count: [1-9]"; then
      return 0
    fi
    elapsed=$(( $(date +%s) - start_ts ))
    if [[ "$elapsed" -ge "$timeout_sec" ]]; then
      return 1
    fi
    sleep 0.5
  done
}

# 自动探测常见 IMU 话题
detect_imu_topic() {
  local timeout_sec="$1"
  local candidate

  if [[ -n "$RTABMAP_IMU_TOPIC" ]] && wait_for_topic "$RTABMAP_IMU_TOPIC" "$timeout_sec"; then
    return 0
  fi

  for candidate in /imu/data /camera/gyro_accel/sample /camera/imu; do
    if wait_for_topic "$candidate" "$timeout_sec"; then
      RTABMAP_IMU_TOPIC="$candidate"
      return 0
    fi
  done

  return 1
}

# 清理上一次学习流程残留进程,避免重复节点/TF 冲突
cleanup_previous_demo_processes() {
  if [[ "$CLEANUP_PREVIOUS_DEMO" != "1" ]]; then
    return 0
  fi

  # Keep cleanup narrow to this learning demo's usual processes.
  pkill -f 'gemini_330_series.launch.py' >/dev/null 2>&1 || true
  pkill -f 'rtabmap_launch rtabmap.launch.py' >/dev/null 2>&1 || true
  pkill -f 'rtabmap_slam/rtabmap' >/dev/null 2>&1 || true
  pkill -f 'rtabmap_odom/rgbd_odometry' >/dev/null 2>&1 || true
  pkill -f 'rtabmap_util/point_cloud_xyzrgb' >/dev/null 2>&1 || true
  sleep 1
}

# 准备 RTAB-Map 数据库:按需删除旧库,避免历史状态干扰教学
prepare_demo_database() {
  mkdir -p "$(dirname "$RTABMAP_DATABASE_PATH")"

  if [[ "$RTABMAP_DELETE_DB_ON_START" == "true" ]]; then
    rm -f "$RTABMAP_DATABASE_PATH"
    echo "已重置学习数据库: $RTABMAP_DATABASE_PATH"
  else
    echo "复用学习数据库: $RTABMAP_DATABASE_PATH"
  fi
}

mkdir -p "$LOG_DIR"
cleanup_previous_demo_processes
prepare_demo_database

# 步骤 1:加载运行环境
echo "[1/4] 加载 ROS 环境"
source_demo_environment

# 步骤 2:启动深度相机节点
echo "[2/4] 启动 Orbbec 深度相机"
launch_command "[learn] depth camera" "
  ros2 launch orbbec_camera gemini_330_series.launch.py \
    camera_name:=camera \
    uvc_backend:=$CAMERA_UVC_BACKEND \
    publish_tf:=true \
    enable_color:=true \
    enable_depth:=true \
    color_width:=640 \
    color_height:=480 \
    color_fps:=30 \
    depth_width:=640 \
    depth_height:=480 \
    depth_fps:=30
"

# 步骤 3:确认关键输入话题已就绪
echo "[3/4] 采样确认关键话题"
for topic in \
  /camera/color/image_raw \
  /camera/depth/image_raw \
  /camera/color/camera_info
 do
  echo "检查话题: $topic"
  if ! wait_for_topic "$topic" "$TOPIC_WAIT_TIMEOUT_SEC"; then
    echo "[错误] 话题未就绪: $topic"
    echo "请确认相机终端是否正常启动,或增大 TOPIC_WAIT_TIMEOUT_SEC 后重试。"
    exit 1
  fi
  if ! wait_for_topic_publisher "$topic" "$TOPIC_WAIT_TIMEOUT_SEC"; then
    echo "[错误] 话题未就绪: $topic"
    echo "该话题存在但没有发布者,请检查相机是否真正完成初始化。"
    exit 1
  fi
 done

# 根据配置决定是否启用 IMU 融合
imu_launch_args="wait_imu_to_init:=false"
if [[ "$RTABMAP_USE_IMU" == "1" ]]; then
  echo "检查 IMU 话题"
  if ! detect_imu_topic "$TOPIC_WAIT_TIMEOUT_SEC"; then
    echo "[错误] 未找到可用 IMU 话题(尝试了 /imu/data, /camera/gyro_accel/sample, /camera/imu)。"
    exit 1
  fi
  echo "检测到 IMU 话题: $RTABMAP_IMU_TOPIC"
  imu_launch_args="imu_topic:=$RTABMAP_IMU_TOPIC wait_imu_to_init:=true"
else
  echo "跳过 IMU 融合(RTABMAP_USE_IMU=$RTABMAP_USE_IMU),使用纯视觉里程计以减少静止抖动。"
fi

# 步骤 4:启动 RTAB-Map 建图与 RViz
echo "[4/4] 启动 RTAB-Map(教学最小配置)"
launch_command "[learn] rtabmap" "
  ros2 launch rtabmap_launch rtabmap.launch.py \
    database_path:=$RTABMAP_DATABASE_PATH \
    rtabmap_viz:=false \
    rviz:=true \
    frame_id:=$RTABMAP_FRAME_ID \
    visual_odometry:=true \
    vo_frame_id:=odom \
    odom_topic:=/odom \
    publish_tf_map:=true \
    publish_tf_odom:=true \
    rgb_topic:=/camera/color/image_raw \
    depth_topic:=/camera/depth/image_raw \
    camera_info_topic:=/camera/color/camera_info \
    $imu_launch_args \
    approx_sync:=true \
    approx_sync_max_interval:=0.02 \
    odom_args:='--Vis/MinInliers 20 --OdomF2M/BundleAdjustment true --Odom/ResetCountdown 0' \
    args:='--Reg/Force3DoF true --RGBD/LinearUpdate $RTABMAP_LINEAR_UPDATE --RGBD/AngularUpdate $RTABMAP_ANGULAR_UPDATE'
"

echo
echo "学习目录已准备完成。"
echo "请打开检查清单: docs/learning/01-depth-camera-to-rtabmap/checklist.md"

source /opt/ros/humble/setup.bash

bash run_depth_to_rtabmap.sh