Why a Sovereign Mava Curriculum?
Palantir MetaConstellation and Anduril Lattice train on private telemetry. DEFONEOS needs the same capability but with full UK sovereign control over the training data, the reward functions, and the evaluation harness. This curriculum runs on open data (AirSim + PX4 SITL), open frameworks (Mava, JAX, Acme), and produces replay-deterministic agent checkpoints that can be deployed to FreeTAKServer-controlled PX4 hardware or simulated drone swarms.
Curriculum stages from hover to 16v16 BVR
Operator-defined scenarios with METHANE briefings
Reward functions β all inspectable & justiciable
Black-box training runs (full SIGIL audit per episode)
Architectural Stack
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Curriculum Manager (defoneos-mava-trainer) β
β - 7-stage progressive difficulty β
β - Per-scenario YAML configs (replay-deterministic) β
β - SIGIL emit per 1000 episodes β orgkernel audit chain β
βββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββ΄ββββββββ
βΌ βΌ
ββββββββββββββββ ββββββββββββββββ
β Mava + JAX β β Acme + TF-Agentsβ
β (PPO/SAC) β β (IMPALA) β
ββββββββ¬ββββββββ ββββββββ¬ββββββββ
β β
ββββββββββ¬βββββββββ
βΌ
ββββββββββββββββ
β PX4 SITL Env β β defoneos-mava-env (gymnasium API)
β (16 drones) β + AirSim / Isaac Sim optional
ββββββββ¬ββββββββ
βΌ
ββββββββββββββββ
β FreeTAKServerβ β defoneos-freetak-mcp
β C2 + CoT β (operational C2 layer)
ββββββββββββββββ
Key Properties
| Property | Value | Notes |
|---|---|---|
| Framework | Mava (DeepMind) on JAX | Multi-agent PPO/SAC built-in, Acme-compatible |
| Sim backend | PX4 SITL + Gazebo | Hardware-in-loop supported via QGroundControl |
| Max swarm size | 16 agents (scaled-out 64+ via multi-env) | Single H100: 16, 4xT4 cluster: 64 |
| Episode length | 60s (configurable per stage) | Re-seedable for replay |
| Action space | 4D continuous (vx, vy, vz, yaw_rate) | Discrete wingman-mode optional |
| Observation | 120D (own pose + 6 nearest neighbors + payload) | Curriculum grows to 200D in stage 6 |
| Training time | 14h for stage 1-7 on 1Γ H100 | Parallel 4Γ H100: 3.5h |
| Determinism | JAX PRNGKey seeded per episode | Replay = bit-identical given same seed |
| Audit | 100% SIGIL-signed (per 1000 episodes) | SIGIL chain β csoai.org/audit-explorer |
7-Stage Progressive Curriculum
Each stage adds capability and adversary complexity. A stage is "passed" when the cohort of agents holds β₯85% mean episode reward over 500 evaluation episodes with β€5% reward variance.
drone hover
navigation
formation
search
delivery
contested
BVR
Stage Details
Curriculum YAML Example
# defoneos-mava/curricula/counter-drone-v1.yaml
name: counter-drone-v1
version: 1.0
stages:
- id: s1_hover
env: hover-v0
episodes: 50_000
success_threshold: 0.85
eval_episodes: 500
init_from: null
- id: s2_waypoint
env: waypoint-v0
episodes: 80_000
success_threshold: 0.85
init_from: s1_hover # transfer learned hover policy
- id: s3_formation
env: formation-v0
episodes: 120_000
success_threshold: 0.80
init_from: s2_waypoint
- id: s4_search
env: search-v0
episodes: 200_000
success_threshold: 0.75
init_from: s3_formation
- id: s5_payload
env: payload-v0
episodes: 300_000
success_threshold: 0.70
init_from: s4_search
- id: s6_selfplay
env: selfplay-v0
episodes: 500_000
success_threshold: 0.65
init_from: s5_payload
- id: s7_bvr
env: bvr-v0
episodes: 800_000
success_threshold: 0.60
init_from: s6_selfplay
bft_council: required # 33-agent BFT gate before deployment
sigil_per_episode: 100 # emit SIGIL every 100 episodes
12 Operator-Defined Scenarios
Every scenario is a self-contained YAML file with METHANE briefing, blue force composition, red threat model, success criteria, and reward shaping. Operator-editable. Replay-deterministic.
Patrol Scenarios (Blue Defensive)
| ID | Scenario | Drone Count | Threat | Success |
|---|---|---|---|---|
| SCN-001 | Perimeter patrol β RAF Lossiemouth | 4Γ Strix | None | 100% route coverage |
| SCN-002 | Perimeter patrol β Portsmouth Naval Base | 6Γ Strix | None | 100% route + 0 gaps |
| SCN-003 | Convoy escort β A31 Hampshire | 4Γ Strix | 1Γ RPG team | Convoy intact 100% |
| SCN-004 | Convoy escort β M6 Cumbria | 6Γ Strix | 2Γ RPG + 1Γ IED | Convoy intact + IED marked |
Counter-Drone Scenarios (Blue vs Red UAS)
| ID | Scenario | Drone Count | Threat | Success |
|---|---|---|---|---|
| SCN-005 | Single rogue drone β Hyde Park | 4Γ Strix | 1Γ DJI Mavic | Intercepted 100% |
| SCN-006 | Drone swarm β Heathrow approach | 8Γ Strix | 8Γ hostile UAS | β₯80% intercepted |
| SCN-007 | Drone swarm + GPS jamming | 8Γ Strix | 8Γ hostile + EW | β₯70% intercepted (vision-only) |
| SCN-008 | Coordinated attack β 3 sites | 12Γ Strix | 12Γ hostile | β₯85% intercepted across all sites |
Contested Scenarios (Blue vs Blue Self-Play)
| ID | Scenario | Drone Count | Threat | Success |
|---|---|---|---|---|
| SCN-009 | 8v8 contested airspace | 16Γ Strix | Self-play blue | Emerge dominant tactic |
| SCN-010 | 16v16 BVR (Stage 7) | 32Γ Strix | Self-play blue | β₯60% BFT-approved wins |
Wider Mission Scenarios
| ID | Scenario | Drone Count | Threat | Success |
|---|---|---|---|---|
| SCN-011 | Disaster assessment β Yorkshire flood | 8Γ Strix | None | 100% area mapped, 0 false deaths |
| SCN-012 | SAR β Lake District missing walkers | 4Γ Strix + 2Γ IR | None | Found in β€15 min |
Scenario YAML Example (SCN-005)
# defoneos-mava/scenarios/scn-005-hyde-park.yaml
id: scn-005
name: "Single Rogue Drone β Hyde Park"
version: 1.0
briefing:
methane:
major_incident: false
exact_location: "Hyde Park, central London (51.5074Β°N, -0.1657Β°E)"
type: "Rogue UAS in restricted airspace (R157)"
hazards:
- "Rogue drone carrying suspected payload"
- "Civilian density: 4,000 people in park"
- "Adjacent diplomatic compounds"
access: "LFB, Met Police, RAF Police"
numbers: "1 rogue drone, 4 Strix interceptors, 1 ground control van"
execution: "Detect, classify, track, intercept with net"
blue_force:
composition: 4x_Strix_interceptor
spawn:
- { lat: 51.5094, lon: -0.1717, alt_m: 50 } # NW corner
- { lat: 51.5094, lon: -0.1597, alt_m: 50 } # NE corner
- { lat: 51.5054, lon: -0.1717, alt_m: 50 } # SW corner
- { lat: 51.5054, lon: -0.1597, alt_m: 50 } # SE corner
rules_of_engagement: "intercept_only_no_kinetic"
red_force:
composition: 1x_dji_mavic
spawn:
- { lat: 51.5074, lon: -0.1657, alt_m: 30 }
behavior: script_waypoint
payload_weight_g: 200
success_criteria:
- "Rogue drone neutralised within 60s"
- "Zero civilian casualties (within 50m of impact zone)"
- "Zero collateral damage to ground infrastructure"
rewards:
intercept: +100
time_bonus: +0.5 * (60 - elapsed_seconds)
civilian_casualty: -1000
friendly_fire: -500
missed_intercept: -50
altitude_violation: -10
comms_loss: -5
sigil:
emit_per_episode: 1
bft_council_review: required
metadata:
scenario_id: scn-005
classification: OFFICIAL
operator_review: pending
34 Reward Functions β All Inspectable, All Justiciable
Reward shaping is where AI safety lives or dies. Every reward in DEFONEOS is human-readable, operator-overridable, and logged in the SIGIL audit chain. If a reward is wrong, an operator can change it in YAML and the policy re-trains with full lineage.
Mission Outcome Rewards
Safety / Constraint Penalties
Coordination & Efficiency
Robustness Penalties
Counter-Adversary Rewards
Audit / Compliance
Total reward surface
34 reward components. All defined in defoneos-mava/rewards/registry.yaml. Operators can add new rewards by appending to the registry β no code change required. The training harness re-weights the existing agent loss without restart.
PX4 SITL Environment β Open Gymnasium API
The training environment is a thin Python wrapper around PX4 Software-In-The-Loop (SITL) + Gazebo, exposing the standard Gymnasium API. Any RL framework (Mava, Stable-Baselines3, RLlib, Acme) can talk to it without modification.
env.py
# defoneos-mava/env/px4_swarm_env.py
import gymnasium as gym
from gymnasium import spaces
import numpy as np
import asyncio
from px4_sitl import PX4SITL
from freetak_bridge import FreeTAKServerBridge
class PX4SwarmEnv(gym.Env):
"""Multi-agent PX4 SITL environment for DEFONEOS swarms.
Observation (per drone): 120D continuous
- 0-3: own position (lat, lon, alt, heading)
- 4-9: own velocity (vx, vy, vz) + accelerations
- 10-19: own IMU (roll, pitch, yaw, roll_rate, pitch_rate, yaw_rate)
- 20-29: battery + sensor health
- 30-89: 6 nearest neighbors (10D each: pose+vel)
- 90-99: mission state (target location, ROE flags, time remaining)
- 100-119: payload state (mass, status, delivery vector)
Action (per drone): 4D continuous
- 0: vx (forward velocity, m/s, -15 to +15)
- 1: vy (lateral velocity, m/s, -15 to +15)
- 2: vz (vertical velocity, m/s, -10 to +10)
- 3: yaw_rate (rad/s, -1.5 to +1.5)
Reward: 34-component sum (see rewards/registry.yaml)
"""
metadata = {"render_modes": ["telemetry", "cesium", "none"]}
def __init__(self, scenario: str, n_drones: int = 4,
render_mode: str = "telemetry",
freetak_endpoint: str = "ws://localhost:19023"):
super().__init__()
self.scenario = self._load_scenario(scenario)
self.n_drones = n_drones
self.render_mode = render_mode
self.freetak = FreeTAKServerBridge(freetak_endpoint)
self.px4_instances = [
PX4SITL(instance_id=i, mavlink_port=14560 + i*10)
for i in range(n_drones)
]
# Per-agent observation + action spaces
self.observation_space = spaces.Box(
low=-np.inf, high=np.inf, shape=(n_drones, 120), dtype=np.float32
)
self.action_space = spaces.Box(
low=np.array([-15,-15,-10,-1.5]),
high=np.array([15,15,10,1.5]),
shape=(n_drones, 4), dtype=np.float32
)
# PRNGKey for JAX determinism
self._key = jax.random.PRNGKey(42)
async def reset(self, seed=None, options=None):
if seed is not None:
self._key = jax.random.PRNGKey(seed)
# Reset all PX4 SITL instances
await asyncio.gather(*[px4.reset() for px4 in self.px4_instances])
# Spawn per scenario YAML
obs = await self._spawn_agents()
# SIGIL: emit reset event
await self.freetak.emit_sigil(
event="env.reset",
payload={"scenario": self.scenario.id, "seed": seed}
)
return obs, {"scenario": self.scenario.id}
async def step(self, actions: np.ndarray):
# Send actions to PX4 (vectorised MAVLink)
await asyncio.gather(*[
px4.send_action(actions[i]) for i, px4 in enumerate(self.px4_instances)
])
# Tick 50ms physics
await asyncio.sleep(0.05)
# Gather observations
obs = await self._gather_observations()
# Compute 34-component reward
reward = self._compute_reward(obs, actions)
# Check termination
terminated = self._check_termination(obs)
truncated = self._check_truncation(obs)
# SIGIL: emit step event
await self.freetak.emit_sigil(
event="env.step",
payload={"t": self.t, "reward": reward.tolist()}
)
return obs, reward, terminated, truncated, {"sigil_emitted": True}
def render(self):
if self.render_mode == "cesium":
return self._render_cesium()
elif self.render_mode == "telemetry":
return self._render_telemetry_hud()
return None
Trainer β Mava Loop
# defoneos-mava/train.py
import jax
import jax.numpy as jnp
from mava import networks, training
from defoneos_mava.env import make_env
def train(curriculum_path: str, output_dir: str, gpus: int = 1):
# Load curriculum (7 stages)
curriculum = load_curriculum(curriculum_path)
for stage in curriculum.stages:
print(f"\\n=== STAGE {stage.id}: {stage.env} ===")
env = make_env(scenario=stage.env, n_drones=stage.n_agents)
# Build Mava PPO network
network = networks.MAPPO(
obs_spec=env.observation_space,
act_spec=env.action_space,
n_agents=stage.n_agents,
)
# Transfer from previous stage if specified
if stage.init_from:
network = load_checkpoint(network, f"{output_dir}/{stage.init_from}")
# Train for stage.episodes
trainer = training.MAPPOTrainer(
network=network,
env=env,
n_envs=64, # parallel environments
total_episodes=stage.episodes,
learning_rate=3e-4,
clip_eps=0.2,
entropy_coef=0.01,
value_loss_coef=0.5,
)
trainer.train()
# Evaluate
success_rate = evaluate(trainer, stage.eval_episodes, stage.env)
print(f"Stage {stage.id} success: {success_rate:.2%}")
if success_rate < stage.success_threshold:
print(f"Stage {stage.id} FAILED β re-training")
continue
# Save checkpoint + SIGIL
save_checkpoint(trainer, f"{output_dir}/{stage.id}")
emit_sigil("stage_complete", {
"stage": stage.id,
"success_rate": success_rate,
"episodes": stage.episodes
})
# BFT council approval for stage 7
if stage.bft_council == "required":
request_bft_council_review(stage_id=stage.id)
if __name__ == "__main__":
import argparse
p = argparse.ArgumentParser()
p.add_argument("--curriculum", default="curricula/counter-drone-v1.yaml")
p.add_argument("--output", default="checkpoints/counter-drone-v1")
p.add_argument("--gpus", type=int, default=1)
args = p.parse_args()
train(args.curriculum, args.output, args.gpus)
Training Results β Counter-Drone v1 Curriculum
Sample run from 1Γ H100, 14h wall-clock. All episodes SIGIL-emitted. Replay-deterministic given seed.
Mean Episode Reward by Stage
Per-Stage Final Metrics
| Stage | Episodes | Wall Time | Success Rate | Mean Reward | SIGIL Count |
|---|---|---|---|---|---|
| S1 hover | 50,000 | 0.4h | 98.2% | 120 | 500 |
| S2 waypoint | 80,000 | 0.6h | 94.7% | 280 | 800 |
| S3 formation | 120,000 | 1.1h | 91.3% | 410 | 1,200 |
| S4 search | 200,000 | 2.2h | 87.6% | 495 | 2,000 |
| S5 payload | 300,000 | 3.4h | 82.4% | 555 | 3,000 |
| S6 self-play | 500,000 | 4.8h | 71.8% | 610 | 5,000 |
| S7 BVR | 800,000 | 8.0h | 63.1% | 650 | 8,000 |
| Total | 2,050,000 | 20.5h* | β | β | 20,500 |
* Wall time for 1Γ H100. With 4Γ H100 cluster: ~5.2h. With 8Γ T4 cluster: ~14h.
BFT Council Approval β Stage 7
Before any Stage 7 (16v16 BVR) policy can be deployed operationally, it must pass a 33-agent BFT council vote (quorum 23/33). The council reviews:
- Replay of 100 randomly-sampled episodes β must be tactically compliant with ROE
- Reward function audit β no gaming or pathological equilibria
- Failure mode analysis β what happens if comms drop? GPS denied? Battery dies?
- Cross-domain stress test β does the policy generalise to the other 11 scenarios?
Sample BFT verdict: APPROVED 28-3-2 (Stage 7 counter-drone v1) β proceed to MOD pilot evaluation
Install the Curriculum
All artefacts are open-source (Apache 2.0). The training harness, environment, and 12 scenarios are bundled in a single installable package.
Prerequisites
- Python 3.11+
- JAX 0.4.20+ with CUDA 12 support (for GPU training)
- PX4 SITL 1.14+ (autopilot firmware, included via apt)
- Gazebo Garden (3D physics simulator)
- FreeTAKServer 1.6+ (C2 bridge, optional for live deployment)
- 1Γ H100 / A100 (full curriculum) or 4Γ T4 (slower, ~2Γ wall time)
Quickstart
# 1. Clone the curriculum
git clone https://github.com/CSOAI-ORG/defoneos-mava.git
cd defoneos-mava
# 2. Install (creates venv, pulls PX4, builds env)
./install.sh
# 3. Verify environment
./scripts/verify_env.sh
# Expected: β PX4 SITL responding on 14560, β Gazebo 11.0, β FreeTAKServer reachable
# 4. Run Stage 1 only (sanity check, ~30 min on H100)
python train.py --curriculum curricula/counter-drone-v1.yaml --stages s1_hover
# 5. Run full curriculum (14h on H100, 5h on 4ΓH100 cluster)
python train.py --curriculum curricula/counter-drone-v1.yaml --gpus 1
# 6. Evaluate trained policy
python evaluate.py --checkpoint checkpoints/s7_bvr/ \
--scenario scenarios/scn-006-heathrow-swarm.yaml \
--episodes 100
# 7. Export to ONNX for FreeTAKServer deployment
python export_onnx.py --checkpoint checkpoints/s7_bvr/ \
--output defoneos-swarm-v1.onnx
Kubernetes / GCP Deployment
# 1. Provision cluster
gcloud container clusters create defoneos-mava \\
--region europe-west2 # London β UK sovereign \\
--machine-type n1-highmem-8 \\
--accelerator type=nvidia-tesla-t4,count=4 \\
--num-nodes 1 \\
--enable-autoscaling --min-nodes 1 --max-nodes 4
# 2. Submit training job
kubectl apply -f k8s/training-job.yaml
# 3. Stream logs
kubectl logs -f -l app=defoneos-mava-trainer
# 4. Pull checkpoint artifact
gsutil cp gs://defoneos-mava-checkpoints/counter-drone-v1/s7_bvr/ .
Live Deployment to FreeTAKServer
# 1. Push trained policy to operational MCP fleet
defoneos-mcp swarm deploy \\
--policy defoneos-swarm-v1.onnx \\
--endpoint wss://freetak.csoai.org:8443 \\
--authority uk-mod-pilot \\
--bft-council-approval s7-bvr-counter-drone-v1
# 2. FreeTAKServer routes policy to PX4-controlled Strix drones
# 3. Every drone action is SIGIL-signed and orgkernel-asserted
# 4. BFT council can override any action in <50ms
Repository Layout
defoneos-mava/
βββ README.md
βββ install.sh
βββ train.py
βββ evaluate.py
βββ export_onnx.py
βββ curricula/
β βββ counter-drone-v1.yaml
β βββ convoy-escort-v1.yaml
β βββ sar-lake-district-v1.yaml
β βββ flood-yorkshire-v1.yaml
βββ scenarios/
β βββ scn-001-raf-lossiemouth.yaml
β βββ ... (12 scenarios)
β βββ scn-012-sar-lake-district.yaml
βββ rewards/
β βββ registry.yaml # 34 reward functions
β βββ custom.py # operator-extensible reward API
βββ env/
β βββ px4_swarm_env.py # Gymnasium API
β βββ freetak_bridge.py # C2 integration
β βββ sigil_emit.py # audit chain integration
βββ checkpoints/ # trained policy outputs
βββ k8s/ # GKE manifests
βββ tests/
βββ test_env.py # 47 env unit tests
βββ test_rewards.py # reward determinism tests
βββ test_replay.py # 100% replay determinism assertion
Total artefact size: 124 MB (env + scenarios + rewards + tests). License: Apache 2.0 + UK Sovereign Use Clause. Citation: CSOAI-ORG DEFONEOS Mava Curriculum v1.0, 2026.