FireDrones is a 2D grid-based multi-agent simulation where autonomous drones coordinate to respond to urban fires. The system models a rectangular workspace divided into square cells, featuring dynamic fire tasks, obstacle buildings, battery and water constraints, path planning with A* and Dijkstra, greedy task allocation, basic collision avoidance, and a real-time pygame visualization.
This project is an academic exercise in multi-agent robotic motion planning. It demonstrates:
- Multi-agent path planning (A*, Dijkstra)
- Task allocation (greedy nearest-agent assignment)
- Basic combinatorial optimization (minimize total assignment cost)
- Dynamic replanning (new fires appear mid-simulation)
- Collision avoidance (wait-based strategy)
- Battery and water resource management
- GUI visualization with real-time metrics
- Labeled test scenarios
- Modular, testable code
- 30×20 grid workspace (configurable)
- 4 autonomous drone agents with state machines
- Fires dynamically spawn during the simulation
- A* and Dijkstra pathfinding (switchable at runtime)
- Greedy task allocator (minimize total distance)
- Priority-based task assignment (toggle with
P) - Wait-based collision avoidance
- Battery and water resource constraints with auto return-to-base
- Pygame GUI with sidebar metrics, path visualization, and keyboard controls
- 8 labeled predefined scenarios
- 30+ unit and scenario tests
Prerequisites: Python 3.11+ and an existing
.venvvirtual environment.
# Activate the virtual environment
.venv\Scripts\activate
# Install dependencies from requirements.txt
py -m pip install -r .\requirements.txt
# Install the project in editable mode
py -m pip install -e .# Activate the virtual environment
source .venv/bin/activate
# Install dependencies from requirements.txt
python -m pip install -r requirements.txt
# Install the project in editable mode
python -m pip install -e .Installing the project in editable mode allows Python to recognize the
firedronespackage inside thesrc/directory.
# Full GUI simulation
py -m firedrones.main
# Headless mode, without pygame window
py -m firedrones.main --headless# Full GUI simulation
python -m firedrones.main
# Headless mode, without pygame window
python -m firedrones.main --headlessPredefined scenarios can be loaded directly from the command line using --scenario. This is useful for recording demonstrations, reproducing test cases, and collecting metrics for the report.
py -m firedrones.main --scenario scenario_1python -m firedrones.main --scenario scenario_1| # | Scenario | What it demonstrates | GUI command |
|---|---|---|---|
| 1 | scenario_1 |
Basic navigation: one drone, one fire, no obstacles. | py -m firedrones.main --scenario scenario_1 |
| 2 | scenario_2 |
Obstacle avoidance: the drone must detour around a wall. | py -m firedrones.main --scenario scenario_2 |
| 3 | scenario_3 |
Multi-agent coordination: four drones serve multiple fires. | py -m firedrones.main --scenario scenario_3 |
| 4 | scenario_4 |
Collision avoidance: drones must resolve a movement conflict. | py -m firedrones.main --scenario scenario_4 |
| 5 | scenario_5 |
Priority handling: high-priority fire should be served first. | py -m firedrones.main --scenario scenario_5 |
| 6 | scenario_6 |
Resource constraint: low battery forces a return to base. | py -m firedrones.main --scenario scenario_6 |
| 7 | scenario_7 |
Dynamic replanning: a new fire appears mid-simulation. | py -m firedrones.main --scenario scenario_7 |
| 8 | scenario_8 |
Unreachable fire: obstacles block access to a task. | py -m firedrones.main --scenario scenario_8 |
Create a folder for metric logs:
mkdir metricsRun a scenario in headless mode and export its metrics:
py -m firedrones.main --scenario scenario_1 --headless --ticks 100 --no-random-fires --export-json metrics\scenario_1.jsonThen inspect the exported metrics:
Get-Content metrics\scenario_1.jsonMain report scenarios:
py -m firedrones.main --scenario scenario_1 --headless --ticks 100 --no-random-fires --export-json metrics\scenario_1.json
py -m firedrones.main --scenario scenario_2 --headless --ticks 100 --no-random-fires --export-json metrics\scenario_2.json
py -m firedrones.main --scenario scenario_3 --headless --ticks 100 --no-random-fires --export-json metrics\scenario_3.json
py -m firedrones.main --scenario scenario_4 --headless --ticks 100 --no-random-fires --export-json metrics\scenario_4.json
py -m firedrones.main --scenario scenario_7 --headless --ticks 100 --no-random-fires --spawn-at-tick 10 --spawn-cell 7,6 --export-json metrics\scenario_7.jsonView exported logs:
Get-Content metrics\scenario_1.json
Get-Content metrics\scenario_2.json
Get-Content metrics\scenario_3.json
Get-Content metrics\scenario_4.json
Get-Content metrics\scenario_7.json# Run scenario 2 with A*
py -m firedrones.main --scenario scenario_2 --algorithm astar --headless --ticks 100 --no-random-fires --export-json metrics\scenario_2_astar.json
# Run scenario 2 with Dijkstra
py -m firedrones.main --scenario scenario_2 --algorithm dijkstra --headless --ticks 100 --no-random-fires --export-json metrics\scenario_2_dijkstra.json
# View both metric logs
Get-Content metrics\scenario_2_astar.json
Get-Content metrics\scenario_2_dijkstra.jsonpy -m pytest
# Or with verbose output
py -m pytest -vpython -m pytest
# Or with verbose output
python -m pytest -vTests do not require pygame. They run entirely headlessly.
| Key | Action |
|---|---|
Space |
Pause / Resume |
R |
Reset simulation |
F |
Manually spawn a fire |
O |
Move a random obstacle |
P |
Toggle task priority mode |
D |
Toggle A* / Dijkstra |
1-8 |
Load predefined scenario |
LClick |
Spawn fire at clicked cell |
Esc |
Quit |
FireDrones/
├── README.md
├── requirements.txt
├── pyproject.toml
├── src/
│ └── firedrones/
│ ├── __init__.py
│ ├── main.py ← entry point
│ ├── config.py ← all constants
│ ├── environment/
│ │ ├── cell.py ← Cell, CellType
│ │ ├── grid.py ← Grid
│ │ ├── region.py ← Region
│ │ └── fire.py ← Fire task
│ ├── agents/
│ │ ├── drone.py ← Drone agent
│ │ └── drone_state.py ← DroneState enum
│ ├── planning/
│ │ ├── astar.py ← A* planner
│ │ ├── dijkstra.py ← Dijkstra planner
│ │ ├── task_allocator.py
│ │ └── collision_avoidance.py
│ ├── control/
│ │ └── controller.py ← GUI and simulator mediator
│ ├── simulation/
│ │ ├── simulator.py ← core simulation engine
│ │ ├── metrics.py ← performance indicators
│ │ └── scenarios.py ← 8 predefined scenarios
│ ├── gui/
│ │ └── pygame_gui.py ← rendering and input
│ └── utils/
│ └── priority_queue.py
└── tests/
├── conftest.py
├── test_astar.py
├── test_dijkstra.py
├── test_grid.py
├── test_task_allocator.py
├── test_collision_avoidance.py
└── test_scenarios.py
A* is a best-first search algorithm that uses a heuristic
A* expands fewer nodes than uninformed search on typical grid problems, making it the default planner.
Dijkstra is Uniform Cost Search with no heuristic, equivalent to using
On the same grid, Dijkstra and A* produce paths of identical length, but Dijkstra usually visits more cells, making it slower.
Why not RRT? Rapidly-exploring Random Trees are designed for continuous configuration spaces. On a discrete grid, exhaustive search with a heuristic, such as A*, is more appropriate and efficient.
Why not CBS? Conflict-Based Search solves the full Multi-Agent Path Finding problem optimally, but it has high computational complexity. It is outside the scope of this academic project.
The simulator uses greedy nearest-agent assignment:
- Collect all active and unassigned fires.
- Sort fires by priority if priority mode is enabled.
- For each fire, assign the nearest available drone.
- Estimate cost using Manhattan distance or planned path length.
When priority mode is active, fires are sorted by ascending priority value, where 0 represents the most urgent fire.
The allocator attempts to assign the most urgent fire first. If a high-priority fire is unreachable, the system skips it safely and continues operating.
Before each movement tick:
- Each drone proposes its next cell.
- If two drones try to occupy the same cell, one drone waits.
- If two drones try to swap positions directly, one drone waits.
- The drone with the higher ID waits.
This is a simple wait-based strategy, not a full optimal MAPF solver.
When a new fire appears or the environment changes, drone paths may become outdated. At each simulation tick, drones with invalid or empty paths automatically replan using the current grid state.
| Parameter | Value |
|---|---|
| Battery per move | 1 |
| Water per extinguish | 25 |
| Low battery threshold | 25 |
| Low water threshold | 25 |
| Recharge rate | 10 / tick |
| Refill rate | 10 / tick |
When battery or water drops below the threshold, the drone abandons its current task, returns to base, and recharges or refills before accepting new assignments.
The objective is to minimize total assignment cost:
where:
drone_iis an available drone agent.fire_jis an active fire task.distanceis Manhattan distance, A* path length, or travel time.- The assignment should reduce the total cost of serving all fires.
The greedy algorithm approximates the optimal assignment by assigning each fire to the nearest available drone.
Tasks should be completed in strict ascending priority order whenever possible:
priority(fire_a) < priority(fire_b)
→ fire_a should be completed before fire_b
If a higher-priority fire is unreachable due to obstacles, the system detects this and continues safely.
| # | Name | Description | GUI command |
|---|---|---|---|
| 1 | scenario_1 |
Single drone, single fire, no obstacles | py -m firedrones.main --scenario scenario_1 |
| 2 | scenario_2 |
Single drone, wall of obstacles, drone must detour | py -m firedrones.main --scenario scenario_2 |
| 3 | scenario_3 |
4 drones, 5 fires, greedy nearest-task allocation | py -m firedrones.main --scenario scenario_3 |
| 4 | scenario_4 |
2 drones on collision course, avoidance triggered | py -m firedrones.main --scenario scenario_4 |
| 5 | scenario_5 |
Priority mode: high-priority fire served before low-priority | py -m firedrones.main --scenario scenario_5 |
| 6 | scenario_6 |
Low battery, drone returns to base mid-mission | py -m firedrones.main --scenario scenario_6 |
| 7 | scenario_7 |
New fire spawns mid-simulation, drone replans | py -m firedrones.main --scenario scenario_7 |
| 8 | scenario_8 |
Fire surrounded by obstacles, system detects unreachability | py -m firedrones.main --scenario scenario_8 |
- Collision avoidance is wait-based.
- Task allocation is greedy and not globally optimal.
- No advanced MAPF solver is implemented.
- No multi-fire triage is included.
- One drone is assigned to one fire at a time.
- Grid movement is 4-connected, so diagonal movement is not supported.
- Pygame visualization requires a display. Use
--headlesswhen running without GUI support.
- Replace greedy allocation with the Hungarian algorithm.
- Implement Conflict-Based Search for optimal multi-agent path planning.
- Add drone-to-drone communication.
- Support diagonal movement with 8-connected grids.
- Add fire intensity and progressive extinguishing.
- Add real-time scenario loading through the GUI.
- Export simulation logs for offline analysis.
- Add multiple base coordination.
- Emergency drones: Coordinate UAV fleets for wildfire or urban fire suppression.
- Warehouse robots: Route multiple robots to pick locations while avoiding collisions.
- Autonomous vehicles: Coordinate multiple vehicles in grid-like traffic environments.
- Search and rescue: Assign drones to emergency locations with priority triage.