Compare commits
38 Commits
cbd31cf8fc
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b896e05552 | |||
| b05eaeaf78 | |||
| 2e87ac83cf | |||
| c04d8aed28 | |||
| 514f34ca3d | |||
| 76539f4625 | |||
| 7911fa3e7e | |||
| a814ef554e | |||
| bb47ee5301 | |||
| 5398e32427 | |||
| da40a56f1d | |||
| 147bea3e03 | |||
| 837375b7ac | |||
| d838531410 | |||
| 180ad16730 | |||
| 8a82cb1c78 | |||
| b7c958ed68 | |||
| b254bb2c55 | |||
| 66b7269f3c | |||
| a290a01f03 | |||
| e6ca835992 | |||
| 9a1512793d | |||
| e171d7ae99 | |||
| dfa692d763 | |||
| ccd5b54450 | |||
| 668b40f330 | |||
| 16b1031b8c | |||
| feb9564ce4 | |||
| 5c7c0677cb | |||
| 3422b084ac | |||
| 6300006859 | |||
| 5bff21265b | |||
| ac4626640c | |||
| 6fc187570c | |||
| ce4eaa1aaf | |||
| c395d4e3b0 | |||
| f05fda71ee | |||
| d8c40eb9ed |
@@ -0,0 +1,17 @@
|
|||||||
|
# Cross-repo consistency (yukart/tool-crosscheck): decisions, topics, doc references,
|
||||||
|
# service lists and front matter checked against their source in every yukart repo.
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
crosscheck:
|
||||||
|
# Gitea dispatches a reusable-workflow call to a runner chosen by the CALLER's runs-on;
|
||||||
|
# without one, any runner may take it (the instance's pi5-runner did, and failed).
|
||||||
|
runs-on: halogen
|
||||||
|
uses: yukart/tool-crosscheck/.gitea/workflows/crosscheck.yml@master
|
||||||
|
with:
|
||||||
|
repo: ${{ github.event.repository.name }}
|
||||||
|
sha: ${{ github.sha }}
|
||||||
|
secrets: inherit
|
||||||
+5
-1
@@ -3,6 +3,10 @@ type: reference
|
|||||||
---
|
---
|
||||||
# Project Yukart Overview
|
# Project Yukart Overview
|
||||||
|
|
||||||
|
**The goal:** take the car's telemetry, add more of our own, and do the calculations off the car's
|
||||||
|
ECUs, so the results inform the **driver**. Yukart controls the driver's information, never the car
|
||||||
|
(architecture decision 31).
|
||||||
|
|
||||||
A Mitsubishi Z27AG Colt Ralliart, plus
|
A Mitsubishi Z27AG Colt Ralliart, plus
|
||||||
|
|
||||||
A Jetson Orin Nano that can:
|
A Jetson Orin Nano that can:
|
||||||
@@ -13,4 +17,4 @@ A Jetson Orin Nano that can:
|
|||||||
- voice recognition with local `whisper`
|
- voice recognition with local `whisper`
|
||||||
- cloud LLM-based reasoning
|
- cloud LLM-based reasoning
|
||||||
- navigation capabilities using GCP APIs (or equivalent)
|
- navigation capabilities using GCP APIs (or equivalent)
|
||||||
- vision via camera module (pending, low priority)
|
- vision via a USB camera (IMX662; on the car unit, roles and inference pending)
|
||||||
@@ -32,10 +32,11 @@ Planned repositories and the reasoning behind the split are described in
|
|||||||
|
|
||||||
| Part | Role |
|
| Part | Role |
|
||||||
|---|---|
|
|---|---|
|
||||||
| NVIDIA Jetson Orin Nano Super (`mikkeli-orin-nano-2`) | Main compute, JetPack r36.4 / Ubuntu 22.04 |
|
| NVIDIA Jetson Orin Nano Super (`mikkeli-orin-nano`) | Car unit: main compute, JetPack r36.4 / Ubuntu 22.04 |
|
||||||
| PEAK PCAN-USB | Car CAN bus access (`can1`, 500 kbps) |
|
| NVIDIA Jetson Orin Nano Super (`mikkeli-orin-nano-2`) | Development unit: builds, CI, bench (see [REPOS](docs/REPOS.md)) |
|
||||||
| Onboard MTTCan | Second CAN interface (`can0`) |
|
| PEAK PCAN-USB | Car CAN bus access (`can_pcan`, 500 kbps measured at the OBD port; nothing transmits on it; OBD-II is on the K-line, through a Bluetooth ELM327) |
|
||||||
| Waveshare SIM7600G-H-M.2 | LTE Cat 4 + GNSS |
|
| Onboard MTTCan | Second CAN interface (`can_onboard`) |
|
||||||
|
| Waveshare SIM7600G-H-M.2 | LTE Cat 4 (povo, fallback uplink below WiFi/Ethernet) + GNSS |
|
||||||
|
|
||||||
Details and rationale live in the environment repo's
|
Details and rationale live in the environment repo's
|
||||||
[bill of materials](gitea:yukart/orin-nano-env-setup).
|
[bill of materials](gitea:yukart/orin-nano-env-setup).
|
||||||
|
|||||||
+65
-16
@@ -16,12 +16,13 @@ rest down.
|
|||||||
## Shape
|
## Shape
|
||||||
|
|
||||||
```
|
```
|
||||||
CAN (can1) ──▶ can-bridge ──┐
|
CAN ──▶ can-bridge ──┐
|
||||||
OBD-II ──▶ obd-poller ─┤
|
OBD-II ──▶ obd-poller ─┤
|
||||||
GNSS ──▶ gnss ├──▶ message bus ──┬──▶ recorder (disk)
|
GNSS ──▶ gnss ├──▶ message bus ──┬──▶ recorder (disk)
|
||||||
camera ──▶ vision* ─┘ ├──▶ hud (local video out)
|
camera ──▶ vision* ─┘ └──▶ telemetry-gw ──▶ remote displays (tablet app)
|
||||||
└──▶ telemetry-gw ──▶ remote displays
|
|
||||||
* later phase, separate task
|
* later phase, separate task
|
||||||
|
|
||||||
|
The Orin runs headless: no local video out. The tablet app is the display.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Services
|
## Services
|
||||||
@@ -29,12 +30,13 @@ rest down.
|
|||||||
| Service | Responsibility | Transmits on CAN |
|
| Service | Responsibility | Transmits on CAN |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `can-bridge` | Read SocketCAN frames, timestamp, publish raw | No |
|
| `can-bridge` | Read SocketCAN frames, timestamp, publish raw | No |
|
||||||
| `obd-poller` | Active OBD-II PID request/response | **Yes** |
|
| `obd-poller` | Active OBD-II PID request/response, on the K-line through the Bluetooth ELM327 (decision 30); never on CAN | **Yes** |
|
||||||
| `gnss` | gpsd client → position, speed, time fix | No |
|
| `gnss` | gpsd client → position, speed, time fix | No |
|
||||||
| `decoder` | Raw frames → named signals via DBC | No |
|
| `decoder` | Raw frames → named signals via DBC | No |
|
||||||
| `recorder` | Persist raw + decoded sessions to disk | 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 |
|
| `telemetry-gw` | Serve external display clients | No |
|
||||||
| `vision` | Camera capture and on-device inference (later) | No |
|
| `orin-status` | The Orin's own health: temperatures, CPU/GPU load, memory, network links, UPS battery → `yukart/host/*` | 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
|
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
|
(kernel modules, device permissions, gpsd) are the environment repo's job, not
|
||||||
@@ -50,7 +52,8 @@ protocol, so it necessarily transmits onto a 2008 car's bus. Constraints that
|
|||||||
follow:
|
follow:
|
||||||
|
|
||||||
- `can-bridge` runs listen-only and must never transmit.
|
- `can-bridge` runs listen-only and must never transmit.
|
||||||
- `obd-poller` is the **single writer**. No other service opens a TX-capable socket.
|
- `obd-poller` is the **single writer**: the only service that sends anything to the car, OBD-II
|
||||||
|
requests on the K-line through the ELM. No service transmits on CAN; none opens a TX-capable CAN socket.
|
||||||
- Transmission is rate-limited and restricted to standard OBD-II diagnostic IDs.
|
- Transmission is rate-limited and restricted to standard OBD-II diagnostic IDs.
|
||||||
- TX is a distinct, explicitly enabled mode — not the default state on boot.
|
- TX is a distinct, explicitly enabled mode — not the default state on boot.
|
||||||
|
|
||||||
@@ -59,7 +62,7 @@ traffic; discovered signals are recorded as a DBC rather than as code.
|
|||||||
|
|
||||||
## Message bus
|
## Message bus
|
||||||
|
|
||||||
**Proposed: MQTT (mosquitto) on the Jetson.** Rationale:
|
**MQTT (mosquitto) on the Jetson**, listening on localhost only: displays reach the bus through `svc-gw`. Rationale:
|
||||||
|
|
||||||
- First-class client libraries on every display target — ESP32 (Arduino
|
- First-class client libraries on every display target — ESP32 (Arduino
|
||||||
`PubSubClient` / ESP-IDF) and Android (Paho) — which is the deciding factor.
|
`PubSubClient` / ESP-IDF) and Android (Paho) — which is the deciding factor.
|
||||||
@@ -73,6 +76,7 @@ traffic; discovered signals are recorded as a DBC rather than as code.
|
|||||||
yukart/can/raw high-rate raw frames, not retained
|
yukart/can/raw high-rate raw frames, not retained
|
||||||
yukart/vehicle/<signal> decoded signals (rpm, speed, coolant, …), retained
|
yukart/vehicle/<signal> decoded signals (rpm, speed, coolant, …), retained
|
||||||
yukart/gnss/fix position/speed/time, retained
|
yukart/gnss/fix position/speed/time, retained
|
||||||
|
yukart/host/<signal> the Orin itself: temperatures, load, links, UPS battery, retained
|
||||||
yukart/system/<service> health and status, retained
|
yukart/system/<service> health and status, retained
|
||||||
yukart/nav/guidance next manoeuvre, distance, street name, retained
|
yukart/nav/guidance next manoeuvre, distance, street name, retained
|
||||||
yukart/nav/route active route geometry, retained
|
yukart/nav/route active route geometry, retained
|
||||||
@@ -84,6 +88,8 @@ Payloads: a self-describing JSON envelope for decoded signals; compact binary fo
|
|||||||
Every envelope carries **two clocks**. `monotonic` is authoritative for ordering
|
Every envelope carries **two clocks**. `monotonic` is authoritative for ordering
|
||||||
and intervals; `timestamp` is the absolute reference and may step, since the car
|
and intervals; `timestamp` is the absolute reference and may step, since the car
|
||||||
boots without network and NTP corrects the clock once LTE connects mid-drive.
|
boots without network and NTP corrects the clock once LTE connects mid-drive.
|
||||||
|
The car unit has no RTC battery, so until then the clock sits at roughly the
|
||||||
|
last shutdown time — offset by however long the car was parked.
|
||||||
|
|
||||||
Topic names, payload schemas and the signal registry live in
|
Topic names, payload schemas and the signal registry live in
|
||||||
[contracts](gitea:yukart/contracts).
|
[contracts](gitea:yukart/contracts).
|
||||||
@@ -140,7 +146,33 @@ pressure, upstream plain request/response.** That is decision 9 generalised from
|
|||||||
route requests to everything the app sends.
|
route requests to everything the app sends.
|
||||||
|
|
||||||
The app always initiates. The Orin listens and never dials out to a display, so
|
The app always initiates. The Orin listens and never dials out to a display, so
|
||||||
reconnection is entirely the client's problem.
|
re-dialling is the client's job. Noticing the loss and redelivering is the Orin's.
|
||||||
|
|
||||||
|
### Link drops are routine, not exceptional
|
||||||
|
|
||||||
|
Measured on the USB path (tablet ↔ PD hub ↔ Orin, 2026-10-09): cutting PD power
|
||||||
|
resets the hub's data port, and the link is back within ~3.5 s with no
|
||||||
|
intervention; restoring it costs ~2.2 s (a gadget reconnect, no detach). In the car that happens at every ignition change, cranking
|
||||||
|
included. Longer outages measured early on were the tablet sleeping on battery,
|
||||||
|
not the link. Design for drops as normal operation. Requirements, not yet implemented:
|
||||||
|
|
||||||
|
- **Failure detection on the Orin.** An app-level heartbeat with a timeout of a
|
||||||
|
few seconds, not TCP alone. A connection on a link that went down can stay
|
||||||
|
half-open long after the app has re-dialled. Reap dead connections and their
|
||||||
|
per-client queues promptly. Link state doesn't help either: a tablet that has
|
||||||
|
fallen asleep on battery leaves the Orin's USB interface up while answering
|
||||||
|
nothing.
|
||||||
|
- **The app keeps the tablet awake while it is the display.** Off charger, Android
|
||||||
|
suspends about a minute after screen-off. Then the link is gone in every way that
|
||||||
|
matters. Kept awake, a PD change costs only 2–3.5 s (measured).
|
||||||
|
- **Downstream (latest-wins):** unchanged. On reconnect, push current state; no
|
||||||
|
resend, since stale samples are worthless.
|
||||||
|
- **Reliable channel (TTS chunks, acknowledgements):** pending items survive a
|
||||||
|
drop. Redeliver on reconnect, or expire by age (an utterance that is several
|
||||||
|
seconds late is better dropped than played). Within the window, a reconnect
|
||||||
|
should resume rather than start over.
|
||||||
|
- **Upstream retries cross reconnects**, so commands carry an idempotency key.
|
||||||
|
At-least-once delivery then never means acting twice.
|
||||||
|
|
||||||
### Cloud links are outbound-initiated
|
### Cloud links are outbound-initiated
|
||||||
|
|
||||||
@@ -183,13 +215,21 @@ rewrite: the seam stays visible even while the process is one.
|
|||||||
## Display contract
|
## Display contract
|
||||||
|
|
||||||
**Every display is data-only.** The Orin sends no video to anything. Each device
|
**Every display is data-only.** The Orin sends no video to anything. Each device
|
||||||
subscribes to the bus and renders itself.
|
subscribes to the bus and renders itself. The one proposed exception is the
|
||||||
|
camera's live view (decision 27): a paced video stream beside the boundary,
|
||||||
|
never on the bus.
|
||||||
|
|
||||||
| Path | Transport | Notes |
|
| Path | Transport | Notes |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `app-flutter` | USB (RNDIS), or WiFi | The display. Full client, richest UI, also the audio sink |
|
| `app-flutter` | USB (NCM: tablet is host, Orin at `192.168.55.1`), or WiFi | The display. Full client, richest UI, also the audio sink |
|
||||||
| `app-esp32` + RLCD/e-ink | WiFi, or USB/BT serial | Low-rate glanceable subset. Not in current scope |
|
| `app-esp32` + RLCD/e-ink | WiFi, or USB/BT serial | Low-rate glanceable subset. Not in current scope |
|
||||||
|
|
||||||
|
Over USB, Android sends app traffic only via its *default* network, and the
|
||||||
|
USB link, having no internet, never becomes default while WiFi or cellular is
|
||||||
|
up. `app-flutter` must request the Ethernet network (`TRANSPORT_ETHERNET`) and
|
||||||
|
bind to it. The Orin's USB-C port can't charge the tablet (no USB PD), so in the
|
||||||
|
car the link runs through a PD hub that feeds the tablet from the car charger.
|
||||||
|
|
||||||
Displays reaching the bus over IP connect to the broker through `svc-gw`, which
|
Displays reaching the bus over IP connect to the broker through `svc-gw`, which
|
||||||
owns the boundary. For transports without IP — BT SPP, plain USB serial —
|
owns the boundary. For transports without IP — BT SPP, plain USB serial —
|
||||||
`svc-gw` bridges serial ↔ bus rather than each device inventing its own protocol.
|
`svc-gw` bridges serial ↔ bus rather than each device inventing its own protocol.
|
||||||
@@ -259,10 +299,11 @@ They are never committed. `tool-mapmint` holds the scripts that fetch and build
|
|||||||
them, plus a report of each build's provenance; `svc-nav` consumes the output. The
|
them, plus a report of each build's provenance; `svc-nav` consumes the output. The
|
||||||
artifacts themselves live outside git and never touch the workstation-side repo.
|
artifacts themselves live outside git and never touch the workstation-side repo.
|
||||||
|
|
||||||
They reach the car by `rsync` **over the LAN only, never over LTE** — the IoT SIM
|
They reach the car by `rsync` **over the LAN only, never over LTE** — the metered
|
||||||
is not a sensible transport for a multi-gigabyte tile set. Gate the sync on a
|
povo SIM is not a sensible transport for a multi-gigabyte tile set. Gate the sync on a
|
||||||
reachable LAN target rather than on an SSID, so the check validates the thing it
|
reachable LAN target rather than on an SSID, so the check validates the thing it
|
||||||
actually needs, and treat NetworkManager's metered flag as a second guard.
|
actually needs, and treat NetworkManager's metered flag as a second guard (set on the `povo`
|
||||||
|
connection).
|
||||||
Cadence is quarterly or on annoyance; road geometry does not move fast enough to
|
Cadence is quarterly or on annoyance; road geometry does not move fast enough to
|
||||||
justify anything more.
|
justify anything more.
|
||||||
|
|
||||||
@@ -312,11 +353,14 @@ rewriting the document.
|
|||||||
| 1 | Separate services over a message bus, not one process | Accepted | 2026-08-14 |
|
| 1 | Separate services over a message bus, not one process | Accepted | 2026-08-14 |
|
||||||
| 2 | Displays are bus subscribers; no display-specific logic upstream | Accepted | 2026-08-14 |
|
| 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 |
|
| 3 | Listen-only sniffing + active OBD-II, single writer | Accepted | 2026-08-14 |
|
||||||
| 4 | MQTT as the bus | **Proposed** | 2026-08-14 |
|
| 4 | MQTT as the bus; raw CAN rate confirmed on the car Orin (svc-core `BUS-BENCH.md`) | Accepted | 2026-10-12 |
|
||||||
| 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 |
|
| 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 |
|
| 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 |
|
| 23 | Services build natively on the development Orin; both units stay on the same L4T release | Accepted | 2026-09-05 |
|
||||||
|
| 24 | Link drops are routine: `svc-gw` detects dead clients by heartbeat and redelivers or expires pending reliable traffic; upstream commands are idempotent | **Proposed** | 2026-10-09 |
|
||||||
|
| 25 | The recorder reads SocketCAN and gpsd directly; raw capture never depends on the broker | Accepted | 2026-10-12 |
|
||||||
|
| 26 | Boundary v0 carries both channels of decision 15 on one WebSocket, each keeping its own semantics (drop-oldest down, acked and idempotent up) | **Proposed** | 2026-10-09 |
|
||||||
| 6 | Local logging first; off-board LTE telemetry deferred | Accepted | 2026-08-14 |
|
| 6 | Local logging first; off-board LTE telemetry deferred | Accepted | 2026-08-14 |
|
||||||
| 7 | Camera/AI inference is a later, separate task | Accepted | 2026-08-14 |
|
| 7 | Camera/AI inference is a later, separate task | Accepted | 2026-08-14 |
|
||||||
| 8 | Self-hosted offline navigation (MapLibre GL + Valhalla + PMTiles) over a cloud maps SDK | Accepted | 2026-09-03 |
|
| 8 | Self-hosted offline navigation (MapLibre GL + Valhalla + PMTiles) over a cloud maps SDK | Accepted | 2026-09-03 |
|
||||||
@@ -332,3 +376,8 @@ rewriting the document.
|
|||||||
| 18 | Stubs publish the real topic and schema, never a fixture-specific one | Accepted | 2026-09-03 |
|
| 18 | Stubs publish the real topic and schema, never a fixture-specific one | Accepted | 2026-09-03 |
|
||||||
| 19 | A service mocks itself behind its own input interface; there is one implementation of each contract | Accepted | 2026-09-04 |
|
| 19 | A service mocks itself behind its own input interface; there is one implementation of each contract | Accepted | 2026-09-04 |
|
||||||
| 20 | Every display is data-only. Directly-driven screens are for permanently installed, car-powered panels only | Accepted | 2026-09-04 |
|
| 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 |
|
||||||
|
| 30 | The car's CAN broadcast is the source for every displayed vehicle value. OBD-II (K-line, ISO 9141-2, through the Bluetooth ELM327) is calibration, recorded beside the frames; a value proven absent from CAN may be displayed from OBD-II, and keeps its `obd_` name either way. GNSS speed is shown beside the decoded speed, to show their error. The ELM is the Orin's only way onto the K-line, so DTC scans go through it too; it is reached over classic Bluetooth serial (SPP, no pairing), BLE only if it proves as easy | Accepted | 2026-10-13 |
|
||||||
|
| 31 | Yukart informs the driver; it never controls the car. It extracts the car's telemetry (CAN, OBD-II), adds its own (GNSS, IMU, camera), and does its calculations off the car's ECUs; the results go to the driver (display, voice). No actuation of any kind (locks, lights, throttle), by CAN transmit or by wiring into the car's switches. Asking is allowed: OBD-II requests over the K-line (live data, DTC reads) are active but leave the ECU as it was. Anything that changes an ECU's state is not (no actuator tests, no coding), with one exception: **DTC clearing** (mode 04) is allowed as a deliberate, driver-initiated action at the driver's own risk. It is never automatic, it asks for confirmation, and it reads and records the codes and freeze frame first, since mode 04 wipes them along with readiness. Decision 3's "no TX on CAN" follows from this rather than only from caution | Accepted | 2026-10-15 |
|
||||||
|
|||||||
+17
-5
@@ -17,10 +17,10 @@ repo, so it lives in the hub rather than in any one of them.
|
|||||||
─────────────────────────────────────────────────────────
|
─────────────────────────────────────────────────────────
|
||||||
serving svc-gw, svc-nav
|
serving svc-gw, svc-nav
|
||||||
processing decoder, recorder
|
processing decoder, recorder
|
||||||
acquisition can-bridge, obd-poller, gnss
|
acquisition can-bridge, obd-poller, gnss, imu
|
||||||
bus mosquitto
|
bus mosquitto
|
||||||
─────────────────────────────────────────────────────────
|
─────────────────────────────────────────────────────────
|
||||||
host kernel modules, gpsd, PipeWire, network orin-nano-env-setup
|
host kernel modules, gpsd, ALSA, network orin-nano-env-setup
|
||||||
```
|
```
|
||||||
|
|
||||||
A layer may depend on the one below it and on the bus. Nothing depends sideways:
|
A layer may depend on the one below it and on the bus. Nothing depends sideways:
|
||||||
@@ -40,7 +40,7 @@ have taken their share.
|
|||||||
| Valhalla server | small; tiles are **mmap'd page cache** | evictable — see below |
|
| Valhalla server | small; tiles are **mmap'd page cache** | evictable — see below |
|
||||||
| Whisper | 273 MB (tiny) → 3.9 GB (large) | `small` is 244 M params; `large` does not fit |
|
| Whisper | 273 MB (tiny) → 3.9 GB (large) | `small` is 244 M params; `large` does not fit |
|
||||||
| GPT-SoVITS | upstream guidance assumes an 8 GB VRAM card | the largest unknown; **measure before designing around it** |
|
| GPT-SoVITS | upstream guidance assumes an 8 GB VRAM card | the largest unknown; **measure before designing around it** |
|
||||||
| vision (Phase 5) | — | competes directly with voice for the same pool |
|
| vision (Phase 5) | capture and H.264: small; inference: — | capture + software H.264 costs ~1.3–2 of 6 CPU cores at 1080p (no NVENC on the Orin Nano); inference competes directly with voice for the same pool |
|
||||||
|
|
||||||
Four rules follow.
|
Four rules follow.
|
||||||
|
|
||||||
@@ -68,8 +68,8 @@ Ordered by what is lost if a tier is late.
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| 0 | kernel modules, gpsd, network | Host layer; nothing of ours can precede it |
|
| 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 |
|
| 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 |
|
| 2 | `decoder`, `gnss`, `imu`, `obd-poller`, `orin-status` | 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 |
|
| 4 | voice — ASR, TTS | Interactive; seconds late is harmless |
|
||||||
|
|
||||||
**Capture first** is the whole principle. A CAN frame missed at tier 1 is gone
|
**Capture first** is the whole principle. A CAN frame missed at tier 1 is gone
|
||||||
@@ -161,6 +161,11 @@ play" player.** It works, and it forecloses streaming permanently — the receiv
|
|||||||
has no way to begin before the last byte. The chunk boundary is what keeps both
|
has no way to begin before the last byte. The chunk boundary is what keeps both
|
||||||
TTS modes available.
|
TTS modes available.
|
||||||
|
|
||||||
|
What *is* fine early on: a receiver that buffers chunks and plays on the
|
||||||
|
terminator. The wire format is already the streaming one, so moving to playback
|
||||||
|
on the first chunk is a change to the receiver alone. Streaming playback is the
|
||||||
|
end state.
|
||||||
|
|
||||||
### The transport is testable with no real TTS
|
### The transport is testable with no real TTS
|
||||||
|
|
||||||
Audio is the lowest-priority part of the system, but its transport does not have
|
Audio is the lowest-priority part of the system, but its transport does not have
|
||||||
@@ -180,6 +185,13 @@ Whisper runs on the Orin, so its input does too. Audio *in* on one box and audio
|
|||||||
*out* on the other is asymmetric on purpose: each side does the simple thing, and
|
*out* on the other is asymmetric on purpose: each side does the simple thing, and
|
||||||
a microphone has no reason to reach the head unit.
|
a microphone has no reason to reach the head unit.
|
||||||
|
|
||||||
|
The USB camera has a microphone (48 kHz stereo, ALSA card `U1080P`): a candidate
|
||||||
|
input, if its placement and quality suit speech. Untested.
|
||||||
|
|
||||||
|
The Orin runs headless with no sound server: capture reads the ALSA device
|
||||||
|
directly. If a service ever needs PipeWire, run it for that service's user
|
||||||
|
(lingering), not as a desktop session.
|
||||||
|
|
||||||
### Degrade when there is no sink
|
### Degrade when there is no sink
|
||||||
|
|
||||||
With no tablet connected the Orin can still hear — the mic is local — but cannot
|
With no tablet connected the Orin can still hear — the mic is local — but cannot
|
||||||
|
|||||||
+19
-102
@@ -24,16 +24,18 @@ contracts repo, not copied into each consumer.
|
|||||||
| [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup) | Jetson host bring-up |
|
| [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup) | Jetson host bring-up |
|
||||||
| [app-flutter](gitea:yukart/app-flutter) | Flutter display client |
|
| [app-flutter](gitea:yukart/app-flutter) | Flutter display client |
|
||||||
| [contracts](gitea:yukart/contracts) | Topic namespace, payload schemas, signal registry |
|
| [contracts](gitea:yukart/contracts) | Topic namespace, payload schemas, signal registry |
|
||||||
| [svc-gw](gitea:yukart/svc-gw) | Telemetry gateway — aggregation and the HTTP surface |
|
| [svc-core](gitea:yukart/svc-core) | Acquisition + processing cluster: `recorder`, `decoder`, `gnss`, `imu`, `orin-status`, `obd-poller` now; `can-bridge` 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-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-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 |
|
| [tool-mock](gitea:yukart/tool-mock) | Mocks and fixtures — a fake car on a workstation |
|
||||||
|
| [tool-crosscheck](gitea:yukart/tool-crosscheck) | Consistency checks across all yukart repos; a reusable workflow every repo runs on push |
|
||||||
|
|
||||||
## Planned
|
## Planned
|
||||||
|
|
||||||
| Repository | Role | Why separate |
|
| Repository | Role | Why separate |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `svc-core` | Acquisition + processing cluster: `can-bridge`, `obd-poller`, `gnss`, `decoder`, `recorder` | Developed and deployed as one unit on the Orin |
|
|
||||||
| `app-esp32` | ESP32 RLCD/e-ink display firmware (Class B) | Embedded toolchain, flashed not deployed |
|
| `app-esp32` | ESP32 RLCD/e-ink display firmware (Class B) | Embedded toolchain, flashed not deployed |
|
||||||
| `tool-analysis` | Offline session analysis, reverse-engineering notebooks | Runs off-car, no runtime coupling |
|
| `tool-analysis` | Offline session analysis, reverse-engineering notebooks | Runs off-car, no runtime coupling |
|
||||||
|
|
||||||
@@ -133,7 +135,8 @@ docs/BOM.md only where the repo owns hardware
|
|||||||
|
|
||||||
| Where | Language | Why it is there |
|
| 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 |
|
| `tool-*` — off-car | **Python** | Batch work where development speed is the only constraint that binds |
|
||||||
| `app-flutter` | **Dart** | Flutter |
|
| `app-flutter` | **Dart** | Flutter |
|
||||||
|
|
||||||
@@ -153,8 +156,12 @@ Two Orin Nanos, and they have different jobs:
|
|||||||
|
|
||||||
| Unit | Role |
|
| Unit | Role |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Development Orin | Build, CI runner, bench testing. Always powered, on the LAN |
|
| Development Orin (`mikkeli-orin-nano-2`) | Build, CI runner, bench testing. Always powered, on the LAN |
|
||||||
| Car Orin | Deployment target only |
|
| Car Orin (`mikkeli-orin-nano`) | Deployment target only |
|
||||||
|
|
||||||
|
The SIM7600 LTE/GNSS hat lives in the car unit only — its custom enclosure doesn't
|
||||||
|
move between units. Anything touching the modem or gpsd is tested on the car unit;
|
||||||
|
on the development unit, `gnss` runs against its own mock input (decision 19).
|
||||||
|
|
||||||
⚠ **The guarantee is version parity, not architecture.** An aarch64 binary built
|
⚠ **The guarantee is version parity, not architecture.** An aarch64 binary built
|
||||||
on a newer userland still fails to load on an older one. A JetPack or Ubuntu
|
on a newer userland still fails to load on an older one. A JetPack or Ubuntu
|
||||||
@@ -209,6 +216,13 @@ because everything above 2.30 comes from one event: glibc 2.34 merged
|
|||||||
`libpthread` and `libdl` into `libc` and re-versioned the moved symbols. Stable,
|
`libpthread` and `libdl` into `libc` and re-versioned the moved symbols. Stable,
|
||||||
but not guaranteed the way a native build is.
|
but not guaranteed the way a native build is.
|
||||||
|
|
||||||
|
⚠ **`std::process::Command` breaks it.** Any binary that spawns a process links
|
||||||
|
`pidfd_spawnp` and `pidfd_getpid` (GLIBC_2.39) from the workstation's sysroot.
|
||||||
|
They are weak references, but the version requirement is not, so the car Orin
|
||||||
|
refuses to load the binary. `check-glibc.sh` catches it; such binaries build
|
||||||
|
natively (svc-core's `orin-status` runs `mmcli`, so its deploy script builds on
|
||||||
|
the development Orin).
|
||||||
|
|
||||||
### Writing style
|
### Writing style
|
||||||
|
|
||||||
Documents describe **what is and what will be**. They are not changelogs — git
|
Documents describe **what is and what will be**. They are not changelogs — git
|
||||||
@@ -253,100 +267,3 @@ for any particular repo**. The `type` discriminates:
|
|||||||
`status` and `updated` make a plan's staleness visible — plans go stale and
|
`status` and `updated` make a plan's staleness visible — plans go stale and
|
||||||
reports do not, and without them a plan from months ago reads identically to one
|
reports do not, and without them a plan from months ago reads identically to one
|
||||||
written this morning. `date` on a report records the moment it describes.
|
written this morning. `date` on a report records the moment it describes.
|
||||||
|
|
||||||
## Naming
|
|
||||||
|
|
||||||
Repositories are named `<group>-<thing>`, with **no project prefix**. The `yukart`
|
|
||||||
Gitea org already namespaces everything, so a `yukart-` prefix would be a constant
|
|
||||||
occupying the most significant characters of every name — pure noise in a sorted
|
|
||||||
index, and the reason the earlier scheme scattered.
|
|
||||||
|
|
||||||
| Group | Meaning |
|
|
||||||
|---|---|
|
|
||||||
| `svc-` | A service running on the Orin |
|
|
||||||
| `app-` | A display client, running off the Orin |
|
|
||||||
| `tool-` | Off-car tooling; never deployed to the car |
|
|
||||||
|
|
||||||
The goal is that **lexical order equals semantic grouping**: opening the org
|
|
||||||
listing puts every service together, every client together, every tool together.
|
|
||||||
The group segment does that work because it is the first thing that varies.
|
|
||||||
|
|
||||||
Ungrouped singletons — `contracts` — are fine where a category has exactly one
|
|
||||||
member and always will.
|
|
||||||
|
|
||||||
`project-yukart` and `orin-nano-env-setup` predate this and keep their names
|
|
||||||
rather than paying a rename for two repos that are stable and easy to find.
|
|
||||||
|
|
||||||
### Reusability
|
|
||||||
|
|
||||||
Nothing in a name marks a repo as reusable. A component that turns out to be
|
|
||||||
genuinely reusable outside Yukart **leaves the org** rather than being renamed —
|
|
||||||
location says it, not spelling.
|
|
||||||
|
|
||||||
## Helper and deployment repos
|
|
||||||
|
|
||||||
Arduino and ESP32 work implies flashing, build and deployment scaffolding that
|
|
||||||
is likely to be reused. Keep those generic and outside the org — e.g. an ESP32
|
|
||||||
project skeleton with the toolchain, flashing scripts and CI already wired up.
|
|
||||||
|
|
||||||
### Gitea template repositories
|
|
||||||
|
|
||||||
Gitea supports these: repo **Settings → Repository → Template**, after which the
|
|
||||||
repo offers **"Use this template"** to create new repos from it.
|
|
||||||
|
|
||||||
Use a template repo for **scaffolding** — a new ESP32 or Arduino project that
|
|
||||||
should start with the toolchain, directory layout and flashing scripts in place.
|
|
||||||
|
|
||||||
Do **not** use one for code you intend to keep updating in one place. Template
|
|
||||||
instantiation is a fork-at-create: the copy has no link back, so improvements to
|
|
||||||
the template never reach repos already created from it. For genuinely shared
|
|
||||||
code, use a git submodule or publish a package instead.
|
|
||||||
|
|
||||||
Rule of thumb: *template for the starting shape, submodule/package for living
|
|
||||||
code.*
|
|
||||||
|
|
||||||
## Linking convention
|
|
||||||
|
|
||||||
Cross-repo links use the `gitea:owner/repo` scheme, as established in the hub
|
|
||||||
README — e.g. `[Environment setup](gitea:yukart/orin-nano-env-setup)`.
|
|
||||||
|
|
||||||
## Per-repo document layout
|
|
||||||
|
|
||||||
Following the convention already set by `orin-nano-env-setup`:
|
|
||||||
|
|
||||||
```
|
|
||||||
README.md minimal portal, points everywhere else
|
|
||||||
docs/
|
|
||||||
GUIDELINES.md implementation practice for this repo
|
|
||||||
BOM.md hardware, where the repo owns hardware
|
|
||||||
impl/
|
|
||||||
plans/ what is intended
|
|
||||||
reports/ what actually happened, and why
|
|
||||||
```
|
|
||||||
|
|
||||||
The `impl/plans` ÷ `impl/reports` split is worth keeping: plans go stale,
|
|
||||||
reports do not. A report like `PCAN-SETUP.md` stays valuable precisely because
|
|
||||||
it records the investigation and the dead ends, not just the final commands.
|
|
||||||
|
|
||||||
### Scaffolding conventions
|
|
||||||
|
|
||||||
No template repository for this. Template instantiation is fork-at-create, so it
|
|
||||||
could not reach the repos that already exist, and the scaffolding is three
|
|
||||||
markdown files — lighter than the machinery to manage it. A template earns its
|
|
||||||
place where there is a real toolchain to carry, which is the ESP32 case above.
|
|
||||||
|
|
||||||
What every component repo has instead:
|
|
||||||
|
|
||||||
```
|
|
||||||
README.md frontmatter, the shared blurb, "What this repo is
|
|
||||||
about", then links to guidelines and impl/
|
|
||||||
docs/GUIDELINES.md frontmatter, numbered sections, ends "## N. To be added"
|
|
||||||
docs/impl/plans/ at least INITIAL.md
|
|
||||||
docs/impl/reports/
|
|
||||||
docs/BOM.md only where the repo owns hardware
|
|
||||||
```
|
|
||||||
|
|
||||||
**Frontmatter follows the document's nature, not its location:** living documents
|
|
||||||
carry `type: default`; historical records do not. So README, GUIDELINES, BOM and
|
|
||||||
plans have it, and reports do not. The hub carries none at all, being the portal
|
|
||||||
rather than a component.
|
|
||||||
|
|||||||
+87
-19
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
type: plan
|
type: plan
|
||||||
status: active
|
status: active
|
||||||
updated: 2026-09-04
|
updated: 2026-10-13
|
||||||
---
|
---
|
||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
@@ -12,12 +12,29 @@ something demonstrable in the car.
|
|||||||
|
|
||||||
Getting the Jetson to see the hardware at all.
|
Getting the Jetson to see the hardware at all.
|
||||||
|
|
||||||
- [x] PCAN-USB working as SocketCAN `can1` (out-of-tree `pcan.ko`, netdev build)
|
- [x] PCAN-USB working as SocketCAN (out-of-tree `pcan.ko`, netdev build)
|
||||||
|
- [x] Stable CAN names by udev: `can_pcan`, `can_onboard` — `canN` order raced at boot
|
||||||
|
- [x] Z27AG OBD-port CAN measured: 500 kbps, not gatewayed, 27 periodic IDs at
|
||||||
|
~890 frames/s; `can_pcan` comes up at 500k on hot-plug (verified 2026-10-13;
|
||||||
|
boot not yet seen), acknowledging rather than listen-only
|
||||||
- [x] SIM7600G-H-M.2 specified and documented
|
- [x] SIM7600G-H-M.2 specified and documented
|
||||||
- [ ] SIM7600 brought up: LTE data path and GNSS via gpsd
|
- [x] SIM7600 LTE data path: QMI (self-built `qmi_wwan`), povo, metered, ranked
|
||||||
- [ ] Kernel-update procedure for the out-of-tree PCAN module — it does not
|
below Ethernet/WiFi; failover tested, Tailscale stays reachable over LTE
|
||||||
survive kernel upgrades and will break silently
|
- [x] SIM7600 GNSS via gpsd (`/dev/sim7600-gps`): 3D fix in the car
|
||||||
|
- [x] Kernel-update procedure for the out-of-tree modules (`pcan`, `qmi_wwan`, `pl2303`) —
|
||||||
|
documented, sources kept in `~/infra`; still manual, and breaks silently
|
||||||
|
if forgotten
|
||||||
|
- [x] Network time pinned to NICT; timezone `Asia/Tokyo`
|
||||||
|
- [x] USB camera (IMX662, Sunplus UVC bridge) recognised on the car unit with
|
||||||
|
in-tree drivers: MJPEG up to 1080p60, microphone; measured in
|
||||||
|
orin-nano-env-setup `docs/impl/reports/USB-CAMERA.md`
|
||||||
|
- [ ] Optional: RTC backup battery (CR2032) — without it the clock boots stale
|
||||||
|
until NTP or GPS corrects it
|
||||||
- [ ] Power and mounting in the car: clean shutdown on ignition off
|
- [ ] Power and mounting in the car: clean shutdown on ignition off
|
||||||
|
(UPS module bench test 2026-10-09: carries the Orin ≥ 20 s across a PD
|
||||||
|
supply swap, enough to ride through cranking)
|
||||||
|
— plan in orin-nano-env-setup `docs/impl/plans/POWER.md`; open: the Orin
|
||||||
|
doesn't auto-start at ignition on while the UPS keeps it powered
|
||||||
|
|
||||||
Tracked in [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup).
|
Tracked in [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup).
|
||||||
|
|
||||||
@@ -25,29 +42,57 @@ Tracked in [orin-nano-env-setup](gitea:yukart/orin-nano-env-setup).
|
|||||||
|
|
||||||
Get real Z27AG traffic onto disk before designing anything around it.
|
Get real Z27AG traffic onto disk before designing anything around it.
|
||||||
|
|
||||||
|
- [x] `recorder` — raw CAN + GNSS sessions on the car unit (`yukart-recorder.service`):
|
||||||
|
starts on traffic or `ctl start`, ends after 15 s idle, marks via `ctl mark` or
|
||||||
|
the app; fsync'd every second, bounded at 50 GB, ~20 MB per hour of driving
|
||||||
|
- [ ] Off-board retention: sessions roll off the car unit once copied to dedicated
|
||||||
|
storage (halogen over tailnet/WiFi, or object storage such as S3), instead of
|
||||||
|
living on board until the cap. Not urgent: first checklist session was 6.2 MB
|
||||||
|
for 16 min, 423 GB free (2026-10-13)
|
||||||
- [ ] `can-bridge` — listen-only, timestamped frames onto the bus
|
- [ ] `can-bridge` — listen-only, timestamped frames onto the bus
|
||||||
- [ ] `recorder` — persist raw sessions
|
|
||||||
- [ ] Capture drive sessions across varied conditions (idle, cold start, moving,
|
- [ ] Capture drive sessions across varied conditions (idle, cold start, moving,
|
||||||
lights/indicators/windows actuated) with a written log of what was done when
|
lights/indicators/windows actuated) with a written log of what was done when
|
||||||
|
|
||||||
The manual event log matters more than it sounds: it is what makes the raw
|
The manual event log matters more than it sounds: it is what makes the raw
|
||||||
capture decodable later.
|
capture decodable later. Marks go into the session itself (`yukart-recorder ctl
|
||||||
|
mark …`, or the app's Mark button), on the same clock as the frames.
|
||||||
|
|
||||||
## Phase 2 — Decode
|
## Phase 2 — Decode
|
||||||
|
|
||||||
- [ ] Correlate captures against the event log; identify recurring frame IDs
|
- [ ] Correlate captures against the event log; identify recurring frame IDs —
|
||||||
- [ ] Build the Z27AG DBC incrementally in `contracts`
|
prior art and first candidates (engine speed in `0x210` / `0x13D`) in contracts
|
||||||
- [ ] `decoder` — raw frames to named signals
|
`docs/impl/reports/Z27AG-CAN-PRIOR-ART.md`
|
||||||
- [ ] `obd-poller` — active OBD-II PIDs, single writer, rate-limited
|
- [ ] Build the Z27AG signal set incrementally in `contracts` — in `signals.json`'s
|
||||||
- [ ] `gnss` — position/speed, useful as ground truth for validating decoded speed
|
`can` blocks, not a hand-kept DBC (one is generated if a tool needs it)
|
||||||
|
- [x] `decoder` — raw frames to named signals, 2026-10-13: svc-core `decoder`, laid out
|
||||||
|
only by the `can` blocks in contracts `signals.json` (rpm, accelerator, brake,
|
||||||
|
handbrake, indicators, side lights, dipped beam, doors, A/C compressor)
|
||||||
|
- [x] `obd-poller` — active OBD-II PIDs, single writer, 2026-10-12: mode 01, the
|
||||||
|
nine `obd2` signals (~10 Hz fast, 1 Hz slow), only while the bus is alive;
|
||||||
|
`can_pcan` out of listen-only. Recorded beside the frames (`vehicle.ndjson`).
|
||||||
|
2026-10-13: the Z27AG answers OBD-II only on the K-line (ISO 9141-2), so the
|
||||||
|
poller now goes through the Bluetooth ELM327 (classic serial, no pairing):
|
||||||
|
the `obd_*` signals, calibration references per decision 30
|
||||||
|
- [ ] OBD-II beyond mode 01: DTC scan (modes 03/07/0A) and MUT-III `21 xx`
|
||||||
|
requests, both needing multi-frame (ISO-TP) replies
|
||||||
|
- [ ] First decoding captures in the car, per contracts
|
||||||
|
`docs/impl/plans/Z27AG-CAPTURES.md` — first checklist session 2026-10-13
|
||||||
|
(117 marks, yukart-data `can-captures/`); jack-stands section still to do
|
||||||
|
- [x] `gnss` — position/speed, useful as ground truth for validating decoded speed,
|
||||||
|
2026-10-13: svc-core `gnss`, the fix as `yukart/gnss/<signal>` (speed, heading,
|
||||||
|
position, altitude, satellites); `yukart/gnss/fix` for navigation still to come
|
||||||
|
|
||||||
OBD-II gives standardised signals quickly and is worth doing early; it also
|
OBD-II gives standardised signals quickly and is worth doing early; it also
|
||||||
provides a cross-check for reverse-engineered ones.
|
provides a cross-check for reverse-engineered ones.
|
||||||
|
|
||||||
## Phase 3 — Bus and contracts
|
## Phase 3 — Bus and contracts
|
||||||
|
|
||||||
- [ ] Stand up the broker; confirm MQTT holds up at real CAN rates (decision 4
|
- [x] Stand up the broker, 2026-10-12: mosquitto 2.0.11 on the car Orin,
|
||||||
in [ARCHITECTURE.md](ARCHITECTURE.md) is still *proposed*)
|
localhost only. First publisher `orin-status` (svc-core,
|
||||||
|
`yukart/host/*`); `svc-gw` subscribes and serves it to the tablet
|
||||||
|
- [x] Confirm MQTT holds up at real CAN rates (`yukart/can/raw`), 2026-10-12:
|
||||||
|
no loss at a saturated bus, per frame or in 10 ms batches; batched costs
|
||||||
|
the broker ~1 % of a core (svc-core `BUS-BENCH.md`)
|
||||||
- [ ] Freeze v1 of topic names and payload schemas
|
- [ ] Freeze v1 of topic names and payload schemas
|
||||||
- [ ] `telemetry-gw` — external client access, including the serial bridge
|
- [ ] `telemetry-gw` — external client access, including the serial bridge
|
||||||
|
|
||||||
@@ -56,13 +101,36 @@ provides a cross-check for reverse-engineered ones.
|
|||||||
Order chosen by feedback speed, not ambition.
|
Order chosen by feedback speed, not ambition.
|
||||||
|
|
||||||
- [ ] One display end-to-end first, proving the contract — `app-flutter` is the
|
- [ ] One display end-to-end first, proving the contract — `app-flutter` is the
|
||||||
quickest to iterate on, and is the only display in current scope
|
quickest to iterate on, and is the only display in current scope.
|
||||||
|
Started: boundary v0 (contracts `docs/BOUNDARY.md`) between `svc-gw` on the
|
||||||
|
car unit and a text-readout starter app; not yet run on the tablet
|
||||||
- [ ] `app-esp32` — RLCD/e-ink glanceable subset. Deferred, no hardware in scope
|
- [ ] `app-esp32` — RLCD/e-ink glanceable subset. Deferred, no hardware in scope
|
||||||
|
|
||||||
## Phase 5 — Camera and inference · *separate task*
|
## Phase 5 — Camera, then inference
|
||||||
|
|
||||||
Deliberately deferred. It is the reason the compute is a Jetson rather than a
|
The camera is on the car unit (2026-10-11). Capturing and recording need no
|
||||||
Pi, but it shares nothing with the CAN work beyond the bus.
|
model and no GPU, so they can come before inference, which stays deferred: it is
|
||||||
|
the reason the compute is a Jetson rather than a Pi, but it shares nothing with
|
||||||
|
the CAN work beyond the bus, and it competes with voice for memory.
|
||||||
|
|
||||||
|
What the hardware gives (USB camera report): MJPEG only, ~7.3 MB/s at 1080p30;
|
||||||
|
no hardware video encoder on the Orin Nano, so H.264 is software, ~1.3–2 of 6
|
||||||
|
cores at 1080p; frame rate falls with the light.
|
||||||
|
|
||||||
|
- [ ] Decide the camera's roles and mounting: dash recording, live view on the
|
||||||
|
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 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)
|
||||||
|
- [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))
|
||||||
|
|
||||||
## Navigation · *parallel workstream*
|
## Navigation · *parallel workstream*
|
||||||
|
|
||||||
@@ -79,7 +147,7 @@ and `tool-mapmint` (artifact builds).
|
|||||||
- [ ] Pull a Japan basemap extract from the Protomaps daily build with
|
- [ ] Pull a Japan basemap extract from the Protomaps daily build with
|
||||||
`go-pmtiles`; static-serve it, with `localIdeographFontFamily` set for CJK
|
`go-pmtiles`; static-serve it, with `localIdeographFontFamily` set for CJK
|
||||||
- [ ] MapLibre page rendering the basemap from a replayed GNSS trace
|
- [ ] MapLibre page rendering the basemap from a replayed GNSS trace
|
||||||
- [ ] Moving map live in the car — **blocked on gpsd in Phase 0**
|
- [ ] Moving map live in the car (GNSS fix available on the car unit)
|
||||||
- [ ] Meili map matching over recorded sessions, as Phase 2 ground truth
|
- [ ] Meili map matching over recorded sessions, as Phase 2 ground truth
|
||||||
- [ ] Routing and turn-by-turn guidance onto `yukart/nav/guidance`
|
- [ ] Routing and turn-by-turn guidance onto `yukart/nav/guidance`
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user