10#include <unordered_set>
15#include <Eigen/Geometry>
17#include <range/v3/view/zip.hpp>
21#include <ArmarXCore/interface/core/UserException.h>
34 const std::optional<Mask>& mask,
38 sceneBounds(sceneBounds),
39 parameters(parameters),
65 Eigen::Vector2f posLocal;
67 sceneBounds.min.x() +
index.x() * parameters.cellSize + parameters.cellSize / 2;
69 sceneBounds.min.y() +
index.y() * parameters.cellSize + parameters.cellSize / 2;
78 return global_T_costmap * costmap_P_pos;
91 if (
index.x() >= grid.rows() ||
index.y() >= grid.cols())
99 if (grid.rows() != mask->rows() or grid.cols() != mask->cols())
101 ARMARX_ERROR <<
"Grid and mask have different dimensions!";
118 const auto localPosition = global_T_costmap.inverse() * globalPosition;
120 const float vX = (localPosition.x() - parameters.cellSize / 2 - sceneBounds.min.x()) /
122 const float vY = (localPosition.y() - parameters.cellSize / 2 - sceneBounds.min.y()) /
125 const int iX = std::round(vX - 0.01);
126 const int iY = std::round(vY - 0.01);
131 std::optional<Costmap::Vertex>
136 if (iX < 0 || iY < 0 || iX >= grid.rows() || iY >= grid.cols())
142 return Vertex{.index =
Index{iX, iY}, .position = globalPosition};
150 const int iXSan = std::clamp<int>(iX, 0, grid.rows() - 1);
151 const int iYSan = std::clamp<int>(iY, 0, grid.cols() - 1);
161 return Vertex{.index =
Index{iXSan, iYSan}, .position = globalPosition};
175 if (mask.has_value())
178 <<
"At least one element has to be valid. Here, all elements are masked out!";
186 float minVal = std::numeric_limits<float>::max();
188 for (
int r = 0; r < grid.rows(); r++)
190 for (
int c = 0;
c < grid.cols();
c++)
192 if (mask.has_value())
194 if (not mask.value()(r,
c))
199 const float currentVal = grid(r,
c);
200 if (currentVal < minVal)
210 if (minVal == std::numeric_limits<float>::max())
212 return {.value = -1, .index = {-1, -1}, .position = {-1, -1}};
215 return {.value = minVal, .index = {row, col}, .position =
toPositionGlobal({row, col})};
221 for (
int r = 0; r < grid.rows(); r++)
223 for (
int c = 0;
c < grid.cols();
c++)
233#pragma omp parallel for schedule(static)
234 for (
int r = 0; r < grid.rows(); r++)
236 for (
int c = 0;
c < grid.cols();
c++)
250 if (mask.has_value())
253 <<
"At least one element has to be valid. Here, all elements are masked out!";
256 Grid newGrid(grid.rows(), grid.cols());
257 const double max = grid.maxCoeff();
258 newGrid.setConstant(
max);
259 for (
int r = 0; r < grid.rows(); r++)
261 for (
int c = 0;
c < grid.cols();
c++)
263 if (mask.has_value())
265 if (not mask.value()(r,
c))
270 Eigen::MatrixXf valueMatrix(
filter.matrix.rows(),
filter.matrix.cols());
271 const int radius_r = (
filter.matrix.rows() - 1) / 2;
272 const int radius_c = (
filter.matrix.cols() - 1) / 2;
273 valueMatrix.setConstant(
max);
274 for (
int i = -radius_r; i <= radius_r; i++)
276 for (
int j = -radius_c; j <= radius_c; j++)
278 const int ri = r + i;
279 const int cj =
c + j;
280 if (ri > 0 and ri < grid.rows() and cj > 0 and cj < grid.cols() and
281 mask.value()(ri, cj))
283 valueMatrix(radius_r + i, radius_c + j) = grid(ri, cj);
287 const double sum =
filter.matrix.sum();
290 const float filteredValue =
291 (valueMatrix.cwiseProduct(
filter.matrix)).sum() / sum;
293 filter.useMinimum ? std::min(grid(r,
c), filteredValue) : filteredValue;
317 return grid(v.index.x(), v.index.y()) == 0.F;
335 grid.array() = other.grid.array().cwiseMin(grid.array());
339 grid.array() = other.grid.array().cwiseMax(grid.array());
344 if (not mask.has_value())
346 mask =
Mask(grid.rows(), grid.cols());
350 Mask otherMask(grid.rows(), grid.cols());
353 if (other.mask.has_value())
359 otherMask.array() *= other.mask->array();
363 mask->array() *= otherMask.array();
366 << mask->array().cast<
float>().sum() / mask->size();
370 grid.array() *= mask->cast<
float>().array();
390 const auto startIdx =
toVertex(other.sceneBounds.
min);
396 const int rows = std::min(other.grid.rows(), grid.rows() - startIdx.index.x());
397 const int cols = std::min(other.grid.cols(), grid.cols() - startIdx.index.y());
399 ARMARX_VERBOSE <<
"Adding other grid to region (" << startIdx.index.x() <<
", "
400 << startIdx.index.y() <<
"), " <<
" (" << startIdx.index.x() + rows <<
", "
401 << startIdx.index.y() + cols <<
")";
406 grid.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() +=
407 weight * other.grid.block(0, 0, rows, cols).array();
409 if (not mask.has_value())
411 mask =
Mask(grid.rows(), grid.cols());
416 << mask->array().cast<
float>().sum() / mask->size();
418 Mask otherMask(grid.rows(), grid.cols());
420 otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).setOnes();
422 if (other.mask.has_value())
428 otherMask.block(startIdx.index.x(), startIdx.index.y(), rows, cols).array() *=
429 other.mask->block(0, 0, rows, cols).array();
433 mask->array() *= otherMask.array();
436 << mask->array().cast<
float>().sum() / mask->size();
440 grid.array() *= mask->cast<
float>().array();
455 if (not mask.has_value())
457 mask =
Mask(grid.rows(), grid.cols());
461 if (other.mask.has_value())
463 otherMask = other.mask.value();
467 otherMask =
Mask(other.grid.rows(), other.grid.cols());
472 [&](
const Index& thisIdx)
479 if (not otherVOpt.has_value())
484 (*this->mask)(thisIdx.x(), thisIdx.y()) =
false;
489 const Index otherIdx = otherVOpt->index;
492 if (other.mask.has_value())
494 (*this->mask)(thisIdx.x(), thisIdx.y()) &=
495 other.mask->operator()(otherIdx.x(), otherIdx.y());
502 this->grid(thisIdx.x(), thisIdx.y()) =
503 std::min(other.grid(otherIdx.x(), otherIdx.y()),
504 this->grid(thisIdx.x(), thisIdx.y()));
507 this->grid(thisIdx.x(), thisIdx.y()) =
508 std::max(other.grid(otherIdx.x(), otherIdx.y()),
509 this->grid(thisIdx.x(), thisIdx.y()));
512 this->grid(thisIdx.x(), thisIdx.y()) *=
513 other.grid(otherIdx.x(), otherIdx.y());
516 throw armarx::InvalidArgumentException{
517 "AddMode is not handled in Costmap::add(...)"};
523 grid.array() *= mask->cast<
float>().array();
528 const std::optional<Costmap::Mask>&
534 std::optional<Costmap::Mask>&
558 return value(v.index);
570 .min = sceneBounds.min + parameters.cellSize * Eigen::Vector2f{
min.cast<
float>()},
571 .max = sceneBounds.min + parameters.cellSize * Eigen::Vector2f{
max.cast<
float>()}};
579 if (mask.has_value())
589 sceneBounds.min = newSceneBounds.min;
590 sceneBounds.max = newSceneBounds.max;
592 ARMARX_VERBOSE <<
"New scene bounds: " << sceneBounds.min <<
" and " << sceneBounds.max;
597 const std::vector<float>& weights)
const
601 const std::vector<float> costmapWeights(weights.begin(), weights.end() - 1);
613 for (
int x = 0;
x < mergedCostmap.
getGrid().rows();
x++)
615 for (
int y = 0; y < mergedCostmap.
getGrid().cols(); y++)
621 for (
const auto&
costmap : costmaps)
624 mergedCostmap.mask.value()(
x, y) &=
631 for (
const auto& [weight,
costmap] :
632 ranges::views::zip(costmapWeights, costmaps))
634 const auto otherCostmapVal =
costmap.value(position);
637 newVal += weight * otherCostmapVal.value();
640 newVal += weights.back() * mergedCostmap.grid(
x, y);
641 mergedCostmap.grid(
x, y) = newVal;
645 mergedCostmap.grid(
x, y) = 0;
650 return mergedCostmap;
657 Filter gaussianFilter{.matrix = Eigen::MatrixXf::Zero(radius * 2 + 1, radius * 2 + 1)};
658 for (
int i = -radius; i <= radius; i++)
660 for (
int j = -radius; j <= radius; j++)
662 gaussianFilter.matrix(radius + i, radius + j) =
663 std::exp(-0.5 * (std::pow(i / sigma, 2.0) + std::pow(j / sigma, 2.0))) /
664 (2 *
M_PI * sigma * sigma);
667 return gaussianFilter;
673 if (not mask.has_value())
675 return grid.cols() * grid.rows();
678 return mask->array().cast<
int>().sum();
692 const auto y =
index.y();
695 for (
int r = 1; r < radius; r++)
697 for (
int i = -r; i <= r; i++)
699 for (
int j = -r; j <= r; j++)
714 std::pair<Costmap::Index, Costmap::Index>
717 if (not mask.has_value())
719 return {
Index{0, 0},
Index{grid.rows() - 1, grid.cols() - 1}};
723 << mask->cast<
float>().sum() / mask->size();
730 for (
int x = 0;
x < grid.rows();
x++)
732 for (
int y = 0; y < grid.cols(); y++)
734 if (mask.value()(
x, y))
736 min.x() = std::min(
min.x(),
x);
737 min.y() = std::min(
min.y(), y);
738 max.x() = std::max(
max.x(),
x);
739 max.y() = std::max(
max.y(), y);
757 auto startIdx = v.index;
761 startIdx.x() = std::max(startIdx.x(), aabbMin.x());
762 startIdx.y() = std::max(startIdx.y(), aabbMin.y());
763 startIdx.x() = std::min(startIdx.x(), aabbMax.x());
764 startIdx.y() = std::min(startIdx.y(), aabbMax.y());
766 startIdx = findClosestValidIndex(startIdx);
775 grid.array() *= mask->cast<
float>().array();
776 auto newMask = mask.value();
782 operator()(
const std::pair<int, int>& v)
const noexcept
784 return v.first * 31 + v.second;
788 std::unordered_set<std::pair<int, int>,
pair_hash> stack;
789 auto visited = newMask;
790 stack.emplace(startIdx.x(), startIdx.y());
792 while (not stack.empty())
796 ARMARX_INFO <<
"After 1000 iterations, current element is " << stack.begin()->first
797 <<
", " << stack.begin()->second;
800 const auto it = stack.begin();
801 const auto idx =
Index{it->first, it->second};
810 visited(idx.x(), idx.y()) = 1;
819 if (grid(idx.x(), idx.y()) == 0.F)
826 newMask.array()(idx.x(), idx.y()) = 1;
828 if (
const std::pair<int, int> element = {idx.x() + 1, idx.y()};
829 element.first < visited.rows() && not visited(element.first, element.second))
831 stack.emplace(element);
833 if (
const std::pair<int, int> element = {idx.x() - 1, idx.y()};
834 element.first > 0 && not visited(element.first, element.second))
836 stack.emplace(element);
838 if (
const std::pair<int, int> element = {idx.x(), idx.y() + 1};
839 element.second < visited.cols() && not visited(element.first, element.second))
841 stack.emplace(element);
843 if (
const std::pair<int, int> element = {idx.x(), idx.y() - 1};
844 element.second > 0 && not visited(element.first, element.second))
846 stack.emplace(element);
850 mask.emplace(newMask);
859 std::optional<Costmap::Vertex>
871 const int maxRadiusCells =
static_cast<int>(std::ceil(maxDistance / parameters.cellSize));
874 for (
int radius = 1; radius <= maxRadiusCells; ++radius)
876 std::optional<Vertex> bestVertex;
877 float bestDistanceSq = std::numeric_limits<float>::max();
880 for (
int i = -radius; i <= radius; ++i)
882 for (
int j = -radius; j <= radius; ++j)
885 if (std::abs(i) != radius && std::abs(j) != radius)
899 if (grid(idx.x(), idx.y()) > 0.F)
902 const float distSq = (candidatePos - position).squaredNorm();
904 if (distSq < bestDistanceSq)
906 bestDistanceSq = distSq;
907 bestVertex =
Vertex{.index = idx, .position = candidatePos};
std::optional< Vertex > findClosestCollisionFreeVertex(const Position &position, float maxDistance) const
Find the closest collision-free position to a given position.
const Grid & getGrid() const
Costmap mergeInto(const std::vector< Costmap > &costmaps, const std::vector< float > &weights) const
Merge a list of costmaps into this costmap.
bool addCostmapWithSameSizeAndPose(const Costmap &other, AddMode mode)
void validateSizes() const
const core::Pose2D & origin() const
static Costmap WithSameDimsAs(const Costmap &other, const Grid &grid, const std::optional< Mask > &mask=std::nullopt)
Create a costmap with the same dimensions and parameters as the given one.
const SceneBounds & getLocalSceneBounds() const noexcept
bool isInCollision(const Position &p) const
void cutToValidBoundingBox()
Position toPositionLocal(const Index &index) const
std::pair< Index, Index > getValidBoundingBox() const
Vertex toVertex(const Position &globalPosition) const
void excludeUnreachable(const Position &position)
std::size_t numberOfValidElements() const
const Parameters & params() const noexcept
Eigen::Matrix< bool, Eigen::Dynamic, Eigen::Dynamic > Mask
std::optional< Costmap::Mask > & getMutableMask() noexcept
Position toPositionGlobal(const Index &index) const
const std::optional< Mask > & getMask() const noexcept
std::optional< Costmap::Vertex > toVertexOrInvalid(const Eigen::Vector2f &globalPosition) const
void filter(const Filter &filter)
void forEachCellParallel(const std::function< void(const Index &)> &f) const
bool isValid(const Index &index) const noexcept
checks whether the cell is masked out
bool add(const Costmap &other, float weight=1.0)
std::pair< int, int > toIndexUnsanitized(const Eigen::Vector2f &globalPosition) const
Costmap(const Grid &grid, const Parameters ¶meters, const SceneBounds &sceneBounds, const std::optional< Mask > &mask=std::nullopt, const core::Pose2D &origin=core::Pose2D::Identity())
void forEachCell(const std::function< void(const Index &)> &f) const
std::optional< float > value(const Index &index) const
#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...
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#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...
#define ARMARX_INFO
The normal logging level.
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
#define ARMARX_VERBOSE
The logging level for verbose information.
This file is part of ArmarX.
This file is part of ArmarX.
This file offers overloads of toIce() and fromIce() functions for STL container types.
std::vector< T > max(const std::vector< T > &v1, const std::vector< T > &v2)
std::vector< T > min(const std::vector< T > &v1, const std::vector< T > &v2)
static Filter gaussian(int radius=2, float sigma=1.)