Layered Control Architecture for Multi-Quadrotor Tracking (Scenario 5)
This example demonstrates a layered control architecture designed for multi-agent target tracking where high-level spatial coordination is handled by a static cellular sheaf and low-level tracking is managed by independent LQR controllers running on distributed Julia worker processes.
Architecture Overview
The control pipeline is structured as follows:
- 2D Coordination Sheaf: Operates on spatial positions ($y, z$), resolving the conflict between target tracking and agent consensus by computing a harmonic extension $\mathbf{q}^*$.
- Distributed Tree Solve: Solves for $\mathbf{q}^*$ across parallel worker processes using tree-parallel message passing on the clique tree of the restricted Laplacian.
- Local Stalk Embedding & Tikhonov Filter: Each worker embeds its 2D spatial reference into its full 6D state space ($[y^*, z^*, 0, 0, 0, 0]^\top$) and smooths it using a Tikhonov reference filter.
- LQR Control: Drives each agent to the filtered reference on its dedicated worker process using an optimal state-feedback gain computed via the Discrete Algebraic Riccati Equation (DARE).
This separation of concerns ensures that the coordination Laplacian $H$ remains strictly positive-definite (full rank) and allows high-level spatial coordination to run efficiently across worker processes.
using CellularSheaves
using CellularSheaves.ControlSheaves.AgentControllers
using CellularSheaves.ControlSheaves.DistributedLayeredControl
using LinearAlgebra
using Plots
using Printf
using DistributedA single house style for every figure below — matches the escort.jl style
default(framestyle = :box, grid = true, gridalpha = 0.18, gridstyle = :dot,
titlefontsize = 10, guidefontsize = 9, legendfontsize = 8, tickfontsize = 8,
markerstrokewidth = 0, size = (1200, 380))Setup Dynamics & Constants
h = 0.05
T_end = 2.0
steps = Int(T_end / h) + 141We use the PlanarQuadrotorDynamics built into AgentControllers.jl
dyn = PlanarQuadrotorDynamics()
Ac, Bc = AgentControllers.continuous_matrices(dyn)
nx = size(Ac, 1)
nu = size(Bc, 2)2Compute Optimal LQR Gain (Discrete-time)
Q_diag = zeros(nx)
Q_diag[1:2] .= 10000.0
Q_diag[3] = 50.0
Q_diag[4] = 500.0
Q_diag[5:6] .= 10.0
Q_lqr = Matrix{Float64}(Diagonal(Q_diag))
R_lqr = Matrix{Float64}(I, nu, nu) * 0.0001
ctrl = LQRController(dyn, h, Q_lqr, R_lqr)CellularSheaves.ControlSheaves.AgentControllers.LQRController([-17.14452492749712 88.26817645626129 12.129118116954233 -7.555914057276707 7.20592528430904 1.07321442932949; 17.144524927497006 88.26817645626127 -12.12911811695422 7.555914057276625 7.20592528430904 -1.0732144293294927])2D Coordination Sheaf Construction
Scenario 5 Configuration:
- Agent 1 tracks Target 1 in z
- Agent 2 tracks Target 2 in yz
- Agents agree in y (Consensus)
We set D = 2 for spatial positions [y, z].
D = 2
NA = 2
NT = 2
TotalV = NA + NT
sheaf = EuclideanSheaf{Float64}(fill(D, TotalV))A network sheaf with 4 vertex stalks and 0 edge stalks.
Coordinate projection matrices
R_y = [1.0 0.0]
R_z = [0.0 1.0]
R_yz = Matrix{Float64}(I, 2, 2)2×2 Matrix{Float64}:
1.0 0.0
0.0 1.0Setup coordination edges
add_sheaf_edge!(sheaf, 1, 2, R_y, R_y)
add_sheaf_edge!(sheaf, 1, 3, R_z, R_z)
add_sheaf_edge!(sheaf, 2, 4, R_yz, R_yz)3Simulation Framework
Ensure we have enough worker processes. Workers start with stderr detached: a worker inherits the parent's stderr, which while these docs are built is the pipe capturing the page's output, and holding it open stalls the build. Each worker still reports through its stdout, which the parent relays.
workers_pids = workers()
if length(workers_pids) < NA
redirect_stderr(devnull) do
addprocs(NA - length(workers_pids);
exeflags = ["--project=$(Base.active_project())",
"--sysimage=$(unsafe_string(Base.JLOptions().image_file))"])
end
workers_pids = workers()
@eval @everywhere using CellularSheaves
@eval @everywhere using CellularSheaves.ControlSheaves.AgentControllers
@eval @everywhere using CellularSheaves.ControlSheaves.DistributedLayeredControl
end
function run_scenario(target_type=:bobbing, mode=:distributed)
local t1_pos, t2_pos
if target_type == :bobbing
omega = 2π * 2 / (40 * h)
t1_pos = t -> [0.0, 1.0 + 0.3sin(omega*t)]
t2_pos = t -> [1.5, 2.0 + 0.3sin(omega*t)]
else
t1_pos = t -> [0.0, 1.0]
t2_pos = t -> [1.5, 2.0]
end
target_trajectory_func = (v, t) -> v == 3 ? t1_pos(t) : t2_pos(t)
init_states = [[-2.0, 0.5, 0.0, 0.0, 0.0, 0.0],
[2.0, 1.0, 0.0, 0.0, 0.0, 0.0]]
agent_configs = [ (init_states[i], dyn, ctrl.K) for i in 1:NA ] # Initialize agent configs for the workers
init_distributed_agents!(workers_pids, agent_configs, h, 0.02) # Initialize workers
prob = LayeredControlProblem(
sheaf=sheaf,
target_nodes=collect((NA+1):(NA+NT)),
target_trajectory_func=target_trajectory_func,
agent_configs=agent_configs,
dt=h,
steps=steps,
pos_dim=D # pass both y and z to the physical controller (no homogeneous coordinate)
)
result = run_layered_simulation(prob, workers_pids; mode=mode, nx=nx)
return result
endrun_scenario (generic function with 3 methods)Execution and Visualization
Run distributed simulation scenarios
res_fixed = run_scenario(:fixed, :distributed)
res_bob = run_scenario(:bobbing, :distributed)LayeredSimulationResult(LayeredControlProblem(A network sheaf with 4 vertex stalks and 3 edge stalks.
, [3, 4], Main.var"##2261".var"#run_scenario##8#run_scenario##9"(Core.Box(Main.var"##2261".var"#run_scenario##2#run_scenario##3"{Float64}(6.283185307179586)), Core.Box(Main.var"##2261".var"#run_scenario##0#run_scenario##1"{Float64}(6.283185307179586))), nothing, nothing, Tuple{Vector{Float64}, PlanarQuadrotorDynamics, Matrix{Float64}}[([-2.0, 0.5, 0.0, 0.0, 0.0, 0.0], PlanarQuadrotorDynamics(9.81, 0.5, 0.01, 0.25), [-17.14452492749712 88.26817645626129 12.129118116954233 -7.555914057276707 7.20592528430904 1.07321442932949; 17.144524927497006 88.26817645626127 -12.12911811695422 7.555914057276625 7.20592528430904 -1.0732144293294927]), ([2.0, 1.0, 0.0, 0.0, 0.0, 0.0], PlanarQuadrotorDynamics(9.81, 0.5, 0.01, 0.25), [-17.14452492749712 88.26817645626129 12.129118116954233 -7.555914057276707 7.20592528430904 1.07321442932949; 17.144524927497006 88.26817645626127 -12.12911811695422 7.555914057276625 7.20592528430904 -1.0732144293294927])], 0.05, 41, 0.0, 2), [-1.9963024171765864 1.9993176924708422; -1.9472236051570877 1.990407868490155; -1.801630306125528 1.9654946846389558; -1.5532977012565852 1.9255373511985092; -1.225403134248643 1.8751619955149559; -0.852457406702256 1.819834940733259; -0.46873341112944694 1.7644684687947072; -0.10209188105530297 1.7127762697506381; 0.22834886921535535 1.6671122753210175; 0.5121028216517901 1.6285964323008972; 0.7458820789200127 1.597380108960741; 0.931642468559826 1.5729499149110866; 1.0746286336989785 1.554408192202188; 1.181708230320708 1.5406989990587387; 1.2601161492575632 1.5307698378473136; 1.316618358349855 1.5236727612779253; 1.357041076653062 1.5186155176789171; 1.3860825534929688 1.514975879846205; 1.407320168451939 1.5122918809608257; 1.4233351250018806 1.5102386756057107; 1.4358932400000082 1.5086001162230385; 1.4461381150078783 1.507240515020394; 1.4547692017583107 1.5060798042998327; 1.4621903901675515 1.5050735670341913; 1.4686242946490542 1.5041982007631458; 1.4741936181401616 1.503440738523179; 1.4789744160041596 1.5027924808938384; 1.483027471099466 1.5022454863121788; 1.486414009153775 1.5017910258495373; 1.4892012059310746 1.5014192558809727; 1.4914618017236578 1.5011195410451545; 1.4932709434998128 1.5008810339156675; 1.494702300427491 1.500693266454803; 1.4958246319422728 1.5005466232075342; 1.4966993518947158 1.5004326469425338; 1.4973792103901975 1.500344178327274; 1.4979079688472414 1.5002753585956434; 1.498320828860151 1.5002215347431154; 1.4986453489927465 1.5001791065931913; 1.4989026097888678 1.5001453489561718; 1.4991084388267542 1.5001182335645453;;; 0.7219990545364317 1.4064420299623546; 1.0402935928777268 1.9641005432907734; 1.1805175984470286 2.168398233091473; 1.2495343601047038 2.2478583862663397; 1.285148274151202 2.284931597634253; 1.2915808735314336 2.291553901088821; 1.269305536954337 2.269302256066413; 1.220647897396589 2.220647504188614; 1.1503891633777668 2.1503891167129305; 1.0654089848572978 2.06540897935586; 0.9740260828605944 1.9740260822149556; 0.885185684558683 1.8851856844831485; 0.8075841109071417 1.807584110898323; 0.7488175450614125 1.7488175450603838; 0.7146384679892617 1.7146384679891413; 0.7083925659002237 1.7083925659002093; 0.7306912312092415 1.7306912312092397; 0.779351715192014 1.7793517151920135; 0.8496107906387891 1.849610790638789; 0.9345910097210746 1.9345910097210746; 1.025973916503094 2.0259739165030934; 1.114814315366873 2.114814315366873; 1.192415889084169 2.192415889084169; 1.2511824549375756 2.251182454937575; 1.285361532010621 2.2853615320106204; 1.291607434099763 2.2916074340997628; 1.2693087687907572 2.269308768790757; 1.220648284807986 2.220648284807986; 1.1503892093612107 2.1503892093612107; 1.065408990278925 2.065408990278925; 0.9740260834969058 1.974026083496906; 0.885185684633127 1.885185684633127; 0.8075841109158314 1.807584110915831; 0.7488175450624247 1.7488175450624244; 0.714638467989379 1.714638467989379; 0.7083925659002371 1.7083925659002372; 0.7306912312092428 1.730691231209243; 0.779351715192014 1.779351715192014; 0.8496107906387893 1.8496107906387893; 0.9345910097210747 1.9345910097210746; 1.0259739165030943 2.025973916503094;;; -1.8092148371443195 0.3338507787928138; -4.112734415632083 0.6871883351444119; -4.287654835066025 0.6246560204721929; -3.2807139737117708 0.4273032469214091; -1.8346058628007198 0.19981474853107206; -0.41660870149549023 -0.0026010206022928292; 0.7269225895979411 -0.15451786025480907; 1.5057757100616773 -0.25002974418084223; 1.9282400913073312 -0.2947649374220972; 2.0555165067603567 -0.29988732049750777; 1.9690450763000067 -0.2779922820500279; 1.7495031294611951 -0.24062009288698824; 1.4652077884328425 -0.1970160077983308; 1.1673821751634348 -0.15376028928858518; 0.8899259020731934 -0.11493800795130411; 0.6517489421173253 -0.08258911965683041; 0.46022542982252285 -0.05725420606394798; 0.3147999221284302 -0.038498552447992204; 0.21017871571235028 -0.025351191698494976; 0.13884211171016206 -0.016634682989977743; 0.09282110996966728 -0.011186857884413058; 0.06480733428474413 -0.00799003726201997; 0.04872671290173139 -0.006229213328128171; 0.03992472504857431 -0.005301171313491184; 0.035100741316132916 -0.004793810953988996; 0.03210443201413699 -0.004450764934208393; 0.029677875354423844 -0.0041319883284673875; 0.02719890309805538 -0.003777020371231266; 0.02445763971889201 -0.003374441117833971; 0.02148037042079449 -0.0029387490385460405; 0.018402746454462597 -0.002494402562392776; 0.015387096620135803 -0.0020659501477059334; 0.01257514286796044 -0.0016728451335750946; 0.010066569695536208 -0.0013275378905739043; 0.007914664684857728 -0.0010356204957600408; 0.006131838849880817 -0.0007970643644475402; 0.004699687336368105 -0.0006078685224697634; 0.003580006628875641 -0.000461683610759611; 0.0027246526447154617 -0.00035117431979421725; 0.0020832340061410753 -0.0002690259887623529; 0.0016083935861964905 -0.00020859411938503735;;; 0.29580662587309625 -0.05458460233262505; 1.8556585804482308 -0.3306937021266116; 3.9823731251028707 -0.6607216351968991; 5.868613654239133 -0.9214381131831876; 7.128949688011582 -1.0749789294155343; 7.67295814590719 -1.121555772725684; 7.5825179939582865 -1.080683903174804; 7.019471966409565 -0.9791959620769963; 6.163621581249938 -0.8437067130603589; 5.176131669244168 -0.6965082529280346; 4.182107660924865 -0.5539346000712965; 3.26625547882174 -0.42632833837895134; 2.476432270873423 -0.3189052039329995; 1.8310988378805342 -0.23299867679313668; 1.3278999699187994 -0.16734149316319943; 0.9516593602492611 -0.11918609123040833; 0.6809064190091191 -0.0851747819161386; 0.4926411898411502 -0.06194400607544783; 0.3654161921421739 -0.04649074608099227; 0.28101383723266327 -0.03635004271052403; 0.22507297958471809 -0.029637692298741358; 0.18701214689233372 -0.02500769589291626; 0.15954591392302175 -0.021564680286129803; 0.1380211849536038 -0.018760677774224358; 0.1197293549766333 -0.01629544977698041; 0.10328853295309956 -0.014031083833816675; 0.08814175261375166 -0.011925281327326504; 0.0741831071804629 -0.00998352056955962; 0.0615025132731363 -0.008227808790070984; 0.05022874958397557 -0.006678607779997613; 0.04044667787859321 -0.005346310977144799; 0.03216552254156492 -0.004229000187240539; 0.025318631784808227 -0.0033138345822543713; 0.019779704663306415 -0.0025801241756236728; 0.015385011669041774 -0.0020027906714227424; 0.011955074162233775 -0.0015554559027005995; 0.009312342505749294 -0.0012128001225934781; 0.00729359190847235 -0.0009521045950590054; 0.005757138583557462 -0.0007540555364465317; 0.004585729235003206 -0.0006029655703917461; 0.003686250484787095 -0.0004865903999901453;;; 8.879962181457264 16.25768119849419; 3.8518193521945356 6.048659334642554; 1.7571408705775369 2.123248257385433; 1.0035295957294728 1.0551578696092379; 0.42102696613044976 0.42777058510730115; -0.1637229909211836 -0.1628784469245933; -0.7272904721626839 -0.7271873539717243; -1.2190151101472273 -1.2190027211402406; -1.5913342506056658 -1.5913327778870936; -1.8078728902130927 -1.8078727163957313; -1.847443189655046 -1.8474431692404512; -1.7061727424214146 -1.7061727400318334; -1.397890203640236 -1.3978902033611822; -0.952772430188933 -0.9527724301563886; -0.41439065269709974 -0.4143906526933062; 0.1645545691355822 0.16455456913602862; 0.7273920432251325 0.7273920432251841; 1.2190273160857668 1.2190273160857708; 1.5913357017852403 1.5913357017852465; 1.8078730615061773 1.8078730615061733; 1.8474432097746036 1.8474432097745965; 1.7061727447765556 1.7061727447765775; 1.397890203915275 1.3978902039152643; 0.9527724302209906 0.9527724302209866; 0.41439065270082454 0.4143906527008293; -0.16455456913515132 -0.16455456913514632; -0.7273920432250811 -0.7273920432250839; -1.2190273160857652 -1.2190273160857643; -1.5913357017852485 -1.5913357017852419; -1.8078730615061724 -1.8078730615061716; -1.8474432097745948 -1.8474432097746016; -1.7061727447765582 -1.7061727447765556; -1.397890203915269 -1.397890203915274; -0.952772430221 -0.95277243022099; -0.4143906527008273 -0.414390652700824; 0.16455456913514943 0.16455456913515165; 0.7273920432250787 0.7273920432250776; 1.2190273160857648 1.2190273160857594; 1.5913357017852454 1.5913357017852516; 1.8078730615061698 1.8078730615061633; 1.847443209774612 1.847443209774609;;; -72.36859348577278 13.354031151712547; -19.772189653737797 0.7794711023513798; 12.77537287638011 -3.2807636892401355; 27.50226157779007 -4.613347252791218; 30.342062858651975 -4.486192682822264; 26.377823593557217 -3.610438082512333; 19.363428050180037 -2.466235503588318; 11.790696768369417 -1.3542398534530096; 5.107878481456748 -0.43516787619718855; -0.016821863335738207 0.23027255318076578; -3.4420353550782576 0.6455289847184291; -5.339642518474198 0.8493585818031575; -6.032171122659909 0.8948048217431395; -5.880853408116395 0.8354239186466863; -5.2173975154932615 0.7174673348445566; -4.309680882741469 0.5764881969343914; -3.351259609050628 0.43690834678090584; -2.465760698713077 0.31331779785732516; -1.7190875579301212 0.21257663212256417; -1.1343766021574078 0.13608371621812532; -0.7064634674623842 0.08182928800446204; -0.41408755993454205 0.046043536891261505; -0.2291372953859675 0.024389420464410394; -0.12294221874031563 0.012732260121069094; -0.07001713055734038 0.0075621542590185165; -0.049835241522496854 0.006159686532205555; -0.04722702486602903 0.006591377697434675; -0.05193186538870942 0.007607340592010292; -0.05771866977782525 0.008495829543881506; -0.06137210214607558 0.00893185362763571; -0.06173285650720027 0.008842005418494864; -0.058893136865871484 0.008296091168978864; -0.05358501322114305 0.007428109396254684; -0.046757913675826254 0.006384180323792926; -0.03931828675131287 0.0052925154687617215; -0.03199474664776368 0.004249729783738249; -0.025291313892744624 0.003318103895372654; -0.01949591440695396 0.0025292925730333315; -0.014718244959453383 0.0018910790655823628; -0.010938500583522068 0.0013948541756922672; -0.008055116214261318 0.001022420599400345], [1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5; 1.5 1.5;;; 1.0927050983124842 2.0927050983124844; 1.1763355756877418 2.176335575687742; 1.2427050983124843 2.2427050983124843; 1.285316954888546 2.285316954888546; 1.3 2.3; 1.285316954888546 2.285316954888546; 1.2427050983124843 2.2427050983124843; 1.176335575687742 2.176335575687742; 1.0927050983124842 2.0927050983124844; 1.0 2.0; 0.9072949016875157 1.9072949016875156; 0.823664424312258 1.823664424312258; 0.7572949016875158 1.757294901687516; 0.714683045111454 1.714683045111454; 0.7 1.7; 0.7146830451114539 1.7146830451114539; 0.7572949016875159 1.757294901687516; 0.823664424312258 1.823664424312258; 0.9072949016875157 1.9072949016875158; 0.9999999999999999 2.0; 1.0927050983124842 2.092705098312484; 1.176335575687742 2.1763355756877423; 1.2427050983124843 2.2427050983124843; 1.2853169548885461 2.285316954888546; 1.3 2.3; 1.2853169548885461 2.285316954888546; 1.2427050983124843 2.2427050983124843; 1.176335575687742 2.176335575687742; 1.0927050983124837 2.092705098312484; 1.0 2.0; 0.9072949016875159 1.9072949016875158; 0.8236644243122582 1.8236644243122582; 0.7572949016875159 1.757294901687516; 0.7146830451114539 1.7146830451114539; 0.7 1.7; 0.7146830451114539 1.7146830451114539; 0.7572949016875157 1.7572949016875157; 0.823664424312258 1.823664424312258; 0.9072949016875161 1.9072949016875163; 0.9999999999999999 1.9999999999999998; 1.0927050983124846 2.0927050983124844])Compare distributed against centralized simulation to verify precision
res_fixed_c = run_scenario(:fixed, :centralised)
divergence = maximum(abs.(res_fixed.sim_data .- res_fixed_c.sim_data))
@printf("Max divergence between centralized and distributed simulation: %.3e\n", divergence)Max divergence between centralized and distributed simulation: 3.553e-14
Animated Multi-panel Visualization
We animate each scenario so the bobbing motion is clearly visible. For each frame k the three panels show:
- y-z position plane with agent trails, target positions, and harmonic extensions
- Tilt angle θ for both agents vs time (up to current frame)
- Per-agent tracking error ||xi[1:2] - q*i|| vs time
The visual style matches the escort example: steelblue agents, purple harmonic extension squares (alpha 0.3), grey star targets.
animate_scenario5(res_fixed;
frame_step = 2,
filename = "scenario5_fixed.gif",
fps = 15,
label_suffix = "fixed targets")
nothing # hide
animate_scenario5(res_bob;
frame_step = 2,
filename = "scenario5_bobbing.gif",
fps = 15,
label_suffix = "bobbing targets")
nothing # hide[ Info: Saved animation to /home/runner/work/CellularSheaves.jl/CellularSheaves.jl/docs/src/generated/layered/scenario5_fixed.gif
[ Info: Saved animation to /home/runner/work/CellularSheaves.jl/CellularSheaves.jl/docs/src/generated/layered/scenario5_bobbing.gif
![Scenario 5 [fixed targets]](../scenario5_fixed.gif)
![Scenario 5 [bobbing targets]](../scenario5_bobbing.gif)
Clean up worker processes
rmprocs(workers_pids)Task (done) @0x00007fe2a1aa11e0