ROS合集(三)RTAB-Map + EuRoC 数据格式概述
文章目录
- 一、加载现有bag文件
- 二、查看消息格式
- 1、命令行查看
- 2、直接去 .msg 文件查看
- 3、ROS Wiki / GitHub
- 三、Odometry、IMU、TF
- 1. 里程计(Odometry)
- 2. IMU(惯性测量单元)
- 3. TF(坐标变换)
- 四、查看.launch文件
- 五、相关文献
一、加载现有bag文件
cd ~/Desktop/Bag
rosbag info MH_01_easy.bag
path: MH_01_easy.bag
version: 2.0
duration: 3:06s (186s)
start: Jun 24 2014 12:02:59.81 (1403636579.81)
end: Jun 24 2014 12:06:06.70 (1403636766.70)
size: 2.5 GB
messages: 47283
compression: none [2456/2456 chunks]
types: geometry_msgs/PointStamped [c63aecb41bfdfd6b7e1fac37c7cbe7bf]
sensor_msgs/Image [060021388200f6f0f447d0fcd9c64743]
sensor_msgs/Imu [6a62c6daae103f4ff57a132d6f95cec2]
topics: /cam0/image_raw 3682 msgs : sensor_msgs/Image
/cam1/image_raw 3682 msgs : sensor_msgs/Image
/imu0 36820 msgs : sensor_msgs/Imu
/leica/position 3099 msgs : geometry_msgs/PointStamped
可以从bag文件的info信息看到,包含如下内容
- 双目(立体)SLAM : /cam0/image_raw 和 /cam1/image_raw
- IMU数据:/imu0
- 精确定位信息: /leica/position https://projects.asl.ethz.ch/datasets/doku.php?id=kmavvisualinertialdatasets
需要注意的是这个/leica/position
的话题并不是RTAB-Map 建图算法本身所必需,它往往是一个高精度测量仪器(Leica)提供的地面真实位置(Ground Truth),用于事后分析误差、对比 SLAM 轨迹和真实轨迹。
根据RTAB-Map的论文中我们可以知道,对于输入来说,需要如下内容
- 单目或者双目相机
- TF
- Odometry
- Laser Scan 或者 Point Cloud (可选)
相较于rosbag info MH_01_easy.bag
的信息,似乎仅仅提供了双目相机和IMU的内容
- /cam0/image_raw
- /cam1/image_raw
- /imu0
缺少关键的TF和Odometry的信息
在ROS合集(二)源码构建 RTAB-Map + EuRoC的基础上使用下述命令,可视化 ROS 节点与话题连接关系的图形工具,直观展示系统拓扑结构。
rqt_graph
可以发现tf话题是存在的
Odometry话题也是存在的
具体原因涉及到详细的代码讲解,请查看后续文章,本文仅仅包含概述部分
二、查看消息格式
1、命令行查看
在有 ROS 环境(且包含对应消息包)的终端下,你可以用以下命令查看任何标准消息的内部字段
# 查看话题类型
rostopic type /imu0
# 显示具体消息结构
rosmsg show sensor_msgs/Imu
zhanyong@ubuntu:~/euroc_rtabmap_ws$ rostopic type /imu0
sensor_msgs/Imu
zhanyong@ubuntu:~/euroc_rtabmap_ws$ rosmsg show sensor_msgs/Imu
std_msgs/Header header
uint32 seq
time stamp
string frame_id
geometry_msgs/Quaternion orientation
float64 x
float64 y
float64 z
float64 w
float64[9] orientation_covariance
geometry_msgs/Vector3 angular_velocity
float64 x
float64 y
float64 z
float64[9] angular_velocity_covariance
geometry_msgs/Vector3 linear_acceleration
float64 x
float64 y
float64 z
float64[9] linear_acceleration_covariance
同理,对 sensor_msgs/Image
、geometry_msgs/PointStamped
也可以做
rostopic type /cam0/image_raw
rosmsg show sensor_msgs/Image
2、直接去 .msg 文件查看
标准消息(sensor_msgs、geometry_msgs、std_msgs)都在官方 ros_common_msgs
仓库里,例如 sensor_msgs/Image
定义在:只要安装了相应包 sudo apt-get install ros-<distro>-sensor-msgs
,这个 .msg 就会在本地 /opt/ros/<distro>/share/sensor_msgs/msg
里。
查询命令如下
rospack find sensor_msgs
# /opt/ros/noetic/share/sensor_msgs
zhanyong@ubuntu:~/Desktop$ rospack find sensor_msgs
/opt/ros/noetic/share/sensor_msgs
zhanyong@ubuntu:~/Desktop$ cd /opt/ros/noetic/share/sensor_msgs
zhanyong@ubuntu:/opt/ros/noetic/share/sensor_msgs$ ll
total 36
drwxr-xr-x 6 root root 4096 Mar 13 20:52 ./
drwxr-xr-x 307 root root 12288 Mar 16 06:37 ../
drwxr-xr-x 2 root root 4096 Mar 13 20:52 cmake/
drwxr-xr-x 2 root root 4096 Mar 13 20:52 migration_rules/
drwxr-xr-x 2 root root 4096 Mar 13 20:52 msg/
-rw-r--r-- 1 root root 1287 Jan 11 2021 package.xml
drwxr-xr-x 2 root root 4096 Mar 13 20:52 srv/
zhanyong@ubuntu:/opt/ros/noetic/share/sensor_msgs$
sensor_msgs/msg/Image.msg
如果仅仅希望了解结构,不希望安装ROS,也可以git ros_common进行查看
git clone https://github.com/ros/common_msgs.git
3、ROS Wiki / GitHub
- https://github.com/ros/common_msgs
- http://wiki.ros.org/common_msgs
其中具体的结构内容,会在后续的文章中进行解析
三、Odometry、IMU、TF
在 ROS 及常见机器人/SLAM 应用中,里程计(Odometry)、IMU(惯性测量单元) 和 TF(坐标变换) 都扮演不同的角色,彼此各有区别又紧密关联。
1. 里程计(Odometry)
- 含义:
里程计(Odometry)通常指机器人(或传感器载体)的 运动估计,包括位姿(位置+朝向)以及可选的速度、加速度等信息。在 ROS 中,常以nav_msgs/Odometry
消息或类似格式发布。 - 来源:
- 轮式机器人:里程计直接来源于编码器(轮子转过的脉冲、角度)。
- 视觉里程计 (VO):利用相机(单目/双目/RGB-D)图像特征跟踪来计算相对运动。
- 视觉-惯性里程计 (VIO):结合 IMU 数据和相机数据做更精确或更鲁棒的运动估计。
- 其它组合:如激光雷达里程计、UWB、GPS 等。
- 用途:
- 给出一个连续的、随时间更新的位姿估计,用于导航、SLAM、运动控制等。
- 在 SLAM 里,里程计可用作增量运动,再辅以回环检测/全局优化以减少漂移。
2. IMU(惯性测量单元)
- 含义:
IMU(Inertial Measurement Unit)包含加速度计和陀螺仪,测量 线加速度 与 角速度。在 ROS 中通常以sensor_msgs/Imu
消息格式发布。 - 性质:
- 原始传感器数据:IMU 提供的原始加速度和角速度往往噪声较大、易漂移,但高频率、动态范围大。
- 不能直接提供位置:仅靠原始 IMU 数据无法长时间直接积分得到稳定位置,因为累积误差严重,需要和其它传感器(相机 / 轮式编码器 / GPS / 激光雷达等)联合使用才能得到可靠的位姿估计。
- 用途:
- 在 VIO (Visual-Inertial Odometry) 中,与相机数据融合以改进瞬时运动估计的精度和鲁棒性。
- 在飞行器/无人机上,IMU 是姿态控制必备的核心传感器。
- 在一些定位/控制算法中,可以用 IMU 的角速度、线加速度做高频补偿或预测。
3. TF(坐标变换)
- 含义:
在 ROS 里,TF(Transform)并不是一个传感器或估计结果,而是一个坐标系管理与变换广播的工具包(tf/tf2 库)。它在 ROS 系统里用来发布“从某个坐标系到另一个坐标系”的变换关系,包括平移和旋转。 - 形式:
- 每个坐标系(frame)用一个字符串命名,如
map
、odom
、base_link
、camera_link
等。 - TF 库会记录这些坐标系之间随时间变化的变换(translation + rotation),可以通过
tf_listener
查询指定时间点的变换,用于数据对齐、可视化、传感器融合等。
- 每个坐标系(frame)用一个字符串命名,如
- 用途:
- 如果你有里程计消息,通常会同时发布一条 TF(例如
odom -> base_link
),让系统知道:此时刻机器人在odom
坐标系下位于哪里、朝向如何。 - 当系统中有多个传感器(相机、IMU、激光雷达等),通过 TF 可以指定它们各自相对于机器人基座(base_link)的安装位姿(外参)。
- RViz、MoveIt 等工具依赖 TF 做数据可视化或运动规划。
- 如果你有里程计消息,通常会同时发布一条 TF(例如
对比点 | 里程计(Odometry) | IMU(Inertial Measurement Unit) | TF(坐标变换) |
---|---|---|---|
本质 | 位姿 / 运动估计(nav_msgs/Odometry ) | 原始或融合后的加速度、角速度测量(sensor_msgs/Imu ) | 坐标系间的变换信息(由 tf2 框架管理和广播) |
数据来源 | 轮式编码器 / 视觉VO / IMU融合 / GPS 等 | 加速度计 + 陀螺仪(可能含磁罗盘) | 通常由节点根据已知外参或在线估计发布变换(如 odom->base_link ) |
功能作用 | 提供当前姿态(位置+朝向)及速度,用于定位/导航 | 提供高频姿态变化信息,用于滤波或里程计融合 | 为多个坐标系间的运算、可视化、数据对齐提供统一接口 |
发布频率 | 典型 10~30 Hz(或更高) | 典型 100~200 Hz(甚至可达 1 kHz) | 根据系统需求和可用数据:可能 10~100+ Hz |
是否直接给位置 | 是,里程计本身就是位姿估计 | 否,IMU 原始值不包含绝对位置,需要整合/融合 | TF 不直接给位置,而是描述坐标系转换关系 |
在 SLAM 中的作用 | 提供基准运动估计,SLAM 再做全局优化 | 辅助视觉/激光,增强姿态估计的可靠性、高频特性 | 统一发布各传感器、地图、机器人底盘等坐标系的相对姿态 |
- Odometry:最终姿态信息,可直接在 RViz 看到机器人移动。
- IMU:加速度 / 角速度的原始测量,需要与里程计或视觉等融合。
- TF:提供坐标变换的框架,不是直接传感器数据或姿态估计,而是管理“世界->机器人->传感器”各坐标系随时间的变换,方便系统内各模块对齐数据。
简单来说:
- IMU 是一个硬件传感器;
- Odometry 是基于传感器或算法推导出来的运动估计;
- TF 是 ROS 中处理各坐标系的软件工具。
四、查看.launch文件
以rtabmap_examples\launch\euroc_datasets.launch为例
git clone https://github.com/introlab/rtabmap_ros.git
具体的参数描述参照注释
<?xml version="1.0"?>
<launch>
<!-- daetz annotation version-->
<!--
Examples:
F2M (default VO):
$ roslaunch rtabmap_examples euroc_datasets.launch
$ rosbag play -.-clock V1_01_easy.bag
For MH sequences, we should set MH_seq to true because the ground truth source is different.
$ roslaunch rtabmap_examples euroc_datasets.launch MH_seq:=true
$ rosbag play -.-clock MH_01_easy.bag
MSCKF (VIO):
$ roslaunch rtabmap_examples euroc_datasets.launch args:="Odom/Strategy 8"
$ rosbag play -.-clock V1_01_easy.bag
We need to ignore the first 24 seconds for correct VIO initialization (drone should not move).
$ roslaunch rtabmap_examples euroc_datasets.launch args:="Odom/Strategy 8" MH_seq:=true
$ rosbag play -.-clock -s 24 MH_01_easy.bag
OKVIS (VIO):
$ roslaunch rtabmap_examples euroc_datasets.launch args:="Odom/Strategy 6 OdomOKVIS/ConfigPath ~/okvis/config/config_fpga_p2_euroc.yaml" MH_seq:=true raw_images_for_odom:=true
$ rosbag play -.-clock MH_01_easy.bag
VINS (VIO):
$ roslaunch rtabmap_examples euroc_datasets.launch args:="Odom/Strategy 9 OdomVINS/ConfigPath ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_imu_config.yaml" MH_seq:=true raw_images_for_odom:=true
$ rosbag play -.-clock MH_01_easy.bag
VINS (VO):
$ roslaunch rtabmap_examples euroc_datasets.launch args:="Odom/Strategy 9 OdomVINS/ConfigPath ~/catkin_ws/src/VINS-Fusion/config/euroc/euroc_stereo_config.yaml" MH_seq:=true raw_images_for_odom:=true
$ rosbag play -.-clock MH_01_easy.bag
-->
<param name="use_sim_time" value="true"/>
<!--
param 表示参数,name表示参数名,value表示参数值,用于设置全局参数,这里设置为true,表示使用仿真时间
-->
<arg name="feature_type" default="6"/>
<arg name="gravity_opt" default="false"/> <!-- Rtabmap will use IMU data to add gravity constraints to graph -->
<!--
arg 表示参数,name表示参数名,default表示默认值
定义或传递可在启动命令行动态指定的参数,可以在 <node>、<include> 或者 Launch 文件中的 if, unless 等地方引用
也可通过命令行传入参数,如 roslaunch rtabmap_examples euroc_datasets.launch MH_seq:=true
-->
<arg name="args" default=""/>
<arg if="$(arg gravity_opt)" name="common_args" default="-d --RGBD/CreateOccupancyGrid false --Odom/FeatureType $(arg feature_type) --Kp/DetectorStrategy $(arg feature_type) --Optimizer/GravitySigma 0.3 $(arg args)"/>
<arg unless="$(arg gravity_opt)" name="common_args" default="-d --RGBD/CreateOccupancyGrid false --Odom/FeatureType $(arg feature_type) --Kp/DetectorStrategy $(arg feature_type) $(arg args) "/>
<!-- if 表示仅仅在参数为真时才会执行,unless 表示仅仅在参数为假时才会执行 -->
<arg name="cfg" default=""/>
<arg name="MH_seq" default="false"/> <!-- For MH sequences, the ground truth is coming from a different topic -->
<arg name="raw_images_for_odom" default="false"/>
<arg name="record_ground_truth" default="false"/>
<arg name="rtabmap_viz" default="true"/>
<arg name="rviz" default="false"/>
<!-- Image rectification and publishing synchronized camera_info-->
<group ns="stereo_camera">
<!-- group 表示将若干节点或配置进行分组,并可以指定命名空间 (ns) 等。这样,组内节点的话题等会自动带有 /stereo_camera 前缀,可使相关节点更加有序。 -->
<node pkg="rtabmap_util" type="yaml_to_camera_info.py" name="yaml_to_camera_info_left">
<param name="yaml_path" value="$(find rtabmap_examples)/launch/config/euroc_left.yaml"/>
<remap from="image" to="/cam0/image_raw"/>
<remap from="camera_info" to="left/camera_info"/>
</node>
<!--
node 表示启动某个可执行文件(ROS 节点)
pkg 表示节点所在的包名
type 表示节点的可执行文件名,指定该包中的哪个可执行文件(或 Python 脚本名)。
name 表示节点的名称(给该节点起一个唯一的名字)
param 表示节点的参数,相较于全局参数,这里是该节点的参数
remap 将某个节点里用到的话题名进行重映射。可以把节点原本订阅或发布的某个话题改名,以适配实际使用环境。
-->
<node pkg="rtabmap_util" type="yaml_to_camera_info.py" name="yaml_to_camera_info_right">
<param name="yaml_path" value="$(find rtabmap_examples)/launch/config/euroc_right.yaml"/>
<param name="frame_id" value="cam1"/>
<remap from="image" to="/cam1/image_raw"/>
<remap from="camera_info" to="right/camera_info"/>
</node>
<node unless="$(arg raw_images_for_odom)" pkg="stereo_image_proc" type="stereo_image_proc" name="stereo_image_proc">
<remap from="left/image_raw" to="/cam0/image_raw"/>
<remap from="right/image_raw" to="/cam1/image_raw"/>
</node>
</group>
<!-- TF frames -->
<node pkg="tf" type="static_transform_publisher" name="imu_base_link" args="0 0 0 3.1415926 -1.570796 0 base_link imu4 100">
<remap from="tf" to="tf_static"/>
</node>
<!-- TF 一般是通过系统安装,放在全局路径下,所以在自己的工作环境下不会看到tf文件夹-->
<node pkg="tf" type="static_transform_publisher" name="cam0_imu_link" args="-0.021640 -0.064677 0.009811 1.555925 0.025777 0.003757 imu4 cam0 100">
<remap from="tf" to="tf_static"/>
</node>
<node pkg="tf" type="static_transform_publisher" name="cam1_imu_link" args="-0.019844 0.045369 0.007862 1.558237 0.025393 0.017907 imu4 cam1 100">
<remap from="tf" to="tf_static"/>
</node>
<!-- For MH sequences, /leica/position doesn't give the orientation, so minimal ground truth error could be as high as 12 cm -->
<node if="$(arg MH_seq)" pkg="tf" type="static_transform_publisher" name="leica_base_link" args="0.120209 -0.0184772 -0.0748903 0 0 0 leica base_link_gt 100">
<remap from="tf" to="tf_static"/>
</node>
<node unless="$(arg MH_seq)" pkg="tf" type="static_transform_publisher" name="vicon_base_link" args="0.12395 -0.02781 -0.06901 0 0 0 vicon/firefly_sbx/firefly_sbx base_link_gt 100">
<remap from="tf" to="tf_static"/>
</node>
<node if="$(arg MH_seq)" pkg="rtabmap_util" type="point_to_tf.py" name="point_to_tf">
<remap from="point" to="/leica/position"/>
<param name="frame_id" value="leica"/>
<param name="fixed_frame_id" value="world"/>
</node>
<node unless="$(arg MH_seq)" pkg="rtabmap_util" type="transform_to_tf.py" name="transform_to_tf">
<remap from="transform" to="/vicon/firefly_sbx/firefly_sbx"/>
<param name="frame_id" value="world"/>
<param name="child_frame_id" value="vicon/firefly_sbx/firefly_sbx"/>
</node>
<node pkg="tf" type="static_transform_publisher" name="world_to_map" args="0.0 0.0 0.0 0.0 0.0 0.0 /world /map 100">
<remap from="tf" to="tf_static"/>
</node>
<node pkg="imu_complementary_filter" type="complementary_filter_node" name="imu_filter" output="screen">
<remap from="imu/data_raw" to="/imu0"/>
<param name="use_mag" value="false"/>
<param name="world_frame" value="enu"/>
<param name="publish_tf" value="false"/>
</node>
<!-- imu_complementary_filter 是一个 imu 数据融合的包,complementary_filter_node 是一个节点,imu_filter 是节点的名字,output="screen" 表示将节点的输出打印到屏幕上 -->
<!-- RTAB-Map -->
<include file="$(find rtabmap_launch)/launch/rtabmap.launch">
<!-- 在一个 Launch 文件中嵌套或导入另一个 Launch 文件,并传递参数给被包含的文件。-->
<arg if="$(arg raw_images_for_odom)" name="rtabmap_args" value="$(arg common_args) --Rtabmap/ImagesAlreadyRectified false"/>
<arg unless="$(arg raw_images_for_odom)" name="rtabmap_args" value="$(arg common_args)"/>
<arg if="$(arg raw_images_for_odom)" name="odom_args" value="--Rtabmap/ImagesAlreadyRectified false"/>
<arg if="$(arg raw_images_for_odom)" name="left_image_topic" value="/cam0/image_raw"/>
<arg unless="$(arg raw_images_for_odom)" name="left_image_topic" value="/stereo_camera/left/image_rect"/>
<arg if="$(arg raw_images_for_odom)" name="right_image_topic" value="/cam1/image_raw"/>
<arg name="stereo" value="true"/>
<arg name="frame_id" value="base_link"/>
<arg name="wait_for_transform" value="0.1"/>
<arg if="$(arg record_ground_truth)" name="ground_truth_frame_id" value="world"/>
<arg if="$(arg record_ground_truth)" name="ground_truth_base_frame_id" value="base_link_gt"/>
<arg name="cfg" value="$(arg cfg)"/>
<arg name="imu_topic" value="/imu/data"/>
<arg name="rtabmap_viz" value="$(arg rtabmap_viz)"/>
<arg name="rviz" value="$(arg rviz)"/>
<arg name="wait_imu_to_init" value="true"/>
</include>
</launch>
launch详细介绍会在后续的文章中进行解析
五、相关文献
-
kmavvisualinertialdatasets – ASL Datasets (ethz.ch)
-
GitHub - introlab/rtabmap_ros: RTAB-Map’s ROS package.
-
Index of /en/noetic/api (ros.org)
-
GitHub - ros/common_msgs
-
Common_msgs - ROS Wiki