svc-vision: new repo for the camera; decision 29 (Python for vision)

The live view moved from app-flutter's test tool to svc-vision, running as
yukart-vision.service on the car Orin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DDwfd6Ki8UfG4hK6W5YpJz
This commit is contained in:
2026-10-12 16:15:04 +09:00
parent a290a01f03
commit 66b7269f3c
4 changed files with 13 additions and 8 deletions
+3 -2
View File
@@ -35,7 +35,7 @@ The Orin runs headless: no local video out. The tablet app is the display.
| `decoder` | Raw frames → named signals via DBC | No |
| `recorder` | Persist raw + decoded sessions to disk; raw CAN and GNSS read directly, not via the bus (decision 25) | No |
| `telemetry-gw` | Serve external display clients | No |
| `vision` | Camera capture (USB UVC, MJPEG), software H.264 encoding, recording and live view; on-device inference later | No |
| `vision` | Camera capture (any V4L2 camera; in practice USB UVC, MJPEG), live view, software H.264 recording; on-device inference later. Repo `svc-vision`, Python (decision 29) | No |
Each ships as a systemd unit. The host-level prerequisites they depend on
(kernel modules, device permissions, gpsd) are the environment repo's job, not
@@ -351,7 +351,7 @@ rewriting the document.
| 2 | Displays are bus subscribers; no display-specific logic upstream | Accepted | 2026-08-14 |
| 3 | Listen-only sniffing + active OBD-II, single writer | Accepted | 2026-08-14 |
| 4 | MQTT as the bus | **Proposed** | 2026-08-14 |
| 5 | Rust for on-Orin services, Python for off-car tooling, Dart for the display client | Accepted | 2026-09-04 |
| 5 | Rust for on-Orin services, Python for off-car tooling, Dart for the display client. One exception: decision 29 | Accepted | 2026-09-04 |
| 21 | Two clocks per envelope; `monotonic` orders, `timestamp` references | Accepted | 2026-09-05 |
| 22 | `signals.json` is the single source for signal name and unit; both are derived, never authored twice | Accepted | 2026-09-05 |
| 23 | Services build natively on the development Orin; both units stay on the same L4T release | Accepted | 2026-09-05 |
@@ -375,3 +375,4 @@ rewriting the document.
| 20 | Every display is data-only. Directly-driven screens are for permanently installed, car-powered panels only | Accepted | 2026-09-04 |
| 27 | Camera live view is the one video stream to the tablet: its own connection beside the boundary, paced to the link (≤ ~4 MB/s), never on the bus. Exception to 20 | **Proposed** | 2026-10-11 |
| 28 | Every Orin → tablet sender caps its socket (`SO_MAX_PACING_RATE`, ~5 MB/s); unpaced bursts stall the USB link for seconds | **Proposed** | 2026-10-11 |
| 29 | `svc-vision` is Python: camera pipelines and inference live in that ecosystem. Exception to 5 | Accepted | 2026-10-12 |
+1 -1
View File
@@ -69,7 +69,7 @@ Ordered by what is lost if a tier is late.
| 0 | kernel modules, gpsd, network | Host layer; nothing of ours can precede it |
| 1 | `mosquitto`, `can-bridge`, `recorder` | **Data is lost if late.** Must reach ready fastest |
| 2 | `decoder`, `gnss`, `obd-poller` | Derive from tier 1; a late start loses nothing |
| 3 | `svc-gw`, `svc-nav` | Serving; a display can wait |
| 3 | `svc-gw`, `svc-nav`, `svc-vision` | Serving; a display can wait |
| 4 | voice — ASR, TTS | Interactive; seconds late is harmless |
**Capture first** is the whole principle. A CAN frame missed at tier 1 is gone
+3 -1
View File
@@ -27,6 +27,7 @@ contracts repo, not copied into each consumer.
| [svc-core](gitea:yukart/svc-core) | Acquisition + processing cluster: `recorder` now; `can-bridge`, `obd-poller`, `gnss`, `decoder` to come |
| [svc-gw](gitea:yukart/svc-gw) | Telemetry gateway — aggregation and the display boundary |
| [svc-nav](gitea:yukart/svc-nav) | Navigation service — routing and guidance |
| [svc-vision](gitea:yukart/svc-vision) | Camera: capture and the live view; recording and inference to come |
| [tool-mapmint](gitea:yukart/tool-mapmint) | Map artifact minting, workstation-side |
| [tool-mock](gitea:yukart/tool-mock) | Mocks and fixtures — a fake car on a workstation |
@@ -133,7 +134,8 @@ docs/BOM.md only where the repo owns hardware
| Where | Language | Why it is there |
|---|---|---|
| `svc-*` — on the Orin | **Rust** | No GC, low resident memory on a shared 8 GB pool, and fast start for the tier-1 capture services |
| `svc-*` — on the Orin, except `svc-vision` | **Rust** | No GC, low resident memory on a shared 8 GB pool, and fast start for the tier-1 capture services |
| `svc-vision` — on the Orin | **Python** | Camera pipelines (GStreamer) and inference live in the Python ecosystem (decision 29). Standard library and host packages only, until a dependency earns its place |
| `tool-*` — off-car | **Python** | Batch work where development speed is the only constraint that binds |
| `app-flutter` | **Dart** | Flutter |
+6 -4
View File
@@ -1,7 +1,7 @@
---
type: plan
status: active
updated: 2026-10-11
updated: 2026-10-12
---
# Roadmap
@@ -97,12 +97,14 @@ cores at 1080p; frame rate falls with the light.
tablet, inference input, or several
- [ ] Measure in the car: daylight and night frame rate and MJPEG size on a road
scene
- [ ] One capture process owns `/dev/video0` (V4L2 is single-reader) and feeds
- [ ] One capture process owns the camera (V4L2 is single-reader) and feeds
the others; sets UVC controls on open, since they reset on re-enumeration
- [ ] Recording: H.264 segments on the recorder's clock and session marks,
bounded like CAN sessions (~2.7 GB per hour at 6 Mbit/s)
- [ ] Live view in `app-flutter` (decision 27, proposed): 720p MJPEG passthrough
(~3.4 MB/s, no CPU) or H.264, paced per decision 28
- [x] Live view (decision 27, proposed), 2026-10-12: `svc-vision` serves 720p
MJPEG passthrough on `:7710` (`yukart-vision.service`, ~2% of a core),
paced per decision 28, camera-agnostic, says when there is no camera;
`app-flutter` shows it with exposure and gain controls
- [ ] Inference — deferred; plan for evicting voice, not for coexisting with it
([ORIN-RUNTIME.md](ORIN-RUNTIME.md))