Skip to content

Repository files navigation

MultiRobotBattle

RoboMaster-style total war on a lit competition arena with rectangular bunker walls, raised elevation pads, lane chicanes and hazard-cap cover: hundreds of custom chassis with coloured armour stripes, turrets and barrel tracers — red and green western bots charge east into blue and yellow eastern lines across a central no-man's-land; casualty bars and KIA counter climb until one allied coalition wins.

Total war — four allied armies and 576 custom bots (RoboMaster-style chassis, turrets, combined-arms echelons) clash on a wide front until one coalition wins. Allied teams never fire on each other; each robot still flocks only with its own colour, chases the nearest enemy, and trades fire when in range. Damage is per-attacker, so focus fire emerges for free — and the battle is built on the same multi-robot stack that hosts a benchmark-gated MAPF zoo (below).

build-jazzy docs live demos MAPF zoo

Swarm battle — multi-army total war

Split a Boids swarm into teams (and allied fronts) and let them fight. Every robot steers from only local information — flock with living teammates, advance on its nearest living enemy, keep spacing, and deal continuous damage to whatever enemy is in range — yet coherent battlefield behaviour emerges. Because damage is per-attacker, a robot caught by three enemies at once melts three times as fast, so the team that keeps formation and concentrates locally wears the other down. Nothing about that is scripted; it falls out of the local rules.

from mrn_coord.battle import ALLIANCE_NAMES, TEAM_NAMES, battle_scenario, simulate

bots, cfg, _ = battle_scenario("grand_alliance")  # 4 armies, 2 allied fronts
res = simulate(bots, cfg, max_ticks=1000)
print(ALLIANCE_NAMES[res.winning_alliance], res.survivors)

# Or fight for the centre instead of annihilation:
bots, cfg, _ = battle_scenario("hill")
res = simulate(bots, cfg, max_ticks=600)
print(res.objective, TEAM_NAMES[res.winner])

bots, cfg, _ = battle_scenario("ctf")
res = simulate(bots, cfg, max_ticks=900)
print(res.objective, TEAM_NAMES[res.winner])

It is built directly on the swarm flocking primitives in mrn_coord.flocking (separation / alignment / cohesion + mutual avoidance); the simulation lives in mrn_coord.battle, is pure Python (no numpy) and deterministic given the seed, and the hero GIF above is rendered straight from it (python3 scripts/make_battle_gif.py). A wounded-retreat behaviour is available (BattleConfig(retreat_frac=...)) but off by default so the default battle always reaches a decisive result.

The same engine, same local rules drive several different kinds of fight — a duel, a three-army free-for-all, unit classes (scout / soldier / tank / sniper, so quality-vs-quantity falls out), and terrain that splits the field:

A 2x2 grid of four RoboMaster-style swarm battles: custom chassis with coloured armour stripes and turrets in each panel — duel, three-army free-for-all, five tanks vs sixteen scouts, and a chokepoint through terrain obstacles. Laser tracers and elimination flashes flicker across every panel until each shows its winner.

Four battles, one engine: a duel, a three-army free-for-all, quality vs quantity (5 tanks vs 16 scouts — and the tanks win), and a chokepoint where terrain splits the field. Pick one with battle_scenario("free_for_all"); render the grid with python3 scripts/make_battle_gallery_gif.py.

576 bots · 45+ MAPF algorithms · browser demos — swarm battles where you swap real planner layers and measure win-rate. Try live: battle demo · MAPF zoo · tournament

Five RoboMaster-style objective battles in a 2x3 grid: king of the hill, domination, capture-the-flag, base assault on the enemy HQ, and escort payload delivery — custom chassis, terrain, and progress meters in each panel until one side wins.

Five win conditions on one engine — hill, domination, CTF, base assault (hold enemy HQ), and escort (push payload to enemy base). Try battle_scenario("hill") … "escort"; render the grid with python3 scripts/make_objective_triple_gif.py.

Charge layers triple panel: greedy vs greedy baseline, ORCA reciprocal charge, and BVC buffered-Voronoi charge on the chokepoint — red collision-free breakthrough vs blue greedy flocking.

ORCA / BVC charge — MAPF zoo collision avoidance as the battle movement filter. Try battle_scenario("orca_charge_duel") or python3 scripts/make_charge_gif.py.

Morale rout — six red tanks break eighteen blue scouts; strength bars collapse and routing survivors flee off the right flank instead of stalling.

Morale / rout — collapsing teams flee off-field instead of drawing. Try battle_scenario("morale_duel") or python3 scripts/make_morale_gif.py.

