rocky_mini: A Robot's Brain, Built and Tested Without the Robot
An embodied AI character (Rocky, the Eridian engineer from Andy Weir's Project Hail Mary) built sim-first on a fully local, zero-cost stack. Every external service sits behind a Protocol with a Fake, so the whole four-latency-domain brain runs and passes 126 tests with no robot, no GPU, and no local LLM in the loop. Sim-tested; hardware bring-up pending.
rocky_mini is a personal homage project: Rocky, the Eridian engineer from Andy Weir's Project Hail Mary, role-played on a Reachy Mini Wireless robot from Pollen Robotics. He is framed as a curious alien child on Earth who learns and remembers across sessions and answers in chords and a ring-modulated voice. Rocky and the Eridians are Weir's creations, not mine, and the Reachy Mini is Pollen's hardware; I am naming both up front.
The reason this is a portfolio piece is not the fun premise. It is that I built and tested the entire software brain in simulation, without the robot, without the GPU, and without the local language model in the loop. The artifact worth pointing at is the structure that makes that possible, and the discipline that keeps it honest about what has actually run.
The seam that lets one codebase live in two worlds
Every external dependency (the language model, speech to text, text to speech, and the robot's own motion and audio surfaces) sits behind a Protocol. The sim and test paths use Fakes. A rule-based SimResponder plays Rocky well enough to drive a real conversation turn and the settings UI; the real path swaps in OllamaLLM behind the same interface. The imports for the real clients are lazy and guarded, so the sim genuinely never touches Ollama, faster-whisper, Piper, or the Reachy SDK.
The left column is code that runs and passes tests today. The right column is written against the same Protocols and documented for the machine with the GPU and the robot, but it has not executed in this build. Keeping those two columns separate is the point, not a disclaimer.
Four latency domains, one process
A talking robot is a real-time problem. If the acknowledgement is late, or the mouth and the voice drift, or two threads fight over a joint, the character breaks. Each of those failures lives in a different time budget, so each gets its own owner: one process runs four threads plus one asyncio loop. A MotionThread at 100 Hz is the single set_target owner, layering breathing, emotes, wobble, and a direction-of-arrival gaze, all clamped to safe limits. A Mixer is the single push_audio_sample owner, blending a ChordBus and a generation-tagged VoiceBus with ring modulation and soft clipping. AudioIn handles the microphone and voice activity detection. The ConversationLoop runs the language turn: an immediate ack chord, a streamed LLM, a chunker that inserts Rocky's tics, then text to speech. The two load-bearing rules are the ownership ones: exactly one thread writes motion, exactly one writes audio, and everything else asks.
Local and zero-cost, by constraint
There are no API keys anywhere. The brain is designed as Ollama-served Qwen2.5-7B-Instruct on an RTX 4080, with an optional Rocky LoRA; speech uses faster-whisper and Piper. The PC is the LAN brain server; the Reachy Mini (a Raspberry Pi CM4) is a thin client that runs no model. The LoRA is a fine-tuning pipeline with a runnable ship-gate eval and a small seed dataset, not a trained artifact: training needs a GPU and WSL2 that were not part of this build.
Honest status
The sim runs end to end. The settings UI works (sim chat, a fact table, a latency meter against a 2.5-second budget, memory export), the suite is green at 126 tests including a sim end-to-end pass and a real-browser Playwright pass, and motion, audio DSP, the brain loop, the naivety gate, memory, and choreography are all built and tested in sim. What is seamed but not run: the Reachy hardware, the real Ollama and speech stack, and LoRA training. Hardware bring-up and the soak-and-failure drills wait on the physical robot; the LoRA waits on a GPU. The next real test is putting the clamps against actual joints.
I wrote up the full methodology, with the concurrency walk-through and both diagrams, as a companion post: