Digital Twin: Designing an Actor-Based SDV Prototype in Rust (Milestone 1)

9 minute read Published: 2026-07-24

At a glance (1 minute)

As driving conditions change, lamps, wipers, and the cluster can all actuate as expected. What can still go wrong is not that response. It is that we cannot reconstruct what the software believed while it was happening: which signal arrived, from where, what changed, in what order — and whether that session can be replayed, without the car in front of us. This prototype is built to provide that reconstructable account.

The Digital Twin is that account, kept live in software. The work separates control from observation, the way a vehicle already separates actuation from the instrument cluster. A software Emulator plays the role of a test bench: it injects driving conditions such as speed, rain, and light onto the vehicle bus (CAN, the car's without nervous system). A Gateway, standing in for a gateway ECU, is the sole owner of that twin. Inside it, a coordinator called the Brain keeps body assemblies such as the headlamp and the wiper in a known driving mode. It also writes every decision to an event recorder on disk, so we can replay the session later and know — post-facto — what happened and when. A separate Dashboard is the instrument cluster and the workshop screen in one: it only watches, in a driver view and an engineer view, over a live diagnostic feed. It does not actuate any assembly (it can, if needs be).

Potential: I have built the twin as an Actor system — isolated, message-driven workers of the kind that have run telecom switches for decades — and I have written it in Rust, the most modern systems programming language, for memory and concurrency safety that a traditional C++ stack does not give us.

📌 Actor systems at scale.

Actor-system based software has been successfully used in

  • Telecom Switches (Ericsson),
  • HFT (Goldman Sachs), and
  • Siemens (industrial automation),

in the past.

That makes this work a candidate SIL (software-in-the-loop) testing and simulation component today: the twin, the bus, and the assemblies already form a virtual testbed which we can drive, watch, and replay.

[ Test-bench emulator ]
         │
         │  vehicle bus (CAN)
         v
┌─ Gateway (gateway-ECU role) ──────────┐
│                                       │
│   Digital Twin = the car in software  │──── live watch ───> [ Dashboard ]
│   Actor Brain + assemblies (Rust)     │
│   driving mode, event recorder        │
│                    │                  │
└────────────────────┼──────────────────┘
                     v
            [ Disk: replayable session log ]

The Architecture At a glance

Stage V has evolved into a multi-process setup on Linux CAN: the Gateway holds a Digital Twin (Brain, assembly actors, ROB, FSM, Observation Tee); a separate TUI Dashboard only observes; runs land on disk as JSONL. Control stays on vcan0; live telemetry uses UDS or Zenoh.

[ Emulator ] ────> (vcan0)  <───────> Wiper Actuator
                     │  ^       │
                     │  │       │
                     v  │       └ <───────> Headlamp Actuator
┌─ Gateway ──────────────────────┐
│ ┌─ Digital Twin ─────────────┐ │
│ │ Brain Actor                │ │
│ │ Assembly actors            │ │ ── (UDS / Zenoh) ──> [ Dashboard (TUI) ]
│ │ ROB · FSM                  │ │
│ │ Observation Tee            │ │
│ └──────────────┬─────────────┘ │
└────────────────┼───────────────┘
                 │
                 v
       [ Disk: JSONL Logs (replayable, schema-versioned)]

Quick snap

Dashboard live

Motivation

Modern Software-Defined Vehicles require a delicate balance: handling unpredictable, high-frequency events while maintaining deterministic, thread-safe system states across simulated actuators and components. To explore how modern systems programming can solve this, I have built a working Digital Car Twin prototype from scratch. By leveraging Rust’s memory safety guarantees and an asynchronous, actor-based architecture driven by #ractor, this project moves from a static vehicle description to a fully reactive runtime environment capable of managing component state transitions — and of publishing that runtime as observation streams, which a separate Dashboard can watch live. Here is a retrospective on how that architecture came together, the critical design choices made along the way, and what it takes to plumbing a modern automotive software, from a software programmer's PoV.


### 📂 Dive Into the Journey & Source Code

This capping post summarizes the engineering sprint through Stage V. If you want to track how this system evolved piece-by-piece, use the links below:

Source for the current baseline: [sdv_simulation_5](https://github.com/nsengupta/sdv_simulation_5) on GitHub (builds on [sdv_simulation_4](https://github.com/nsengupta/sdv_simulation_4)).


Core Architecture

To make this reactive model work without physical hardware, the architecture relies on a clean separation of concerns between a simulated environment, a protocol gateway, an isolated actor runtime, and an observation consumer:


Solving the Causal Ordering Challenge at the Edge

While an actor-based architecture provides an elegant, single-threaded processing guarantee per mailbox, a major architectural challenge emerged when integrating the asynchronous emulator: Causal Inversion.

The Problem: Out-of-Order 'Tell-Backs'

The simulator injects events randomly and these events reach the core Digital Twin Brain Actor's mailbox in any order. The Brain Actor often issues asynchronous instructions (Tell) to multiple sub-assemblies (children Actors). However, response events (Tell-Back) from these assemblies can arrive back at the brain actor's mailbox out of order or interleaved.

If processed naively, overlapping event processing breaks the state machine's determinism. It introduces interleaving anomalies into the transition ledger, destroying our ability to perform an authentic, offline replay of the vehicle's history.

The Solution: Implementing a Reorder Buffer (ROB)

To enforce deterministic state transitions and ensure the ledger narrates the absolute truth of event causality, I implemented a Reorder Buffer data structure directly within the single-threaded actor loop (Stage IV; still the coordination core in Stage V).


From Coordination to Observation (Stage V)

Stage IV proved multi-assembly coordination. Stage V asks a different question: once the twin’s decisions are truthful, how do we publish them without coupling presentation to the Brain?

The answer is emit-facts / format-at-the-edge. The twin and Gateway publish rich diagnostic and ledger streams; the Dashboard’s panes filter and glyph what a driver or an engineer needs to see. CAN stays first for the vehicle bus; Zenoh in this milestone is an observation carrier, not a bus replacement. Five processes share the stage: Gateway, Dashboard, Emulator, Headlamp actuator, and Wiper actuator.


What This Milestone Achieves & The Horizon Ahead

The Baseline

What this effort through Stage V establishes is a robust, software-only capture of how a vehicle behaves from start to stop — plus a live and on-disk observation surface of that behaviour. By using only #rustlang (actors via #ractor, TUI via #ratatui), and a lean automation layer of #bash scripts to spin up executables and collect telemetry, the prototype validates that real-time digital twin tracking can be achieved deterministically without relying on heavy proprietary suites. The current runnable baseline is [sdv_simulation_5](https://github.com/nsengupta/sdv_simulation_5).

The Roadmap

There is plenty of territory left to map. Moving forward, the natural progression deepens standards compliance and closes the loop from capture to offline reconstruction:

  1. Ecosystem Middleware: Abstracting more of the custom I/O layer with Eclipse SDV technologies such as uProtocol (transport-agnostic service communication). Zenoh is already in use for live observation in peer mode; evolving it toward a proper router topology (and broader pub/sub scaling) remains ahead.
  2. Deterministic Replay Tooling: Stage V already tees versioned observation files (manifest + diagnostic.jsonl + ledger.jsonl) as the capture substrate. The next step is a dedicated offline tool (or Dashboard mode) that plays those runs back without a live Gateway and reconstructs the Digital Twin’s behaviour step-by-step.
  3. Advanced FSM & Safety Refinements: Transitioning bare-minimum diagnosis mechanics into structured safety verification functions rather than loose conditional expressions; replacing match-expression guard patterns with dedicated Entry/Exit lifecycle functions in the state transition matrix; completing twin shutdown / disband so quit waits for Off before tearing down actors.
  4. Richer Live Surfaces: The #ratatui Dashboard is in place (driver / engineer / ledger panes). Ahead: visibility swatches, notice styling by diagnostic level, optional ASCII glyph fallbacks, and surfacing active ROB-turn counts from the Brain rather than ledger-hop stamps alone.