Fog times artillery dual panel: scouts reveal enemies through limited vision while red mortar carriers lob splash rounds — spectator map on the left, red fog vision with detonation rings on the right.

Fog × artillery — scouts spot under limited vision, then indirect splash clears clustered infantry. Try battle_scenario("fog_artillery") or python3 scripts/make_fog_artillery_gif.py.

Artillery barrage — red mortar carriers lob splash rounds into clustered blue infantry; orange detonation rings expand on impact while RoboMaster-style chassis advance across arena cover.

Artillery splash — indirect rounds with area damage and friendly-fire risk. Try battle_scenario("artillery_barrage") or python3 scripts/make_artillery_gif.py.

Fog of war dual panel: left shows the full spectator map, right shows red team vision where unseen blue robots are hidden until scouts make contact — count-aware wedge strikes after contact.

Fog of war — robots only sense enemies within range (and line of sight). Scouts spot first; count-aware focus fire after contact. Try battle_scenario("fog_ambush") or python3 scripts/make_fog_gif.py.

Capture the flag meets the MAPF zoo — same spawn, two movement stacks. Compare in the browser demo or the matchup ladder in tournament.html.

The battle stack is modular — swap one layer at a time and measure the effect with scripts/battle_gate.py (pinned win-rates in benchmarks/expected_metrics/battle_gate.json):

Layer Config What it does
Tactics tactics / tactics_by_team nearest, count-aware TeamHOI-lite, or distilled transformer
Assignment assignment hungarian (combat utility) or cbs_ta (grid BFS + Murty path-aware matching)
Formation formation line / wedge / screen / square via displacement consensus
Maneuver maneuver / maneuver_by_team greedy pursuit vs grid A* / prioritized / CBS / LaCAM-PIBT

On the chokepoint (same soldiers, Hungarian assignment + wedge held fixed), red's MAPF maneuver vs blue greedy is pinned in battle_gate:

Red movement layer Red win-rate vs greedy blue Notes
Greedy (baseline) ~50% symmetric — no planner advantage
Grid A* 67% routes around wall bunkers
Prioritized MAPF 67% collision-free column through gaps
CBS (GIF only) optimal joint paths — too slow for CI gate

| LoS & cover | require_los, obstacles | terrain blocks or attenuates fire |

2x2 grid of RoboMaster-style chokepoint battles: four panels swap only red's movement layer while blue stays on greedy pursuit — greedy baseline, grid A* maneuver, prioritized MAPF, and CBS joint planning — with wall bunkers and elevation pads; laser tracers flicker until each panel shows its winner.

Headline demo — same soldiers, terrain, assignment, and formation; only red's movement layer changes (greedy → A* → prioritized → CBS) while blue stays greedy. MAPF planners win ~67% on the chokepoint (pinned in battle_gate); CBS is shown here but skipped in CI because joint CBS replanning is too slow. Render with python3 scripts/make_maneuver_gif.py; try prioritized vs greedy live in the browser battle demo.

Side-by-side chokepoint battles with RoboMaster-style chassis: left panel shows greedy straight-line pursuit through three terrain gaps; right panel shows the same soldiers with Hungarian assignment, wedge formation, and prioritized MAPF maneuver — red plans paths around obstacles while blue charges in, lasers flicker, and one side wins.

Prioritized MAPF vs greedy on the chokepoint — red plans around wall bunkers while blue charges straight. Legacy two-panel render; the 2×2 grid above is the full movement-layer sweep.

Side-by-side chokepoint battles with RoboMaster-style chassis comparing MAPF layers on red: left panel uses Hungarian assignment with greedy pursuit; right panel stacks CBS-TA path-aware assignment with prioritized MAPF maneuver and wedge formation — red routes through gaps while lasers fire.

Full MAPF stack on red — CBS-TA assignment (who engages whom around terrain) plus prioritized maneuver (how they route there), vs Hungarian + greedy on the same spawn. Render with python3 scripts/make_mapf_stack_gif.py.

Side-by-side king-of-the-hill total-war contests with 36 robots: left panel uses Hungarian assignment and greedy pursuit; right panel stacks CBS-TA assignment with prioritized MAPF maneuver — both armies fight for a dashed yellow control circle at the centre while hold progress ticks up.

MAPF on an open-field total-war contest — same 18 vs 18 spawn fighting for the hill; red runs Hungarian + greedy (left) or CBS-TA + prioritized MAPF (right). Render with python3 scripts/make_mapf_total_war_gif.py.

