TinyRobotics
Loading...
Searching...
No Matches
GridMap.h
1#pragma once
2#include <cmath>
3#include <cstdint>
4#include <vector>
5
6#include "IMap.h"
7#include "TinyRobotics/utils/AllocatorPSRAM.h"
8#include "TinyRobotics/utils/Common.h"
9#include <TinyRobotics/serialize/MapSerializer.h>
10
11namespace tinyrobotics {
12
13/**
14 * @class GridMap
15 * @ingroup maps
16 * @brief A grid map for spatial representation, navigation, and planning.
17 *
18 * The GridMap class models the environment as a regular grid of cells, each
19 * storing a state (e.g., occupancy, height, or other user-defined data). It
20 * supports:
21 * - Conversion between world coordinates and grid cell indices
22 * - Efficient cell state access and modification
23 * - Binary Bayesian updates for probabilistic occupancy mapping
24 * - Custom cell validity logic (e.g., for dynamic obstacles)
25 * - Extraction of valid neighbor cells for pathfinding (8-connected)
26 *
27 * Typical use cases include:
28 * - Robot navigation and path planning
29 * - Occupancy grid mapping (SLAM)
30 * - Environment modeling for simulation or AI
31 *
32 * Features:
33 * - Template parameter for cell state type (e.g., occupancy, height)
34 * - World-to-cell and cell-to-world coordinate conversion
35 * - Fast access to cell state by index or coordinate
36 * - Probabilistic update using log-odds for sensor fusion
37 * - Customizable cell validity via callback (see setValidityCallback)
38 * - Customizable cell state conversion via callback (see
39 * setCellStateCallback)
40 * - Neighbor cell extraction for graph-based search
41 *
42 * Callback Notes:
43 * - Validity Callback: Use setValidityCallback to provide custom logic for
44 * determining if a cell is valid (e.g., for dynamic obstacles or special
45 * terrain). The callback receives cell indices and an optional reference
46 * pointer.
47 * - Cell State Callback: Use setCellStateCallback to provide custom logic for
48 * converting StateType to CellState (e.g., for visualization or compatibility
49 * with algorithms expecting CellState). The callback receives the cell value
50 * and an optional reference pointer.
51 * - Use setReference to set the reference pointer passed to both callbacks.
52 *
53 * Example:
54 * @code
55 * GridMap<CellState> map(100, 100, 0.1f); // 10m x 10m, 10cm resolution
56 * Coordinate<float> pos(1.2, 3.4);
57 * map.setCell(pos, CellState::OCCUPIED);
58 * auto state = map.getCell(5, 5);
59 *
60 * // For GridMap<float>, set a callback to convert float to CellState
61 * map.setCellStateCallback([](const float& v, void*) {
62 * return v > 0.5f ? CellState::OCCUPIED : CellState::FREE;
63 * });
64 * @endcode
65 *
66 * @tparam StateType The type stored in each cell (e.g., occupancy, height)
67 * @tparam T Numeric type for coordinates (default: float)
68 */
69
70template <typename StateT = CellState, typename T = DistanceM>
71class GridMap : public IMap<T> {
72 public:
73 /// Cell structure to represent grid cell indices
74 struct Cell {
75 size_t cx; // Cell X index
76 size_t cy; // Cell Y index
77 };
78
79 /// Default constructor
80 GridMap() = default;
81 /// Construct with cell counts and resolution in meters
82 GridMap(int xCount, int yCount, DistanceM resolutionM)
83 : resolution(resolutionM) {
84 resize(xCount, yCount);
85 }
86 /// Construct with cell counts and Distance object for resolution
87 GridMap(int xCount, int yCount, Distance resolution)
88 : xCount(xCount), yCount(yCount) {
89 this->resolution = resolution.getValue(DistanceUnit::M);
90 resize(xCount, yCount);
91 }
92
93 /// World to cell conversion
94 bool worldToCell(DistanceM wx, DistanceM wy, Cell& cell) const {
95 cell.cx = static_cast<int>(std::floor((wx - origin.x) / resolution));
96 cell.cy = static_cast<int>(std::floor((wy - origin.y) / resolution));
97 return (cell.cx >= 0 && cell.cx < xCount && cell.cy >= 0 &&
98 cell.cy < yCount);
99 }
100
101 /// Cell to world (center of cell)
102 void cellToWorld(int cx, int cy, DistanceM& wx, DistanceM& wy) const {
103 wx = origin.x + (static_cast<float>(cx) + 0.5f) * resolution;
104 wy = origin.y + (static_cast<float>(cy) + 0.5f) * resolution;
105 }
106
107 /// Provide the workd coordinates for the cell
108 Coordinate<DistanceM> toWorld(int cx, int cy) const {
109 Coordinate<DistanceM> coord;
110 cellToWorld(cx, cy, coord.x, coord.y);
111 return coord;
112 }
113
114 /// Provide access to cell state by cell index
115 bool getCell(int cx, int cy, StateT& result) {
116 if (cx < 0 || cx >= xCount || cy < 0 || cy >= yCount)
117 return false; // Out of bounds
118 result = data[cy * xCount + cx];
119 return true;
120 }
121
122 /// Provide access to cell state by cell index
123 bool getCell(int cx, int cy, CellState& result) const {
124 if (cx < 0 || cx >= xCount || cy < 0 || cy >= yCount)
125 return false; // Out of bounds
126 if constexpr (std::is_same<StateT, CellState>::value) {
127 result = data[cy * xCount + cx];
128 return true;
129 } else {
130 if (get_cellstate_cb == nullptr) {
131 return false;
132 }
133 result = get_cellstate_cb(data[cy * xCount + cx], reference);
134 return true;
135 }
136 }
137
138 /// Provide access to cell state by coordinate
139 bool getCell(Coordinate<T>& coord, CellState& result) {
140 Cell cell;
141 if (worldToCell(coord.x, coord.y, cell)) {
142 return getCell(cell.cx, cell.cy);
143 }
144 return false; // Out of bounds
145 }
146
147 /// Provide access to cell state by coordinate
148 void setCell(Cell& cell, CellState value) {
149 if (cell.cx >= 0 && cell.cx < xCount && cell.cy >= 0 && cell.cy < yCount)
150 data[cell.cy * xCount + cell.cx] = value;
151 }
152
153 /// Set cell state (for initialization or manual updates)
154 void setCell(int cx, int cy, CellState value) {
155 if (cx >= 0 && cx < xCount && cy >= 0 && cy < yCount)
156 data[cy * xCount + cx] = value;
157 }
158
159 /// Set cell state by coordinate (converts to cell index internally)
160 void setCell(Coordinate<T>& coord, CellState value) {
161 Cell cell;
162 if (worldToCell(coord.x, coord.y, cell)) {
163 setCell(cell.cx, cell.cy, value);
164 }
165 }
166
167 /// Determine all neighboring cells (8-connected) for a given cell coordinate.
169 Cell cell;
170 worldToCell(from.x, from.y, cell);
171 int cx = static_cast<int>(cell.cx);
172 int cy = static_cast<int>(cell.cy);
173 std::vector<Cell> neighbors;
174 if (cx < xCount - 1)
175 neighbors.push_back(
176 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy)});
177 if (cx > 0)
178 neighbors.push_back(
179 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy)});
180 if (cy < yCount - 1)
181 neighbors.push_back(
182 {static_cast<size_t>(cx), static_cast<size_t>(cy + 1)});
183 if (cy > 0)
184 neighbors.push_back(
185 {static_cast<size_t>(cx), static_cast<size_t>(cy - 1)});
186
187 if (cx < xCount - 1 && cy < yCount - 1)
188 neighbors.push_back(
189 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy + 1)});
190 if (cx > 0 && cy < yCount - 1)
191 neighbors.push_back(
192 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy + 1)});
193 if (cx < xCount - 1 && cy > 0)
194 neighbors.push_back(
195 {static_cast<size_t>(cx + 1), static_cast<size_t>(cy - 1)});
196 if (cx > 0 && cy > 0)
197 neighbors.push_back(
198 {static_cast<size_t>(cx - 1), static_cast<size_t>(cy - 1)});
199 return neighbors;
200 }
201
202 /// Get world coordinates of neighboring cells (for pathfinding or navigation)
204 std::vector<Coordinate<T>> neighbors;
205 for (auto& cell : getNeighborCells(from)) {
206 Coordinate<T> neighbor;
207 cellToWorld(cell.cx, cell.cy, neighbor.x, neighbor.y);
208 neighbors.push_back(neighbor);
209 }
210 return neighbors;
211 }
212
213 /// Resize the grid to new dimensions
214 void resize(int newXCount, int newYCount) {
215 xCount = newXCount;
216 yCount = newYCount;
217 data.resize(xCount * yCount);
218 }
219
220 /// Get the number of cells in the x direction
221 int getXCount() const { return xCount; }
222
223 /// Get the number of cells in the y direction
224 int getYCount() const { return yCount; }
225
226 /// Get the cell resolution in meters
227 float getResolution() const { return resolution; }
228
229 /// Defines the resolution in meters
230 void setResolution(float resM) { resolution = resM; }
231
232 /**
233 * @brief Check if a coordinate is within the map bounds.
234 * @param coord The coordinate to check.
235 * @return true if the coordinate is inside the map, false otherwise.
236 */
237 bool isValid(const Coordinate<T>& coord) const {
238 int cx, cy;
239 cx = static_cast<int>((coord.x - origin.x) / resolution);
240 cy = static_cast<int>((coord.y - origin.y) / resolution);
241 bool in_range = cx >= 0 && cx < xCount && cy >= 0 && cy < yCount;
242 if (!in_range) return false; // Out of bounds
243 return is_valid_cb(cx, cy, reference);
244 }
245
246 /// Set the callback for converting StateType to CellState
247 void setCellStateCallback(CellState (*cb)(const StateT&, void* ref)) {
248 get_cellstate_cb = cb;
249 }
250
251 /// Set the callback for cell validity checking
252 void setValidityCallback(bool (*cb)(int cx, int cy, void* ref)) {
253 is_valid_cb = cb != nullptr ? cb : isValid;
254 }
255
256 /// Set the reference pointer passed to callbacks
257 void setReference(void* ref) { reference = ref; }
258
259 /// Write map to output
261 return serializer.write(*this, out);
262 }
263
264 /// Read map from input
266 return serializer.read(*this, in);
267 }
268
269 protected:
270 // Grid parameters
271 int xCount = 0; // Number of cells in x direction
272 int yCount = 0; // Number of cells in y direction
273 float resolution = 0; // Meters per cell
274 Coordinate<T> origin; // World coordinate of cell (0,0)
275 void* reference = this; // Optional reference for validity callback
276 bool (*is_valid_cb)(int cx, int cy, void*) = isValid;
277 CellState (*get_cellstate_cb)(const StateT&, void* ref) = nullptr;
278
279 // Map data: e.g. 0=free, 100=occupied, -1=unknown
280 std::vector<StateT, AllocatorPSRAM<StateT>> data;
281 // Serialization
282 GridMapSerializer<GridMap<StateT>,StateT, T> serializer;
283
284
285 /// Default validity check: a cell is valid if it's not occupied. This can be
286 /// overridden with a custom callback for more complex logic (e.g., dynamic
287 /// obstacles, special terrain, etc.).
288 static bool isValid(int cx, int cy, void* ref) {
289 GridMap<StateT, T>* self = (GridMap<StateT, T>*)ref;
290 StateT result;
291 if (!self->getCell(cx, cy, result)) return false; // Out of bounds
292 if constexpr (std::is_same<StateT, CellState>::value) {
293 return result != CellState::OCCUPIED;
294 } else {
295 // For non-CellState types, always return true or provide custom logic
296 return true;
297 }
298 }
299};
300
301} // namespace tinyrobotics
Custom allocator that uses ESP32's PSRAM for memory allocation.
Definition: AllocatorPSRAM.h:21
A generic 3D coordinate class for robotics, navigation, and spatial calculations.
Definition: Coordinate.h:57
Represents a distance measurement with unit conversion support.
Definition: Distance.h:40
A grid map for spatial representation, navigation, and planning.
Definition: GridMap.h:71
size_t readFrom(Stream &in)
Read map from input.
Definition: GridMap.h:265
void resize(int newXCount, int newYCount)
Resize the grid to new dimensions.
Definition: GridMap.h:214
void cellToWorld(int cx, int cy, DistanceM &wx, DistanceM &wy) const
Cell to world (center of cell)
Definition: GridMap.h:102
void setValidityCallback(bool(*cb)(int cx, int cy, void *ref))
Set the callback for cell validity checking.
Definition: GridMap.h:252
GridMap(int xCount, int yCount, DistanceM resolutionM)
Construct with cell counts and resolution in meters.
Definition: GridMap.h:82
Coordinate< DistanceM > toWorld(int cx, int cy) const
Provide the workd coordinates for the cell.
Definition: GridMap.h:108
size_t writeTo(Print &out)
Write map to output.
Definition: GridMap.h:260
bool getCell(int cx, int cy, StateT &result)
Provide access to cell state by cell index.
Definition: GridMap.h:115
GridMap(int xCount, int yCount, Distance resolution)
Construct with cell counts and Distance object for resolution.
Definition: GridMap.h:87
std::vector< Coordinate< T > > getNeighbors(Coordinate< T > from) const
Get world coordinates of neighboring cells (for pathfinding or navigation)
Definition: GridMap.h:203
int getXCount() const
Get the number of cells in the x direction.
Definition: GridMap.h:221
GridMap()=default
Default constructor.
void setResolution(float resM)
Defines the resolution in meters.
Definition: GridMap.h:230
void setCell(Cell &cell, CellState value)
Provide access to cell state by coordinate.
Definition: GridMap.h:148
void setCellStateCallback(CellState(*cb)(const StateT &, void *ref))
Set the callback for converting StateType to CellState.
Definition: GridMap.h:247
std::vector< Cell > getNeighborCells(const Coordinate< DistanceM > from) const
Determine all neighboring cells (8-connected) for a given cell coordinate.
Definition: GridMap.h:168
void setReference(void *ref)
Set the reference pointer passed to callbacks.
Definition: GridMap.h:257
bool isValid(const Coordinate< T > &coord) const
Check if a coordinate is within the map bounds.
Definition: GridMap.h:237
void setCell(Coordinate< T > &coord, CellState value)
Set cell state by coordinate (converts to cell index internally)
Definition: GridMap.h:160
int getYCount() const
Get the number of cells in the y direction.
Definition: GridMap.h:224
bool getCell(int cx, int cy, CellState &result) const
Provide access to cell state by cell index.
Definition: GridMap.h:123
bool worldToCell(DistanceM wx, DistanceM wy, Cell &cell) const
World to cell conversion.
Definition: GridMap.h:94
float getResolution() const
Get the cell resolution in meters.
Definition: GridMap.h:227
static bool isValid(int cx, int cy, void *ref)
Definition: GridMap.h:288
void setCell(int cx, int cy, CellState value)
Set cell state (for initialization or manual updates)
Definition: GridMap.h:154
bool getCell(Coordinate< T > &coord, CellState &result)
Provide access to cell state by coordinate.
Definition: GridMap.h:139
Abstract interface for 2D grid maps and occupancy maps in TinyRobotics.
Definition: IMap.h:79
DistanceUnit
Supported distance units for conversion and representation.
Definition: Distance.h:10
CellState
Cell state for occupancy grid mapping (e.g., UNKNOWN, FREE, OCCUPIED).
Definition: Common.h:32
Cell structure to represent grid cell indices.
Definition: GridMap.h:74