Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New env and gaussian #199

Merged
merged 2 commits into from
Apr 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions DSSE/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
"""Core module of the project. Contains the main logic of the application."""

from .environment.env import DroneSwarmSearch
from .environment.coverage_env import CoverageDroneSwarmSearch
from .environment.constants import Actions

__all__ = [
"DroneSwarmSearch",
"CoverageDroneSwarmSearch",
"Actions"
]
95 changes: 65 additions & 30 deletions DSSE/environment/coverage_env.py
Original file line number Diff line number Diff line change
@@ -1,36 +1,46 @@
from gymnasium.spaces import Discrete
from .env_base import DroneSwarmSearchBase
from .constants import Actions
from .constants import Actions, Reward


# TODO: Match env_base to conv_env -> If using particle sim, redo __init__ and reset.
class CoverageDroneSwarmSearch(DroneSwarmSearchBase):
metadata = {
"name": "DroneSwarmSearchCPP",
}
reward_scheme = Reward(
default=0,
leave_grid=-100,
exceed_timestep=-100,
drones_collision=-100,
search_cell=0,
search_and_find=10000,
)

def __init__(
self,
grid_size=7,
render_mode="ansi",
render_grid=False,
render_grid=True,
render_gradient=True,
vector=(-0.5, -0.5),
disperse_constant=10,
vector=(3.1, 3.2),
dispersion_inc=0.1,
dispersion_start=0.5,
timestep_limit=100,
disaster_position=(0, 0),
drone_amount=1,
drone_speed=10,
drone_probability_of_detection=0.9,
pre_render_time=0,
pre_render_time=10,
) -> None:
super().__init__(
grid_size,
render_mode,
render_grid,
render_gradient,
vector,
disperse_constant,
dispersion_inc,
dispersion_start,
timestep_limit,
disaster_position,
drone_amount,
Expand All @@ -44,16 +54,21 @@ def __init__(
self.all_states = {
(x, y) for x in range(self.grid_size) for y in range(self.grid_size)
}
self.repeated_coverage = 0
self.cumm_pos = 0

def reset(self, seed=None, options=None):
obs, infos = super().reset(seed=seed, options=options)
obs, _ = super().reset(seed=seed, options=options)
self.seen_states = {pos for pos in self.agents_positions.values()}
self.not_seen_states = self.all_states - self.seen_states
infos = self.compute_infos(False)
self.cumm_pos = 0
self.repeated_coverage = 0
return obs, infos

def create_observations(self):
observations = {}
self.probability_matrix.step(self.drone.speed)
# self.probability_matrix.step(self.drone.speed)

probability_matrix = self.probability_matrix.get_matrix()
for agent in self.agents:
Expand All @@ -73,12 +88,12 @@ def step(self, actions: dict[str, int]) -> tuple:
if not self._was_reset:
raise ValueError("Please reset the env before interacting with it")

# TODO: Define the reward_scheme
terminations = {a: False for a in self.agents}
rewards = {a: 0 for a in self.agents}
rewards = {a: self.reward_scheme.default for a in self.agents}
truncations = {a: False for a in self.agents}
prob_matrix = self.probability_matrix.get_matrix()
self.timestep += 1

prob_matrix = self.probability_matrix.get_matrix()
for agent in self.agents:
if agent not in actions:
raise ValueError("Missing action for " + agent)
Expand All @@ -87,30 +102,39 @@ def step(self, actions: dict[str, int]) -> tuple:
if drone_action not in self.action_space(agent):
raise ValueError("Invalid action for " + agent)

drone_x, drone_y = self.agents_positions[agent]
if drone_action != Actions.SEARCH.value:
new_position = self.move_drone((drone_x, drone_y), drone_action)
if not self.is_valid_position(new_position):
rewards[agent] = self.reward_scheme["out_of_bounds"]
else:
self.agents_positions[agent] = new_position
rewards[agent] = (
prob_matrix[drone_y][drone_x] * 10000
if prob_matrix[drone_y][drone_x] * 100 > 1
else -100
)
self.seen_states.add(self.agents_positions[agent])
self.not_seen_states.remove(self.agents_positions[agent])

# Check truncation conditions (overwrites termination conditions)
if self.timestep >= self.timestep_limit:
rewards[agent] = self.reward_scheme["exceed_timestep"]
rewards[agent] = self.reward_scheme.exceed_timestep
truncations[agent] = True
continue

drone_x, drone_y = self.agents_positions[agent]
new_position = self.move_drone((drone_x, drone_y), drone_action)
if not self.is_valid_position(new_position):
rewards[agent] = self.reward_scheme.leave_grid
continue

self.agents_positions[agent] = new_position
new_x, new_y = new_position
if new_position in self.not_seen_states:
reward_poc = 1 / (self.timestep) * prob_matrix[new_y, new_x] * 1_000
rewards[agent] = self.reward_scheme.search_cell + reward_poc
self.seen_states.add(new_position)
self.not_seen_states.remove(new_position)
# Probability of sucess (POS) = POC * POD
self.cumm_pos += prob_matrix[new_y, new_x] * self.pod
else:
self.repeated_coverage += 1

self.timestep += 1
# Get dummy infos
is_completed = len(self.not_seen_states) == 0
infos = {drone: {"completed": is_completed} for drone in self.agents}
self.render()
if is_completed:
# TODO: Proper define reward for completing the search (R_done)
rewards = {
drone: self.reward_scheme.search_and_find for drone in self.agents
}
terminations = {drone: True for drone in self.agents}
infos = self.compute_infos(is_completed)

self.compute_drone_collision(terminations, rewards, truncations)
# Get observations
Expand All @@ -120,5 +144,16 @@ def step(self, actions: dict[str, int]) -> tuple:
self.agents = []
return observations, rewards, terminations, truncations, infos

def compute_infos(self, is_completed: bool) -> dict[str, dict]:
# TODO: Is this the best way to inform the coverage rate, Cum_pos and repetitions?
coverage_rate = len(self.seen_states) / len(self.all_states)
infos = {
"is_completed": is_completed,
"coverage_rate": coverage_rate,
"repeated_coverage": self.repeated_coverage / len(self.all_states),
"acumulated_pos": self.cumm_pos,
}
return {drone: infos for drone in self.agents}

def action_space(self, agent):
return Discrete(8)
5 changes: 2 additions & 3 deletions DSSE/environment/env_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ def __init__(
self.drone = DroneData(
amount=drone_amount,
speed=drone_speed,
probability_of_detection=drone_probability_of_detection,
)
self.pod = drone_probability_of_detection

# Error Checking
if self.drone.amount > self.grid_size * self.grid_size:
Expand Down Expand Up @@ -95,7 +95,6 @@ def calculate_simulation_time_step(
"""
return cell_size / (drone_max_speed - wind_resistance) # in seconds

@abstractmethod
def render(self):
self.pygame_renderer.render_map()
self.pygame_renderer.render_entities(self.agents_positions.values())
Expand Down Expand Up @@ -209,7 +208,7 @@ def compute_drone_collision(self, terminations, rewards, truncations):
):
truncations[drone_1_id] = True
terminations[drone_1_id] = True
rewards[drone_1_id] = self.reward_scheme["drones_collision"]
rewards[drone_1_id] = self.reward_scheme.drones_collision

def move_drone(self, position, action):
"""
Expand Down
16 changes: 16 additions & 0 deletions DSSE/environment/particle_sim.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from datetime import datetime, timedelta
from opendrift.models.oceandrift import OceanDrift

def open_drift(lat, lon, time, number, radius, duration, outfile):
o = OceanDrift()
o.add_readers_from_list(['https://tds.hycom.org/thredds/dodsC/GLBy0.08/expt_93.0/uv3z'])
o.seed_elements(lat=lat, lon=lon, time=time, number=number, radius=radius)

# Running the model
o.run(duration=duration, outfile=outfile)

lat_final = o.elements.lat
lon_final = o.elements.lon

# Return final positions as list of tuples (latitude, longitude)
return list(zip(lat_final, lon_final))
23 changes: 23 additions & 0 deletions basic_coverage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from DSSE import CoverageDroneSwarmSearch

env = CoverageDroneSwarmSearch(
grid_size=40,
drone_amount=3,
dispersion_inc=0.1,
vector=(3.2, 3.1),
render_mode="human",
)

opt = {
"drones_positions": [(0, 10), (10, 10), (20, 10)],
}
obs, info = env.reset(options=opt)

step = 0
while env.agents:
step += 1
actions = {agent: env.action_space(agent).sample() for agent in env.agents}
observations, rewards, terminations, truncations, infos = env.step(actions)
print(f"Step: {step}")

print(infos["drone0"])