Costmap.cpp
Go to the documentation of this file.
1 #include "Costmap.h"
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <limits>
6 #include <optional>
7 
8 #include <Eigen/Core>
9 #include <Eigen/Geometry>
10 
11 #include <IceUtil/Time.h>
12 
13 #include <VirtualRobot/BoundingBox.h>
14 #include <VirtualRobot/CollisionDetection/CollisionChecker.h>
15 #include <VirtualRobot/CollisionDetection/CollisionModel.h>
16 #include <VirtualRobot/Visualization/CoinVisualization/CoinVisualizationFactory.h>
17 #include <VirtualRobot/XML/ObjectIO.h>
18 
21 
25 
26 #include "range/v3/view/zip.hpp"
27 
29 {
30 
31 
32  Costmap::Costmap(const Grid& grid,
33  const Parameters& parameters,
34  const SceneBounds& sceneBounds,
35  const std::optional<Mask>& mask,
36  const core::Pose2D& origin) :
37  grid(grid),
38  mask(mask),
39  sceneBounds(sceneBounds),
40  parameters(parameters),
41  global_T_costmap(origin)
42  {
43  validateSizes();
44  }
45 
46  void
47  Costmap::validateSizes() const
48  {
49  if (mask.has_value())
50  {
51  ARMARX_CHECK_EQUAL(grid.rows(), mask->rows());
52  ARMARX_CHECK_EQUAL(grid.cols(), mask->cols());
53  }
54  }
55 
58  {
60 
61  Eigen::Vector2f posLocal;
62  posLocal.x() =
63  sceneBounds.min.x() + index.x() * parameters.cellSize + parameters.cellSize / 2;
64  posLocal.y() =
65  sceneBounds.min.y() + index.y() * parameters.cellSize + parameters.cellSize / 2;
66 
67  return posLocal;
68  }
69 
72  {
73  const Costmap::Position costmap_P_pos = toPositionLocal(index);
74  return global_T_costmap * costmap_P_pos;
75  }
76 
77  bool
78  Costmap::isValid(const Costmap::Index& index) const noexcept
79  {
80  if (index.x() < 0 || index.y() < 0)
81  {
82  return false;
83  }
84 
85  if (index.x() >= grid.rows() || index.y() >= grid.cols())
86  {
87  return false;
88  }
89 
90  if (mask.has_value())
91  {
92  return mask.value()(index.x(), index.y());
93  }
94 
95  return true;
96  }
97 
99  Costmap::toVertex(const Eigen::Vector2f& globalPosition) const
100  {
101  const auto localPosition = global_T_costmap.inverse() * globalPosition;
102 
103  const float vX = (localPosition.x() - parameters.cellSize / 2 - sceneBounds.min.x()) /
104  parameters.cellSize;
105  const float vY = (localPosition.y() - parameters.cellSize / 2 - sceneBounds.min.y()) /
106  parameters.cellSize;
107 
108  const int iX = std::round(vX - 0.01);
109  const int iY = std::round(vY - 0.01);
110 
111  const int iXSan = std::clamp<int>(iX, 0, grid.rows() - 1);
112  const int iYSan = std::clamp<int>(iY, 0, grid.cols() - 1);
113 
114  // FIXME accept one cell off
115 
116  // ARMARX_CHECK_GREATER(iX, 0);
117  // ARMARX_CHECK_GREATER(iY, 0);
118 
119  // ARMARX_CHECK_LESS_EQUAL(iX, grid.rows() - 1);
120  // ARMARX_CHECK_LESS_EQUAL(iY, grid.cols() - 1);
121 
122  return Vertex{.index = Index{iXSan, iYSan}, .position = globalPosition};
123  }
124 
125  const SceneBounds&
127  {
128  return sceneBounds;
129  }
130 
133  {
134  ARMARX_TRACE;
135 
136  if (mask.has_value())
137  {
138  ARMARX_CHECK_GREATER(mask->array().sum(), 0)
139  << "At least one element has to be valid. Here, all elements are masked out!";
140  }
141 
142  // index of the min element
143  Grid::Index row = 0;
144  Grid::Index col = 0;
145 
146  // value of the min element
147  float minVal = std::numeric_limits<float>::max();
148 
149  for (int r = 0; r < grid.rows(); r++)
150  {
151  for (int c = 0; c < grid.cols(); c++)
152  {
153  if (mask.has_value())
154  {
155  if (not mask.value()(r, c)) // skip invalid cells
156  {
157  continue;
158  }
159 
160  const float currentVal = grid(r, c);
161  if (currentVal < minVal)
162  {
163  minVal = currentVal;
164  row = r;
165  col = c;
166  }
167  }
168  }
169  }
170 
171  return {.value = minVal, .index = {row, col}, .position = toPositionGlobal({row, col})};
172  }
173 
174  void
175  Costmap::filter(const Filter &filter)
176  {
177  ARMARX_TRACE;
178 
179  ARMARX_CHECK(filter.matrix.rows() % 2 == 1 and filter.matrix.cols() % 2 == 1);
180 
181  if (mask.has_value())
182  {
183  ARMARX_CHECK_GREATER(mask->array().sum(), 0)
184  << "At least one element has to be valid. Here, all elements are masked out!";
185  }
186 
187  Grid newGrid(grid.rows(), grid.cols());
188  const double max = grid.maxCoeff();
189  newGrid.setConstant(max);
190  for (int r = 0; r < grid.rows(); r++)
191  {
192  for (int c = 0; c < grid.cols(); c++)
193  {
194  if (mask.has_value())
195  {
196  if (not mask.value()(r, c)) // skip invalid cells
197  {
198  continue;
199  }
200 
201  Eigen::MatrixXf valueMatrix(filter.matrix.rows(), filter.matrix.cols());
202  const int radius_r = (filter.matrix.rows() - 1) / 2;
203  const int radius_c = (filter.matrix.cols() - 1) / 2;
204  valueMatrix.setConstant(max);
205  for (int i = -radius_r; i <= radius_r; i++)
206  {
207  for (int j = -radius_c; j <= radius_c; j++)
208  {
209  const int ri = r + i;
210  const int cj = c + j;
211  if (ri > 0 and ri < grid.rows() and cj > 0 and cj < grid.cols() and mask.value()(ri, cj))
212  {
213  valueMatrix(radius_r + i, radius_c + j) = grid(ri, cj);
214  }
215  }
216  }
217  const double sum = filter.matrix.sum();
218  if (sum > 0)
219  {
220  const float filteredValue = (valueMatrix.cwiseProduct(filter.matrix)).sum() / sum;
221  newGrid(r,c) = filter.useMinimum ? std::min(grid(r,c), filteredValue) : filteredValue;
222  }
223  }
224  }
225  }
226  grid = newGrid;
227  }
228 
229  const Costmap::Parameters&
230  Costmap::params() const noexcept
231  {
232  return parameters;
233  }
234 
235  const Costmap::Grid&
237  {
238  return grid;
239  }
240 
241  bool
243  {
244  const auto v = toVertex(p);
245  return grid(v.index.x(), v.index.y()) == 0.F;
246  }
247 
248  bool
249  Costmap::add(const Costmap& other, const float weight)
250  {
251  // ensure that both grid and mask are the same size
252  ARMARX_TRACE;
253 
254  validateSizes();
255  other.validateSizes();
256 
257  const auto startIdx = toVertex(other.sceneBounds.min);
258 
259  // merge other grid into this one => use max costs
260 
261  // It might happen that this costmap is not large enough to cover the other costmap
262  // In this case, only add the part to this costmap that is possible.
263  const int rows = std::min(other.grid.rows(), grid.rows() - startIdx.index.x());
264  const int cols = std::min(other.grid.cols(), grid.cols() - startIdx.index.y());
265 
266  ARMARX_VERBOSE << "Adding other grid to region (" << startIdx.index.x() << ", "
267  << startIdx.index.y() << "), "
268  << " (" << startIdx.index.x() + rows << ", " << startIdx.index.y() + cols
269  << ")";
270 
271  ARMARX_TRACE;
272 
273  // add the costs of the other mask to this one by a weighting factor
274  grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() +=
275  weight * other.grid.block(0, 0, rows, cols).array();
276 
277  if (not mask.has_value())
278  {
279  mask = Mask(grid.rows(), grid.cols());
280  mask->setOnes();
281  }
282 
283  ARMARX_VERBOSE << "Fraction of valid cells before merge: "
284  << mask->array().cast<float>().sum() / mask->size();
285 
286  Mask otherMask(grid.rows(), grid.cols());
287  otherMask.setZero();
288  otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).setOnes();
289 
290  if (other.mask.has_value())
291  {
292  ARMARX_TRACE;
293 
294  // union of masks
295  ARMARX_DEBUG << "Union of masks";
296  otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() *=
297  other.mask->block(0, 0, rows, cols).array();
298  }
299 
300  ARMARX_TRACE;
301  mask->array() *= otherMask.array();
302 
303  ARMARX_VERBOSE << "Fraction of valid cells: "
304  << mask->array().cast<float>().sum() / mask->size();
305 
306  // apply mask to grid => all invalid points will be 0
307  ARMARX_TRACE;
308  grid.array() *= mask->cast<float>().array();
309 
310  return true;
311  }
312 
313  bool
314  Costmap::add(const Costmap& other, const AddMode mode)
315  {
316  // ensure that both grid and mask are the same size
317  ARMARX_TRACE;
318  validateSizes();
319  other.validateSizes();
320 
321  const auto startIdx = toVertex(other.sceneBounds.min);
322 
323  // merge other grid into this one => use max costs
324 
325  // It might happen that this costmap is not large enough to cover the other costmap
326  // In this case, only add the part to this costmap that is possible.
327  const int rows = std::min(other.grid.rows(), grid.rows() - startIdx.index.x());
328  const int cols = std::min(other.grid.cols(), grid.cols() - startIdx.index.y());
329 
330  ARMARX_VERBOSE << "Adding other grid to region (" << startIdx.index.x() << ", "
331  << startIdx.index.y() << "), "
332  << " (" << startIdx.index.x() + rows << ", " << startIdx.index.y() + cols
333  << ")";
334 
335  ARMARX_TRACE;
336 
337  // add the costs of the other grid to this one by either min or max operator
338  switch (mode)
339  {
340  case AddMode::MIN:
341  grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() =
342  other.grid.block(0, 0, rows, cols)
343  .array()
344  .cwiseMin(
345  grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array());
346  break;
347 
348  case AddMode::MAX:
349  grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() =
350  other.grid.block(0, 0, rows, cols)
351  .array()
352  .cwiseMax(
353  grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array());
354  break;
355  }
356 
357 
358  if (not mask.has_value())
359  {
360  mask = Mask(grid.rows(), grid.cols());
361  mask->setOnes();
362  }
363 
364  Mask otherMask(grid.rows(), grid.cols());
365  otherMask.setZero();
366  otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).setOnes();
367 
368  if (other.mask.has_value())
369  {
370  ARMARX_TRACE;
371 
372  // union of masks
373  ARMARX_DEBUG << "Union of masks";
374  otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() *=
375  other.mask->block(0, 0, rows, cols).array();
376  }
377 
378  ARMARX_TRACE;
379  mask->array() *= otherMask.array();
380 
381  ARMARX_VERBOSE << "Fraction of valid cells: "
382  << mask->array().cast<float>().sum() / mask->size();
383 
384  // apply mask to grid => all invalid points will be 0
385  ARMARX_TRACE;
386  grid.array() *= mask->cast<float>().array();
387 
388  return true;
389  }
390 
391  const std::optional<Costmap::Mask>&
392  Costmap::getMask() const noexcept
393  {
394  return mask;
395  }
396 
397  std::optional<Costmap::Mask>&
399  {
400  return mask;
401  }
402 
403  std::optional<float>
405  {
406  if (not isValid(index))
407  {
408  ARMARX_IMPORTANT << "Requested index " << index << " but it is masked out!";
409  return std::nullopt;
410  }
411 
412  return grid(index.x(), index.y());
413  }
414 
415  std::optional<float>
416  Costmap::value(const Position& position) const
417  {
418  ARMARX_DEBUG << "value ...";
419 
420  const auto v = toVertex(position);
421  return value(v.index);
422  }
423 
424  Costmap
425  Costmap::mergeInto(const std::vector<Costmap>& costmaps,
426  const std::vector<float>& weights) const
427  {
428  ARMARX_CHECK_EQUAL(costmaps.size() + 1, weights.size());
429 
430  const std::vector<float> costmapWeights(weights.begin(), weights.end() - 1);
431  ARMARX_CHECK_EQUAL(costmapWeights.size(), costmaps.size());
432 
433  ARMARX_VERBOSE << "Merging into with weights " << weights;
434 
435  Costmap mergedCostmap = *this;
436 
437  ARMARX_CHECK(mergedCostmap.mask.has_value());
438 
439  for (int x = 0; x < mergedCostmap.getGrid().rows(); x++)
440  {
441  for (int y = 0; y < mergedCostmap.getGrid().cols(); y++)
442  {
443  ARMARX_TRACE;
444  const Costmap::Position position = mergedCostmap.toPositionGlobal(Index{x, y});
445 
446  // merge masks
447  for (const auto& costmap : costmaps)
448  {
449  ARMARX_TRACE;
450  mergedCostmap.mask.value()(x, y) &=
451  costmap.isValid(costmap.toVertex(position).index);
452  }
453 
454  if (mergedCostmap.isValid(Index{x, y}))
455  {
456  float newVal = 0;
457  for (const auto& [weight, costmap] :
458  ranges::views::zip(costmapWeights, costmaps))
459  {
460  const auto otherCostmapVal = costmap.value(position);
461  ARMARX_CHECK(otherCostmapVal.has_value());
462 
463  newVal += weight * otherCostmapVal.value();
464  }
465 
466  newVal += weights.back() * mergedCostmap.grid(x, y);
467  mergedCostmap.grid(x, y) = newVal;
468  }
469  else
470  {
471  mergedCostmap.grid(x, y) = 0;
472  }
473  }
474  }
475 
476  return mergedCostmap;
477  }
478 
479  Costmap::Filter Costmap::Filter::gaussian(int radius, float sigma) {
480  ARMARX_CHECK(radius > 0 and sigma > 0.);
481  Filter gaussianFilter
482  {
483  .matrix = Eigen::MatrixXf::Zero(radius * 2 + 1, radius * 2 + 1)
484  };
485  for (int i = -radius; i <= radius; i++) {
486  for (int j = -radius; j <= radius; j++) {
487  gaussianFilter.matrix(radius + i, radius + j) =
488  std::exp(-0.5 * (std::pow(i / sigma, 2.0) +
489  std::pow(j / sigma, 2.0))) /
490  (2 * M_PI * sigma * sigma);
491  }
492  }
493  return gaussianFilter;
494  }
495 
496  std::size_t
498  {
499  if(not mask.has_value())
500  {
501  return grid.cols() * grid.rows();
502  }
503 
504  return mask->array().cast<int>().sum();
505  }
506 
507 
508 } // namespace armarx::navigation::algorithms
armarx::navigation::algorithms::Costmap::getMask
const std::optional< Mask > & getMask() const noexcept
Definition: Costmap.cpp:392
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:180
armarx::navigation::algorithms::Costmap::Index
Eigen::Array2i Index
Definition: Costmap.h:44
armarx::navigation::algorithms::Costmap::getMutableMask
std::optional< Costmap::Mask > & getMutableMask() noexcept
Definition: Costmap.cpp:398
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
armarx::navigation::algorithms::Costmap::AddMode::MAX
@ MAX
util.h
index
uint8_t index
Definition: EtherCATFrame.h:59
armarx::navigation::algorithms::Costmap::Costmap
Costmap(const Grid &grid, const Parameters &parameters, const SceneBounds &sceneBounds, const std::optional< Mask > &mask=std::nullopt, const core::Pose2D &origin=core::Pose2D::Identity())
Definition: Costmap.cpp:32
ARMARX_CHECK_GREATER
#define ARMARX_CHECK_GREATER(lhs, rhs)
This macro evaluates whether lhs is greater (>) than rhs and if it turns out to be false it will thro...
Definition: ExpressionException.h:116
armarx::navigation::algorithms::Costmap::Filter::matrix
Eigen::MatrixXf matrix
Definition: Costmap.h:32
armarx::max
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
Definition: VectorHelpers.h:267
armarx::navigation::algorithms::Costmap::Vertex::index
Index index
Definition: Costmap.h:59
armarx::navigation::algorithms
This file is part of ArmarX.
Definition: aron_conversions.cpp:13
c
constexpr T c
Definition: UnscentedKalmanFilterTest.cpp:43
armarx::navigation::algorithms::Costmap::Grid
Eigen::MatrixXf Grid
Definition: Costmap.h:46
armarx::navigation::algorithms::Costmap::Vertex
Definition: Costmap.h:57
armarx::navigation::algorithms::Costmap::Filter
Definition: Costmap.h:30
armarx::navigation::algorithms::Costmap::toPositionGlobal
Position toPositionGlobal(const Index &index) const
Definition: Costmap.cpp:71
armarx::navigation::algorithms::Costmap::Mask
Eigen::Matrix< bool, Eigen::Dynamic, Eigen::Dynamic > Mask
Definition: Costmap.h:49
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::navigation::algorithms::Costmap::getLocalSceneBounds
const SceneBounds & getLocalSceneBounds() const noexcept
Definition: Costmap.cpp:126
armarx::navigation::core::Pose2D
Eigen::Isometry2f Pose2D
Definition: basic_types.h:34
Costmap.h
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
armarx::navigation::algorithms::Costmap::AddMode::MIN
@ MIN
M_PI
#define M_PI
Definition: MathTools.h:17
armarx::navigation::algorithms::Costmap::params
const Parameters & params() const noexcept
Definition: Costmap.cpp:230
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:177
armarx::navigation::algorithms::Costmap::Optimum
Definition: Costmap.h:93
armarx::navigation::algorithms::Costmap::toPositionLocal
Position toPositionLocal(const Index &index) const
Definition: Costmap.cpp:57
armarx::navigation::algorithms::Costmap::toVertex
Vertex toVertex(const Position &globalPosition) const
Definition: Costmap.cpp:99
armarx::navigation::algorithms::Costmap::getGrid
const Grid & getGrid() const
Definition: Costmap.cpp:236
armarx::navigation::algorithms::Costmap::Position
Eigen::Vector2f Position
Definition: Costmap.h:45
max
T max(T t1, T t2)
Definition: gdiam.h:48
armarx::navigation::algorithms::Costmap::value
std::optional< float > value(const Index &index) const
Definition: Costmap.cpp:404
armarx::navigation::algorithms::SceneBounds
Definition: types.h:29
armarx::navigation::algorithms::Costmap::mergeInto
Costmap mergeInto(const std::vector< Costmap > &costmaps, const std::vector< float > &weights) const
Definition: Costmap.cpp:425
ExpressionException.h
armarx::ctrlutil::v
double v(double t, double v0, double a0, double j)
Definition: CtrlUtil.h:39
armarx::navigation::algorithms::SceneBounds::min
Eigen::Vector2f min
Definition: types.h:31
armarx::navigation::algorithms::Costmap::filter
void filter(const Filter &filter)
Definition: Costmap.cpp:175
armarx::navigation::algorithms::Costmap::AddMode
AddMode
Definition: Costmap.h:106
armarx::navigation::algorithms::Costmap::Parameters::cellSize
float cellSize
How big each cell is in the uniform grid.
Definition: Costmap.h:27
armarx::navigation::algorithms::Costmap::Parameters
Definition: Costmap.h:21
armarx::navigation::algorithms::Costmap::optimum
Optimum optimum() const
Definition: Costmap.cpp:132
Eigen::Matrix< bool, Eigen::Dynamic, Eigen::Dynamic >
eigen.h
Logging.h
min
T min(T t1, T t2)
Definition: gdiam.h:42
ARMARX_CHECK_EQUAL
#define ARMARX_CHECK_EQUAL(lhs, rhs)
This macro evaluates whether lhs is equal (==) rhs and if it turns out to be false it will throw an E...
Definition: ExpressionException.h:130
armarx::navigation::algorithms::Costmap::isValid
bool isValid(const Index &index) const noexcept
checks whether the cell is masked out
Definition: Costmap.cpp:78
armarx::navigation::algorithms::Costmap::add
bool add(const Costmap &other, float weight=1.0)
Definition: Costmap.cpp:249
armarx::navigation::algorithms::Costmap::numberOfValidElements
std::size_t numberOfValidElements() const
Definition: Costmap.cpp:497
armarx::navigation::algorithms::Costmap::Filter::gaussian
static Filter gaussian(int radius=2, float sigma=1.)
Definition: Costmap.cpp:479
types.h
armarx::navigation::algorithms::Costmap::isInCollision
bool isInCollision(const Position &p) const
Definition: Costmap.cpp:242
armarx::navigation::algorithms::Costmap
Definition: Costmap.h:13