Side-by-side capture-the-flag contests with RoboMaster chassis: left panel uses Hungarian assignment and greedy pursuit; right panel stacks CBS-TA assignment with prioritized MAPF maneuver — both armies fight for a yellow diamond flag at the centre while carriers run home through laser fire.

MAPF on capture-the-flag — same 10 vs 10 spawn; red runs Hungarian + greedy (left) or CBS-TA + prioritized MAPF (right). Render with python3 scripts/make_ctf_mapf_gif.py; try the dual panel in the browser demo.

A wide RoboMaster competition grid: two dense battle lines of eighty custom red and blue chassis per side advance from opposite flanks, collide in the centre in a chaotic melee of barrel tracers, and one army is wiped out.

Kingdom-scale clash — 80 vs 80 soldiers in opposing battle lines on a 100×56 field (spatial-hash accelerated). Render with python3 scripts/make_kingdom_gif.py or battle_scenario("kingdom").

And under the hood, that battlefield is the same multi-robot stack that hosts a pip-installable, benchmark-gated MAPF algorithm zoo:

A pip-installable MAPF algorithm zoo

The same 12x12 multi-agent path-finding instance with 14 agents solved side by side by four algorithms — CBS finds the optimal sum-of-costs 123, prioritized planning 129, PIBT and LaCAM flow greedily at 280 — each agent a coloured disc sliding to its goal ring, collision-free

The same 12×12 instance, 14 agents, solved side by side by four MAPF algorithms — optimal CBS finds sum-of-costs 123 while prioritized planning, PIBT, and LaCAM flow greedily higher. One of 45+ algorithms in the zoo, each faithfully reproduced from its paper and benchmark-gated.

45+ Multi-Agent Path Finding algorithms, faithfully reproduced from their papers and benchmark-gated — pure Python, ROS-free.

Most MAPF code online is one algorithm per repo, in C++, wired to a build system. This is the whole family in one importable package: solve an instance and compare paradigms in five lines — no ROS and no compiler. Every solver is reproduced from its source paper and benchmark-gated in CI, so each claim (a WIN, a LOSS, or an equivalence vs. a reference solver) is measured.

pip install "git+https://github.com/rsasaki0109/MultiRobotBattle"
from mrn_coord.mapf import GridWorld, cbs

grid = GridWorld(5, 5)
agents = {"1": ((0, 2), (4, 2)), "2": ((2, 0), (2, 4))}   # two crossing agents

sol = cbs(grid, agents)            # optimal, sum-of-costs Conflict-Based Search
print(sol.cost, sol.makespan)      # -> 9 5  (collision-free, optimal)

Swap cbs for ecbs, lacam, mapf_lns, pbs, mstar, … — they share the same (grid, agents) interface. The core has zero required dependencies (only the LP-based bcp needs pip install "...[bcp]" for numpy/scipy).

Try it without installing anything — live browser demos run the pure-Python engine via Pyodide: the swarm battle (duel / allied fronts / kingdom lines / chokepoint / MAPF duel) and the MAPF zoo (pick instance + solver, watch paths animate). Local fallback: python3 -m http.server from docs/demo/.

A taste of the catalogue

A representative slice — the full paper-by-paper catalogue with every solver's honest gated result is in docs/coordination.md:

