ROS透過
rosbag play
命令進行bag包的播放,下面我們分析下ROS是如何進行bag包播放的。
目錄
rosbag play
的程式碼位於
ros_commtools\rosbag\src\player.cpp
中,實作的類主要為
Player
。
首先我們看下Player的建構函式,可以看到透過PlayerOptions選項來構造Player。
Player
::
Player
(
PlayerOptions
const
&
options
)
:
options_
(
options
),
// 選項
paused_
(
false
),
// If we were given a list of topics to pause on, then go into that mode
// by default (it can be toggled later via 't' from the keyboard).
pause_for_topics_
(
options_
.
pause_topics
.
size
()
>
0
),
pause_change_requested_
(
false
),
requested_pause_state_
(
false
),
terminal_modified_
(
false
)
{
// 訂閱暫停服務
ros
::
NodeHandle
private_node_handle
(
"~"
);
pause_service_
=
private_node_handle
.
advertiseService
(
"pause_playback"
,
&
Player
::
pauseCallback
,
this
);
}
Player的選項如下
PlayerOptions::PlayerOptions() :
prefix(""),
quiet(false),
start_paused(false),
at_once(false),
bag_time(false),
bag_time_frequency(0.0),
time_scale(1.0), // 時間尺度
queue_size(0), // 佇列大小
advertise_sleep(0.2),
try_future(false),
has_time(false),
loop(false), // 迴圈播放
time(0.0f),
has_duration(false),
duration(0.0f),
keep_alive(false),
wait_for_subscribers(false),
rate_control_topic(""),
rate_control_max_delay(1.0f),
skip_empty(ros::DURATION_MAX)
{
}
介面
Player的介面只有一個就是
publish
介面,由於函數較長,我們將分步驟分析。
讀取bag包
根據options中的bag包名稱,讀取bag包
// 遍歷輸入的所有bag
for
(
string
const
&
filename
:
options_
.
bags
)
{
ROS_INFO
(
"Opening %s"
,
filename
.
c_str
());
try
{
// 讀取bag包
shared_ptr
<
Bag
>
bag
(
boost
::
make_shared
<
Bag
>
());
bag
->
open
(
filename
,
bagmode
::
Read
);
bags_
.
push_back
(
bag
);
}
catch
(
const
BagUnindexedException
&
ex
)
{
std
::
cerr
<<
"Bag file "
<<
filename
<<
" is unindexed. Run rosbag reindex."
<<
std
::
endl
;
return
;
}
}
註冊釋出
尋找開始時間到結束時間內的訊息並且註冊釋出。
// Publish all messages in the bags
View
full_view
;
for
(
shared_ptr
<
Bag
>&
bag
:
bags_
)
full_view
.
addQuery
(
*
bag
);
const
auto
full_initial_time
=
full_view
.
getBeginTime
();
const
auto
initial_time
=
full_initial_time
+
ros
::
Duration
(
options_
.
time
);
ros
::
Time
finish_time
=
ros
::
TIME_MAX
;
if
(
options_
.
has_duration
)
{
finish_time
=
initial_time
+
ros
::
Duration
(
options_
.
duration
);
}
View
view
;
TopicQuery
topics
(
options_
.
topics
);
if
(
options_
.
topics
.
empty
())
{
for
(
shared_ptr
<
Bag
>&
bag
:
bags_
)
view
.
addQuery
(
*
bag
,
initial_time
,
finish_time
);
}
else
{
for
(
shared_ptr
<
Bag
>&
bag
:
bags_
)
view
.
addQuery
(
*
bag
,
topics
,
initial_time
,
finish_time
);
}
// 添加topic到publishers_
for
(
const
ConnectionInfo
*
c
:
view
.
getConnections
())
{
advertise
(
c
);
}
釋出時間和訊息
在迴圈中釋出時間和訊息。
while
(
true
)
{
// 釋出時間
time_translator_
.
setTimeScale
(
options_
.
time_scale
);
start_time_
=
view
.
begin
()
->
getTime
();
time_translator_
.
setRealStartTime
(
start_time_
);
bag_length_
=
view
.
getEndTime
()
-
view
.
getBeginTime
();
// Set the last rate control to now, so the program doesn't start delayed.
last_rate_control_
=
start_time_
;
time_publisher_
.
setTime
(
start_time_
);
ros
::
WallTime
now_wt
=
ros
::
WallTime
::
now
();
time_translator_
.
setTranslatedStartTime
(
ros
::
Time
(
now_wt
.
sec
,
now_wt
.
nsec
));
time_publisher_
.
setTimeScale
(
options_
.
time_scale
);
if
(
options_
.
bag_time
)
time_publisher_
.
setPublishFrequency
(
options_
.
bag_time_frequency
);
else
time_publisher_
.
setPublishFrequency
(
-
1.0
);
paused_time_
=
now_wt
;
// 釋出訊息
for
(
const
MessageInstance
&
m
:
view
)
{
if
(
!
node_handle_
.
ok
())
break
;
doPublish
(
m
);
}
// 是否一致保存命令列
if
(
options_
.
keep_alive
)
while
(
node_handle_
.
ok
())
doKeepAlive
();
// 是否迴圈
if
(
!
options_
.
loop
)
{
std
::
cout
<<
std
::
endl
<<
"Done."
<<
std
::
endl
;
break
;
}
}
釋出訊息
上一步中在迴圈所有的topic,透過doPublish釋出訊息。
while
((
paused_
||
delayed_
||
!
time_publisher_
.
horizonReached
())
&&
node_handle_
.
ok
())
{
bool
charsleftorpaused
=
true
;
while
(
charsleftorpaused
&&
node_handle_
.
ok
())
{
ros
::
spinOnce
();
if
(
pause_change_requested_
)
{
processPause
(
requested_pause_state_
,
horizon
);
pause_change_requested_
=
false
;
}
switch
(
readCharFromStdin
()){
// 1. 空格暫停
case
' '
:
processPause
(
!
paused_
,
horizon
);
break
;
// 2. 單步執行
case
's'
:
if
(
paused_
)
{
time_publisher_
.
stepClock
();
ros
::
WallDuration
shift
=
ros
::
WallTime
::
now
()
-
horizon
;
paused_time_
=
ros
::
WallTime
::
now
();
time_translator_
.
shift
(
ros
::
Duration
(
shift
.
sec
,
shift
.
nsec
));
horizon
+=
shift
;
time_publisher_
.
setWCHorizon
(
horizon
);
(
pub_iter
->
second
).
publish
(
m
);
printTime
();
return
;
}
break
;
case
't'
:
pause_for_topics_
=
!
pause_for_topics_
;
break
;
// 檔結尾,看是暫停還是延遲做時間補償
case
EOF
:
if
(
paused_
)
{
printTime
();
time_publisher_
.
runStalledClock
(
ros
::
WallDuration
(
.1
));
ros
::
spinOnce
();
}
else
if
(
delayed_
)
{
printTime
();
time_publisher_
.
runStalledClock
(
ros
::
WallDuration
(
.1
));
ros
::
spinOnce
();
// You need to check the rate here too.
if
(
rate_control_sub_
==
NULL
||
(
time_publisher_
.
getTime
()
-
last_rate_control_
).
toSec
()
<=
options_
.
rate_control_max_delay
)
{
delayed_
=
false
;
// Make sure time doesn't shift after leaving delay.
ros
::
WallDuration
shift
=
ros
::
WallTime
::
now
()
-
paused_time_
;
paused_time_
=
ros
::
WallTime
::
now
();
time_translator_
.
shift
(
ros
::
Duration
(
shift
.
sec
,
shift
.
nsec
));
horizon
+=
shift
;
time_publisher_
.
setWCHorizon
(
horizon
);
}
}
else
charsleftorpaused
=
false
;
}
}
printTime
();
time_publisher_
.
runClock
(
ros
::
WallDuration
(
.1
));
ros
::
spinOnce
();
}
總結
ROS中透過
play.cpp
呼叫
Player
類來讀取bag包,並且釋出其中的訊息。
ROS Bag分析
ROS 釋出訂閱模式
ROS導航模組