betterosi - a python library for reading and writing open-simulation-interface files
A python library for reading and writing ASAM OSI (Open-Simulation-Interace) files (either .osi binary traces or MCAP files) using protobuf-py for typed, ergonomic protobuf message classes.
- Supports writing and reading either mcap or osi files with
betterosi.Writerandbetterosi.read. - View OSI or MCAP file containing OSI GroundTruth
betterosi-viewer <filepath.mcap / filepath.osi>(adapted from esmini) - Convert osi to mcap with
betterosi-to-mcap <filepath to osi>.
The library uses code from esmini (betterosi/viewer.py) under MPL 2.0 license. Reading and writing of .osi traces and MCAP files is implemented in betterosi/io.py.
The library uses buf with protoc-gen-py to generate typed Python code from the protobuf definitions of open-simulation-interface.
Since OSI and esmini are under MPL, also this repository is published under MPL-2.0 license.
OSI Version Support
betterosi supports multiple ASAM OSI versions simultaneously (currently 3.7.0 and 3.8.0).
By default, OSI 3.8.0 is used:
import betterosi
gt = betterosi.GroundTruth(...)
To target a specific OSI version, import the corresponding version namespace:
from betterosi import v3_7_0 as betterosi # uses OSI 3.7.0
or
from betterosi import v3_8_0 as betterosi # uses OSI 3.8.0
Differences to Standard OSI
The proto definitions extend standard OSI definitions in the following ways:
- Add
MapAsamOpenDriveMessage: Packages the XML content of an ASAM OpenDRIVE map in a proto Message
See omega-prime for details.
Install
pip install betterosi
Create an OSI or MCAP trace
To create an OSI or MCAP trace, you need to use betterosi.Writer. After creating the OSI Message of your desire, just add it to the Writer as shown in the examples below for either MCAP traaces or OSI traces.
import betterosi
with betterosi.Writer("test.mcap") as writer:
gt = betterosi.GroundTruth(...)
writer.add(gt)
with betterosi.Writer("test.osi") as writer:
sv = betterosi.SensorView(...)
writer.add(sv)
Below a full example is given which creates three files, and MCAP trace and and OSI trace with GroundTruth messages and a MCAP trace of SensorViews. If you use the code, you obviously just need one of the writers.
import betterosi
NANOS_PER_SEC = 1_000_000_000
with (
betterosi.Writer("test.mcap") as writer_mcap,
betterosi.Writer("test.osi") as writer_osi,
betterosi.Writer("test_sv.mcap") as writer_sv,
):
moving_object = betterosi.MovingObject(
id=betterosi.Identifier(value=42),
type=betterosi.MovingObjectType.UNKNOWN,
base=betterosi.BaseMoving(
dimension=betterosi.Dimension3D(length=5, width=2, height=1),
position=betterosi.Vector3D(x=0, y=0, z=0),
orientation=betterosi.Orientation3D(roll=0.0, pitch=0.0, yaw=0.0),
velocity=betterosi.Vector3D(x=1, y=0, z=0),
),
)
gt = betterosi.GroundTruth(
version=betterosi.InterfaceVersion(
version_major=3, version_minor=8, version_patch=0
),
timestamp=betterosi.Timestamp(seconds=0, nanos=0),
moving_object=[moving_object],
host_vehicle_id=betterosi.Identifier(value=0),
)
sv = betterosi.SensorView(
version=betterosi.InterfaceVersion(
version_major=3, version_minor=8, version_patch=0
),
timestamp=betterosi.Timestamp(seconds=0, nanos=0),
global_ground_truth=gt,
host_vehicle_id=betterosi.Identifier(value=0),
)
# Generate 1000 OSI messages for a duration of 10 seconds
for i in range(1000):
total_nanos = i * 0.01 * NANOS_PER_SEC
gt.timestamp.seconds = int(total_nanos // NANOS_PER_SEC)
gt.timestamp.nanos = int(total_nanos % NANOS_PER_SEC)
moving_object.base.position.x += 0.5
sv.timestamp = gt.timestamp
writer_mcap.add(gt)
writer_osi.add(gt)
writer_sv.add(sv)
When writing MCAP messages you can specifiy the topic in the writer and the add function by setting the topic argument. When reading such files, pass the topic(s) as a list to the mcap_topics argument, e.g. mcap_topics=["ground_truth"]. By default, all channels with an OSI schema are read.
Read OSI and MCAP
With betterosi.read you can read an mcap or osi trace. read returns a generator. With the following code, you can get a list of the GroundTruth messages from a trace, even if the GroundTruth are nested inside SensorViews. It works the same for OSI traces (see below for how the message type of an .osi file is determined).
import betterosi
ground_truths = list(betterosi.read("test_sv.mcap", return_ground_truth=True))
print([len(ground_truths), ground_truths[0]])
Above code prints the following:
[1000, GroundTruth(version=InterfaceVersion(version_major=3, version_minor=8, version_patch=0), timestamp=Timestamp(seconds=0, nanos=0), host_vehicle_id=Identifier(value=0), moving_object=[MovingObject(id=Identifier(value=42), base=BaseMoving(dimension=Dimension3d(length=5.0, width=2.0, height=1.0), position=Vector3d(x=0.5, y=0.0, z=0.0), orientation=Orientation3d(roll=0.0, pitch=0.0, yaw=0.0), velocity=Vector3d(x=1.0, y=0.0, z=0.0)), type=MovingObject.Type.UNKNOWN)])]
If you want a list of the sensor views directly:
import betterosi
sensor_views = betterosi.read("test_sv.mcap", return_sensor_view=True)
print(next(sensor_views))
The above prints:
SensorView(version=InterfaceVersion(version_major=3, version_minor=8, version_patch=0), timestamp=Timestamp(seconds=0, nanos=0), global_ground_truth=GroundTruth(version=InterfaceVersion(version_major=3, version_minor=8, version_patch=0), timestamp=Timestamp(seconds=0, nanos=0), host_vehicle_id=Identifier(value=0), moving_object=[MovingObject(id=Identifier(value=42), base=BaseMoving(dimension=Dimension3d(length=5.0, width=2.0, height=1.0), position=Vector3d(x=0.5, y=0.0, z=0.0), orientation=Orientation3d(roll=0.0, pitch=0.0, yaw=0.0), velocity=Vector3d(x=1.0, y=0.0, z=0.0)), type=MovingObject.Type.UNKNOWN)]), host_vehicle_id=Identifier(value=0))
MCAP files store the message type of every channel, so reading them only needs the file name. .osi traces do not store it, and GroundTruth and SensorView messages cannot always be told apart from their bytes. read determines the type of an .osi file in this order:
- the
osi_message_typeargument, if given, - the OSI trace file naming convention (
<timestamp>_<type>_...osi, e.g.20240101T120000Z_gt_380_700_1000_drive.osi) or a singlegt/sv/ ... part in the file name, - only with
return_ground_truth/return_sensor_view: decoding the first message. If that is ambiguous, aValueErrorasks you to passosi_message_type.
import betterosi
any_osi_message = betterosi.read("test.osi", osi_message_type="GroundTruth")
any_osi_message = betterosi.read("test.mcap")
Generate library code
pip install protoc-gen-py buf-bin
python gen_protos.py
gen_protos.py compiles every version directory in osi-proto/<version>/ into betterosi/generated/<version>/ (with buf generate and an inline protoc-gen-py template, so no buf.yaml / buf.gen.yaml is needed) and builds the osi3_descriptor_set.pb used for MCAP schemas. Pass versions to generate only those, e.g. python gen_protos.py 3.8. The generated code is not committed; it is also created automatically by the build hook during uv sync / pip install.
Name
The library is called betterosi because it was formerly based on the python-betterproto2 library for handling protobufs.
LICENSE and Copyright
This code is published under MPL-2.0 license. It utilizes and modifies parts of esmini (betterosi/viewer.py) under MPL-2.0 and open-simulation-interface osi-proto/* under MPL-2.0.
Acknowledgements
This package is developed as part of the SYNERGIES project.
Funded by the European Union. Views and opinions expressed are however those of the author(s) only and do not necessarily reflect those of the European Union or European Climate, Infrastructure and Environment Executive Agency (CINEA). Neither the European Union nor the granting authority can be held responsible for them.
Notice
Metadata
Release files for betterosi 0.9.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| betterosi-0.9.0.tar.gz | 279.1 kB | With provenance | Details for betterosi-0.9.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| betterosi-0.9.0-py3-none-any.whl | Python 3 | none | any | Details for betterosi-0.9.0-py3-none-any.whl |
Total release size: 1.2 MB
Release files / betterosi-0.9.0.tar.gz
| Download URL | betterosi-0.9.0.tar.gz |
|---|---|
| Size | 279.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
66353959b59cb4fb685e585fe0112cc5b820782785400a0a2ade13b3df5f27ac
|
|
BLAKE2b-256 checksum How to use checksums |
c96ab72420f7a165ceb996ae78011f84b280e69c4bc46a0bb1fda33be12f5bae
|
| 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 Oct 20, 2026.
Transparency logRelease files / betterosi-0.9.0-py3-none-any.whl
| Download URL | betterosi-0.9.0-py3-none-any.whl |
|---|---|
| Size | 910.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
12f6004928d023afafb4d9234c257da6dd3dbc8079fc26dd8d88f74d46fd7686
|
|
BLAKE2b-256 checksum How to use checksums |
97dcf5041b6099b83d6192a397127ae32f42be26b7717edcb61ffa2010f12c8d
|
| 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 Oct 20, 2026.
Transparency logSecurity for betterosi 0.9.0
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for betterosi-0.9.0.tar.gz
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 Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.