Skip to main content

ROSBag Doctor

ROSBag Doctor

CI Python ROS 2 License

Find bad ROS 2 recordings before they become bad datasets. rosbag-doctor checks SQLite3 and MCAP bags for timing gaps, bad rates, timestamp problems, incomplete topic coverage, and configured sensor-sync limits. It works directly on bag files and does not require a ROS installation.

At a glance

rosbag-doctor my_recording/ --config doctor.yaml
ROSBag Doctor  FAIL
sqlite3 · 1 file(s) · 773 messages · 5.99 s

Topic               Messages      Rate   Max gap   p95 jitter   Coverage
/camera/image_raw         173   28.83 Hz  266.7 ms      0.0 ms      99.6%
/imu                      600  100.00 Hz   10.0 ms      0.0 ms     100.0%

Sensor sync
camera-imu  p95 offset 3.67 ms  max offset 3.67 ms

1 error, 0 warnings
✗ /camera/image_raw  Maximum gap 266.7 ms exceeds 100.0 ms

The example above comes from the checked-in demo recording. The camera stream deliberately loses several frames; the IMU remains healthy.

What it checks

Without a config file, ROSBag Doctor performs conservative checks that are useful on unknown recordings:

  • missing files referenced by metadata.yaml
  • metadata message-count mismatches
  • conflicting message types for the same topic
  • MCAP CRC failures when checksums are present
  • timestamp regressions
  • repeated timestamps
  • timestamp zeroes
  • unusually large gaps on otherwise regular streams
  • empty bags and topics

With a YAML policy, it can enforce:

  • required topics and topic globs
  • expected rate with tolerance
  • maximum message gap
  • p95 timing jitter
  • minimum recording coverage
  • minimum message count
  • maximum topic start delay or early stop
  • p95 and worst-case timestamp offset across sensor streams
  • minimum or maximum bag duration

Every check can be exported as JSON, and a failing policy returns exit code 1 for CI.

Supported inputs

  • ROS 2 SQLite3 bags (.db3 and .sqlite3)
  • MCAP files (.mcap)
  • split rosbag2 directories described by metadata.yaml
  • direct bag files when metadata.yaml is unavailable

ROSBag Doctor reads recording timestamps and topic metadata. It does not deserialize ROS messages, so it can inspect timing without having the message packages installed.

Install

From a clone:

git clone https://github.com/sylvesterkaczmarek/rosbag-doctor.git
cd rosbag-doctor
python -m pip install .

For development:

python -m pip install -e '.[dev]'
pytest

Check a bag

rosbag-doctor ./rosbag2_2026_08_09-00_15_42

Use a policy when the expected recording contract is known:

rosbag-doctor ./run-042 --config doctor.yaml

Write a JSON report:

rosbag-doctor ./run-042 --config doctor.yaml --json report.json

Print JSON to stdout:

rosbag-doctor ./run-042 --format json

Treat warnings as failures:

rosbag-doctor ./run-042 --strict

Exit codes:

Code Meaning
0 pass, or warnings without --strict
1 one or more health checks failed
2 bad input, unreadable bag, or invalid config

Define expected recording quality

version: 1

topics:
  /imu:
    required: true
    rate_hz: 200
    rate_tolerance: 0.05
    max_gap_ms: 20
    max_jitter_ms: 2
    min_coverage: 0.98

  /camera/*:
    required: true
    rate_hz: 30
    rate_tolerance: 0.10
    max_gap_ms: 100

sync:
  - name: camera-imu
    reference: /camera/image_raw
    topics: [/camera/image_raw, /imu]
    max_p95_offset_ms: 8
    max_offset_ms: 20

Exact topic rules take precedence over globs. See docs/configuration.md for all fields.

Make a baseline from a good run

A known-good bag can seed a policy:

rosbag-doctor baseline ./known-good-run -o doctor.yaml

The generated file marks observed topics as required and adds coverage limits. Rate and gap rules are inferred only for streams that look periodic, so event-driven topics are not automatically treated as fixed-rate sensors. Review the policy once, commit it with the robot software, then use it on later recordings.

Compare two runs

rosbag-doctor compare ./run-before-change ./run-after-change

This reports added and removed topics plus changes in effective rate and maximum gap. JSON output is also available:

rosbag-doctor compare ./before ./after --format json

Use it in CI

- name: Check recorded test data
  run: |
    rosbag-doctor artifacts/test-run \
      --config config/rosbag-doctor.yaml \
      --json rosbag-doctor-report.json

A failed recording returns a non-zero exit code, so a hardware-in-the-loop or simulation pipeline can stop before a broken bag is uploaded or used for evaluation.

See docs/ci.md for a complete example.

Demo without ROS

The repository includes a small generator that writes a rosbag2-compatible SQLite database with a deliberate camera dropout:

python examples/make_demo_bag.py .demo-bag
rosbag-doctor .demo-bag --config examples/doctor.yaml

The command should fail because the configured camera gap limit is exceeded.

How the numbers are calculated

For each topic, ROSBag Doctor keeps the recorded timestamp sequence and calculates:

  • effective rate as (message_count - 1) / recorded_duration
  • median period from positive consecutive timestamp differences
  • maximum gap from the largest positive consecutive difference
  • p95 jitter from absolute deviation around the median period
  • coverage as the topic time span divided by the bag time span
  • sensor offset using nearest timestamps to the configured reference stream

Timestamp regressions are checked in recorded order rather than hidden by sorting the bag first.

See docs/checks.md for definitions and issue codes.

Scope

ROSBag Doctor checks recording-container integrity and timeline health. It does not currently inspect message payloads, image corruption, ROS header stamps, TF graph connectivity, calibration correctness, or whether sensor values are physically plausible.

That distinction is intentional. The tool can run on a laptop or CI runner without ROS message packages and can catch recording failures before payload-specific analysis begins.

See docs/formats.md and docs/limitations.md.

Repository layout

rosbag-doctor/
├── .github/workflows/       # CI
├── assets/social/           # repository social card
├── docs/                    # checks, config, CI, formats and limitations
├── examples/                # demo bag generator and policy
├── src/rosbag_doctor/       # readers, statistics, checks and CLI
├── tests/                   # SQLite, MCAP, policy, baseline and CLI tests
├── CITATION.cff
├── LICENSE
├── Makefile
├── pyproject.toml
└── README.md

Cite this repository

If you use or adapt this repository, please cite:

Kaczmarek, S. (2026). ROSBag Doctor. GitHub. https://github.com/sylvesterkaczmarek/rosbag-doctor

@software{Kaczmarek_2026_ROSBag_Doctor,
  author = {Sylvester Kaczmarek},
  title  = {{ROSBag Doctor}},
  year   = {2026},
  url    = {https://github.com/sylvesterkaczmarek/rosbag-doctor}
}

License

MIT. See LICENSE.

© Sylvester Kaczmarek · https://www.sylvesterkaczmarek.com

Metadata

Release files for rosbag-doctor 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for rosbag-doctor 0.1.1
File Size Uploaded
rosbag_doctor-0.1.1.tar.gz 26.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for rosbag-doctor 0.1.1
File Interpreter ABI Platform
rosbag_doctor-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 49.4 kB

Release files / rosbag_doctor-0.1.1.tar.gz

Download URL rosbag_doctor-0.1.1.tar.gz
Size 26.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6fafb277e3ece83e46f74620e15662c8106f260e72cd9a618a91665d5297d760
BLAKE2b-256 checksum
How to use checksums
97439dfaee7a0e64b9246a7f860584432f5d9ccb98d5d22c823f5ad250843b28
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release files / rosbag_doctor-0.1.1-py3-none-any.whl

Download URL rosbag_doctor-0.1.1-py3-none-any.whl
Size 23.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3de55878524a43b1ce95813de00bf339652f2a7b8fb66dbaf64cf5fb50f8e7bd
BLAKE2b-256 checksum
How to use checksums
03e321cacc87719ab9be942602dc85d0ad0c330c522ba5ed0af7cfb2bbeefdd1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page