# Multi-agent RL navigation — trustworthy autonomous systems (simulation only) **Repo: `dummy033485/multi-agent-rl-navigation`** **Status: model repo live. No Hugging Face Space has been created.** A shared decentralized PPO policy for 2D multi-agent navigation, compared honestly against a textbook classical controller on the *same* seeded layouts. Built as a portfolio project for an application to Prof. Jiachen Li's Trustworthy Autonomous Systems Lab (Georgia Tech): robotics, trustworthy AI/ML, reinforcement learning, control, optimization and multi-agent systems. ## What this is — and is not - **Is:** a self-contained 2D physics simulator (written for this project, no external environment, no hardware, no proprietary data) + a learned shared policy + a classical baseline + a fixed, seeded evaluation. - **Is not:** a robot result. No hardware was used. The RL policy has **no formal safety guarantee** — every safety number below is empirical, measured on the simulator's fixed test layouts. The wall-trap layout is deliberately constructed to defeat a pure potential-field controller. ## Simulator (`sim.py`) - Arena 12x12 with walls. Agents: double-integrator kinematics, `dt=0.1`, max speed 1.2, max acceleration 3.0, agent radius 0.25, episode cap 200 steps. - Each layout: 3 circle obstacles (radius 0.5–0.8) **and** 1 wall/barrier segment. Two layout families, generated with fixed seeds: - *general*: a short (3 m) barrier segment at a random mid-arena angle. - *trap*: a vertical segment at x=6, y in [2,10], with gaps only at the ends; starts on the left, goals near-directly opposite on the right, so a direct potential field is attracted right and repelled left and stalls in the middle. - Decentralized 20-dim observation per agent: own velocity, relative goal vector + distance, nearest circle obstacle (direction, surface distance, radius), nearest wall segment (direction, distance, both endpoint vectors), nearest other agent (direction, distance), nearest arena-wall distance. (Endpoint vectors were added after a failed training run: with only the nearest segment point, the policy could not observe where the wall ends — see Limitations and `BUILD_REPORT.md`.) - Collision = distance to an obstacle/wall segment/arena wall below the agent radius, or agent–agent distance below 0.5. A colliding arrival is never counted as a success. Safety margin for violation counting: 0.15. ## Policies - **Classical** (`policies.py`, exact gains documented in code): PD controller toward the goal (KD=2.0) + radial repulsive potential field (ETA=6.0, influence distance D0=2.0) from the nearest obstacle, segment and neighbour. No learning, no tangential escape trick — the honest textbook baseline. It stalls (timeout, 0 collisions) on every trap layout. - **RL**: shared decentralized PPO, MLP 20→128→128 (the observation grew from 15 to 20 dims when segment-endpoint features were added), Gaussian actions, tanh-bounded to the acceleration limit. One set of weights drives every agent from its own observation. Training recipe (all in `train.py`, PPO from scratch with torch only): the policy was warm-started by behaviour cloning the classical controller on general layouts only (400 episodes, 4 agents, final action MSE 0.079), then PPO fine-tuned in four phases — 1-agent mixed (trap ramp), 1-agent trap-only with an exploration reset, 1-agent mixed consolidation, and 4-agent mixed — 300 iterations × 128-step rollouts × 16 envs per phase (614,400 environment steps per phase; 2,457,600 in the kept chain plus the BC dataset). Earlier from-scratch PPO runs failed outright (success 5–15%) and are documented, with their fixes, in `BUILD_REPORT.md` — they are not hidden. The final phase's curve is `training_curve.csv` (earlier phases have their own `training_curve_*.csv` files). ## Evaluation protocol (exact) Fixed layouts, identical for every policy and condition: - Single-agent: seeds 1000–1019 (10 general + 10 trap, alternating). - 4-agent: seeds 2000–2011 (6 general + 6 trap, alternating). - RL evaluated with the deterministic (mean) action; classical is deterministic. Noise conditions reuse the same noise RNG seed per layout/condition for both policies. - Conditions: clean; observation noise σ=0.05; observation noise σ=0.10; dynamics disturbance (added acceleration noise σ=1.0); obstacle shift +0.5 (circles and segments shifted +0.5 in x). - Metrics are per agent-episode (20 single-agent and 48 4-agent episodes per policy per condition; 728 rows in `eval.csv`). Steps-to-goal and clearance are reported on successes only; safety-violation rate is the fraction of active steps with clearance < 0.15. ## Results (copied from `results.json` — no other numbers are claimed) ### Clean | Policy | Agents | Success | Collision | Obstacle coll. | Agent–agent coll. | Timeout | Steps (succ.) | Path len. | Min clearance (succ.) | Safety-viol. rate | General | Trap | |---|---|---|---|---|---|---|---|---|---|---|---|---| | Classical | 1 | 0.450 | 0.000 | 0.000 | 0.000 | 0.550 | 73.22 | 5.35 | 0.588 | 0.000 | 0.90 | 0.00 | | RL | 1 | 0.700 | 0.200 | 0.200 | 0.000 | 0.100 | 86.00 | 9.59 | 0.419 | 0.033 | 0.60 | 0.80 | | Classical | 4 | 0.396 | 0.000 | 0.000 | 0.000 | 0.604 | 160.42 | 5.87 | 0.690 | 0.000 | 0.79 | 0.00 | | RL | 4 | 0.792 | 0.146 | 0.146 | 0.000 | 0.062 | 141.18 | 10.63 | 0.505 | 0.019 | 0.75 | 0.83 | Reading, honestly: the RL policy wins on success everywhere, and is the only policy that solves trap layouts or coordinates 4 agents (zero agent–agent collisions clean). It loses on safety in the single-agent case: 20% collisions and lower clearance than the classical controller's 0% collisions. The classical controller never collides clean — it fails by timing out at the wall instead. RL paths are longer (detours). ### Robustness (success / collision) | Condition | RL 1-agent | Classical 1-agent | RL 4-agent | Classical 4-agent | |---|---|---|---|---| | Clean | 0.700 / 0.200 | 0.450 / 0.000 | 0.792 / 0.146 | 0.396 / 0.000 | | Obs. noise σ=0.05 | 0.750 / 0.150 | 0.450 / 0.100 | 0.646 / 0.292 | 0.333 / 0.125 | | Obs. noise σ=0.10 | 0.600 / 0.300 | 0.200 / 0.250 | 0.542 / 0.396 | 0.104 / 0.396 | | Dynamics disturbance σ=1.0 | 0.650 / 0.200 | 0.350 / 0.000 | 0.771 / 0.167 | 0.375 / 0.000 | | Obstacle shift +0.5 | 0.550 / 0.300 | 0.350 / 0.000 | 0.708 / 0.250 | 0.375 / 0.000 | The RL policy degrades gracefully on success under every perturbation and stays well above classical, but its collision rate rises under noise (up to 0.396 at σ=0.10, 4-agent) — the trustworthy claim is *measured robustness of success*, not a safety proof. ### Ablation — neighbour observation, 4-agent clean | Policy | Success | Collision | Agent–agent collision | |---|---|---|---| | RL with neighbour observation | 0.792 | 0.146 | 0.000 | | RL with neighbour observation zeroed | 0.146 | 0.708 | 0.333 | Removing the neighbour observation collapses coordination: success falls from 0.792 to 0.146 and agent–agent collisions jump from 0 to 0.333. The shared policy genuinely uses the neighbour channel; it is not four independent single-agent policies. ## Files in this repo - `ppo_policy.pt` — final shared PPO weights (ActorCritic, obs dim 20). - `sim.py`, `policies.py`, `train.py`, `bc.py`, `evaluate.py`, `plots.py` - `results.json`, `eval.csv` (728 rows), `training_curve.csv` (final 4-agent phase) + `training_curve_single.csv`, `training_curve_single2.csv`, `training_curve_ft1.csv`, `training_curve_ft2.csv`, `training_curve_ft3.csv` (earlier phases) - `plots/trap_trajectories.png`, `plots/four_agent.png`, `plots/clean_comparison.png`, `plots/training_curve.png` - `space/` — self-contained Gradio demo (copies of `sim.py`, `policies.py`, `ppo_policy.pt` + `app.py`, `requirements.txt`). See `SMOKE_TEST.md`. ## Limitations 1. 2D simulation only. No hardware, no real robot, no vision (the observation is ground-truth state features + optional noise, not pixels). 2. The wall trap is constructed. It demonstrates a known potential-field failure mode; it does not prove the policy generalises to arbitrary unseen maps (the +0.5 obstacle-shift condition is the only unseen-layout test, and success there is 0.55–0.71, not 1.0). 3. No formal safety guarantee. Collisions still occur (5–40% depending on condition). A safety shield / control-barrier filter would be the natural next step and is explicitly **not** claimed here. 4. Small evaluation: 20 single-agent and 12 four-agent layouts. Differences of a few percent are within layout noise; the large effects (trap 0.00 → 0.80–0.83, 4-agent 0.396 → 0.792, ablation 0.792 → 0.146) are the ones to rely on. 5. Training required a behaviour-cloning warm-start and a phased curriculum (including a trap-only phase that temporarily caused catastrophic forgetting of general layouts, fixed by a mixed consolidation phase). From-scratch PPO did not learn this task in ~1.9M environment steps of attempts. See `BUILD_REPORT.md`. ## Reproduce ```bash # shared venv (torch, numpy, matplotlib, gradio); proxy vars must be unset for gradio imports export VENV=~/workspace/rul-project/venv/bin/python env -u http_proxy -u https_proxy -u HTTP_PROXY -u HTTPS_PROXY -u all_proxy -u ALL_PROXY -u no_proxy -u NO_PROXY $VENV bc.py --episodes 400 --n-agents 4 --epochs 30 --out bc_policy.pt env -u ... $VENV train.py --iterations 300 --rollout 128 --n-agents 1 --init bc_policy.pt --out ppo_ft1.pt --curve training_curve_ft1.csv # (trap-only exploration-reset weights were made from ppo_ft1.pt by setting log_std to -0.5; see BUILD_REPORT.md) env -u ... $VENV train.py --iterations 300 --rollout 128 --n-agents 1 --init ppo_ft1_explore.pt --trap-prob 1.0 --trap-ramp-iters 1 --ent-coef 0.02 --out ppo_ft2.pt --curve training_curve_ft2.csv env -u ... $VENV train.py --iterations 300 --rollout 128 --n-agents 1 --init ppo_ft2.pt --trap-prob 0.5 --trap-ramp-iters 1 --out ppo_ft3.pt --curve training_curve_ft3.csv env -u ... $VENV train.py --iterations 300 --rollout 128 --n-agents 4 --init ppo_ft3.pt --trap-prob 0.5 --trap-ramp-iters 1 --out ppo_policy.pt --curve training_curve.csv env -u ... $VENV evaluate.py --weights ppo_policy.pt --out results.json --csv eval.csv env -u ... $VENV plots.py --weights ppo_policy.pt --results results.json --curve training_curve.csv --outdir plots ``` All commands are run from `~/workspace/li-project/`. Fixed seeds throughout (training seed 0, BC seed 123, evaluation layout seeds 1000–1019 / 2000–2011, noise seeds 90000+layout index).