Algorithm Paper One-line idea Gated result
CBS Sharon et al. 2015 optimal two-level conflict-based search the reference optimum
CBSH Li et al. 2019 admissible WDG heuristic + cardinal split same optimum, ~13× fewer expansions
ECBS Barer et al. 2014 bounded-suboptimal focal search cost ≤ w·opt, far fewer nodes
EECBS Li et al. 2021 WDG bound + Explicit Estimation Search ~1.9× fewer than ECBS at tight w
FECBS Chan et al. 2021 lend the unused suboptimality budget ~9× fewer than ECBS, dense tight-w
EPEA* Goldenberg et al. 2014 generate only f-matching children ~58× fewer nodes than joint A*
M* / rM* Wagner & Choset 2011 subdimensional expansion coupling stays at the irreducible group
ICTS Sharon et al. 2013 increasing-cost tree over MDDs same optimum, orthogonal to CBS
rectangle Li et al. 2019 barrier constraints break crossing symmetry ~20× blowup collapse
BCP Lam et al. 2019 branch-cut-and-price (LP / duality) LP-certified optimum (gap zero)
LaCAM Okumura 2023 complete config search driven by PIBT scales to large teams
MAPF-LNS2 Li et al. 2022 collision-minimizing anytime repair feasible where CBS busts its budget
Push-and-Rotate de Wilde et al. 2014 constructive push/swap/rotate primitives solves packed grids search blows up on
flow Yu & LaValle 2013 anonymous makespan as integer max-flow polynomial, self-certified optimum
RHCR Li et al. 2021 rolling-horizon lifelong MAPF sustained warehouse throughput
Footstep + multi-humanoid MAPF Hornung et al. 2012 anytime footstep A* + body-deconflicted teams bounded-suboptimal; team body-collision-free
ZMP preview-control walking Kajita et al. 2003 footstep plan → dynamically stable CoM trajectory ZMP stays in the support foot (preview ~100× tighter)
Capture Point push recovery Pratt et al. 2006 step to ξ = x + ẋ/ω₀ to absorb a push step there captures; short/long falls; big push N-step
DCM walking control Englsberger et al. 2015 backward-recursion DCM reference + tracking law over a footstep plan error → 0 at chosen rate k; open-loop blows up at ω
Trajectory-free MPC walking Wieber 2006 constrained-QP MPC: hard ZMP-in-support box + jerk/velocity objective hard constraint keeps ZMP legal under a push where unconstrained tips over
Auto-footstep MPC walking Herdt et al. 2010 footsteps become QP variables (second change of vars → still a box QP) capture step recovers a push the fixed-foot MPC falls under; frozen feet ≡ Wieber
Walking stabilizer (LIPM tracking) Kajita et al. 2010 closed-loop ZMP feedback p = p^ref + k_p e + k_v ė (k_p>1 beats the LIP instability), ZMP clipped to the foot open-loop ZMP playback diverges under a push; the stabilizer rejects it — until the ankle saturates and a step is needed
Push recovery (ankle/hip/step) Stephens 2007 decision surfaces on the capture point ξ; a flywheel (hip) widens the foot's capturable interval by Δ_hip, then a step closed-form Δ_hip matches exact bang-bang LIPPF sim (printed eq. 15 is a typo); ankle ⊂ hip ⊂ step nest
N-step capturability Koolen et al. 2012 N-step capture region ξ_N = foot + l_max·Σ e^{−kωT} (geometric series); bounded limit ξ_∞ past which no number of steps recovers closed form certified against exact greedy LIPM rollout; point/foot/reaction models = capture_point / push_recovery ankle / hip
Resolved Momentum Control Kajita et al. 2003 whole-body: centroidal momentum matrix h = A(q)·q̇, resolve a momentum + foot-constraint command by inertia-matrix pseudo-inverse first multibody leg; momentum matrix certified vs finite-difference; L=0 ⇒ internal counter-rotation (= reaction-mass/hip); kick with foot pinned
dRRT (discrete RRT) Solovey, Salzman & Halperin 2014 continuous-space multi-robot motion planning: explore the implicit tensor-product roadmap (∏ |Vᵢ| vertices) with an RRT driven by a direction oracle O_d needle-in-a-haystack: a 3.1M-vertex 4-robot swap solved with a 6-node tree; oracle 10/10 vs random-neighbour 1/10; plans collision-free ≥ 2r by exact continuous checks
dRRT* Shome, Solovey, Dobson, Halperin & Bekris 2020 asymptotically-optimal dRRT: keep the explored implicit roadmap as a graph, return its Dijkstra shortest path; anytime + informed sampling converges to the brute optimum over the full composite roadmap (within 2%, exact 9/10), monotone anytime cost, beats plain dRRT every time; informed sampling shrinks the explored graph 186→34
K-CBS (kinodynamic) Kottinger, Almagor & Lahijanian 2022 CBS with dynamics: Dubins-car robots, a kinodynamic RRT low level in state×time, space–time constraint tubes on conflict first dynamics model in the zoo; trajectories dynamically feasible (|ω|≤ω_max, exact arc propagation) and collision-free ≥ r_i+r_j; resolves every crossing where uncoordinated paths collide
Path–velocity decomposition Kant & Zucker 1986; O'Donnell & Lozano-Pérez 1989 fix each robot's geometric path, schedule only speed along it: A-star over the coordination space [0,1]ⁿ around the collision regions classic coordination diagram; resolves timing conflicts by velocity tuning (makespan optimal vs brute BFS), collision-free by construction; honestly returns None when only rerouting would help
Buffered Voronoi Cells Zhou, Wang, Bandyopadhyay & Schwager 2017 decentralized position-space avoidance: each robot moves toward its goal within its own Voronoi cell, retracted inward by its radius ORCA's position-space cousin; collision-free by construction (cells ≥ 2r apart); reaches goals where a naive baseline collides (7/12), stays safe even in symmetric deadlocks
Control Barrier Functions Wang, Ames & Egerstedt 2017 minimally-invasive safety filter: project the nominal go-to-goal control onto the safe polyhedron ḣ_ij ≥ −γh_ij (a QP = Euclidean projection) control-theoretic third of the avoidance trio; forward-invariance keeps ‖p_i−p_j‖ ≥ 2r, filter inactive when no conflict, beats a naive baseline (9/12), clears the symmetric crossing BVC deadlocks on
Token Swapping Yamanaka et al. 2014 min-swap-count reconfiguration: no blanks, every vertex holds a token, the only move is an adjacent swap; minimise total swaps distinct from the blank-mover reconfigurers; exact n! BFS plus closed forms — a path optimum is the inversion count, a K_n optimum is n − cycles — both beating BFS at n = 11; ⌈D/2⌉ lower bound; naive descent stalls (honest negative)

