Skip to main content

carla-driver-interface

Both ends of NVlabs/alpasim's egodriver.EgodriverService gRPC contract, light enough for a driving policy to depend on:

  • policy side: implement BaseDriver.drive() and serve it with run_server() / serving() (or carla-driver-interface serve --policy route_follower, or any package.module:Class through load_policy()); session bookkeeping, frame retention, ego history and the rig/local conversion are handled for you;
  • a CARLA-free runtime for tests: testing.FakeLoop drives any policy server over real gRPC on a straight road, rendering each declared pinhole camera (carla-driver-interface demo --driver localhost:50051).

The CARLA runtime lives in autoware_carla_scenario: with ego.entity=carla_driver it drives the same policy server inside a scenario. An upstream alpasim runtime drives it too — nothing about the alpasim wire format is changed, and carla-driver-interface alpasim packages a policy as alpasim's driver service (Running a policy in alpasim).

Install

$ pip install carla-driver-interface

Python 3.10–3.14. Dependencies: grpcio, protobuf (4.25+), numpy, scipy, Pillow. No CARLA, no alpasim distribution: the wire contract is vendored (proto/README.md) with field numbers and service names unchanged.

Writing a policy

from carla_driver_interface.driver import BaseDriver, DriveContext, DriveResult
from carla_driver_interface.geometry import Pose, Trajectory
from carla_driver_interface.server import run_server


class MyPolicy(BaseDriver):
    name = "my_policy"

    def drive(self, ctx: DriveContext) -> DriveResult:
        frame = ctx.session.latest_frame("camera_front")  # .as_array() -> RGB
        plan = Trajectory.empty()
        for i in range(1, 41):  # 4 s ahead, in the rig frame (x forward, y left)
            plan.append(
                ctx.time_now_us + i * 100_000, Pose.from_xyz_yaw(0.5 * i, 0.0, 0.0, 0.0)
            )
        return DriveResult(trajectory_in_rig=plan)


run_server(MyPolicy(), port=50051)

Every sensor reaches a policy the same way: as a frame in session.frame_history (CameraFrame, LidarFrame), announced through on_frame, each with as_array(). Beyond alpasim's cameras, a policy run by autoware_carla_scenario gets LiDAR sweeps (rig frame) that way and, with map_dir set to its copy of the runtime's driver.map_dir, the world's map as files (ctx.map) and every traffic light resolved into that map's own elements (ctx.stop_lines(), carla_driver_interface.hdmap). CARLA ground truth arrives in ctx.renderer_data (None when an alpasim runtime drives the policy).

Testing a policy without CARLA

$ carla-driver-interface serve --policy route_follower --port 50051 &
$ carla-driver-interface demo --driver localhost:50051 --steps 200

or, in a test, FakeLoop("localhost:50051", [FakeCamera()]) from carla_driver_interface.testing (a context manager; .run(steps) returns a LoopResult). The ego follows the returned plan exactly, so what is tested is the policy and the wire, not a controller.

Running a policy in alpasim

alpasim runs its driver as a container its wizard starts, and its runtime drives it over the same egodriver.EgodriverService. Three commands take a policy there.

1. Serve in alpasim mode. serve --mode alpasim serves the policy as before, except for the two opaque bytes fields the contract leaves to each runtime:

--mode carla (default) --mode alpasim
DriveRequest.renderer_data read as CarlaRendererData (ctx.renderer_data, ctx.map, LiDAR) never parsed: it is alpasim's renderer's own payload; ctx.renderer_data is None
DriveResponse.debug_info.unstructured_debug_info a CarlaDriveDebugInfo the pickled dict alpasim's evaluation unpickles (policy_name, inference_seconds, scalars), as alpasim's own driver answers

Everything else (sessions, frames, ego history, the rig/local conversion) is the same, and so is a policy: BaseDriver does not change. SIGTERM stops the server cleanly in either mode, so docker stop does too.

2. Build an image.

$ carla-driver-interface alpasim build-image --tag my-policy:0.1 \
      --install ./my_policy --policy my_policy.driver:MyPolicy \
      --policy-arg cruise_speed_mps=8

installs carla-driver-interface (this version from PyPI, or --carla-driver-interface PATH for a checkout) and every --install requirement (a name, a URL, or a local project directory or wheel, copied into the build context) with one pip install on --base-image (default python:3.12-slim; any image with python, pip and bash). The policy and its --policy-args are baked in as the image's defaults (CARLA_DRIVER_INTERFACE_POLICY, CARLA_DRIVER_INTERFACE_POLICY_ARGS), its module is imported at build time (--no-check-policy to skip), and the image serves alpasim mode on port 50051 when run by itself. --apt adds Debian packages, --no-build writes the context and its Dockerfile without building, and --docker-arg passes arguments to docker build.

