OccupancyGrid
Represents a 2D grid mapping world cells to occupancy status, using the same FREE/OCCUPIED/UNKNOWN convention as ROS's nav_msgs/OccupancyGrid.
Reference semantics on construction
Construction does not defensively copy a contiguous input np.ndarray — the array is stored by reference. If you intend to mutate the source array after constructing the OccupancyGrid, pass arr.copy() explicitly. The data property always returns a defensive copy, so reads are safe either way.
Parameters
| Field | Type | Description |
|---|---|---|
data | np.ndarray | (H, W) int8 array of cell values (FREE, OCCUPIED, or UNKNOWN). Stored by reference, not copied — see the warning above. |
resolution | float | Meters per cell. |
origin_x | float | World x-coordinate of cell (0, 0)'s lower-left corner. Defaults to 0.0. |
origin_y | float | World y-coordinate of cell (0, 0)'s lower-left corner. Defaults to 0.0. |
origin_yaw | float | Rotation of the grid relative to world axes, in radians. Defaults to 0.0. ROS represents this as a quaternion on info.origin.orientation; here it's stored as a plain yaw float instead. |
Raises
| Exception | Condition |
|---|---|
TypeError | data is not an np.ndarray |
ValueError | data.dtype is not int8 |
ValueError | data is not 2-D |
ValueError | data contains a value outside {UNKNOWN} ∪ [FREE, OCCUPIED] (i.e. outside {-1} ∪ [0, 100]) |
Constants
| Constant | Value | Description |
|---|---|---|
FREE | 0 | Cell is known to be unoccupied. |
OCCUPIED | 100 | Cell is known to be occupied. |
UNKNOWN | -1 | Cell occupancy is unknown. |
These are class-level constants, accessible as OccupancyGrid.FREE (or off any instance) without constructing a grid.
Attributes
| Attribute | Type | Description |
|---|---|---|
data | np.ndarray | Defensive copy of the underlying (H, W) int8 grid. Mutating the returned array does not affect the OccupancyGrid. |
shape | tuple[int, int] | (height, width) cell counts. |
height | int | Grid height in cells (shape[0]). |
width | int | Grid width in cells (shape[1]). |
resolution | float | Meters per cell. |
origin_x | float | World x-coordinate of cell (0, 0)'s lower-left corner. |
origin_y | float | World y-coordinate of cell (0, 0)'s lower-left corner. |
origin_yaw | float | Grid's rotation relative to world axes, in radians. |
None of these attributes have setters — an OccupancyGrid is immutable after construction (unless you mutate the array you originally passed in by reference; see the warning above).
Methods
| Method | Description |
|---|---|
OccupancyGrid.coerce(value) | Returns value unchanged if it's already an OccupancyGrid; if it's a dict, calls OccupancyGrid(**value) (requires keys data/resolution, origin_x/origin_y/origin_yaw optional). Raises TypeError for any other input. |
Operators
| Operation | Behavior |
|---|---|
g == other | True only if other is also an OccupancyGrid with element-equal grid data and equal resolution/origin_x/origin_y/origin_yaw. False if other is an OccupancyGrid that differs in any of those. NotImplemented (effectively False) if other isn't an OccupancyGrid. |
np.asarray(g) | Works directly via __array__, returning the (H, W) int8 grid (metadata is not included). Always returns a copy; copy=False raises ValueError. |
hash(g) | Not supported — the grid array is stored by reference and can be mutated in place. |
len(g) is not supported — OccupancyGrid doesn't define __len__.
Example
"""Demonstrates the Telekinesis OccupancyGrid datatype."""
import time
import numpy as np
from loguru import logger
from telekinesis import datatypes
def occupancy_grid_example():
"""Demonstrate creation with the FREE/OCCUPIED/UNKNOWN constants, access, and serialization."""
# ======================= Create ============================================
height, width = 20, 20
data = np.full((height, width), datatypes.OccupancyGrid.UNKNOWN, dtype=np.int8)
data[10, 5] = datatypes.OccupancyGrid.OCCUPIED
data[10, 6] = datatypes.OccupancyGrid.FREE
data[10, 7] = datatypes.OccupancyGrid.OCCUPIED
grid = datatypes.OccupancyGrid(data, resolution=0.05, origin_x=-5.0, origin_y=-5.0, origin_yaw=0.0)
logger.info(f"Created OccupancyGrid: {grid}")
# ======================= Inspect ===========================================
logger.info(
f"shape={grid.shape}, height={grid.height}, width={grid.width}, "
f"resolution={grid.resolution} m/cell"
)
logger.info(f"origin_x={grid.origin_x}, origin_y={grid.origin_y}, origin_yaw={grid.origin_yaw}")
occupied = int(np.sum(grid.data == datatypes.OccupancyGrid.OCCUPIED))
free = int(np.sum(grid.data == datatypes.OccupancyGrid.FREE))
unknown = int(np.sum(grid.data == datatypes.OccupancyGrid.UNKNOWN))
logger.info(f"occupied={occupied}, free={free}, unknown={unknown}")
# ======================= Serialize / Deserialize ===========================
start = time.perf_counter()
serialized = datatypes.serialize(grid)
serialization_ms = (time.perf_counter() - start) * 1000
start = time.perf_counter()
deserialized = datatypes.deserialize(serialized)["param_0"]
deserialization_ms = (time.perf_counter() - start) * 1000
assert grid == deserialized, "round-trip mismatch"
logger.info(f"Deserialized OccupancyGrid: {deserialized}")
logger.info(f"Round-trip successful: {grid == deserialized}")
logger.info(f"Serialization time: {serialization_ms:.3f} ms")
logger.info(f"Deserialization time: {deserialization_ms:.3f} ms")
if __name__ == "__main__":
occupancy_grid_example()