The MAPF zoo is the coordination layer of a larger stack:

ROS 2-native multi-robot simulation, navigation, and coordination — a deterministic, pure-core, CI-tested stack for developing and benchmarking multi-robot motion algorithms without hardware.

Localization lives in a companion repo: cooperative multi-agent localization (rosbag-centric, real-data benchmarks on UTIAS MR.CLAM / KITTI) is multirobot-localization. This repo answers how the robots move; that one answers where they are. They meet at the message contract (mrn_msgs/AgentState, RelativePoseConstraint): the simulator here emits them, that repo consumes them.

What It Is

A 2D, deterministic multi-robot world plus the coordination and navigation that moves robots through it. Every layer is a pure, ROS-free algorithm core unit-tested in CI, with thin ROS/CLI wiring on top.

  • Simulation (mrn_sim) — a deterministic 2D world: unicycle kinematics, circular obstacles with collision, and V2V / GNSS / range-bearing sensor models. It emits the localization message contract and accepts cmd_vel, so it is the plant the rest of the stack drives. An optional Gazebo adapter (mrn_gazebo) runs the same contract on a 3D physics world.
  • Navigation (mrn_sim.navigate, mrn_sim.kinodynamic) — point-to-point navigation: occupancy grid from the obstacles, grid A* planning, pure-pursuit following, with reciprocal multi-robot collision avoidance and replanning around dynamic obstacles — plus a continuous-space Hybrid A* kinodynamic planner (bounded turning radius, Dubins curves + analytic expansion) for smooth, feasibly-followable paths, DWA / MPC (iLQR) optimizing local controllers for accel-limited tracking, a Control Barrier Function QP safety filter for provable collision-free steering, and a certified body-true safety shield whose braking speed cap keeps the robot body — not a look-ahead point — collision-free under the accel limit, even against moving obstacles, and reciprocally for several shielded robots in adversarial mutual pursuit with no shared coordination (scripts/certify_shield.py).
  • Coordination (mrn_coord) — multi-agent path finding (optimal Conflict-Based Search, bounded-suboptimal ECBS, complete satisficing LaCAM, and anytime MAPF-LNS that scale further, prioritized planning and Priority-Based Search that reorders to break head-on deadlocks, all over a space-time A* or drop-in SIPP safe-interval low level, plus lifelong / online MAPF — stepped by PIBT or planned on a rolling horizon (RHCR) — with auction / Hungarian task allocation for warehouse-style endless-task throughput), ORCA reciprocal local collision avoidance, decentralized formation control, cooperative coverage (frontier + greedy/Hungarian allocation), and swarm flocking (Boids: separation / alignment / cohesion + obstacle avoidance + migration + predator evasion + leader following).
  • Benchmark environment (mrn_sim.benchmark) — plug your own multi-robot policy into a Scenario and get comparable metrics (success, makespan, path length, clearance, inter-robot distance, collisions). Five baseline policies ship for comparison — grid A* + pursuit, Hybrid A* kinodynamic, DWA local control, MPC (iLQR receding-horizon optimization, space-time avoidance), and ORCA — and and an end-to-end MAPF executor (mrn_sim.mapf_exec) runs a discrete grid plan in the continuous world — exposing where the discrete guarantee breaks down and bridging it with a Temporal-Plan-Graph schedule — while a bodied-AMR executor (mrn_sim.amr_footprint) replays the same plan as a rectangular differential-drive robot, surfacing the turning cost and the aisle width below which the footprint overlaps where the point plan called it safe — scripts/compare_planners.py tabulates them all across the bundled scenarios (benchmarks/comparison.md). ros2 run mrn_sim mrn_sim_bench crossing runs a bundled scenario with a baseline policy. MAPF also loads the standard MovingAI .map/.scen format (ros2 run mrn_coord mrn_mapf_bench), so the planners can be evaluated on the community benchmark suite.

