當前位置: 華文星空 > 知識

rosbag play代碼分析

2022-02-06知識

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分析

  • rosbag record代碼分析
  • rosbag play代碼分析
  • rosbag_storage總體介紹(一)
  • rosbag_storage bag實作(二)
  • rosbag_storage 查詢bag包(三)
  • rosbag_storage 檔讀寫(四)
  • ROS 釋出訂閱模式

  • ROS釋出訂閱實作(二)
  • ROS導航模組

  • ROS navigation導航模組(一)