frame = drone.frame() # BGR image or None
vision = retina.encode(frame) # per-eye grids: brightness, ftb, btf, up, down, loom, loom_speed
vision = illusion.apply(vision, gesture, t) # optional hand -> optic-flow illusion
tel = drone.telemetry() # altitude, yaw rate, battery, position
inputs = encoder.encode(vision, tel.yaw_rate) # {group: Hz per neuron}
rates = brain.tick(inputs, ms=dt*1000) # run the connectome for one tick, mean Hz per group
cmd = decoder.update(rates, dt) # linear read-out + escape reflex
cmd = safety.filter(cmd, tel, dt) # limits always win
drone.send(cmd)Code: runtime.py.
| module | responsibility | key classes |
|---|---|---|
brain/connectome.py |
signed synapse matrix, cell-type labels, group selection by regex + side, save/load .npz, sensorimotor subgraph, MaleCNS feather builder |
Connectome, GroupSpec, build_malecns |
brain/lif.py |
event-driven LIF simulation, delays, refractoriness, Poisson input, tonic bias, cheap copies | LIFNetwork, LIFParams |
brain/brain.py |
ties connectome + LIF + config groups; tick() returns group rates; copy() for swarms |
Brain |
brain/synthetic.py |
MiniFly | build_minifly |
senses/retina.py |
numpy optic flow per grid cell, affine looming estimate | Retina, VisualFrame |
senses/gestures.py |
MediaPipe / OpenCV / scripted hands, illusions | GestureIllusion, MediaPipeHands, OpenCVHands |
senses/encoder.py |
features → Poisson rates per input neuron | InputEncoder |
motor/decoder.py |
descending-neuron rates → FlightCommand, baselines, escape, cruise braking |
MotorDecoder |
safety.py |
clamps, slew rate, ceiling fade, floor, geofence, watchdog, battery, flight time | SafetyGovernor, Telemetry |
drones/* |
backends | SimDrone, TelloDrone, CrazyflieDrone, MavlinkDrone, UDPBridgeDrone |
calibrate.py |
stimulus battery + ridge regression read-out | calibrate |
viz/dashboard.py |
matplotlib dashboard, OpenCV window, GIF writer | Dashboard |
Connectivity is a CSC matrix W[post, pre] of signed synapse counts. When neurons spike, their columns are
gathered with one vectorised np.repeat / np.bincount pass, so cost scales with spikes × out-degree,
not with the total number of synapses. Arriving input sits in a ring buffer for delay / dt steps.
Membrane updates are in-place numpy operations over all neurons; refractory neurons are tracked as a short
index list. For very dense bursts it falls back to a sparse matrix-vector product.
Everything lives in YAML: src/flydrones/defaults.yaml is the full
reference, files in configs/ override parts of it (--config). Dictionaries merge deeply,
except terms blocks, which replace.
- New sensory channel: compute a grid or scalar feature, add a group in
inputs:withfeature: <name>(scalar features can be passed throughInputEncoder.encode(..., extra={...})). - New motor read-out: add a group in
outputs:and a term indecoder.axes. - New drone: subclass
Drone(HARDWARE.md). - New connectome (FlyWire, BANC...): produce a
Connectomewithweights,types,sidesand save it.
docs/index.html + docs/live/ is the GitHub Pages demo. engine.js is a line-by-line JavaScript port of the
LIF simulator, retina, encoder, gesture illusions, decoder, safety governor and simulated drone.
tools/export_web_brain.py writes MiniFly and the default config to docs/live/minifly.json, and CI runs
node tools/check_web_engine.mjs to make sure the browser version still climbs, holds, escapes and descends like
the Python one. Rendering uses a vendored copy of three.js (MIT, docs/vendor/): models.js builds the drone, the fly mascot and the swatter from primitives, room.js the bedroom, app.js the HUD, the swat game, picking and sound. Browser-only additions to the engine: neuron poking (Brain.poke), non-solid moving obstacles that the camera can see, and a giant-fiber jump used by the game.