Architecture

            ┌──────────── mrn_sim — the deterministic world ───────────┐
            │  unicycle kinematics · obstacles · collision · sensors    │
   cmd_vel ─▶  navigate (A* + pursuit + avoidance) / swarm / coordination│
            │  ─▶ AgentState · RelativePoseConstraint · ground truth     │
            └───────────────────────────────┬───────────────────────────┘
                                             │ message contract (mrn_msgs)
                          ▼                                ▼
              localization consumer                  Gazebo (mrn_gazebo)
              (multirobot-localization repo)         3D physics, same contract

The layers connect only through message contracts and pure interfaces, so each is testable and replaceable in isolation. The simulator emits exactly what a cooperative-localization consumer (the companion repo) ingests.

Demos

All animations are driven by the real algorithms (no hand-drawn paths) and are deterministic; regenerate with the matching scripts/make_*_gif.py.

The MAPF algorithm zoo — mrn_coord carries 45+ multi-agent path-finding algorithms faithfully reproduced from their papers in pure Python, each benchmark-gated (WIN / LOSS / honest-equivalence checked in CI against pinned metrics). The clearest way to feel the collection is to watch several of them solve the same instance at once — optimal solvers (CBS) find the cheapest joint plan while fast greedy ones (prioritized planning, PIBT, LaCAM) flow at a higher sum-of-costs:

The same 12x12 multi-agent path-finding instance with 14 agents solved side by side by four algorithms — CBS finds the optimal sum-of-costs 123, prioritized planning 129, PIBT and LaCAM flow greedily at 280 — each agent a coloured disc sliding to its goal ring, collision-free

Render your own with any solver or a side-by-side panel:

python3 scripts/animate_mapf.py --solver lacam --agents 12 --out out/lacam.gif
python3 scripts/animate_mapf.py --gallery cbs,prioritized,pibt_swap,lacam \
    --width 12 --height 12 --agents 14 --seed 7 --out out/gallery.gif

The full catalogue — CBS and its whole family (CBSH, ECBS, EECBS, FECBS, ICBS, MA-CBS, disjoint, BCP), optimal joint-space search (M*, rM*, EPEA*, ICTS, Standley), constructive solvers (Push-and-Rotate/Swap, TSWAP, Bibox, flow, DDM), the LaCAM/PIBT line, lifelong engines (RHCR, Token Passing, TPTS), and execution layers (k-robust, switchable-ADG) — is documented algorithm-by-algorithm, with the honest gated result of each, in docs/coordination.md.

The graph decides the cost — a different corner of the zoo is Token Swapping (Yamanaka et al. 2014): no blank cells, every vertex holds a token, and the only move is an adjacent swap — minimise the number of swaps. Sort the same reversed rainbow on three topologies and the optimum changes completely: a path needs the full inversion count (21), a cycle uses its wrap-around edge (9), a complete graph sorts in n − cycles (3) — it finishes and waits while the path is still grinding. Each panel is driven by the real optimal solver (python3 scripts/make_token_swap_gif.py):

The same reversed rainbow of seven coloured tokens sorts into order on three graphs side by side: on a path the tokens bubble past their neighbours and it takes 21 adjacent swaps; on a 7-cycle the wrap-around edge cuts it to 9; on the complete graph any two tokens swap directly and it is sorted in just 3, finishing first and holding while the path still works. A live counter ticks up under each.

Humanoid footstep planning → dynamically stable walk — the zoo also drops to the footstep resolution of a walking humanoid: search-based footstep planning (Hornung et al. 2012) places the feet, then ZMP preview control (Kajita et al. 2003) generates the center-of-mass trajectory that walks them — the induced Zero-Moment Point (orange) stays under each support foot while the CoM (cyan) sways from foot to foot, the dynamic-stability criterion made visible. Both are the real mrn_coord.mapf code; regenerate with python3 scripts/make_footstep_walk_gif.py:

Left: a top-down floor where a humanoid's planned footsteps zigzag forward as oriented rectangles, the current support foot highlighted, the center of mass tracing a cyan swaying path and the zero-moment point an orange line that hugs the support foot. Right: the lateral motion over time — the stepped reference ZMP, the induced ZMP tracking it, and the CoM swaying smoothly between, with a moving time cursor.

