drone-env / rl /model.py
manikandan-n-07's picture
Standardization: Final validator-compatible structure and inference standardization
675f070
Raw
History Blame Contribute Delete
2.41 kB
"""
rl/model.py
PyTorch model definitions: MapEncoder CNN + PathQNet MLP.
"""
from __future__ import annotations
import math
from typing import List
import torch
import torch.nn as nn
import torch.nn.functional as F
ACTIONS = ["UP", "DOWN", "LEFT", "RIGHT", "WAIT"]
ACTION2IDX = {a: i for i, a in enumerate(ACTIONS)}
CELL2IDX = {"road": 0, "building": 1, "tree": 2, "obstacle": 3, "delivery": 4, "done_del": 5, "drone": 6}
class MapEncoder(nn.Module):
"""Encodes a flat cell-type grid to a latent embedding using a mini-CNN."""
def __init__(self, embed_dim: int = 64):
super().__init__()
self.embed = nn.Embedding(len(CELL2IDX) + 1, 8) # +1 for unknown
self.conv = nn.Sequential(
nn.Conv2d(8, 16, 3, padding=1), nn.ReLU(),
nn.Conv2d(16, 32, 3, padding=1), nn.ReLU(),
nn.AdaptiveAvgPool2d((4, 4)),
)
self.fc = nn.Linear(32 * 4 * 4, embed_dim)
def forward(self, cell_ids: torch.Tensor) -> torch.Tensor:
B = cell_ids.size(0)
x = self.embed(cell_ids) # (B, H*W, 8)
x = x.permute(0, 2, 1) # (B, 8, H*W)
side = int(math.ceil(math.sqrt(x.size(-1))))
pad_len = side * side - x.size(-1)
if pad_len > 0:
x = F.pad(x, (0, pad_len))
x = x.reshape(B, 8, side, side)
x = self.conv(x)
x = x.reshape(B, -1)
return F.relu(self.fc(x))
class PathQNet(nn.Module):
"""
Maps (map_embed, drone_xy_norm, battery, target_xy_norm) → Q(a) for 5 actions.
"""
def __init__(self, embed_dim: int = 64):
super().__init__()
in_dim = embed_dim + 5 # embed + 2(drone) + 1(battery) + 2(target)
self.map_encoder = MapEncoder(embed_dim=embed_dim)
self.net = nn.Sequential(
nn.Linear(in_dim, 128), nn.LayerNorm(128), nn.ReLU(),
nn.Linear(128, 128), nn.ReLU(),
nn.Linear(128, 64), nn.ReLU(),
nn.Linear(64, len(ACTIONS)),
)
def forward(
self,
cell_ids: torch.Tensor, # (B, H*W)
drone_xy: torch.Tensor, # (B, 2) normalised
battery: torch.Tensor, # (B, 1)
target_xy: torch.Tensor, # (B, 2) normalised
) -> torch.Tensor:
map_emb = self.map_encoder(cell_ids)
state = torch.cat([map_emb, drone_xy, battery, target_xy], dim=-1)
return self.net(state)