Drone. Swarm
(ex_drone v0.3.0)
View Source
Coordinator for a named set of drones.
A swarm owns membership and group operations. Each member remains a
normal Drone.Vehicle under Drone.Supervisor. Per-drone Drone.Safety
still gates every command; the swarm adds plan-time formation separation
and fail-fast fan-out for coordinated commands.
Formations are one-shot geometric planners (Drone.Formation), not
continuous flocking controllers.
Quick start
{:ok, swarm} =
Drone.Swarm.start(
name: :advisors,
members: [
{:good, adapter: :sim, initial_x: 0, initial_y: 0},
{:bad, adapter: :sim, initial_x: 0, initial_y: 0}
],
spacing_cm: 100,
min_separation_cm: 80
)
{:ok, _} = Drone.Swarm.connect_sdk(swarm)
{:ok, _} = Drone.Swarm.takeoff(swarm)
{:ok, _} = Drone.Swarm.run(swarm, :front)
{:ok, _} = Drone.Swarm.land(swarm)
:ok = Drone.Swarm.stop(swarm)Member-list sugar (anonymous swarm, returns a pid):
{:ok, swarm} =
Drone.Swarm.start([
{:left, adapter: :sim, initial_x: -50},
{:right, adapter: :sim, initial_x: 50}
])Result shapes
Coordinated ops return a map of per-member outcomes:
{:ok, %{good: :ok, bad: :ok}}
{:error, :partial, %{good: :ok, bad: {:error, {:safety, :max_altitude}}}}Default policy is fail-fast: stop issuing further member commands after
the first error. Already-completed members are not undone; call land/1 or
emergency/1 explicitly.
emergency/1 fans out from the caller process using a membership table, so
it is not blocked by an in-flight run/2 or other coordinated call.
See also
Drone.Formation— formation plannersdocs/swarm.md— user guidedocs/design/v0_2_0_deferred.md— deferred flocking / closed-loop work
Summary
Types
Outcome for a single swarm member after a coordinated operation.
Map of member name → per-member result for a coordinated operation.
Options accepted by start/1 after normalization.
Handle used to address a running swarm.
Functions
Returns the child specification used by Drone.Swarm.Supervisor.
Sends SDK-mode activation to every member (fail-fast).
Emergency-stops every member (best-effort; does not fail-fast).
Coordinated land for every member (fail-fast).
Returns the ordered list of member drone names.
Runs a formation, a per-drone mission map, or a custom function.
Starts a supervised swarm and connects all members.
Stops the swarm process.
Coordinated takeoff for every member (fail-fast).
Returns a map of telemetry snapshots for every member.
Looks up a named swarm process.
Types
Outcome for a single swarm member after a coordinated operation.
:ok— command succeeded with no payload{:ok, term()}— succeeded with a value (e.g. mission reply list){:error, term()}— failed; reason may be{:safety, atom()},:simulated_failure,:not_connected, etc.
Examples
:ok
{:ok, [:ok, :ok]}
{:error, {:safety, :max_altitude}}
{:error, :simulated_failure}
@type results() :: %{optional(atom()) => member_result()}
Map of member name → per-member result for a coordinated operation.
Keys are the drone names from membership (atoms such as :good, :left).
Example
%{good: :ok, bad: {:error, {:safety, :max_altitude}}}
@type start_opts() :: [ name: atom(), members: [{atom(), keyword()}], spacing_cm: pos_integer(), min_separation_cm: pos_integer(), heading_deg: integer(), timeout: timeout() ]
Options accepted by start/1 after normalization.
Fields / keys
| Key | Type | Default | Meaning |
|---|---|---|---|
:name | atom() | none | Register swarm in Drone.Swarm.Registry |
:members | [{atom(), keyword()}] | required | Ordered member list |
:spacing_cm | pos_integer() | 100 | Neighbor spacing for formations |
:min_separation_cm | pos_integer() | 80 | Plan-time Separate check |
:heading_deg | integer() | 0 | Formation travel heading (yaw 0 = +Y) |
:timeout | timeout() | 60_000 | Default GenServer.call timeout for ops |
Each member keyword list is passed to Drone.connect/2 (plus :name).
Common member opts: :adapter (:sim | :tello | module),
:initial_x, :initial_y, :initial_z, :initial_yaw, :safety,
simulator failure injection opts.
Example
[
name: :patrol,
members: [
{:alpha, adapter: :sim, initial_x: -50, safety: [indoor: true]},
{:bravo, adapter: :sim, initial_x: 50}
],
spacing_cm: 120,
min_separation_cm: 100,
heading_deg: 0
]
Handle used to address a running swarm.
atom()— registered name passed as:nametostart/1(looked up viaDrone.Swarm.Registry), e.g.:advisorspid()— process id of an anonymous swarm
Examples
:advisors
#PID<0.123.0>
Functions
@spec child_spec(start_opts()) :: Supervisor.child_spec()
Returns the child specification used by Drone.Swarm.Supervisor.
Parameters
opts(start_opts/0) — swarm start options
Returns
A supervisor child spec map with :temporary restart and a shutdown
allowance for member disconnects.
Example
Drone.Swarm.child_spec(name: :demo, members: [{:a, adapter: :sim}, {:b, adapter: :sim}])
@spec connect_sdk( swarm(), keyword() ) :: {:ok, results()} | {:error, :partial, results()} | {:error, :not_found}
Sends SDK-mode activation to every member (fail-fast).
Parameters
swarm(swarm/0) — swarm name or pidopts(keyword()) —:timeoutoverrides the swarm default
Returns
{:ok, results()}— all members entered SDK mode{:error, :partial, results()}— stopped after first failure{:error, :not_found}
Example
{:ok, %{good: :ok, bad: :ok}} = Drone.Swarm.connect_sdk(:advisors)
Emergency-stops every member (best-effort; does not fail-fast).
Fans out from the caller process using the swarm membership table, so
this is not queued behind an in-flight run/2, takeoff/1, or other
coordinator call. Continues through all members even if one fails. Bypasses
normal safety on each vehicle.
Parameters
swarm(swarm/0) — swarm name or pid
Returns
{:ok, results()}— always returns the full per-member map when the swarm exists{:error, :not_found}
Example
{:ok, %{good: :ok, bad: :ok}} = Drone.Swarm.emergency(:advisors)
@spec land( swarm(), keyword() ) :: {:ok, results()} | {:error, :partial, results()} | {:error, :not_found}
Coordinated land for every member (fail-fast).
Parameters
swarm(swarm/0) — swarm name or pidopts(keyword()) —:timeoutoverrides the swarm default
Returns
{:ok, results()}{:error, :partial, results()}{:error, :not_found}
Example
{:ok, _} = Drone.Swarm.land(:advisors)
Returns the ordered list of member drone names.
Parameters
swarm(swarm/0) — swarm name or pid
Returns
{:ok, [atom()]}— membership order, e.g.{:ok, [:good, :bad]}{:error, :not_found}— unknown swarm
Example
{:ok, [:good, :bad]} = Drone.Swarm.members(:advisors)
@spec run( swarm(), Drone.Formation.formation() | %{required(atom()) => Drone.Mission.t()} | (map() -> term()), keyword() ) :: {:ok, results()} | {:error, term()} | {:error, :partial, results()}
Runs a formation, a per-drone mission map, or a custom function.
When target is a formation atom, opts may include formation planner
options (:heading_deg, :spacing_cm, :min_separation_cm, :leader,
:origin, :side, :radius_cm, :columns) which override swarm defaults.
Use :timeout to override the call timeout.
Parameters
swarm(swarm/0) — swarm name or pidtarget— one of:Drone.Formation.formation()— e.g.:front,:vee,:circle%{atom() => Drone.Mission.t()}— per-member missions (membership order; unknown keys are rejected before any flight)(map() -> term())— function receiving%{name => name}member map; must return:ok,{:ok, term()}, or{:error, term()}
opts(keyword()) — formation and/or:timeoutoptions
Returns
{:ok, results()}— all members succeeded (or custom function returned ok){:error, :partial, results()}— fail-fast stop during mission execution{:error, reason}— plan-time error such as:separation_violation,:too_few_drones,:unsupported_formation,:unsupported_run_target,{:unknown_members, [atom()]},{:invalid_run_result, term()}{:error, :not_found}— unknown swarm
Examples
{:ok, _} = Drone.Swarm.run(swarm, :front)
{:ok, _} = Drone.Swarm.run(swarm, :echelon, side: :left, heading_deg: 90)
good = Drone.Mission.new() |> Drone.Mission.move(:forward, 40)
bad = Drone.Mission.new() |> Drone.Mission.move(:up, 200)
{:error, :partial, results} = Drone.Swarm.run(swarm, %{good: good, bad: bad})
{:ok, _} =
Drone.Swarm.run(swarm, fn members ->
Enum.each(members, fn {_k, name} -> Drone.hover(name, seconds: 1) end)
:ok
end)
Starts a supervised swarm and connects all members.
Accepts either:
- A keyword list with
:membersand optional swarm options - A bare member list
[{name, opts}, ...]or[name, ...](defaults to:sim)
Members are started via Drone.connect/2 under Drone.Supervisor.
The swarm process itself is started under Drone.Swarm.Supervisor.
Like Drone.connect/2, this returns a swarm handle (:name or pid)
and does not link the caller. Use child_spec/1 under your own supervisor
when you need OTP linking/restart semantics.
Parameters
arg(t:start_opts/0 | [{atom(), keyword()}] | [atom()]) — start options or member list
Returns
{:ok, swarm()}— swarm name when:namewas given, otherwise the pid{:error, :name_already_taken}— swarm name already registered{:error, term()}— member connect failure or supervisor error
Examples
{:ok, :advisors} =
Drone.Swarm.start(
name: :advisors,
members: [
{:good, adapter: :sim, initial_x: 0},
{:bad, adapter: :sim, initial_x: 0, safety: [max_altitude_cm: 50]}
]
)
{:ok, pid} =
Drone.Swarm.start([
{:left, adapter: :sim, initial_x: -50},
{:right, adapter: :sim, initial_x: 50}
])
Stops the swarm process.
By default disconnects all member vehicles. Pass disconnect: false to
leave vehicles running after the coordinator exits.
Parameters
swarm(swarm/0) — swarm name or pidopts(keyword()) — options::disconnect(boolean(), defaulttrue) — disconnect members:timeout(timeout()) — call timeout
Returns
:ok{:error, :not_found}
Examples
:ok = Drone.Swarm.stop(:advisors)
:ok = Drone.Swarm.stop(swarm, disconnect: false)
@spec takeoff( swarm(), keyword() ) :: {:ok, results()} | {:error, :partial, results()} | {:error, :not_found}
Coordinated takeoff for every member (fail-fast).
Members must already be in SDK mode (connect_sdk/1).
Parameters
swarm(swarm/0) — swarm name or pidopts(keyword()) —:timeoutoverrides the swarm default
Returns
{:ok, results()}{:error, :partial, results()}{:error, :not_found}
Example
{:ok, _} = Drone.Swarm.takeoff(:advisors)
Returns a map of telemetry snapshots for every member.
Parameters
swarm(swarm/0) — swarm name or pidopts(keyword()) —:timeoutoverrides the swarm default
Returns
{:ok, %{atom() => map()}}— keys are member names; values include at least:x,:y,:z,:yaw,:battery,:flying,:mode{:error, {member :: atom(), reason :: term()}}— first telemetry failure{:error, :not_found}— unknown swarm
Example
{:ok, tel} = Drone.Swarm.telemetry(:advisors)
tel.good.flying
#=> true
Looks up a named swarm process.
Parameters
name(atom()) — registry key, e.g.:advisors
Returns
pid()when registerednilwhen not found
Example
pid = Drone.Swarm.whereis(:advisors)