Why preview control? The Zero-Moment Point must stay inside the support polygon (the foot on the ground) or the robot tips over. The preview term — a look-ahead at the future footsteps — is exactly what keeps it there: with it, the ZMP threads every support foot (green, 100% inside); a reactive controller with no look-ahead overshoots each footfall and leaves the feet (red, 36%) — same plan, same feet (python3 scripts/make_zmp_figure.py):

A figure of the Zero-Moment Point. Left: a top-down floor with the planned footsteps as coloured rectangles; the preview-control ZMP (green) threads through every support foot while a no-preview reactive ZMP (red) overshoots upward past the feet at every step, leaving the support polygon. Right: the ZMP tracking its stepped reference over time, forward (a staircase climb) and lateral (the side-to-side sway), with the center of mass that produces it.

And it scales to a team: several humanoids plan footsteps to their goals and prioritized footstep MAPF deconflicts their bodies tick by tick, so they cross a shared area without touching — a lower-priority humanoid waits or detours. Each deconflicted plan is then run through the same ZMP preview-control walking simulator as above, so every humanoid is a real swaying center of mass with its ZMP held over the lit support foot — not a disc sliding along footstep centres (python3 scripts/make_footstep_mapf_gif.py):

Three humanoids on a shared floor, each its own colour, plan footsteps from their starts to their goal rings and walk them with ZMP preview control: each one's center of mass traces a side-to-side swaying trail across the floor while a small ZMP dot stays on the lit support foot, and their translucent torso discs cross in the middle without ever overlapping, a lower-priority humanoid detouring around the others.

And when a standing humanoid is pushed, where should it step to not fall? The Capture Point xi = x + v/omega0 (Pratt et al. 2006), on the same inverted pendulum: step there and the push is absorbed; step short or long and it topples (python3 scripts/make_capture_point_gif.py):

Three side-by-side inverted-pendulum humanoids take the same push. The left steps short of the capture point and topples, the middle steps exactly to the capture point marked on the ground and rights itself, the right steps past it and topples the other way.

Coordination — MAPF (Conflict-Based Search / prioritized), formation control, frontier coverage. Each has a CLI demo (mrn_mapf_demo, mrn_formation_demo, mrn_coverage_demo) and a thin ROS node; the top GIF shows CBS + formation.

Simulation & swarm — the mrn_sim 2D world and Boids flocking, the same foundation from a handful of robots to a swarm:

Robots roam a 2D world with obstacles, exchanging V2V links Seventy agents flock via separation, alignment, and cohesion

Flocking through the collision-aware world — migrate to a goal, flee a predator, and a multi-phase mission (regroup → migrate → evade → reach):

A flock migrating to a goal around obstacles A flock fleeing a pursuing predator A swarm carrying out a multi-phase mission

Navigation — grid A* plan + pure-pursuit follow to a goal, with reciprocal multi-robot avoidance and replanning around a moving obstacle:

Robots planning A* paths around obstacles to their goals Robots navigating to crossing goals while avoiding each other A robot replanning around a moving obstacle

ORCA — Optimal Reciprocal Collision Avoidance: two crowds walk straight at each other and pass through, collision-free, each picking the velocity closest to its goal that stays provably safe (mrn_coord.orca, regenerate with scripts/make_orca_gif.py). Our port is checked against the reference RVO2 library — same scenarios, same velocity to ~1e-5 (benchmarks/orca_rvo2.md):

Two crowds of agents walk into each other and pass through collision-free via ORCA reciprocal avoidance

Warehouse AMR fleet — lifelong (online) MAPF: a fleet of autonomous mobile robots takes an endless stream of pickup/dropoff tasks through a shelf-and-aisle warehouse, stepped collision-free by PIBT with cost-aware task allocation. The running counter tracks throughput (tasks served per timestep) — the metric a real fleet is judged on (mrn_coord.lifelong, regenerate with scripts/make_warehouse_gif.py):

Twelve autonomous mobile robots stream endless pickup/dropoff tasks through a shelf-and-aisle warehouse, collision-free via PIBT, while a counter shows the throughput per timestep

The same engine scales to a full fleet system — 100 AMRs working a six-by-nine shelf floor, every per-timestep move still the collision-free PIBT configuration, the counter climbing past 25 tasks/step (scripts/make_warehouse_gif.py --preset fleet):

A hundred autonomous mobile robots swarm a large shelf-and-aisle warehouse floor on a lifelong-MAPF schedule, collision-free via PIBT, the counter showing over twenty-five tasks served per timestep

3D physics — Gazebo — the same algorithms run in the mrn_gazebo (gz sim, Harmonic) 3D world: three robots cross the obstacle arena under the repo's own A* grid planning + pure-pursuit + reciprocal avoidance, driven over cmd_vel.

