← All skills

Skill

Software architecture

Designing how a system's parts fit together. Achilles V2 and V3, SKIDADDLE's controllers, scheduler and allocator, and one harness for every VLA.

4 log entries3 projects

AchillesBest example

V3: designing for performance

Commit

Also shows: C++Performance & SIMD

For V3, I wrote a full design document before writing any code. The target was concrete: simulate my club's six-robot arena of holonomic turret robots fast enough to train RL policies on a laptop CPU, since we don't have much GPU compute. Training takes millions of steps, so throughput was the goal, and V3 doubled as a performance learning exercise. One rule drove most decisions: only costs paid every frame matter. Only joint state, plus the contact solver's last solution, persists between frames. Everything else is either static data built at load time or scratch memory rebuilt each step and reused between passes. That rule became three pieces: fields; small operations that declare exactly which fields they read and write; and traversals that apply those operations across the joint tree, all checked at compile time. What the compiler can't check, the engine checks as it starts, and CI runs death tests confirming that a bad setup, like a model that doesn't fit its layout or an overflowing arena, stops it with a clear message instead of quietly corrupting a run. I kept V2's physically meaningful types but moved them into the math rather than the structure, so adding collisions or rendering meant adding passes instead of rewiring classes.

The V3 design: each frame runs eight passes in one direction (actuator torques, ABA forward dynamics, integration, kinematics, then broad phase, narrow phase, contact resolution and a debug render), and below it, which data is static, which persists between frames, and which is rebuilt each frame in a reused arena
V3 as designed. Collisions changed once I built them (see above).

Achilles

V2: types with physical meaning

Code

Also shows: Kinematics & DynamicsC++

A major part of my design philosophy for V2 was making sure every quantity had a physical meaning. Velocity, acceleration and force each had their own type, and they only combined in physically valid ways: an acceleration integrated into a velocity, and an inertia turned a force into an acceleration. Every link, joint and actuator also carried a typed frame, so frames belonging to different kinds of objects couldn't be mixed up. The core of this model was the joint system. Each joint is defined by its motion subspace S, and a CRTP template turns that into fixed-size math, including the articulated-inertia step, so each joint type can be specialized. Because Achilles was meant to model my club's robots, I also built sensor models, starting with an encoder, with Gaussian noise, Gauss–Markov drift, a low-pass filter and scale-factor error. But when I started planning collisions and rendering, I realized the rigid architecture and very specific type flow had built me into a corner.

Simulated encoder readings on one swinging joint: the true angle, then readings with white noise, Gauss–Markov drift, a 5 Hz low-pass filter, a 3% scale-factor error, and all of them combined, which drifts visibly away from the truth over time
Each line is a V2 sensor model reading the same joint, from the April 14 code brought back to life.

FIRST Tech Challenge

Reworking SKIDADDLE's control

Code

Also shows: Motion planningJava

Over winter break I took SKIDADDLE apart into pieces that each do one job. Controllers now only ask for accelerations: one along the path, one back onto it, one for heading, each with a weight and a priority. A scheduler runs whichever are active, and a default controller brings the robot to a stop when none are. A new allocator turns the requests into motor space and hands out each motor's remaining headroom, highest priority first and shared by weight within a priority, so correcting off the path can come before speeding along it without any motor going past its limits. The look-ahead, before the most expensive part of the loop, became a precompute: when a path is made, one backward pass finds the fastest speed the robot can enter each turn at, so the loop only reads it.

Diagram: once per path, a path generator feeds precomputed speed limits; every loop, controllers send requests to a scheduler, which feeds an allocator that drives the wheels
The follower after the rework.

Planning in motor space means a turn's sideways acceleration comes out of the same wheel budget as speeding up and slowing down. Road Runner limits acceleration only along the path, so on a tight hairpin at the same limits its plan asks for more grip than the wheels have, falls behind and overshoots the end. With its limit lowered to 30 in/s² it stays within 9 in; SKIDADDLE stays within 4.5 in and stops 0.7 s sooner. It isn't ahead everywhere: on sharp corners Road Runner with the lower limit tracks tighter, and the rework doesn't correct sideways error yet, so it can stop a few inches off the end point.

Four runs around the same hairpin. Road Runner at the shared 50 in/s² limit falls behind in the curve and overshoots the end by 22 in. With its limit lowered to 30 it strays 9.1 in and stops after 7.2 s. Pedro Pathing strays 8.1 in and stops after 7.2 s. SKIDADDLE strays 4.5 in, stops after 6.5 s, and ends 3.1 in from the end point
Simulated: one mecanum robot model and the same speed and acceleration limits for all three. Road Runner 1.0.1 and Pedro Pathing 3.0.2 run from their source; SKIDADDLE's plan is followed with feedforward and a position term.

Benchmarking VLAs for ARCHITECT

An evaluation harness for any VLA

Code

Also shows: PythonRobot learning

Every VLA ships its own server, and they don't agree on how to talk: openpi's π0 and π0.5 use WebSockets, GR00T uses ZMQ and MolmoAct2 uses HTTP. I wrote one harness that hides all of that behind a single interface: a new model, task or whole benchmark is a short YAML file, not new code.

On the robot, the harness asks the policy for a chunk of actions, runs eight of them open loop, then asks again, recording the scene and wrist cameras throughout. The operator scores each rollout, everything is saved as it goes so an interrupted run resumes, and one script turns the results into success rates per task and per prompt.

Diagram of the harness: YAML configs feed run_eval.py, which drives the DROID robot and asks one policy client for chunks of actions; the client reaches π0 and π0.5 over WebSocket, GR00T N1.7 over ZMQ and MolmoAct2 over HTTP; every rollout's scores and videos go to a results folder that compute_results.py turns into CSVs
How a run fits together.
π0.5 putting the banana on the plate.