录制和回放数据
录制数据(创建bag文件)
这一部分将指导你如何从正在运行的ROS系统中记录话题数据。话题数据将被积累到一个袋(bag)文件中。
首先,在不同的终端中分别执行:
终端1:
roscore
终端2:
rosrun turtlesim turtlesim_node
终端3:
rosrun turtlesim turtle_teleop_key
这将启动两个节点——turtlesim可视化工具和一个能让你用键盘方向键控制turtlesim的节点。如果你选中了启动turtle_teleop_key的终端窗口,你应该会看到如下内容:
Reading from keyboard
---------------------------
Use arrow keys to move the turtle. 'q' to quit.
按键盘上的箭头键可以使乌龟在屏幕上移动。请注意,要移动乌龟,你必须选中启动turtle_teleop_key的终端窗口,而不是turtlesim。
录制所有发布的话题
首先让我们来检查一下当前系统中发布的所有话题。打开一个新终端:
rostopic list -v
输出类似于:
Published topics:
* /rosout_agg [rosgraph_msgs/Log] 1 publisher
* /rosout [rosgraph_msgs/Log] 2 publishers
* /turtle1/pose [turtlesim/Pose] 1 publisher
* /turtle1/color_sensor [turtlesim/Color] 1 publisher
* /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher
Subscribed topics:
* /rosout [rosgraph_msgs/Log] 1 subscriber
* /turtle1/cmd_vel [geometry_msgs/Twist] 1 subscriber
已发布主题的列表是唯一可能被记录在数据日志文件中的消息类型,因为只有发布的消息才能被录制。由teleop_turtle发布的/turtle1/cmd_vel话题是指令消息,作为turtlesim进程的输入。消息/turtle1/color_sensor和/turtle1/pose是turtlesim发布的输出消息。
现在我们将记录发布的数据。打开一个新终端:
mkdir ~/bagfiles
cd ~/bagfiles
rosbag record -a
这里我们只是创建了一个临时目录来记录数据,然后运行rosbag record带选项-a,表明所有发布的话题都应该积累在一个bag文件中。
然后回到turtle_teleop节点所在的终端窗口并控制乌龟随意移动10秒钟左右。
在运行rosbag record的窗口中按Ctrl+C以退出。现在查看~/bagfiles目录中的内容,你应该会看到一个以年份、日期和时间开头且扩展名是.bag的文件。这就是传说中的袋文件,它包含rosbag record运行期间由任何节点发布的所有话题。
检查并回放bag文件
现在我们已经使用rosbag record命令录制了一个bag文件,接下来我们可以使用rosbag info查看它的内容,或用rosbag play命令回放。首先我们来看看袋子里记录了什么。我们可以执行info命令检查bag文件的内容而不回放它。在bag文件所在的目录下执行以下命令:
rosbag info <your bagfile>
你会看到:
path: 2014-12-10-20-08-34.bag
version: 2.0
duration: 1:38s (98s)
start: Dec 10 2014 20:08:35.83 (1418270915.83)
end: Dec 10 2014 20:10:14.38 (1418271014.38)
size: 865.0 KB
messages: 12471
compression: none [1/1 chunks]
types: geometry_msgs/Twist [9f195f881246fdfa2798d1d3eebca84a]
rosgraph_msgs/Log [acffd30cd6b6de30f120938c17c593fb]
turtlesim/Color [353891e354491c51aabe32df673fb446]
turtlesim/Pose [863b248d5016ca62ea2e895ae5265cf9]
topics: /rosout 4 msgs : rosgraph_msgs/Log (2 connections)
/turtle1/cmd_vel 169 msgs : geometry_msgs/Twist
/turtle1/color_sensor 6149 msgs : turtlesim/Color
/turtle1/pose 6149 msgs : turtlesim/Pose
这些信息告诉你bag文件中所包含话题的名称、类型和消息数量。我们可以看到,在之前使用rostopic命令查看到的五个已公告的话题中,其实只有四个在我们录制期间发布了消息。因为我们带-a参数选项运行rosbag record命令时系统会录制下所有节点发布的所有消息。
下一步是回放bag文件以再现系统运行过程。首先用Ctrl+C杀死之前运行的turtle_teleop_key,但让turtlesim继续运行。在终端中bag文件所在目录下运行以下命令:
rosbag play <your bagfile>
在这个窗口中你应该会立即看到如下类似信息:
[ INFO] [1418271315.162885976]: Opening 2014-12-10-20-08-34.bag
Waiting 0.2 seconds after advertising topics... done.
Hit space to toggle paused, or 's' to step.
默认模式下,rosbag play命令在公告每条消息后会等待一小段时间(0.2秒)才真正开始发布bag文件中的内容。等待一段时间是为了可以通知订阅者,消息已经公告且数据可能会马上到来。如果rosbag play在公告消息后立即发布,订阅者可能会接收不到几条最先发布的消息。等待时间可以通过-d选项来指定。
最终/turtle1/cmd_vel话题将会被发布,同时在turtuelsim中乌龟应该会像之前用turtle_teleop_key控制它那样开始移动。从运行rosbag play到乌龟开始移动时所经历时间应该近似等于之前在本教程开始部分运行rosbag record后到开始按下键盘发出控制命令时所经历时间。你可以通过-s参数选项让rosbag play不从bag文件的开头开始,而是从某个指定的时间开始。最后一个可能比较有趣的参数选项是-r选项,它允许你通过设定一个参数来改变消息发布速率。如果执行:
rosbag play -r 2 <your bagfile>
你应该会看到乌龟的运动轨迹有点不同了,这时的轨迹应该是相当于当你以两倍的速度通过按键发布控制命令时产生的轨迹。
录制数据子集
当运行一个复杂的系统时,比如PR2软件套装,会有几百个话题被发布,有些话题亦会发布大量数据(比如包含摄像头图像流的话题)。在这种系统中,将包含所有话题的日志写入一个bag文件到磁盘通常是不切实际的。rosbag record命令支持只录制特定的话题到bag文件中,这样就可以只录制用户感兴趣的话题。
如果还有turtlesim节点在运行,先退出他们,然后重新启动键盘控制节点相关的启动文件:
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
在bag文件所在目录下执行以下命令:
rosbag record -O subset /turtle1/cmd_vel /turtle1/pose
上述命令中的-O参数告诉rosbag record将数据记录到名为subset.bag的文件中,而后面的topic参数告诉rosbag record只能订阅这两个指定的话题。然后通过键盘控制乌龟随意移动几秒钟,最后按Ctrl+C退出rosbag record命令。
现在看看bag文件中的内容(rosbag info subset.bag)。你应该会看到如下类似信息,里面只包含指定的话题:
path: subset.bag
version: 2.0
duration: 12.6s
start: Dec 10 2014 20:20:49.45 (1418271649.45)
end: Dec 10 2014 20:21:02.07 (1418271662.07)
size: 68.3 KB
messages: 813
compression: none [1/1 chunks]
types: geometry_msgs/Twist [9f195f881246fdfa2798d1d3eebca84a]
turtlesim/Pose [863b248d5016ca62ea2e895ae5265cf9]
topics: /turtle1/cmd_vel 23 msgs : geometry_msgs/Twist
/turtle1/pose 790 msgs : turtlesim/Pose
rosbag录制和回放的局限性
在上一小节中,你可能已经注意到了乌龟的路径可能并没有完全地映射到原先通过键盘控制时产生的路径上——整体形状应该是差不多的,但没有完全一样。这是因为turtlesim的移动路径对系统定时精度的变化非常敏感。rosbag受制于其本身的性能无法完全复制录制时的系统运行行为,rosplay也一样。对于像turtlesim这样的节点,当处理消息的过程中系统定时发生极小变化时也会使其行为发生微妙变化,用户不应该期望能够完美地模仿系统行为。
现在你已经学会了如何录制和回放数据
从bag文件中读取消息
首先,您需要一个袋(bag)文件。可以按照上一教程自己生成,或从https://webviz.io下载一个示例文件:
wget https://open-source-webviz-ui.s3.amazonaws.com/demo.bag
接下来的教程将用上面的示例文件进行演示。有两种方法从bag文件中回放或提取消息。
请注意,下面所有的命令中,前面都有一个time,这样做可以同时输出执行每个命令花费的时间,而且有时这些命令需要很长时间,因此使用time命令了解给定命令所需的时间是有必要的。如果您不想使用它,可以放心删除下面任何命令中的time。
方法1:立即回放消息并在多个终端中查看输出
- 你需要知道你想从bag文件中读取的准确话题名。那让我们看看袋子里有什么。在任何终端中用这个命令,来手动检查所有已发布的话题,以及向每个话题发布了多少消息:
- time rosbag info demo.bag
- # 或者你已经知道话题名称的话:
time rosbag info mybag.bag | grep -E "(topic1|topic2|topic3)"
你会看到:
$ time rosbag info demo.bag
path: demo.bag
version: 2.0
duration: 20.0s
start: Mar 21 2017 19:37:58.00 (1490150278.00)
end: Mar 21 2017 19:38:17.00 (1490150298.00)
size: 696.2 MB
messages: 5390
compression: none [600/600 chunks]
types: bond/Status [eacc84bf5d65b6777d4c50f463dfb9c8]
diagnostic_msgs/DiagnosticArray [60810da900de1dd6ddd437c3503511da]
diagnostic_msgs/DiagnosticStatus [d0ce08bc6e5ba34c7754f563a9cabaf1]
nav_msgs/Odometry [cd5e73d190d741a2f92e81eda573aca7]
radar_driver/RadarTracks [6a2de2f790cb8bb0e149d45d297462f8]
sensor_msgs/Image [060021388200f6f0f447d0fcd9c64743]
sensor_msgs/NavSatFix [2d3a8cd499b9b4a0249fb98fd05cfa48]
sensor_msgs/PointCloud2 [1158d486dd51d683ce2f1be655c3c181]
sensor_msgs/Range [c005c34273dc426c67a020a87bc24148]
sensor_msgs/TimeReference [fded64a0265108ba86c3d38fb11c0c16]
tf2_msgs/TFMessage [94810edda583a504dfda3829e70d7eec]
velodyne_msgs/VelodyneScan [50804fc9533a0e579e6322c04ae70566]
topics: /diagnostics 140 msgs : diagnostic_msgs/DiagnosticArray
/diagnostics_agg 40 msgs : diagnostic_msgs/DiagnosticArray
/diagnostics_toplevel_state 40 msgs : diagnostic_msgs/DiagnosticStatus
/gps/fix 146 msgs : sensor_msgs/NavSatFix
/gps/rtkfix 200 msgs : nav_msgs/Odometry
/gps/time 192 msgs : sensor_msgs/TimeReference
/image_raw 600 msgs : sensor_msgs/Image
/obs1/gps/fix 30 msgs : sensor_msgs/NavSatFix
/obs1/gps/rtkfix 200 msgs : nav_msgs/Odometry
/obs1/gps/time 136 msgs : sensor_msgs/TimeReference
/radar/points 400 msgs : sensor_msgs/PointCloud2
/radar/range 400 msgs : sensor_msgs/Range
/radar/tracks 400 msgs : radar_driver/RadarTracks
/tf 1986 msgs : tf2_msgs/TFMessage
/velodyne_nodelet_manager/bond 80 msgs : bond/Status
/velodyne_packets 200 msgs : velodyne_msgs/VelodyneScan
/velodyne_points 200 msgs : sensor_msgs/PointCloud2
real 0m1.003s
user 0m0.620s
sys 0m0.283s
可以看到,有30条消息发布在/obs1/gps/fix话题上,有40条消息发布在/diagnostics_agg话题上。让我们把这些提取出来。
- 在终端1(比如本终端)中,启动roscore,这样可以运行必需的ROS主节点:
roscore
- 打开另一个终端(试试按下Ctrl + Shift + T),订阅/obs1/gps/fix话题并复读该话题上发布的所有内容,同时用tee命令转储到一个yaml格式的文件中以便之后查看:
rostopic echo /obs1/gps/fix | tee topic1.yaml
你会看到:
$ rostopic echo /obs1/gps/fix | tee topic1.yaml
WARNING: topic [/obs1/gps/fix] does not appear to be published yet
- 再打开一个新终端,订阅另一个话题/diagnostics_agg。
rostopic echo /diagnostics_agg | tee topic2.yaml
你会看到:
$ rostopic echo /diagnostics_agg | tee topic2.yaml
WARNING: topic [/diagnostics_agg] does not appear to be published yet
- 对其他你感兴趣的话题重复这一步骤,每个话题必须有自己的终端。
- 再打开另一个新终端来回放bag文件。这一次我们将尽可能快地回放bag文件(使用--immediate选项),只会发布我们感兴趣的话题。格式如下:
time rosbag play --immediate demo.bag --topics /topic1 /topic2 /topic3 /topicN
本例中,命令如下:
time rosbag play --immediate demo.bag --topics /obs1/gps/fix /diagnostics_agg
你会看到:
$ time rosbag play --immediate demo.bag --topics /obs1/gps/fix /diagnostics_agg
[ INFO] [1591916465.758724557]: Opening demo.bag
Waiting 0.2 seconds after advertising topics... done.
Hit space to toggle paused, or 's' to step.
[RUNNING] Bag Time: 1490150297.770734 Duration: 19.703405 / 19.703405
Done.
real 0m1.570s
user 0m0.663s
sys 0m0.394s
- 完成!现在看一下你的两个终端,每个终端都订阅了一个话题,每个话题类型的所有消息用YAML格式输出,每条消息之间用---分割。用你喜欢的文本编辑器(最好支持YAML的语法高亮,例如Visual Studio Code)来查看文件中的消息。例如,topic1.yaml中的最后两条消息是这样的:
- ---
- header:
- seq: 4027
- stamp:
- secs: 1490150296
- nsecs: 66947432
- frame_id: "gps"
- status:
- status: 0
- service: 1
- latitude: 37.4008017844
- longitude: -122.108119889
- altitude: -6.4380177824
- position_covariance: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
- position_covariance_type: 0
- ---
- header:
- seq: 4028
- stamp:
- secs: 1490150297
- nsecs: 744347249
- frame_id: "gps"
- status:
- status: 0
- service: 1
- latitude: 37.4007565466
- longitude: -122.108159482
- altitude: -6.35130467023
- position_covariance: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
- position_covariance_type: 0
---
如果由于一些原因某个rostopic进程丢失了消息,可以使用Ctrl+C终止该进程,然后重新启动它,并再次调用rosbag play命令。
方法2:使用ros_readbagfile脚本轻松地提取感兴趣的话题
来源:这部分教程是根据本文件中首次发表的指引改编的,Python脚本来自:ros_readbag.py。
注意:您可以杀死任何正在运行的进程。比如说连roscore都不需要运行。
- 下载并安装`ros_readbag.py`:
- # Download the file
- wget https://raw.githubusercontent.com/ElectricRCAircraftGuy/eRCaGuy_dotfiles/master/useful_scripts/ros_readbagfile.py
- # Make it executable
- chmod +x ros_readbagfile.py
- # Ensure you have the ~/bin directory for personal binaries
- mkdir -p ~/bin
- # Move this executable script into that directory as `ros_readbagfile`, so that it will
- # be available as that command
- mv ros_readbagfile.py ~/bin/ros_readbagfile
- # Re-source your ~/.bashrc file to ensure ~/bin is in your PATH, so you can use this
- # new `ros_readbagfile` command you just installed
. ~/.bashrc
- 通过rosbag info命令确定要从bag文件中读取的准确话题名,如上面方法1的第一步所示。
- 然后使用ros_readbagfile,大体格式如下:
ros_readbagfile <mybagfile.bag> [topic1] [topic2] [topic3] [...]
要阅读上面方法1中显示的相同消息,请使用:
time ros_readbagfile demo.bag /obs1/gps/fix /diagnostics_agg | tee topics.yaml
就是这样!你会看到它快速打印出所有70条信息。以下是终端输出的最后部分:
key: "Early diagnostic update count:"
value: "0"
-
key: "Zero seen diagnostic update count:"
value: "0"
=======================================
topic: /obs1/gps/fix
msg #: 30
timestamp (sec): 1490150297.770734310
- - -
header:
seq: 4028
stamp:
secs: 1490150297
nsecs: 744347249
frame_id: "gps"
status:
status: 0
service: 1
latitude: 37.4007565466
longitude: -122.108159482
altitude: -6.35130467023
position_covariance: [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]
position_covariance_type: 0
=======================================
Total messages found = 70.
DONE.
real 0m2.897s
user 0m2.457s
sys 0m0.355s
现在用你喜欢的文本编辑器打开topics.yaml,看看它从bag文件中提取的所有消息。
请注意,尽管我给了这个文件一个“.yaml”扩展名,但并不代表所有部分都是正确的YAML格式。相反,尽管文件中存储的每条消息都是有效的YAML语法,但是消息之间的标题和行分隔符(例如=====)不是有效的。请记住这一点,避免试图将输出解析为YAML。如果你愿意,也可以很容易地修改ros_readbagfile这个Python脚本来删除这些非YAML特性。
为什么用ros_readbagfile而不是rostopic echo -b呢?
- 因为rostopic极其地慢! 举个例子,就算在高配计算机(4核8线程的奔腾i7和m.2固态硬盘)上运行这个命令,也需要11.5分钟才能读取一个18GB的bag文件!
time rostopic echo -b large_bag_file.bag /topic1
而用ros_readbagfile脚本,在相同计算机上只要花费1分钟37秒就能读取同样的话题和18GB的bag文件!因此ros_readbagfile比rostopic快了11.5/(1+37/60) = 大约7倍!
time ros_readbagfile large_bag_file.bag /topic1
- 因为rostopic一次只能读取单个话题,而ros_readbagfile可以同时读取任意多的话题!
ros_readbagfile <mybagfile.bag> [topic1] [topic2] [topic3] [...] [topic1000]
就酱。
现在,您已经了解了如何从预先录制的包文件中读取消息
roswtf入门
在开始本教程之前,请确保roscore没有运行。
对于Linux,您可以通过以下方式检查roscore是否在运行(如果看到类似这样的一行包含rosmaster,这是roscore的一部分,则说明roscore正在运行)
- $ ps -ef | grep -i rosmaster
- 00:00:00 /usr/bin/python /opt/ros/kinetic/bin/rosmaster
安装检查
roswtf可以检查你的系统并尝试发现问题,我们来试试看:
$ roscd rosmaster
$ roswtf
你应该会看到(各种详细的输出信息):
Package: rosmaster
================================================================================
Static checks summary:
No errors or warnings
================================================================================
ROS Master does not appear to be running.
Online graph checks will not be run.
ROS_MASTER_URI is [http://localhost:11311]
如果你的ROS安装没问题,应该会看到类似上面的输出信息,它的含义是:
- Package: rosmaster:roswtf使用当前目录中的任何内容来确定其执行的检查。这个输出告诉我们是在包rosmaster的目录中启动了roswtf。
- Static checks summary:它会报告任何关于文件系统或非运行时(比如无需roscore的场景)的问题。本例显示我们没有错误。
- ROS Master does not appear to be running.:roscore未在运行。roswtf不会做任何ROS在线检查。
在线检查
下一步,我们需要启动一个Master,所以要在新终端启动roscore,然后继续。
现在按照相同的顺序再次运行以下命令:
$ roscd
$ roswtf
你应该会看到:
No package or stack in context
======================================================
Static checks summary:
No errors or warnings
======================================================
Beginning tests of your ROS graph. These may take awhile...
analyzing graph...
... done analyzing graph
running graph rules...
... done running graph rules
Online checks summary:
Found 1 warning(s).
Warnings are things that may be just fine, but are sometimes at fault
WARNING The following node subscriptions are unconnected:
* /rosout:
* /rosout
roscore已经运行,roswtf刚刚做了一些ROS图的在线检查。根据您运行的ROS节点的数量,这可能需要很长时间才能完成。如你所见,这一次它产生了一个警告:
WARNING The following node subscriptions are unconnected:
* /rosout:
* /rosout
这一次,roscd在没有参数的情况下运行,这可能会带到一个没有ROS包的目录,因此我们看到了一条消息:No package or stack in context。
既然roscore已经运行了所以roswtf做了一些运行时检查。检查过程的长短取决于正在运行的ROS节点数量,可能会花费很长时间才能完成。正如你看到的,这一次出现了警告:
WARNING The following node subscriptions are unconnected:
* /rosout:
* /rosout
roswtf发出警告,rosout节点订阅了一个没有节点向其发布的话题。在本例中,这正是所期望看到的,因为除了roscore没有任何其它节点在运行,所以我们可以忽略该警告。
错误
roswtf会对一些系统中看起来异常但可能是正常的运行情况发出警告。也会对确实有问题的情况报告错误。
接下来我们给ROS_PACKAGE_PATH环境变量设置一个bad值,并退出roscore以简化检查输出信息。
$ roscd
$ ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf
这次我们会看到:
Stack: ros
================================================================================
Static checks summary:
Found 1 error(s).
ERROR Not all paths in ROS_PACKAGE_PATH [bad] point to an existing directory:
* bad
================================================================================
Cannot communicate with master, ignoring graph checks
正如你看到的,roswtf发现了一个有关ROS_PACKAGE_PATH设置的错误。
roswtf还可以发现很多其它类型的问题。如果你发现自己被构建或通信的问题难住了,可以尝试运行roswtf看能否为你指明正确的方向。
现在你已经知道如何使用roswtf了
错误
roswtf会对一些系统中看起来异常但可能是正常的运行情况发出警告。也会对确实有问题的情况报告错误。
接下来我们给ROS_PACKAGE_PATH环境变量设置一个bad值,并退出roscore以简化检查输出信息。
$ roscd
$ ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf
这次我们会看到:
Stack: ros
================================================================================
Static checks summary:
Found 1 error(s).
ERROR Not all paths in ROS_PACKAGE_PATH [bad] point to an existing directory:
* bad
================================================================================
Cannot communicate with master, ignoring graph checks
正如你看到的,roswtf发现了一个有关ROS_PACKAGE_PATH设置的错误。
roswtf还可以发现很多其它类型的问题。如果你发现自己被构建或通信的问题难住了,可以尝试运行roswtf看能否为你指明正确的方向。
现在你已经知道如何使用roswtf了
错误
roswtf会对一些系统中看起来异常但可能是正常的运行情况发出警告。也会对确实有问题的情况报告错误。
接下来我们给ROS_PACKAGE_PATH环境变量设置一个bad值,并退出roscore以简化检查输出信息。
$ roscd
$ ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf
这次我们会看到:
Stack: ros
================================================================================
Static checks summary:
Found 1 error(s).
ERROR Not all paths in ROS_PACKAGE_PATH [bad] point to an existing directory:
* bad
================================================================================
Cannot communicate with master, ignoring graph checks
正如你看到的,roswtf发现了一个有关ROS_PACKAGE_PATH设置的错误。
roswtf还可以发现很多其它类型的问题。如果你发现自己被构建或通信的问题难住了,可以尝试运行roswtf看能否为你指明正确的方向。
现在你已经知道如何使用roswtf了