Skip to content

donework/flaight

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

flaight

light → flight → flaight.

By Dominic Neuburg — MIT License

PyPI version Python versions License: MIT CI

Choreography library for Crazyflie drone light shows. This project is maintained with the help of vibe coding. Define timed sequences of movements and light effects in Python, simulate them without hardware, then fly them for real.

The centrepiece feature: describe a show in plain language — "two drones take off, fly apart and alternate between red and blue" — and flaight uses a local language model to turn that description directly into a Show object, ready to simulate or fly.


Installation

pip install flaight

Requires Python 3.10+. Hardware flight requires a Crazyradio USB dongle and cflib. The natural-language parser requires a running Ollama server.


Core concepts

A Show holds one or more named drones, each with a DroneTrack — an ordered list of (time, action) pairs called keyframes. All times are in seconds from show start. Multiple actions on the same drone at the same time are allowed (e.g. Takeoff and SetColor simultaneously).


Quick start

from flaight import Color, FadeColor, FlightRunner, Hover, Land, MoveTo, SetColor, Show, Takeoff

show = Show()
d1 = show.add_drone("cf1", uri="radio://0/80/2M/E7E7E7E701")

d1.at(0.0, Takeoff(height=1.0, duration=2.0))
d1.at(0.0, SetColor(Color.RED))
d1.at(2.0, MoveTo(x=1.0, y=0.0, z=1.0, duration=3.0))
d1.at(5.0, FadeColor(color=Color.BLUE, duration=1.5))
d1.at(6.5, Land(duration=2.0))
d1.at(6.5, SetColor(Color.OFF))

Simulate without hardware:

from flaight import DryRunRunner
DryRunRunner(show).run()            # instant printout
DryRunRunner(show, realtime=True).run()  # sleep-accurate timing

Fly for real:

FlightRunner(show).run()

FlightRunner runs a collision check before connecting. If two drones come within 20 cm of each other it raises CollisionError and aborts.


Actions

Action Key parameters Notes
Takeoff(height, duration) height in metres, duration in seconds First action for any flying drone
Land(duration) duration in seconds Last action for any flying drone
Hover(duration) duration in seconds Stay in place
MoveTo(x, y, z, yaw, duration) x/y/z in metres from origin, yaw in degrees Absolute world-frame position
SetColor(color) Color instance Instant LED change
FadeColor(color, duration, steps) target color, fade time, interpolation steps Smooth LED transition

Colors

from flaight import Color

# Named presets
Color.RED, Color.GREEN, Color.BLUE
Color.WHITE, Color.OFF
Color.YELLOW, Color.CYAN, Color.MAGENTA
Color.ORANGE, Color.PURPLE

# Custom RGB (0–255)
teal = Color(r=0, g=180, b=160)

# Interpolate between two colors
mid = Color.RED.lerp(Color.BLUE, 0.5)   # t in 0..1

Two-drone example

from flaight import Color, FadeColor, FlightRunner, Hover, Land, MoveTo, SetColor, Show, Takeoff

show = Show()
d1 = show.add_drone("cf1", uri="radio://0/80/2M/E7E7E7E7E7")
d2 = show.add_drone("cf2", uri="radio://0/81/2M/E7E7E7E7E7")

# cf1 — left drone, starts blue
d1.at(0.0, Takeoff(height=1.0, duration=2.0))
d1.at(0.0, SetColor(Color.BLUE))
d1.at(2.5, MoveTo(x=-0.5, y=0.0, z=1.2, duration=2.0))
d1.at(4.5, FadeColor(color=Color.RED, duration=1.5))
d1.at(6.0, Hover(duration=2.0))
d1.at(8.0, Land(duration=2.0))
d1.at(8.0, SetColor(Color.OFF))

# cf2 — right drone, mirrors cf1 with swapped colors
d2.at(0.0, Takeoff(height=1.0, duration=2.0))
d2.at(0.0, SetColor(Color.RED))
d2.at(2.5, MoveTo(x=0.5, y=0.0, z=1.2, duration=2.0))
d2.at(4.5, FadeColor(color=Color.BLUE, duration=1.5))
d2.at(6.0, Hover(duration=2.0))
d2.at(8.0, Land(duration=2.0))
d2.at(8.0, SetColor(Color.OFF))

if __name__ == "__main__":
    import sys
    if "--fly" in sys.argv:
        FlightRunner(show).run()
    else:
        from flaight import DryRunRunner
        DryRunRunner(show, realtime="--realtime" in sys.argv).run()

Collision validation

from flaight import validate, CollisionError

try:
    validate(show)              # default safety distance: 20 cm
    validate(show, safety=0.5)  # custom distance in metres
except CollisionError as e:
    print(e)

FlightRunner and DryRunRunner both call validate automatically before running.


Generating shows from natural language (AI parser)

The centrepiece of flaight: describe a drone show in plain words — in any language — and a local language model (via Ollama) translates that description directly into flaight objects. No exec(), no generated code being run: the model returns structured JSON that flaight safely converts into a Show.

There are two modes of input:

Mode What you provide What the model does
Direct (parse) Explicit choreography instructions Follows them literally — no added creativity
Thematic (parse_thematic) A theme, mood, or emotion Freely designs formations, movement arcs, and a colour palette to express it

Prerequisites

Ollama must be running locally with a model pulled, e.g.:

ollama pull llama3.2
ollama serve

Mode 1 — direct choreography

Describe exactly what the drones should do. The model follows your instructions without adding anything not asked for.

from flaight import DryRunRunner, ShowParser

parser = ShowParser(host="http://localhost:11434", model="llama3.2")

show = parser.parse(
    "Two drones take off, fly apart to opposite sides, alternate red and blue, then land.",
    uris=["radio://0/80/2M/E7E7E7E701", "radio://0/80/2M/E7E7E7E702"],
)
DryRunRunner(show).run()

Mode 2 — thematic / emotional

Describe a theme, mood, or feeling. The model acts as a creative choreographer: it picks formations, movement arcs, altitude dynamics, and a colour palette that express the theme.

show = parser.parse_thematic(
    "Melancholy — a lone farewell slowly fading into darkness.",
    uris=["radio://0/80/2M/E7E7E7E701", "radio://0/80/2M/E7E7E7E702",
          "radio://0/80/2M/E7E7E7E703"],
)
DryRunRunner(show, realtime=True).run()

Other examples of thematic prompts:

  • "Euphoria — an explosion of energy and colour"
  • "A thunderstorm building and breaking"
  • "Sunrise over the mountains"
  • "Tense standoff, then sudden release"

Summary before generating

Before building the full choreography, ask the model for a plain-text preview — useful to check that the description was understood or that the creative interpretation fits your vision:

# Mode 1 — summarizes the explicit instructions
summary = parser.summarize(description, uris=uris)

# Mode 2 — describes the creative concept the model would build
summary = parser.summarize_thematic(description, uris=uris)

print(summary)

Interactive session (REPL)

run_interactive starts a loop: enter a description → confirm the summary → generate the show → simulate or fly → next show.

parser.run_interactive(
    uris=["radio://0/80/2M/E7E7E7E701", "radio://0/80/2M/E7E7E7E702"],
    fly=False,       # True → real hardware instead of dry-run
    realtime=True,   # play dry-run in real time
)

Saving generated shows as scripts

Any AI-generated show can be exported as a standalone Python script — for archiving, manual editing, or re-running later:

from flaight import save_script
path = save_script(show)           # → ai_generated/show_<timestamp>.py
print(f"Saved: {path}")

The saved script is fully self-contained and supports the same --fly / --realtime flags as the bundled examples.


Exporting shows as scripts

Any Show — whether hand-crafted or AI-generated — can be exported as a standalone Python script:

from flaight import save_script, to_script

print(to_script(show))                        # print to stdout
path = save_script(show)                      # ai_generated/show_<timestamp>.py
path = save_script(show, output_dir="shows")  # custom directory

Coordinate system

  • Origin (0, 0, 0) is the centre of the flying area.
  • x/y range: roughly −2 m to +2 m.
  • z = 0 is the floor; typical flying height is 1.0 m.
  • Yaw is in degrees; 0° faces the positive x direction.

CLI reference

flaight — AI parser (installed command)

After pip install flaight the flaight command is available directly in your shell — no script to create.

At the start of each iteration you choose between the two input modes:

  [1] Describe the choreography directly
  [2] Describe a theme / emotion — AI designs the show

Mode 1 follows your instructions literally; mode 2 lets the model act as a creative choreographer and design formations, movement arcs, and a colour palette from a theme or emotion. After choosing a mode you enter a description, review a plain-text summary, confirm, and the show is generated and run.

# Basic interactive session
flaight

# Custom Ollama host and model
flaight --host http://localhost:11434 --model llama3.2

# Remote Ollama server on the local network
flaight --host http://192.168.1.10:11434 --model mistral

# Three drones with explicit URIs
flaight --uris radio://0/80/2M/E7E7E7E701 radio://0/80/2M/E7E7E7E702 radio://0/80/2M/E7E7E7E703

# Real-time dry-run
flaight --realtime

# Fly for real
flaight --fly

# Print raw LLM response for debugging
flaight --verbose
Flag Default Description
--host http://localhost:11434 Ollama server URL
--model llama3.2 Ollama model name
--uris two placeholder URIs Ordered list of Crazyflie URIs
--realtime off Play dry-run in real time
--fly off Run on real hardware
--verbose off Log raw LLM JSON response
--timeout 300 Ollama request timeout in seconds

Example scripts

The examples/ directory contains additional runnable scripts. All share a common set of flags:

Flag Description
(none) Dry-run: print all events instantly, no hardware needed
--realtime Dry-run with real-time delays (sleeps between events)
--fly Execute on real Crazyflie hardware via Crazyradio

examples/two_drones.py — hand-coded show: synchronized takeoff, mirrored movement, cross-fading colors.

python examples/two_drones.py
python examples/two_drones.py --realtime
python examples/two_drones.py --fly

examples/color_test.py — cycles both drones through eight colors using FadeColor, with a 1-second wave offset. Drones stay on the ground.

python examples/color_test.py
python examples/color_test.py --realtime
python examples/color_test.py --fly

examples/ai_show.py — generates two shows programmatically: one from explicit instructions (direct mode) and one from a theme (thematic mode). Each result is saved as a script via save_script().

python examples/ai_show.py
python examples/ai_show.py --realtime
python examples/ai_show.py --fly

About

Open source Python library for drone flight and light choreography

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages