carla_canvas
Swap CARLA's camera and LiDAR rendering for 3D Gaussian Splatting or any other renderer, behind the CARLA API, for typesafe_carla programs.
The program does not change. Point CARLA_CANVAS_CONFIG at a configuration
file and the sensors it names are rendered by a renderer service over gRPC
instead of the CARLA server; listen(), poll(), wait_for_data(),
carla.Image / carla.LidarMeasurement and enable_for_ros() keep working,
and the ROS 2 topics keep CARLA's names and types.
概要 (日本語) — CARLA のセンサー描画 (カメラ / LiDAR) を 3D Gaussian Splatting などの別手法に差し替える互換ライブラリです。ユーザーのプログラムは変更不要で、 起動時の環境変数
CARLA_CANVAS_CONFIGで設定ファイル (どのレンダラーを使うか、 読み込むアセット、どのセンサーを差し替えるか) を指定します。クライアントと レンダラーは gRPC で CARLA API 互換のメッセージをやり取りし、計測値は LibCarla のImage/LidarMeasurementそのものとして届きます。enable_for_ros()された センサーの ROS 2 (DDS) 出力は carla_canvas が CARLA と同じトピック名・型で行うので、 レンダラー実装側も利用者も通信バックエンドを意識する必要はありません。
# examples/camera_lidar.py: typesafe_carla's camera example, unchanged
import typesafe_carla as carla
...
camera = world.spawn_actor(cam_bp, carla.Transform(carla.Location(1.5, 0.0, 2.0)),
attach_to=vehicle).as_sensor()
camera.listen(4)
world.tick()
image = camera.wait_for_data(5.0).as_image() # rendered by carla_canvas
CARLA_CANVAS_CONFIG=examples/splatsim.yaml typesafe-codon run examples/camera_lidar.py
How it works
typesafe_carla program (Codon / Python) unchanged
│ listen / tick / enable_for_ros
▼
libtypesafe_carla_ffi ── sensor source (C ABI 5.3) ──▶ libcarla_canvas_plugin.so
│ unclaimed sensors, everything else │ claimed sensors: snapshot,
▼ │ sensor attributes, actors
CARLA server (LibCarla RPC + streams) ▼ gRPC (proto/carla_canvas/v1)
carla_canvas renderer service
├─ Renderer: 3DGS / primitives / yours
├─ CARLA's LiDAR model, encodings
└─ ROS 2 over DDS (CARLA's topics)
- typesafe_carla loads the plugin (its launcher finds it through an entry
point when
CARLA_CANVAS_CONFIGis set). The plugin claims the sensors the configuration lists; CARLA never streams them. - Each
world.tick()(and each tick in asynchronous mode) the plugin sends the world snapshot, new and removed actors, and the sensors due (bysensor_tick) in oneRendercall. Measurements come back in CARLA's raw layouts and are queued as LibCarla's ownImage/LidarMeasurementobjects: in synchronous mode a callback receives frame N inside thetick()that returned N. - The renderer service does everything CARLA-specific: sensor blueprint attributes, depth / segmentation encodings, the ray-cast LiDAR model (sweep per tick, channels, drop-off, intensity, noise, semantic LiDAR), and the ROS 2 output. A renderer only draws a camera view (and optionally casts rays).
See docs/design.md.
Install
pip install 'carla-canvas[client]' # the client (where the typesafe_carla program runs) + typesafe-carla
pip install 'carla-canvas[ros]' # ROS 2 output over DDS without a ROS installation
pip install carla-canvas # a renderer host, e.g. next to splatsim on a GPU machine
pip install 'carla-canvas[gsplat]' # the built-in Gaussian splatting on CUDA
The wheels (Linux x86_64 and aarch64, manylinux_2_28) carry the sensor plugin
with gRPC, protobuf and yaml-cpp linked in: no system packages. A source build
(another platform, or --no-binary carla-canvas) uses the system's gRPC C++,
protobuf and yaml-cpp when they are installed, or builds them from source:
pip install --no-binary carla-canvas 'carla-canvas[client]' \
-C cmake.define.CARLA_CANVAS_FETCH_DEPS=ON # needs a C++17 compiler and git; ~20 min
Use
export CARLA_CANVAS_CONFIG=canvas.yaml
typesafe-codon run main.py # the plugin starts the renderer if needed
python main.py # import typesafe_carla.carla: the same
carla-canvas serve --config canvas.yaml # or run the renderer yourself (another host, GPU)
eval "$(carla-canvas env --config canvas.yaml)"; ./main # a built executable
carla-canvas info # plugin, configuration, renderer reachable?
A configuration (docs/configuration.md):
renderer:
backend: splatsim.carla_canvas:SplatsimRenderer # or primitives, gaussian_splatting, package.module:Class
endpoint: "localhost:50051"
command: [carla-canvas, serve, --config, "{config}"] # started when nothing answers
assets:
maps:
"Town10HD*": {scene: scenes/town10.usdz} # aligned through the map's geo-reference
actors:
"vehicle.*": sedan # the scene bundle's actor asset
options: {device: cuda}
sensors: # rendered by carla_canvas; every other sensor stays CARLA's
- sensor.camera.rgb
- {type: sensor.lidar.ray_cast, role_name: "top*"}
ros:
backend: auto # rclpy if available, else CycloneDDS
Renderers
| backend | what it draws | outputs |
|---|---|---|
splatsim.carla_canvas:SplatsimRenderer |
3D Gaussian Splatting for real use: splatsim's scene USDZ, actor bank, gsplat cameras and its LiDAR rasterizer (learned intensity, ray drop), geo-referenced to the CARLA map | RGB; LiDARs |
gaussian_splatting |
reference and test implementation: a 3DGS map (INRIA .ply) plus per-blueprint actor splats; a numpy EWA rasterizer (or gsplat) |
RGB, depth, semantic and instance segmentation; LiDARs from depth |
primitives |
reference renderer: the ground plane and every actor's bounding box, ray traced (no assets) | everything, with exact ray casts |
package.module:Class |
yours: subclass carla_canvas.Renderer |
what you declare |
Writing one takes two methods (docs/renderers.md):
from carla_canvas import CameraFrame, CameraView, Renderer, WorldInfo
class MyRenderer(Renderer):
name = "mine"
camera_outputs = frozenset({"rgb", "depth"})
def load_world(self, world: WorldInfo) -> None: ... # load world.map_name's assets
def render_camera(self, view: CameraView) -> CameraFrame: ... # view.camera: pose, size, fov
ROS 2
sensor.enable_for_ros() on a rendered sensor publishes, as the CARLA server
would: /carla/[<parent>/]<ros_name>/image + /camera_info, or
/point_cloud, plus /tf and /clock, stamped with simulation time
(docs/ros2.md). It is implemented once, in carla_canvas
(carla_canvas.ros), over CycloneDDS with ROS 2's wire types (no ROS
installation needed) or rclpy; renderers never deal with it.
Development
uv venv && uv pip install -e ../typesafe_carla # TSC_BACKEND=mock for the mock CARLA
uv pip install --no-build-isolation -e . # builds the plugin
cmake -S . -B build && cmake --build build -j && ctest --test-dir build
uv run pytest # unit, service, ROS, end to end (Codon + mock)
uv run python tools/gen_proto.py # after editing proto/
Releasing
Label the PR release:patch, release:minor or release:major: merging it
bumps the version, tags v<version> and publishes the wheels (x86_64,
aarch64) and the sdist to PyPI (.github/workflows/release.yml). A PR without
a release label releases nothing. A manual run of the Release workflow is a dry
run (with publish: to TestPyPI).
Status
| client plugin (C++, gRPC) | ✅ synchronous and asynchronous rendering, renderer auto-start, sensor_tick, episodes (load_world) |
| sensors | ✅ sensor.camera.rgb, .depth, .semantic_segmentation, .instance_segmentation, sensor.lidar.ray_cast, .ray_cast_semantic |
| renderers | ✅ splatsim (hakuturu583/splatsim#9; tested with stand-ins, not yet on a GPU); reference: ✅ primitives, ✅ gaussian_splatting (numpy rasterizer tested; its gsplat path untested in CI) |
| ROS 2 | ✅ CycloneDDS (tested) and rclpy transports |
| verified | typesafe_carla mock backend end to end; the typesafe_carla side also against LibCarla ue5-dev (C ABI tests); not yet against a CARLA server |
Metadata
Release files for carla-canvas 0.2.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 | |
|---|---|---|---|
| carla_canvas-0.2.0.tar.gz | 102.3 kB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| carla_canvas-0.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| carla_canvas-0.2.0-py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.28+ ARM64, Linux glibc 2.26+ ARM64 | Details |
Total release size: 15.2 MB
Release files / carla_canvas-0.2.0.tar.gz
| Download URL | carla_canvas-0.2.0.tar.gz |
|---|---|
| Size | 102.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8b566a10f4d516f7c404dd4e1ad6b6c6bfaf379425ee603f70cf00cb2a193072
|
|
BLAKE2b-256 checksum How to use checksums |
edf3ecebb8bd0805cb1f630dcfb2be05139365afb0b4f8aa158dd656b76b3f4e
|
| 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 15, 2026.
Transparency logRelease files / carla_canvas-0.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | carla_canvas-0.2.0-py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 8.0 MB |
| Tags | Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
d904a5a6fc30427bcaae90538140b59f5366cca926b7166eff58b3ed83fe2494
|
|
BLAKE2b-256 checksum How to use checksums |
75411e0b1516634af03e5d17114e74a335bff40317d4d5bddfef57e2f4705158
|
| 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 15, 2026.
Transparency logRelease files / carla_canvas-0.2.0-py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl
| Download URL | carla_canvas-0.2.0-py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 7.1 MB |
| Tags | Linux glibc 2.26+ ARM64 Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
baa5ebfe296bdd2391f7a76b5d5bc8846e396d67aaa7b5804f23a1be85b5fd2f
|
|
BLAKE2b-256 checksum How to use checksums |
7461c70bee6db21b80b148b3bc7de9c2af593a70e8b548d92fdaf24c3b4ddd80
|
| 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 15, 2026.
Transparency log