3. Hand it to alpasim's wizard.

$ carla-driver-interface alpasim wizard-config --image my-policy:0.1 --out ./alpasim-conf
$ cd alpasim && uv run alpasim_wizard deploy=local topology=1gpu \
      driver=carla_driver_interface 'hydra.searchpath=[file:///abs/path/to/alpasim-conf]' \
      wizard.log_dir=$PWD/run

writes the config group driver=carla_driver_interface (--name to change it): the image as alpasim's driver service (services.driver, an external image), started with serve --mode alpasim on the port the wizard assigns, logging to <log_dir>/driver/. --policy and --policy-arg override the image's defaults, --volume and --env add mounts (weights, say) and variables, and --camera ID[:WxH][@HZ] replaces alpasim's default cameras. --pinhole ID:HFOV[@X,Y,Z[,ROLL,PITCH,YAW]] has the renderer render camera ID through an undistorted pinhole model instead (runtime.extra_cameras), mounted in the rig frame (x forward, y left, z up, from the ground below the rear axle; m and degrees) and at its --camera resolution; the recorded vehicle's hood mask is then turned off. With NRE, ID must be one of the scene's cameras. Everything else -- renderer, physics, controller, scenes, evaluation -- is alpasim's, unchanged.

For example, VisionPilot (vision_pilot.driver.VisionPilotDriver) on its own camera, a 1920x1280, 50 degree pinhole at 10 Hz:

$ carla-driver-interface alpasim wizard-config --name vision_pilot \
      --image vision-pilot-alpasim:0.1 --out ./alpasim-conf \
      --volume /path/to/weights:/mnt/weights:ro \
      --camera camera_front_wide_120fov:1920x1280@10 \
      --pinhole camera_front_wide_120fov:50@2.969,-0.0243,2.116,-0.10,0.11,0.23

Successor to carla-driver-interface 0.1.0

This package replaces the previous carla-driver-interface 0.1.0 of this repository, which had two halves: a driver built on the alpasim-grpc distribution (Python 3.11–3.12 only), and a CARLA runtime (CarlaRuntime, CarlaWorldAdapter, FakeWorld, carla-driver-interface run). From 1.0.0:

  • the driver half is this package. It was developed as autoware-carla-egodriver, a workspace member of autoware_carla_scenario, and moves here under the old name so policies and scenario packages version independently;
  • the runtime half is autoware_carla_scenario's ego.entity=carla_driver, which owns the CARLA world, the ego and the traffic, and drives the policy over the same contract. testing.FakeLoop replaces FakeWorld for CARLA-free tests.

Migrating from 0.1.0

Policies port by changing imports; BaseDriver, DriveContext, DriveResult, SessionState, CameraFrame, Pose and Trajectory keep their names and fields.

0.1.0 1.0.0
carla_driver_interface.driver.base (BaseDriver, DriveContext, ...) carla_driver_interface.driver
carla_driver_interface.driver.server (run_server, serving, build_server) carla_driver_interface.server
carla_driver_interface.driver.service.CarlaEgodriverServicer carla_driver_interface.service.EgodriverServicer
carla_driver_interface.driver.policies carla_driver_interface.policies (plus load_policy)
carla_driver_interface.grpc_api (messages, stubs) carla_driver_interface.protocol
carla_driver_interface.grpc_api.extension (pack_renderer_data, unpack_debug_info, ...) carla_driver_interface.protocol
carla_driver_interface.grpc_api.API_VERSION_MESSAGE carla_driver_interface.protocol.ALPASIM_API_VERSION
alpasim_grpc.v0.{common,egodriver,sensorsim}_pb2 carla_driver_interface.protocol.{common,egodriver,sensorsim,carla_driver}_pb2
carla_driver_interface.geometry (Pose, Trajectory), .polyline unchanged
geometry.quat_mul, yaw_to_quat_xyzw, quat_to_yaw, ... Pose methods, or scipy.spatial.transform.Rotation
BaseDriver.on_image(session, frame) BaseDriver.on_frame(session, frame) (cameras and LiDAR)
carla_driver_interface.fakes.FakeWorld carla_driver_interface.testing.FakeLoop
carla_driver_interface.runtime (CarlaRuntime, ...), carla-driver-interface run autoware_carla_scenario, ego.entity=carla_driver
carla_driver_interface.compat, carla-driver-interface compat-report removed

alpasim_grpc.v0.runtime_pb2 (RolloutSpec, SimulationReturn, ...) is not vendored: it is the runtime's own contract, not the egodriver one. Messages not re-exported by name in protocol (CarlaDriveSessionInfo, CarlaCompatReport, ...) remain available from protocol.carla_driver_pb2.

Versioning

