ROSBag Doctor
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 (
.db3and.sqlite3) - MCAP files (
.mcap) - split rosbag2 directories described by
metadata.yaml - direct bag files when
metadata.yamlis 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
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rosbag_doctor-0.1.1.tar.gz.
File metadata
- Download URL: rosbag_doctor-0.1.1.tar.gz
- Upload date:
- Size: 26.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fafb277e3ece83e46f74620e15662c8106f260e72cd9a618a91665d5297d760
|
|
| MD5 |
5690929a44567106d9a730fb77ca35d3
|
|
| BLAKE2b-256 |
97439dfaee7a0e64b9246a7f860584432f5d9ccb98d5d22c823f5ad250843b28
|
Provenance
The following attestation bundles were made for rosbag_doctor-0.1.1.tar.gz:
Publisher:
release.yml on sylvesterkaczmarek/rosbag-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rosbag_doctor-0.1.1.tar.gz -
Subject digest:
6fafb277e3ece83e46f74620e15662c8106f260e72cd9a618a91665d5297d760 - Sigstore transparency entry: 2390798203
- Sigstore integration time:
-
Permalink:
sylvesterkaczmarek/rosbag-doctor@113e92fd4c6ed5976486c13158637025102e8e70 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sylvesterkaczmarek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@113e92fd4c6ed5976486c13158637025102e8e70 -
Trigger Event:
push
-
Statement type:
File details
Details for the file rosbag_doctor-0.1.1-py3-none-any.whl.
File metadata
- Download URL: rosbag_doctor-0.1.1-py3-none-any.whl
- Upload date:
- Size: 23.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3de55878524a43b1ce95813de00bf339652f2a7b8fb66dbaf64cf5fb50f8e7bd
|
|
| MD5 |
5f43b7f07b992f858affe01dac1645de
|
|
| BLAKE2b-256 |
03e321cacc87719ab9be942602dc85d0ad0c330c522ba5ed0af7cfb2bbeefdd1
|
Provenance
The following attestation bundles were made for rosbag_doctor-0.1.1-py3-none-any.whl:
Publisher:
release.yml on sylvesterkaczmarek/rosbag-doctor
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rosbag_doctor-0.1.1-py3-none-any.whl -
Subject digest:
3de55878524a43b1ce95813de00bf339652f2a7b8fb66dbaf64cf5fb50f8e7bd - Sigstore transparency entry: 2390798414
- Sigstore integration time:
-
Permalink:
sylvesterkaczmarek/rosbag-doctor@113e92fd4c6ed5976486c13158637025102e8e70 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/sylvesterkaczmarek
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@113e92fd4c6ed5976486c13158637025102e8e70 -
Trigger Event:
push
-
Statement type: