How to Compare Multiple Bag Files in PlotJuggler

One recording tells you what happened. Two recordings — before and after a fix, run A against run B — tell you whether anything actually improved. Comparing multiple bag files is one of the most searched PlotJuggler workflows, and it is also where the two generations differ most clearly: PlotJuggler 4 explicitly supports loading several recordings, aligning their timelines and overlaying their signals, while PJ3 offers the same goal through a more manual workflow built on its per-curve time offsets. This tutorial covers both, carefully labelled. Foundations: opening ROS bags and MCAP files and the Tutorials hub.

What a fair comparison requires

Overlaying two recordings is easy; overlaying them fairly is the skill. Three conditions decide whether the picture means anything:

  • The same signals. The same topics and fields from each recording, plotted the same way — ideally by reusing one layout for both, so nothing about the view changes except the data.
  • Aligned timelines. Recordings almost never start at the same instant. Until their starts are aligned on a shared reference event, every difference you see may be nothing but a time shift. Alignment uses time offsets — the whole subject of the time offset tutorial.
  • The same conditions, noted. Different firmware, a different course, a windy afternoon — write down what else changed, or the prettier curve will get credit it has not earned.

In PlotJuggler 4: several recordings, one timeline

PJ4 was designed for this job. Its documented workflow is to load several recordings, align their timelines, and overlay the signals, with a per-dataset time offset available for each loaded recording so you can shift an entire run earlier or later as one unit. The practical sequence:

  1. Load the first recording and build the view you want — the signals, the plots, the arrangement — following the bag and MCAP workflow.
  2. Load the second recording alongside it. Its signals join the workspace as their own dataset, distinguishable from the first.
  3. Plot the corresponding signals from both datasets on the same axes. Same topic, same field, two runs — the overlay is the comparison.
  4. Align before judging: pick an event that exists in both runs (a launch, a step input, a distinctive manoeuvre), measure how far apart it sits in the two datasets, and apply a per-dataset offset to the later-starting run so the events coincide. The method is worked through in the time offset tutorial.
  5. Save the result as a layout. PJ4 layouts (schema v4) store the viewport and the per-dataset offsets, so the aligned comparison reopens as you left it instead of needing re-alignment every session.

In PlotJuggler 3: the careful manual workflow

PJ3 predates the per-dataset model, so be precise about what it offers. PJ3 loads data files, plots their series, saves and reloads layouts, and — the key tool here — supports per-curve time offset / time-shift alignment, a long-standing PJ3 feature. A careful PJ3 comparison therefore goes like this: open the recordings, plot the matching curves, and use the per-curve time-shift controls to bring the shared reference event into alignment curve by curve. It reaches the same picture, but the alignment lives on individual curves rather than on a dataset as a whole, which has two consequences you should plan around: it takes longer as the number of compared signals grows, and it is easier to shift one curve of a run and forget another. Work systematically — align every compared curve against the same reference event, then verify with a second event elsewhere in the recording (see the checks in the time offset tutorial) — and save the aligned view as a layout immediately.

Reading the overlay without fooling yourself

  • Compare shapes first, peaks second. A run that reaches the same peak more smoothly is usually the better run; a single higher spike is often noise wearing a costume.
  • Zoom to the event, not the whole run. Differences that matter happen at transitions — starts, stops, disturbances. The shared timeline lets you inspect the same seconds in both runs.
  • Quantify one thing. Pick a number the overlay suggests (settling time, overshoot, steady error) and verify it with the curves — or with a derived series from a Lua custom function — before declaring a winner.
  • Distrust perfect alignment. If every event lines up to the sample after shifting, suspect that you aligned on the event you are now “discovering”. Check against an independent second event.

A worked example (illustrative)

A test team records a bag before and after a controller tuning change. They load both recordings, overlay the commanded and measured velocity from each, and find the “after” run apparently responding a full second sooner. Before celebrating, they align on the step input that exists in both bags — and discover the second recording simply started half a second later relative to the manoeuvre. After applying that offset, a real improvement remains, but it is a faster settle, not a magical one-second gain. The offset step is what separated the two conclusions; skip it and the comparison would have published the wrong one. (This example is illustrative — the workflow it demonstrates is the documented one above.)

When the recordings are not bags but repeated CSV exports, the same workflow applies unchanged — see CSV import. And when the runs are still being produced, live streaming plus saved layouts gives you the comparison view in real time. The install routes for both lines are in the Install guides, the files themselves are on the Download page, and everything in the series is indexed on the Tutorials hub.

Similar Posts