carla-driver-interface follows semantic versioning on its own, independently of autoware_carla_scenario or any policy. The compatibility unit is the gRPC wire contract (proto/): any 1.x policy server and any runtime built against 1.x interoperate, so depend on it with a range (carla-driver-interface>=1.0,<2), not an exact pin.

  • patch: fixes that change neither the wire nor the Python API;
  • minor: additions — new fields or messages in the carla_driver extension (old peers ignore them), new Python API;
  • major: anything that breaks a peer or a policy — a changed or removed field number, a renamed service, a removed Python API.

The vendored alpasim contract is never edited; following a newer alpasim revision is a minor release when it is wire-compatible and a major one when it is not.

Development

$ uv sync --group dev
$ uv run ruff check . && uv run ruff format --check .
$ uv run mypy src tests scripts
$ uv run pytest -q

The generated protobuf modules in src/carla_driver_interface/_proto are committed; regenerate them with uv run python scripts/compile_protos.py (on Python 3.10–3.12, see proto/README.md). --check, also run by the test suite, verifies they are current.

Releasing

Releases are cut by merging, and go to PyPI from .github/workflows/release.yml through Trusted Publishing — no API token is stored anywhere.

Every PR carries exactly one of the labels bump patch, bump minor or bump major (the Check Version Bump Label check fails otherwise), chosen by the versioning rules above. Do not edit version in pyproject.toml by hand. When the PR is merged to main, the release workflow:

  1. bumps version in pyproject.toml by the label, re-locks (uv lock) and pushes the commit chore: bump version to X.Y.Z to main;
  2. tags it vX.Y.Z and creates the GitHub Release;
  3. builds the sdist and wheel (uv build), checks them with twine check --strict, installs the wheel into a clean virtual environment on every supported Python (3.10–3.14) and runs carla-driver-interface --help;
  4. publishes to PyPI.

A merge without a bump label releases nothing.

Re-running a failed release. If a step after the bump failed (the tag, the Release, PyPI), do not re-run the push run — it would bump again. Run the workflow by hand instead (Actions → Release → Run workflow, on main) with the version it bumped to, e.g. 1.2.3. It builds from that version's tag, or else from its chore: bump version to 1.2.3 commit, never from a later main, and reuses an existing tag or Release; files already on PyPI are skipped.

The successor keeps the predecessor's version line: it starts from 0.1.0, and the PR that introduces it is merged with bump major, which releases 1.0.0 like any other release.

Pushing to main. The bump commit and the tag are pushed with GITHUB_TOKEN, which works while main has no branch protection rules. If main gets a ruleset whose bypass list does not include github-actions[bot], add a GH_PAT repository secret (a token that may push to main and create tags); the workflow uses it whenever it exists.

One-time PyPI setup. PyPI needs a (pending) trusted publisher for the project carla-driver-interface (the distribution name in pyproject.toml) with owner hakuturu583, repository carla_driver_interface, workflow release.yml and environment pypi (PyPI → Your account → Publishing), and this repository needs a pypi environment (Settings → Environments). Until then the publish job fails and the built distributions remain available as the run's pypi-dist artifact.

License

Apache-2.0. The vendored alpasim protos are Apache-2.0 as well (proto/LICENSE.alpasim).

Metadata

Release files for carla-driver-interface 1.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 carla-driver-interface 1.1.1
File Size Uploaded Provenance
carla_driver_interface-1.1.1.tar.gz 67.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for carla-driver-interface 1.1.1
File Interpreter ABI Platform
carla_driver_interface-1.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 142.4 kB

Release files / carla_driver_interface-1.1.1.tar.gz

Download URL carla_driver_interface-1.1.1.tar.gz
Size 67.3 kB
Tags Source
SHA-256 checksum
How to use checksums
6502ac0b11b0874b62939eb921c2f73056b1c64d758d2fc6344d90450b0aeeeb
BLAKE2b-256 checksum
How to use checksums
63d8025f558bfa5e0074501d1ef68bf86aa6f7532a27419ebc0fb671ccbf16bd
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 10, 2026.

Transparency log

Release files / carla_driver_interface-1.1.1-py3-none-any.whl

Download URL carla_driver_interface-1.1.1-py3-none-any.whl
Size 75.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
72ce6199ebdd0121d849b9eada73dd70e2ce3e0e7b5f6cfdf0454c8f0492fb0a
BLAKE2b-256 checksum
How to use checksums
2aa55e5b829cadc52afc39c39a6492a19be56b17a3b95248dfcf8f8ca4494c17
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 10, 2026.

Transparency log

Security for carla-driver-interface 1.1.1

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.

Select from 2 files

Attestation for carla_driver_interface-1.1.1.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 10, 2026.

Transparency log

A 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.

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