Configuration

How helios.toml resolves, every knob it exposes, camera-index discovery, and virtual-camera notes.

Per-device tuning lives in helios.toml. Moving Helios between machines is a profile change, never a code change.

Resolution order

Effective config is resolved lowest to highest:

  1. Built-in defaults.
  2. The [default] table in helios.toml.
  3. The [profiles.<hostname>] table matching this machine's hostname.
  4. CLI flags.

Get the hostname key for a machine:

python -c "import socket; print(socket.gethostname())"

Then add a profile keyed by it, overriding only what you need:

[profiles.your-hostname]
model      = "yoloe-11s-seg.pt"   # -11s (fast) ... -11l (accurate)
imgsz      = 640                  # 480/512/640/896 — lower = faster
conf       = 0.25                 # detection/draw threshold
target_fps = 30

A typo'd hostname fails silently — by design

A missing or misspelled profile key falls back to [default], so the machine simply runs untuned rather than erroring. If tuning appears to have no effect, confirm the profile key matches the real hostname exactly.

How the loader handles bad input

  • Unknown keys are dropped, in both [default] and the host profile.
  • A value that cannot be coerced to its declared type falls back to that field's built-in default, so one bad value cannot take down the whole load.
  • A missing or unreadable config file falls back to the built-in defaults — no profile, no overrides.
  • Malformed TOML raises. This is a real operator error and is not swallowed into a silent default.

CLI flags

--device, --camera, --model, --imgsz, --target-fps, and --conf override everything else. uvicorn is not flag-aware, so use a module entry point when experimenting — for example python -m app.vision --selftest --camera 700 honors helios.toml and the flags together.

Every knob

KeyDefaultMeaning
device"cuda:0"CUDA device to load the detector onto. Validated at startup; there is no CPU path.
camera_index0OpenCV camera index to capture from.
capture_backend"auto""auto" tries DirectShow then Media Foundation; or force "dshow" / "msmf".
model"yoloe-11s-seg.pt"YOLOE weights. Bigger variants are more accurate and slower.
tracker"bytetrack.yaml"Ultralytics tracker config used for counting. botsort.yaml for occlusion.
imgsz640Inference size. Larger improves small-object recall at an FPS cost.
target_fps30Caps the capture loop, which saves GPU and stabilizes pacing.
conf0.25Confidence threshold for detecting and drawing. Higher = fewer, steadier boxes.
max_concepts8Upper bound on how many concepts Claude may return for one prompt.
llm_model"claude-sonnet-4-6"Claude model used for concept mapping and policy parsing.
starter_concepts["person", "laptop", "cup", "bottle", "chair"]Vocabulary bound before the first chat message.
smoothing_window8Frames in the rolling majority vote for PPE and zone state.
smoothing_min_ratio0.6Fraction of the window that must agree to hold a state.
smoothing_evict_after15Frames a vanished track survives before its history is dropped.
tiling_enabledfalseStartup default for the tiling toggle only; the live UI toggle wins afterward.
tile_size640Tile edge length for the tiling path. Config-only.
tile_overlap128Tile overlap in pixels for the tiling path. Config-only.

See Tuning for how to choose values for the smoothing, FPS, and tiling knobs.

Camera selection

OpenCV identifies cameras only by index and cannot name them, so finding the right one is trial and error. List what is available:

python -m app.capture --list      # prints  index -> device name

Set the winning index (and, on Windows, a backend) in the machine's profile:

[profiles.your-hostname]
camera_index    = 1
capture_backend = "auto"          # "auto" tries DSHOW then MSMF; or force "dshow"/"msmf"

If the listing command reports that cv2_enumerate_cameras is not installed, install it — it is the optional package that provides index-to-name discovery.

Sources, by latency

Best first:

  • Built-in or USB webcam — plug and play.
  • A phone as a virtual webcam (via an app such as Camo or EpocCam) — registers a virtual camera device. Use a wired USB connection for lowest latency. The phone app must be running and showing a live preview first, or the virtual device is missing or yields black frames.
  • A mirrorless camera through an HDMI-to-USB UVC capture card (1080p) or a vendor webcam utility (around 720p) — appears as an ordinary camera index.

Virtual-camera notes

Virtual webcams often return a successful read but all-black frames on the wrong backend. capture_backend = "auto" opens with DirectShow first (most compatible), falls back to Media Foundation, and rejects an all-black test frame automatically — so this is usually handled for you.

If the feed is black with no error card, the phone or capture app most likely is not streaming yet: open its preview, or reconnect the USB cable.

Helios also tolerates a short burst of dropped frames from a real webcam; only a sustained run of failed reads escalates to the CAMERA_UNAVAILABLE error card.