Layered Control Architecture for 6-Agent Escort Formation ($SE(3)$ Homogeneous Sheaf)
This example demonstrates scaling the layered control architecture to a 6-agent formation escorting a slow-moving target in 3D space using an $SE(3)$ Homogeneous Affine Cellular Sheaf.
High-Level API
We use the new Formations and DistributedLayeredControl modules to significantly simplify the setup of the escort ring and the execution of the distributed local controllers.
using CellularSheaves
using CellularSheaves.Formations
using CellularSheaves.AgentControllers
using CellularSheaves.DistributedLayeredControl
using LinearAlgebra
using Statistics
using Distributed
using Plots
using PrintfA single house style for every figure below
default(framestyle = :box, grid = true, gridalpha = 0.18, gridstyle = :dot,
titlefontsize = 10, guidefontsize = 9, legendfontsize = 8, tickfontsize = 8,
markerstrokewidth = 0, size = (720, 380))
const RING_COLOR = :steelblue
const TARGET_COLOR = :blackSetup 10D Quadrotor Dynamics & DARE Solver
dyn = QuadrotorDynamics()
DT = 0.05
nx = 10Compute Optimal LQR Gain via Discrete Algebraic Riccati Equation (DARE)
Q_diag = [500.0, 500.0, 500.0, 150.0, 150.0, 100.0, 100.0, 100.0, 5.0, 5.0]
Q_lqr = Matrix(Diagonal(Q_diag))
R_lqr = Matrix(Diagonal([0.005, 0.005, 0.005]))
lqr_controller = LQRController(dyn, DT, Q_lqr, R_lqr)
K_lqr = lqr_controller.KSE(3) Homogeneous Coordination Sheaf Construction (D = 4)
6 agents in a single escort ring.
const NA = 6
const NT = 1
const TV1 = NA + 1
r_ring = 0.3Build the escort ring, with Agent 1 pinned as the observer
sheaf = build_escort_ring(NA, TV1, r_ring; observers=[1])Fast-moving target trajectory
target1_pos(node, t) = [0.5cos(0.5*t), 0.5sin(0.5*t), 1.5 + 0.1sin(1.0*t), 1.0]Provision Worker Processes
Provision exactly NA worker processes (one for each agent's flight computer)
workers_pids = addprocs(NA; exeflags = "--project=$(Base.active_project())")Provide the cellular sheaves environment to all workers
@everywhere workers_pids begin
using CellularSheaves
endSimulation Framework
STEPS = 200
epsilon = 0.02Start agents in a line along the x-axis
init_states = [[r_ring*i/NA, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] for i in 1:NA]Create heterogeneous agent properties (varying mass and inertia)
dyns = [QuadrotorDynamics(m=0.5 + 0.05*i, Ixx=0.01 + 0.002*i, Iyy=0.01 + 0.002*i) for i in 1:NA]
K_lqrs = [LQRController(d, DT, Q_lqr, R_lqr).K for d in dyns]
agent_configs = [(init_states[i], dyns[i], K_lqrs[i]) for i in 1:NA]
prob = LayeredControlProblem(sheaf, [TV1], target1_pos, agent_configs, DT, STEPS, r_ring)Run distributed simulation
init_distributed_agents!(workers_pids, agent_configs, DT, epsilon)
sim_d_res = run_layered_simulation(prob, workers_pids; mode=:distributed)Run centralized simulation
init_distributed_agents!(workers_pids, agent_configs, DT, epsilon)
sim_c_res = run_layered_simulation(prob, workers_pids; mode=:centralised)
divergence = maximum(abs.(sim_d_res.sim_data .- sim_c_res.sim_data))
@printf("Max divergence between centralized and distributed 6-agent simulation: %.3e\n", divergence)Clean up worker processes
rmprocs(workers_pids)Multi-Projection Trajectory & Attitude Dynamics Visualization
We can now easily animate the simulation using the Plots.jl recipe framework. The animate_layered_escort function abstracts away the complex multi-panel visualization. (Note: this relies on the CellularSheavesPlots package extension)
animate_layered_escort(sim_d_res; frame_step = 4, filename = "layered_escort_tracking.gif", fps = 10)
nothing # hide![]()