Source code for irsim.lib.path_planners.grid_planner_base
"""Shared grid geometry for the occupancy-grid planners (A*, JPS)."""
from __future__ import annotations
import contextlib
import numpy as np
from irsim.world.map import EnvGridMap
[docs]
class GridPlannerBase:
"""Bounds, resolution, and grid shape derived from an environment map.
Both :class:`~irsim.lib.path_planners.a_star.AStarPlanner` and
:class:`~irsim.lib.path_planners.jps.JPSPlanner` search the same discrete
grid, so they derive their search space the same way.
"""
def __init__(self, env_map: EnvGridMap) -> None:
"""
Derive the search grid from an environment map.
Args:
env_map: Environment map (any :class:`~irsim.world.map.EnvGridMap`
compatible object).
"""
self._map = env_map
self.obstacle_list = env_map.obstacle_list[:]
off = np.asarray(env_map.world_offset, dtype=float).flatten()
self.origin_x = float(off[0])
self.origin_y = float(off[1])
self.min_x, self.min_y = 0, 0 # grid indices are 0-based
self.max_x = self.origin_x + env_map.width
self.max_y = self.origin_y + env_map.height
# When map has a grid, use its actual resolution and shape so planner grid
# matches collision lookups (avoids "Open set is empty" on resolution mismatch).
grid = getattr(env_map, "grid", None)
gr = None
if grid is not None and hasattr(env_map, "grid_resolution"):
with contextlib.suppress(Exception):
gr = env_map.grid_resolution
if grid is not None and gr is not None:
self.resolution = gr[0] # m/cell; assume square cells (gr[0]==gr[1])
self.x_width = grid.shape[0]
self.y_width = grid.shape[1]
else:
self.resolution = env_map.resolution
self.x_width = round((self.max_x - self.origin_x) / self.resolution)
self.y_width = round((self.max_y - self.origin_y) / self.resolution)