Three robots cross a 3D Gazebo arena of cylindrical obstacles via the repo's A* grid planning, pure pursuit, and reciprocal avoidance, each sweeping a 360-degree LiDAR whose returns trace the obstacles

Each carries a **360° LiDAR** whose live returns are overlaid on the render, so you can watch the lasers trace the obstacles and the other robots. It is rendered and recorded **fully offscreen on the GPU** (no GUI, no desktop window) by `scripts/record_gazebo_gif.py` — the 3D counterpart to the deterministic 2D demos, driven by the same algorithms.

The same offscreen seam runs the other layers in 3D too — ORCA crowds passing through each other, Boids swarming past obstacles (with the flock's LiDAR point cloud), CBS + formation funneling through a doorway, and a warehouse AMR fleet working a lifelong-MAPF schedule around the racking — each driven by the matching mrn_coord algorithm and sharing one recording harness (scripts/_gz_record.py, scripts/record_gazebo_{orca,swarm,coord,warehouse}_gif.py):

Two streams of robots in a 3D Gazebo world pass through each other collision-free via ORCA Twelve robots flock through a 3D Gazebo arena past obstacles via Boids rules, their LiDAR returns drawn as a point cloud Three robots funnel through a doorway via Conflict-Based Search then assemble a formation in 3D Gazebo, their LiDAR tracing the wall Six autonomous mobile robots work a 3D Gazebo shelf-and-aisle warehouse on a lifelong-MAPF schedule, their 360-degree LiDAR tracing the racking

Packages

Package Role
mrn_msgs message contracts (agent state, V2V relative-pose constraints, …) — the interface to the localization consumer
mrn_sim deterministic 2D world (kinematics, obstacles, sensors), point-to-point navigation, and the swarm driver; a mrn_sim_world ROS node
mrn_coord coordination: MAPF (CBS / prioritized), formation control, coverage, swarm flocking — pure cores, CLI demos, and thin ROS nodes
mrn_gazebo optional Gazebo (gz sim) adapter: bridges model poses into AgentState so a 3D physics world can be the plant (requires Gazebo; not in CI)

Quick Start

Just the MAPF zoo, no ROS? See the pip quickstart above — pip install the pure-Python coordination core and solve an instance in five lines, no colcon required.

For the full simulation / navigation / Gazebo stack, build with ROS 2 Jazzy:

source /opt/ros/jazzy/setup.bash
colcon build --symlink-install
source install/setup.bash

Run the coordination CLI demos (pure, no ROS daemon):

ros2 run mrn_coord mrn_mapf_demo            # Conflict-Based Search
ros2 run mrn_coord mrn_formation_demo       # formation control
ros2 run mrn_coord mrn_coverage_demo        # frontier allocation

Drive a planned path through the simulator, or close a formation loop in ROS:

ros2 launch mrn_sim mapf_through_sim.launch.py use_rviz:=true
ros2 launch mrn_coord formation_closed_loop.launch.py use_rviz:=true

Regenerate any demo GIF: python3 scripts/make_<name>_gif.py.

See docs/simulation.md, docs/coordination.md, and docs/gazebo.md.

Continuous Integration

Every push builds the workspace and runs colcon test over all packages (the pure algorithm cores), then exercises the coordination CLI demos end-to-end. A final benchmark gate (scripts/benchmark_gate.py) runs the bundled scenarios and the MovingAI MAPF example and compares their metrics against checked-in expectations in benchmarks/expected_metrics/, so a regression that drops a goal, introduces a collision, worsens a makespan / sum-of-costs, or cuts the 40-AMR fleet's throughput fails the build — the benchmarks are a guarded contract, not decoration.

Three further jobs check our implementations against the reference libraries they reproduce, each built from source so the core build never depends on it: our ORCA against RVO2 (same velocity to ~1e-5, benchmarks/orca_rvo2.md); our MAPF search against libMultiRobotPlanning — CBS reproducing its identical optimal sum-of-costs (benchmarks/mapf_libmrp.md) and ECBS honoring the same w·optimal suboptimality bound (benchmarks/ecbs_libmrp.md); and the PIBT core that steps the warehouse/fleet demos against the paper author's own pypibt — every configuration we emit judged collision-free by the reference's own validator, over the full lifelong run (benchmarks/pibt_pypibt.md). "Faithful port", "optimal solver", "bounded-suboptimal", and "collision-free PIBT" are measured contracts, not claims.

License

Apache-2.0.

Releases

Packages

Contributors

Languages