Skip to main content

RPi Camera Ensemble: Central orchestration of video acquisition across multiple Raspberry Pi cameras.

Project description

CI Docs PyPI License

RPi Camera Ensemble

Central orchestration of synchronized video acquisition across multiple Raspberry Pi cameras.

Runs a Conductor on the acquisition machine that controls one or more Agents: each Agent is a FastAPI server running on an individual Raspberry Pi, managing local camera acquisition and GPIO-based TTL synchronization.


Features

  • Multi-camera orchestration: start/stop all cameras with a single call; UUID-guarded sessions prevent accidental stops
  • TTL synchronization: per-frame GPIO pulses (emitter) and edge-detection timestamps (receiver) via lgpio (RPi 4 + RPi 5) or legacy pigpio
  • PiCamera2 backend: uses the modern libcamera stack; preview and recording states
  • REST API on each agent: GET /status, POST /agent/acquisition/init, POST /agent/acquisition/record, POST /agent/acquisition/stop
  • Heartbeat monitoring: background health checks with configurable interval
  • Disk space check: pre-acquisition guard based on estimated bitrate and free space
  • CLI: rce-agent run/config entrypoints for systemd or docker-compose deployment

Architecture

Conductor (acquisition machine)
    │  HTTP via aiohttp
    ├── AgentClient → Agent (RPi cam01)  →  Acquisition → PiCamera2
    ├── AgentClient → Agent (RPi cam02)  →  Acquisition → PiCamera2
    └── AgentClient → Agent (RPi cam03)  →  Acquisition → PiCamera2
                                                    │
                                              TTL out pin (lgpio)
                                              TTL in  pin (lgpio)

Requirements

  • Python ≥ 3.10
  • On each Raspberry Pi: picamera2 (system package), lgpio (pip install lgpio), libgpiod2 (system package)
  • On the acquisition machine (Conductor): aiohttp

Installation

# Acquisition machine (Conductor only)
pip install "rpi-camera-ensemble[conductor]"

# On each Raspberry Pi (Agent + TTL sync)
pip install "rpi-camera-ensemble[agent,lgpio]"

Install from source:

git clone https://github.com/MurineShiftWork/rpi-camera-ensemble.git
cd rpi-camera-ensemble
pip install -e ".[agent,lgpio,dev]"

Quick start

Agent (on each Raspberry Pi)

Create a config file (agent.yaml):

instance_name: cam01
api_host: 0.0.0.0
api_port: 8000
data_dir: /data/recordings
log_dir: /data/logs

Start the agent:

rce-agent run --config agent.yaml

Generate a template config:

rce-agent config --to-template agent.template.yaml

Conductor (on the acquisition machine)

from rpi_camera_ensemble.conductor.conductor import Conductor
from rpi_camera_ensemble.config.acquisition import EnsembleAcquisitionConfig
from rpi_camera_ensemble.config.conductor import ConductorConfig

ensemble_cfg = EnsembleAcquisitionConfig.from_yaml("ensemble.yaml")
conductor_cfg = ConductorConfig(log_dir="/data/logs/conductor")

with Conductor(config=conductor_cfg, ensemble_config=ensemble_cfg) as conductor:
    conductor.setup_agents()
    conductor.initialize_acquisition(
        acquisition_path="/data/recordings/experiment_001",
        acquisition_name="experiment_001",
    )
    conductor.start_preview()
    conductor.start_recording()
    # ... recording ...
    conductor.stop_acquisition()

Ensemble config (ensemble.yaml):

agents:
  cam01:
    api_url: http://192.168.1.10:8000
  cam02:
    api_url: http://192.168.1.11:8000

TTL synchronization

TTL is configured per agent inside ensemble.yaml under camera_config.ttl. Default backend is lgpio (recommended):

agents:
  cam01:
    api_url: http://192.168.1.10:8000
    camera_config:
      ttl:
        library: lgpio
        gpiochip: 0       # RPi 4: 0, RPi 5: 4
        out_pin: 25       # emits 1 ms pulse on each encoded frame
        in_pin: 23        # records edge timestamps from external trigger
        out_duration_us: 1000

lgpio requires libgpiod2 on the Pi:

sudo apt install libgpiod2
pip install lgpio

RPi 5 note: set gpiochip: 4 (RP1 southbridge). RPi 4 uses gpiochip: 0.


Output structure

Each agent writes files named {basename}.rce.{instance}.{timestamp}.{type}.{ext}:

/data/recordings/experiment_001/
├── session.rce.cam01.20260101_120000_000000.video.h264
├── session.rce.cam01.20260101_120000_000000.ttl_out.npz
├── session.rce.cam01.20260101_120000_000000.ttl_in.npz
├── session.rce.cam02.20260101_120000_000000.video.h264
├── session.rce.cam02.20260101_120000_000000.ttl_out.npz
└── session.rce.cam02.20260101_120000_000000.ttl_in.npz

Load and validate a session from the acquisition machine:

from rpi_camera_ensemble.io import RCESession, validate_session

session = RCESession.from_directory("/data/recordings/experiment_001/")
qc = validate_session(session)
qc.print_report()

Deployment

See deploy/ for Ansible playbooks to provision Raspberry Pis (apt packages, Python venv, systemd service).


Development

uv sync --extra dev
uv run pre-commit install
uv run pytest

License

Copyright (c) 2025 Lars B. Rollik. All rights reserved. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rpi_camera_ensemble-0.4.5.tar.gz (92.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rpi_camera_ensemble-0.4.5-py3-none-any.whl (70.6 kB view details)

Uploaded Python 3

File details

Details for the file rpi_camera_ensemble-0.4.5.tar.gz.

File metadata

  • Download URL: rpi_camera_ensemble-0.4.5.tar.gz
  • Upload date:
  • Size: 92.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpi_camera_ensemble-0.4.5.tar.gz
Algorithm Hash digest
SHA256 69019ccd12b53c01546a50052b5aae16d840b2baed585c074283eafab61c0fac
MD5 1696a223fcef1eadcda6e722b99ef0c9
BLAKE2b-256 92d97137ae79a22d6821e5e851d7f0a12050cad55203a1fbe553fc63e4a4c927

See more details on using hashes here.

File details

Details for the file rpi_camera_ensemble-0.4.5-py3-none-any.whl.

File metadata

  • Download URL: rpi_camera_ensemble-0.4.5-py3-none-any.whl
  • Upload date:
  • Size: 70.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for rpi_camera_ensemble-0.4.5-py3-none-any.whl
Algorithm Hash digest
SHA256 42060249086958e0bd960706a5a746ea97d280f2b5c8afd0fcb58c633d81189e
MD5 0178e4b96ba681d012ec42360b5b16f3
BLAKE2b-256 dd2e61648f2dd2654f13beb1faa65a08e84c9b7cf405f948672a9f20713f99bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page