A browser-scale coupled demonstrator of the KiteGen Carousel per
CAROUSEL.md: every wing, tether and the rotor solved as one system, with plant
power an emergent quantity per DT-MOD-015 — never asserted from
decoupled per-wing calculations. This is an F0-class toy of the F1 twin: real
dimensions and canonical parameters, radically reduced fidelity. Frame: ring centre at
the origin, +x downwind, +z up.
This build ships the carousel_500k neural champion baked
in: 6 kites at the trained operating point (11.2 m/s wind at operating altitude,
ring at 4.4 m/s), flown by a 243-parameter neural policy found by evolution strategies
(generation 104, 506 laps). Champion laps measured 3.17 MW mean electrical power
(≈ 15 MW scale-equivalent at the 30-kite reference). The classical strategy-table
stack described in §3 remains in the file as the optional fallback pilot.
Wind is a logarithmic profile referenced to the canonical operating-altitude figures (fit to the UAE profile table in §2.5 of the reference: 5.4 m/s @ 100 m, 8 @ 800, 10 @ 2 000), with optional slow sinusoidal gusting. Density falls exponentially with altitude — the reference flags this 20 % effect explicitly in note [N-16]:
On load, Wref is rescaled so the wind at the swarm's mean operating altitude is 11.2 m/s — the single condition point the champion was trained at. The wind slider moves the plant off that point; the policy cannot observe wind speed, so off-design degradation is expected and is itself a finding.
Each wing — 6 in this build, slider up to 300 — is a point mass with a heading state, the same model as the single-kite twin, at Carousel scale. With attachment point a on the ring, er = (p−a)/|·|, heading h kept in the tangent plane, and apparent wind va = W(z)x̂ − v:
The wings fly at full lift through the whole rotation — there is no depower phase;
the flight path, not the sheet, manages the unfavourable sector (§3 below). Lift fades
smoothly to zero (a smoothstep over the last luff_fade of radial inflow)
as the apparent wind turns radial — luff stall at the edge of the wind window:
Heading follows the turn-rate law with the passive gravity/trim yaw pair (crossover va* = √(kgravg/ktrim)), a weathervane term keeping heading and velocity coherent (a wing cannot fly sideways; session-2 addition), and a dihedral side force for sideslip damping — see the kite twin's notes for the derivation. The steering input u ∈ [−1,+1] comes from the neural policy in this build (§3); the classical PD pursuit supplies it in recovery and fallback modes:
Wing speed is emergent. Crosswind theory for the bare wing predicts Vk ≈ E·Vw·cosθ — at E = 15 that is ~120 m/s, the demonstrated sector speed. In the coupled model the wings settle well below that: the two 10 mm × 5 km lines bow under their own drag, and the tilted tension they deliver at the wingtips (drag area CddL ≈ 50 m² per line, 100 m² per pair, against the wing's ~20 m² of CDA) is a first-order speed limit even though no drag term is ever added to the wing itself. The telemetry reports the actual distribution so the number is measured, not assumed.
What flies here: a single multilayer perceptron shared by every kite, queried every physics frame — 13 local observations → 16 tanh units → one steering command u ∈ [−1,+1]. Its 241 weights plus the two generator-law exponents (log₁₀k, log₁₀c) — 243 numbers total — were trained by evolution strategies: a Gaussian over parameter space, 10 candidates per generation with the reigning champion defending slot 0, each flown for a full ring lap chained from the previous best lap's end state, top 3 refitting the distribution. The network was first behaviour-cloned from the classical controller below, then evolved. Objective per completed lap: J = 100 × meanPower(MW) − 0.1 × kite-loops; any crash, sustained negative traction or tension whip scores 0.
The classical stack remains in the file and flies whenever the policy is off
(or hands back — see protections): each wing's mean flight direction is read from a
phase-indexed 12-bin strategy table (elevation, azimuth, sweep amplitude per bin of
ring angle γi = Θ + 2πi/N), flown as a figure-eight between
waypoints at ±amp about the mean direction — or a pure carve when the
bin's amplitude falls below carve_amp — with PD pursuit
(u = clamp(kp ang − kd ȧng), derivative
low-passed and reset across tack flips). The shipped table is the CEM shape-search
winner (deep-gybe regime: ±40° azimuth rails, full side-to-side flip across
the upwind sector; audited 5.93 MW at 30 kites). SPSA and CEM optimisers for that
stack are in the file, off by default in this build.
Three protective behaviours sit outside the pilot, as in any real autopilot — the policy is never allowed to opt out of them: radial station-keeping (a wing whose line goes badly slack, >5 %, is handed back to the classical controller, which shifts its pattern downwind until the wind carries it back to the leash); a recovery override — any wing below 400 m altitude or 12° elevation abandons its pattern and climbs straight downwind until above 30° and clear; and a last-resort relaunch — a wing that still reaches the floor (<160 m) is re-placed on its loop at flying speed with fresh lines (counted in telemetry, and charged to every training objective as a crash).
2 lines per wing to one attachment (12 lines at this build's 6 kites; 600 at the full swarm), each 5 km of 10 mm Dyneema® (0.062 kg/m, EA ≈ 5×106 N) discretised into 10 XPBD segments in this build (20 at default fidelity) — tension-only inequality constraints with elastic compliance α = Lseg/EA. The tether reaches the kite only as axial tension at the wingtips, and since the wingtip attachment lies along the span — perpendicular to the flight path — that force does no work against the kite's motion: tether drag is never added to the wing's polar. Drag acts on the tether elements themselves (normal component of relative wind per segment), shaping sag, bowing and the direction the tension finally pulls from — it influences kite dynamics only through the line:
A line severs above 700 kN (session-2 load-shedding physics: whip states must
terminate as they would in reality) — the severed line falls freely, its constraints and
drag are skipped, and it is restored with fresh lines when its wing relaunches; every
severance is charged as a crash. ⚠ The true 10 mm MBL is 100–130 kN; 700 kN is
SF 2 over the accepted 350 kN working limit, so only whip events sever — the
[R2-1] tether-strength inconsistency stands (see §7).
The rotor is a rigid ring with one rotational degree of freedom Θ and inertia
I = mringR², with mring = 80,000 t (5,093 kg/m,
session-2 user spec), magnetically levitated — no bearing friction is modelled. With a
partial swarm the ring inertia and the generator torque scale by N/300
(scale_ring_to_swarm), so 6-kite dynamics stay representative of the full
plant's coupling. Each tether's anchor segment couples to it inside the same
Gauss–Seidel solve: the constraint gradient with respect to Θ is
gΘ = −n̂·(ẑ×r), giving the
ring a generalised inverse mass gΘ²/I in the denominator and a
rotation update per correction. Torque is therefore never computed as a formula — it is
what 600 anchor constraints do to Θ:
The generator is a commanded electromagnetic braking law with two optimised coefficients (session 2): a quadratic droop term kΩ² and a linear damping term c|Ω| — the distributed generators/maglev present a torque component proportional to speed, counted as generation. Both exponents were trained with the policy; at the champion operating point the linear term carries most of the torque (k = 1014.94, c = 1013.72 — the CEM search independently found that a near-linear generator law beats quadratic droop here, and the linear term stabilises the operating point against elastic whip). There is no ring-speed target — the operating speed is fully emergent; a hard overspeed brake engages only above 45 m/s, as rotor protection. The generator only ever brakes, opposing the actual rotation direction, capped at τmax:
The reference flags a factor-of-two inconsistency between its two published power
conventions: A applies k = 0.35 to F = ½ρACLE²Vw²;
B applies the same k on top of cos²θ, i.e. an effective 0.175. The twin
requirement (DT-MOD-015) is that neither constant is adopted — the reduction
must emerge from the simulated flight path. This page computes it live:
The validation sweep steps wind speed across the published §3.6 range, lets the plant settle at each point, and overlays the measured (Vw, P) pairs on the published curve (0.43 GW @ 5 m/s … 5.97 GW @ 12 m/s, nameplate 5 GW crossed at ~11.2 m/s). Where the emergent curve lands relative to the published one is the finding.
What this build measures is a frozen champion, not a running optimiser: the
policy weights are fixed at generation 104, training is off, and the telemetry — the
emergent k readout, the ring speed, the power tile — is the live coupled solution under
that pilot. Verified on a clean load: ~3.5 MW mean over the first 10 sim-minutes,
tensions ~120/220 kN, zero crashes, producing from the first second. The 10 mm ×
5 km lines carry little weight (~0.6 t per wing pair against hundreds of kN of lift),
so the swarm flies with wide margin. Caveats cut both ways: the policy is one point in
a search that had not converged to any provable optimum (headroom above it), and the
model still omits winching and wake losses (which cut the other way). Either way, the
factor between the coupled result and the published assumption is exactly the number
DT-MOD-015 exists to produce.
| Quantity | Value | Source note |
|---|---|---|
| Wings / area / CL | 6 this build (10–300 slider; [N-2] canonical 300) · 300 m² · 1.0 | [N-2], §2.2 |
| Wing (kite) mass | 400 kg incl. pod (~4 kN weight) | user spec, §2.2 |
| E = CL/CD | 15 design (8–30 slider) | [N-5], §3.3 |
| Tethers | 2/wing · 5 km · ∅10 mm Dyneema · 10 segments this build · severs >700 kN | user spec (supersedes §2.3) |
| Ring | ∅5 000 m · 80,000 t · maglev (frictionless) · inertia & generator ×N/300 | §2.1; session-2 user spec |
| Generator law | τ = kΩ² + c|Ω|; k = 1014.94, c = 1013.72 (trained) · overspeed brake 45 m/s | session 2, §5 above |
| Pilot | neural policy 13→16→1, 243 params (ES gen 104); classical strategy table as fallback | §3 above |
| Line twist limit | ±1.5 turns, counted above 10 m/s airspeed (accounted, not physically enforced) | session-2 user constraint |
| Wind / density | UAE profile fit, 11.2 m/s at operating altitude this build; ρ(z) | §2.5, [N-16] |
| XPBD solver | α = Lseg/EA · 5 substeps · 6 iterations (adaptive 8/9 when vk>120 m/s or T>500 kN) | — |
Fitting levers, in order: Edesign (plant power, wing speed),
the policy weights (SIM.state.policyW — or switch to the classical stack
with PARAMS.policy_on = false and edit
SIM.state.stratEl/stratPsi), gk/loop geometry (loop period),
ring linear density (spin-up time).
window.SIM exposes fastForward(), telemetry() and
startSweep() for headless runs.