Terms of the evolutionary algorithm, of the quadcopter-frame
domain it optimizes, and of the result pages themselves. Companion to
the gallery, the family tree page and the README.
the evolutionary algorithm
Genome / gene
The complete parametric description of one frame: 12 numbers that
morph the official Source One V6 plate drawings — arm
stretch/width/waist/thickness, front and rear sweep about the drawing's
bolt anchors, plate scales, deck gap, battery wedge, material. Scale genes
at ×1.00 reproduce the real V6 exactly. The genome is all the optimizer may
change — battery, motors, propellers and electronics are fixed kit.
Candidate / individual
One genome, built into a frame and evaluated. Identified everywhere by its
genome hash.
Genome hash
A stable 12-hex-digit ID derived (SHA-1) from the gene values rounded to
10−6. Identical genomes always get identical IDs, which is what makes
caching, resuming and lineage queries trivial.
Population
The set of candidates alive in one generation (default 16).
Generation
One full cycle: propose a population → evaluate every candidate in all
scenarios → select who breeds. Generation 0 is seeded: the real
Source One V6 7-inch DC itself (every scale gene at ×1.00, measured from
the official drawing) plus random design-space probes.
Fitness
The single scalar being minimized. Here: mean Wh/km across
scenarios + λ · worst-scenario Wh/km with λ = 0.5. The mean rewards
general efficiency; the worst-case term punishes designs that are great in five
scenarios and terrible in one. aggregation: minimax switches to pure
worst-case. Lower is better; invalid candidates get fitness = ∞.
Selection (tournament)
To pick a parent, draw k = 3 random candidates from
the previous generation and keep the fittest. Repeats per parent. Gentle,
noise-tolerant selection pressure.
Crossover (SBX)
Simulated Binary Crossover: a child gene is sampled around its two parents’
values with a spread controlled by the distribution index η (higher η = children
closer to the parents). The digital analogue of breeding two good frames.
Mutation
Gaussian noise added to a subset of genes. Its standard deviation
(sigma, a fraction of each gene’s range) is adaptive: it decays
per generation, so early search is exploratory and late search fine-tunes. The
recorded mutation magnitude is the RMS of the normalized perturbation.
Operator
How a candidate came to exist: seed, crossover,
mutation, immigrant, elite, pivot,
designer (or cmaes).
Stored per candidate and shown as edge colors in the family tree.
Elitism / elite carry-over
The top 2 candidates pass into the next generation unchanged, so the best
design can never be lost. Drawn as dotted vertical pass-through edges in
lineage.svg.
Random immigrant
An occasional entirely random genome injected into the population
(10 % per non-elite slot) to keep genetic diversity from collapsing.
Lineage / parentage
Every candidate records parent_a, parent_b, its operator, its
birth generation and mutation magnitude — a self-referencing SQLite table, so
airloom lineage <hash> walks the full ancestor chain
(WITH RECURSIVE) and shows how fitness improved along it.
Designer round
Every 6 generations a headless Claude receives the elites, their
per-scenario numbers and the invalid-design histogram, and proposes new
genome vectors as reasoned design hypotheses (operator
designer, violet edges in the family tree; rationales in
results/designer_log.md). Complements the GA's local operators
with non-local, physics-informed jumps.
Lab notebook (narrator)
After each generation, one headless-Claude call writes three short notes
per new candidate — hypothesis (the pre-flight “why”: what the
search had observed and what bet this design makes), method (what
concretely changed vs. the parents), result (outcome vs. parents and
best-so-far, or the constraint that killed it, plus an idea worth testing
next). Rule-based fallback notes are written instantly; the enrichment runs
off the loop's critical path. Shown in the gallery detail blocks and the
family-tree hover cards.
Patience / pivot
Plateau escape. If the best-so-far fails to improve by ≥0.5 % for
6 generations (the longest gap healthy searches showed was 4), the next
generation pivots: half the non-elite offspring are bred by crossing
a tournament winner with a far parent — the most genetically
distant still-decent candidate from the whole run — with mutation
temporarily boosted. Plateaus that survive another window escalate to fully
random far-parents. Pivot generations are marked ⟳ on the progress
chart and pivot edges are teal in the family tree.
Early-reject
Optional cost control: fly the cheap calm_warm scenario first; a
candidate much worse than the population median skips the other five scenarios
and receives a finite penalized fitness — evolution still gets gradient
information without paying full price.
Convergence
Fitness vs. generation (convergence.png): population median and
best-so-far falling and flattening as the search settles.
CMA-ES
Covariance Matrix Adaptation Evolution Strategy, the flag-gated alternative
optimizer (--optimizer cmaes). It samples each generation from one
adapted multivariate Gaussian, so candidates have no discrete parents;
distribution-level provenance (mean and step size σ per generation) is stored
instead, and the family tree is skipped.
Determinism / resume
Per-generation RNG seeds and fixed per-scenario turbulence seeds make
evaluations reproducible; after a crash or a graceful stop,
airloom run resumes the latest run by default (--fresh
forces a new one), continuing from the last completed generation and
re-using every already-evaluated genome via its hash.
the domain
the shape (what the genes mean)
Arm length / width / waist scale
Stretch and reshape the REAL Source One V6 arm outlines: the shaft
between the bolt tongue and the motor mount stretches (wheelbase, disk
clearance), its width scales, and the waist gene adds extra mid-shaft
narrowing (drag vs. stiffness). The tongue and the 16 × 19 mm motor
pattern never deform.
Arm thickness
Plate thickness of the arms (real V6: 6 mm carbon).
Front / rear sweep
Rotate each arm pair about its plate anchor. The front anchors are exact
bolt-pattern registrations from the official drawing (stock: 31.4°); left
and right arms keep their true mirrored chirality.
Plate length / width / thickness scale
Stretch the real main / mid / top deck plates with their cutouts and
holes; the 30.5 mm stack pattern stays pinned exactly.
Deck gap
Standoff length between mid and top plate (real: M3×30). Must fit the
FC/ESC stack; also sets the vertical margin between the rear props and the
top-plate corners.
Battery wedge (battery_wedge_deg)
Nose-down tilt of the cell pack on the top plate, trimming the biggest
bluff body in forward flight.
Material (gene)
Selects the frame material — plates and arms — from the platform
library: CNC-cut carbon plate, carbon-fiber nylon (PA12-CF), PET-CF, PLA+,
PETG or ASA. Each brings its own density, tensile strength and stiffness, so
the optimizer trades mass against the structural constraints. Printed values
are XY-direction datasheet numbers; layer-line weakness is only covered by
the global 1.5 safety factor.
the target metric
Wh/km (watt-hours per kilometer)
Electrical energy drawn from the battery per kilometer flown — the
quantity being minimized. Computed per scenario over the fixed 4 km mission,
then aggregated (see Fitness). Typical values here for the 7-inch
class: ≈ 3.5 Wh/km calm, ≈ 5–6 Wh/km in storm conditions.
Target & record lines
The two horizontal benchmarks on the gallery progress chart:
≈ 5.0 Wh/km aggregate — what current 7-inch long-range practice
(~8 g/W at cruise) works out to on this scenario portfolio — and
≈ 4.0 as a record-class stretch (ultralight record builds at
~2 Wh/km calm cruise). Both set in config/scenarios.yaml.
Mission
Identical in every scenario: 2 km north, then 2 km south, at 12 m/s
commanded ground speed, 30 m altitude. Flying both directions keeps
headwind/tailwind honest.
Scenario portfolio
Six weather cases every candidate must fly: calm_warm (baseline),
cold_headwind, storm (wind + severe gusts + 25 mm/h rain),
crosswind, gusty_light (severe gusts, light mean wind),
hot_thin (1500 m density altitude). Evaluating the portfolio prevents
overfitting the frame to one weather condition.
All-up mass (AUW)
Frame mass (the only variable part, from the genome) plus the fixed
0.78 kg of battery (470 g self-built 6S1P 21700 Li-Ion), four 2806-class
motors with props, FC stack, camera, VTX and receiver. The baseline deck
comes out around 1.0 kg AUW and hovers at ≈ 150 W on the measured
Master Airscrew GF 7×4 data.
constraints (fitness = ∞ when violated)
Deck gap vs. FC/ESC stack
The standoff gap between the plates must fit the FC/ESC stack (the
30.5 mm hole pattern itself stays pinned exactly in every morph).
Arm tongue interlock
The placed arm outlines must not overlap on the main plate — the real
V6's notch interlock is not redesigned, so colliding tongues invalidate the
design. Tends to bite when both sweeps sit near their minimums with wide
arms.
Tongue bolts on plate
Each arm tongue's bolt pair must land on main-plate material. Sweeps far
from stock need the plate scales to grow with them, or the bolts walk off
the plate.
Plate web width
Every morphed deck plate must keep ≥ 80 % of the stock
part’s narrowest material web between its holes, cutouts and outer
edge. The FC-stack holes stay pinned while the plates scale, so shrinking
plates would otherwise slide cutouts into the holes and collapse the webs
to unprintable slivers.
Plate thickness (printed)
Printed materials require ≥ 1.6 mm deck plates (CNC
carbon: ≥ 1.2 mm) — per-material floors set in
config/platform.yaml.
Rotor clearance
Rotor disks need ≥ 5 mm from each other, and the prop disks are
checked against the deck/battery in 3D (the real V6's rear props sweep
below the top-plate corners; the deck gap trades that margin).
Violations are still meshed and archived (_INVALID STLs) — the
failures are instructive — but never simulated.
Rotor saturation
If holding the commanded speed would require rotor speeds above the motor
ceiling (or per-motor power beyond its limit) for more than half a second
cumulative, the candidate fails that scenario.
Arm stress / deflection
Euler–Bernoulli cantilever check under the worst per-rotor thrust seen in
any scenario, times a 1.5 safety factor: root bending stress ≤ the
genome-selected material's tensile strength, and tip deflection ≤ 5 % of
arm length.
Resonance (1P)
The arm’s first bending natural frequency must sit outside ±15 % of the
rotor’s hover rotation frequency (the “1P” excitation), or vibration would
shake the frame apart.
the physics vocabulary
Advance ratio J, CT, CP
Propeller performance is tabulated as thrust and power coefficients versus
advance ratio J = V/(nD): thrust
T = ρn²D⁴CT, shaft power
P = ρn³D⁵CP. Ground truth here: the UIUC
Propeller Database measurements of the Master Airscrew GF 7×4 prop
(2-blade; the kit's 3-blade 7×4 is absorbed into the same tables).
Motor + ESC efficiency
Fixed 85 % conversion from electrical to shaft power (ESC = electronic
speed controller). Wh/km is measured on the electrical side.
CdA / drag table
Drag area (drag coefficient × projected area, m²). Computed per candidate
by rasterizing the frame’s STL along the flow at tilt angles 0–60°, per
component class (arms vs body), each with its own Cd. Drag =
½ ρV² CdA.
Rotor wash / download
The induced downwash of each rotor presses on the arm planform underneath
its disk — an interference penalty that makes wide arms under rotors
expensive.
Tilt
A quadcopter can only push along its rotor axes, so it leans into the
relative wind to fly forward or fight gusts; tilt sets both the thrust demand
and which silhouette the frame shows the airflow.
Dryden turbulence (MIL-F-8785C)
The standard military gust model: time-correlated random wind with
prescribed intensity σ and length scales (light / moderate / severe map to
15 / 30 / 45 kt wind at 20 ft). Each scenario uses a fixed seed, so every
candidate flies the identical gust history — fitness differences are frame
differences, never gust luck.
Density altitude / air density ρ
Hot or high air is thin (lower ρ): rotors must spin faster for the same
thrust, and drag falls. hot_thin flies at the density found 1500 m up a
standard atmosphere.
Rain model
Empirical, per NASA heavy-rain research (NASA TP-2671): a water film adds
mass on upward-facing area, suspended rain adds momentum drag, and rotor
thrust coefficient is derated 15 %.
Component drag buildup
Phase-A method: total drag ≈ sum of per-component projected areas ×
handbook drag coefficients. Fast and honest about being approximate; Phase B
would replace it with OpenFOAM CFD on the top candidates.
reading the results
Progress chart
Top of the gallery: every candidate in evaluation order. Gray dots are
evaluated designs, drawn at their aggregate Wh/km (lower is better); hollow
dots are valid but so poor they are clipped at the 95th-percentile cap and
pinned to the top edge. The black step line tracks the best-so-far; its
black dots are the improvements and click through to the detail cards. Red
×’s on the ∞ row above the scale are invalid designs
(fitness = ∞, see constraints) that never flew. Teal ⟳
axis ticks mark pivot generations; the dashed horizontal rules are the
target & record lines. Every legend entry explains itself on hover.
Improvement setter / run champion
A candidate that lowered the best-so-far score when it was evaluated
(the black dots of the step line). Setters render inverted —
ink background, bright text — in both the generation grids and the
detail list; the run champion, the best design of the entire run,
additionally carries a rust outline and chip.
Detail card
One per candidate, below the grids: the metric table (energy score =
fitness, scenario mean and worst, frame mass, material, birth), the lab
notebook’s hypothesis / method / result notes (see
Lab notebook), and the scenario results & genome
tables — per-scenario Wh/km, average power and max tilt, plus every
gene value. The side column shows the parents’ renders.
3D overlay
Click any render to open the interactive viewer; it starts zoomed to
fit the whole frame at a tilted-down view (drag to rotate, ⌘-drag to
pan, scroll to zoom, double-click resets, esc closes; corner buttons snap
to fit / front / top / bottom / left / right / default views). Four tabs:
full-kit model, the complete candidate; evolved components,
just the deck + arms that evolution changes; compare with oldest
ancestor, the candidate’s evolved parts next to its lineage
root’s, both rotating and zooming in sync; and evolution
difference, superimposing the evolved parts — solid color = this
candidate, gray ghost = oldest ancestor, fixed kit hidden — starting
near top-down, where plan-shape changes read best.
Family tree
lineage.html: one row per generation, candidates ordered
best→worst, edge colors naming the operator (see Operator,
Elitism, Lineage). Hover any node for the candidate’s
card and its full ancestry; click to pin the highlight while scrolling.
See the README for physics assumptions, sources, and known
limitations in full.