Weekly Robotics logo
Weekly Robotics Beginner-friendly tutorials, every week
Recording & Replaying Data with rosbag2
Software & AI

Recording & Replaying Data with rosbag2

Testing on live hardware every time you tweak a line of code is slow and sometimes dangerous. Learn rosbag2: how to record everything your robot's topics say, replay it on demand, and use it to debug and test without touching real hardware.

Say your perception node has a bug that only shows up when the robot drives past a particular reflective surface. To debug it today, you’d need the robot, that exact hallway, and however many attempts it takes to reproduce the glitch. That’s a lot of setup for one bug fix — and if the reflective surface is a rare stretch of hallway on the third floor, you might be doing a lot of walking.

rosbag2 solves this by letting you record everything your topics published once, then replay that exact sequence of messages as many times as you want — no robot, no hallway, no walking. This week: recording, inspecting, and replaying data, plus the storage-format choice that trips up almost everyone the first time.

What You’ll Learn

  • What a “bag” actually is and why it’s the backbone of offline debugging and regression testing
  • How to record topics with ros2 bag record, and the tradeoffs of recording everything versus a specific list
  • The two storage formats — mcap and sqlite3 — and which one you actually want
  • How to inspect a bag with ros2 bag info and replay it with ros2 bag play
  • The common pitfalls that turn a bag from a useful tool into a confusing one

What a Bag Actually Is

A bag is just a file (or small folder of files) containing a timestamped log of every message that crossed one or more topics during a recording session. Under the hood, ros2 bag record is a node like any other: it subscribes to the topics you tell it to, and instead of doing something with each message, it writes the message, its topic name, and its arrival timestamp to disk.

That simple idea unlocks a lot:

  • Debugging without hardware. Capture the exact sensor stream that triggered a bug once, then replay it on your laptop as many times as it takes to fix.
  • Regression testing. Keep a library of bags — “robot approaching a doorway,” “IMU during a hard turn” — and replay them against new code to check nothing broke.
  • Sharing data. Send a teammate a bag instead of asking them to reproduce a scenario on their own hardware.
  • Offline development. Build and test a node that consumes point cloud or SLAM data entirely from a recording, before the real sensor even arrives on your bench.
Live nodes bag file (.mcap) Any node ros2 bag record ros2 bag play
A bag file sits between "live now" and "replayed later" — recording turns real topics into a file, playback turns that file back into the same topics for any node to subscribe to.

To playing nodes, a bag is indistinguishable from the real thing: ros2 bag play republishes messages on the same topic names with the same types, so a node built to consume /scan or /camera/image_raw doesn’t know or care whether the data is coming from live hardware or a file on disk.

Recording Data

The basic command subscribes to whatever topics you name and writes them to a bag as they arrive:

# Record two specific topics
ros2 bag record /scan /odom

# Record every topic currently visible, and pick up new ones that appear mid-recording
ros2 bag record -a

# Give the output a specific name instead of the default timestamped one
ros2 bag record -o hallway_bug_repro /scan /odom /tf

Recording specific topics is almost always the better default. -a is convenient for “capture everything, sort it out later,” but on a robot with high-rate camera or point cloud topics it can fill a disk in minutes. Recording only what you actually need keeps bags small and fast to replay.

A bag is a directory, not a single file — it holds one or more data files plus a metadata.yaml describing what’s inside. Stop a recording with Ctrl-C; the metadata gets written out cleanly, so avoid killing the process with kill -9, which can leave a bag without valid metadata.

Choosing a Storage Format

ros2 bag record writes to one of two storage plugins, set with --storage:

mcap (default since ROS 2 Iron)sqlite3 (the original default)
Write throughputHigher — better for high-rate topics like camerasLower under heavy load
ToolingReadable by Foxglove and other non-ROS toolsROS-specific; needs ros2 bag or a SQLite client
File layoutOne .mcap file per bag (usually)One .db3 file per bag
When to reach for itDefault choice for new recordingsReading an older bag, or a workflow that specifically expects SQLite

Unless you have a reason to do otherwise, don’t pass --storage at all — the default is mcap, and it’s the right choice for nearly every recording you’ll make. You’ll mostly need --storage sqlite3 when you’re re-generating an old bag format for a tool that hasn’t caught up, or when opening a .db3 bag someone handed you from an older setup.

Inspecting a Bag

Before replaying anything, ros2 bag info tells you what’s actually in the file — topic names, message counts, types, and duration:

ros2 bag info hallway_bug_repro
Files:             hallway_bug_repro.mcap
Bag size:          48.2 MiB
Storage id:        mcap
Duration:          32.104s
Start:             Jul 20 2026 09:14:02.331
End:               Jul 20 2026 09:14:34.435
Messages:          9841
Topic information: Topic: /scan | Type: sensor_msgs/msg/LaserScan | Count: 320
                    Topic: /odom | Type: nav_msgs/msg/Odometry     | Count: 3201
                    Topic: /tf   | Type: tf2_msgs/msg/TFMessage    | Count: 6320

This is worth running out of habit before you play anything back — it catches the common mistake of recording the wrong topic name (a typo means an empty bag with zero messages on that topic, not an error).

Playing Data Back

ros2 bag play republishes the recorded messages on their original topics, in their original order and timing:

# Play back at recorded speed
ros2 bag play hallway_bug_repro

# Play back at half speed — useful for watching a fast event closely
ros2 bag play hallway_bug_repro -r 0.5

# Loop continuously, handy for a long-running visual check
ros2 bag play hallway_bug_repro -l

# Replay only a subset of topics
ros2 bag play hallway_bug_repro --topics /scan /tf

While it’s playing, any node subscribed to /scan, /odom, or /tf behaves exactly as it would with the real robot running — you can point RViz at it, feed it into a perception node, or drive it through a bug you’re chasing, all without hardware attached.

If the nodes consuming the bag expect simulation time rather than wall-clock time (for example, anything using use_sim_time, the same parameter from Week 27’s launch files), add --clock:

ros2 bag play hallway_bug_repro --clock 100

This makes ros2 bag play publish to /clock at 100 Hz while it plays, so every node with use_sim_time:=true — including lifecycle-managed stacks like Nav2 — stays in sync with the recorded timestamps instead of the wall clock.

Common Pitfalls

Reaching for -a on a bandwidth-heavy robot. A single 1080p camera topic can produce gigabytes per minute. Record specific topics unless you genuinely need everything, and check ros2 topic hz <topic> beforehand if you’re unsure how heavy a topic is.

Killing a recording with kill -9 or a hard power-off. The bag’s metadata.yaml is finalized when recording stops cleanly. An interrupted recording can leave a bag that ros2 bag info can’t fully read. Always stop with Ctrl-C and give it a moment to flush.

Forgetting --clock when downstream nodes expect simulation time. If a node was launched with use_sim_time:=true and nothing is publishing to /clock, that node’s internal clock never advances — timestamps stay frozen and anything time-dependent (like tf2 transform lookups) silently fails to find data. Pass --clock during playback whenever the consuming nodes use sim time.

Assuming playback rate doesn’t matter. Played back too fast (-r 2.0 or higher) on a slow subscriber, messages can arrive faster than the node can process them, and its input queue silently drops the overflow. If a playback-driven test behaves differently than the live robot did, try -r 1.0 or slower before assuming the bug is in your code.

Mixing up topics recorded vs. topics needed. ros2 bag play only republishes what was actually recorded. If your test node also needs /tf_static or a parameter that isn’t a topic at all, a bag alone won’t supply it — you may still need to launch a small supporting node or set static parameters manually alongside playback.

Recap

What you wantHow to do it
Record specific topicsros2 bag record /topic1 /topic2
Record everything currently visibleros2 bag record -a
Name the output bagros2 bag record -o <name> ...
See what’s inside a bagros2 bag info <bag>
Replay at recorded speedros2 bag play <bag>
Replay slower / fasterros2 bag play <bag> -r <rate>
Loop playbackros2 bag play <bag> -l
Keep sim-time nodes synced during playbackros2 bag play <bag> --clock <hz>

A bag turns a one-time, hard-to-reproduce moment on real hardware into a file you can replay a hundred times from your desk. Combined with the topics, services, and lifecycle nodes you’ve built over the last few weeks, it’s the tool that makes offline debugging and regression testing actually practical.

Next week is our regular news roundup: Dispatch #10, a look at what’s actually shipping, deploying, and getting funded across robotics this summer — and how it connects back to the ROS 2 tools you’ve been building with these last few weeks.