回放时最关键的概念是仿真时钟。当 --clock 选项启用时,ros2bag会发布 /clock 话题,所有使用 use_sim_time:=true 的节点将使用这个时钟而非系统时钟。
# 回放时发布时钟
ros2 bag play my_recording --clock
# 节点使用仿真时钟
ros2 run my_pkg my_node --ros-args -p use_sim_time:=true
# 在Launch中设置
Node(
package='my_pkg',
executable='my_node',
parameters=[{'use_sim_time': True}]
)
use_sim_time:=true,否则时间戳不匹配会导致数据丢失或行为异常。特别是TF和导航节点。
# 回放时重映射话题
ros2 bag play my_recording \
--remap /scan:=/robot1/scan \
--remap /odom:=/robot1/odom
# 用于多机器人回放对比
# 只回放特定话题
ros2 bag play my_recording --topics /scan /odom
# 排除话题
ros2 bag play my_recording --exclude-topics /camera/image_raw
# 从第10秒开始回放
ros2 bag play my_recording --start-offset 10
# 只回放30秒
ros2 bag play my_recording --duration 30
# 从第10秒开始回放30秒
ros2 bag play my_recording --start-offset 10 --duration 30
# 加速回放
ros2 bag play my_recording --rate 2.0
# 慢放(调试用)
ros2 bag play my_recording --rate 0.25
# 暂停/恢复(需要信号处理)
# kill -SIGUSR1 <pid> # 暂停
# kill -SIGUSR2 <pid> # 恢复
#!/usr/bin/env python3
"""自定义bag回放控制器 - 支持暂停/步进"""
import rclpy
from rclpy.node import Node
from rosbag2_py import SequentialReader, StorageOptions, ConverterOptions
from std_msgs.msg import String
import threading
class BagReplayController(Node):
"""带控制功能的bag回放器"""
def __init__(self):
super().__init__('bag_replay_controller')
self.declare_parameter('bag_path', '')
self.declare_parameter('topics', [''])
self.declare_parameter('rate', 1.0)
self.is_paused = False
self.step_mode = False
# 订阅控制命令
self.cmd_sub = self.create_subscription(
String, '/replay_cmd', self.cmd_callback, 10)
self.get_logger().info('回放控制器就绪')
self.get_logger().info(' 命令: pause / resume / step / stop')
def cmd_callback(self, msg):
"""处理回放控制命令"""
cmd = msg.data.lower()
if cmd == 'pause':
self.is_paused = True
self.get_logger().info('⏸️ 回放已暂停')
elif cmd == 'resume':
self.is_paused = False
self.step_mode = False
self.get_logger().info('▶️ 回放已恢复')
elif cmd == 'step':
self.step_mode = True
self.is_paused = False
self.get_logger().info('⏭️ 单步模式')
elif cmd == 'stop':
self.get_logger().info('⏹️ 回放停止')
def main(args=None):
rclpy.init(args=args)
node = BagReplayController()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
# 转换存储格式
ros2 bag convert -i my_recording -o converted \
--storage-id sqlite3
# 合并多个bag
ros2 bag convert -i bag1 bag2 -o merged_bag
录制激光和里程计数据,回放时运行SLAM节点,对比不同SLAM参数的建图效果。
以0.25x, 0.5x, 1x, 2x, 4x速率回放同一段数据,观察导航算法在不同速率下的鲁棒性。
录制一个机器人的数据,回放时重映射到两个不同命名空间,模拟两个机器人的数据流。
经验值:+150 XP