12#include <Eigen/Geometry>
14#include <opencv2/imgproc.hpp>
16#include <range/v3/view/zip.hpp>
32 const std::optional<Mask>& mask,
36 sceneBounds(sceneBounds),
37 parameters(parameters),
44 Costmap3D::validateSizes()
const
51 auto columns = size.y();
56 columns = mask->cols();
59 for (
const auto& g : grid)
72 Eigen::Vector2f posLocal;
74 sceneBounds.min.x() +
index.x() * parameters.cellSize + parameters.cellSize / 2;
76 sceneBounds.min.y() +
index.y() * parameters.cellSize + parameters.cellSize / 2;
85 return global_T_Costmap3D * Costmap3D_P_pos;
128 if (mask.has_value())
134 return not mask.value()(
index.x(),
index.y());
142 const auto localPosition = global_T_Costmap3D.inverse() * globalPosition;
144 const float vX = (localPosition.x() - parameters.cellSize / 2 - sceneBounds.min.x()) /
146 const float vY = (localPosition.y() - parameters.cellSize / 2 - sceneBounds.min.y()) /
149 const int iX = std::round(vX - 0.01);
150 const int iY = std::round(vY - 0.01);
153 const int iXSan = std::clamp<int>(iX, 0, size.x() - 1);
154 const int iYSan = std::clamp<int>(iY, 0, size.y() - 1);
164 return Vertex{.index =
Index{iXSan, iYSan}, .position = globalPosition};
170 const float degrees = (360.F / parameters.orientations) *
index;
171 return {.index =
index, .degrees = degrees};
177 float deg = std::fmod(degrees, 360.F);
190 const float tmp = (degrees + (180.F / parameters.orientations)) /
191 (360.F / parameters.orientations);
193 index = std::min(
index, parameters.orientations - 1);
194 return {.index =
index, .degrees = degrees};
197 std::pair<Costmap3D::Rotation, Costmap3D::Rotation>
201 const float step = 360.F / parameters.orientations;
203 int lowIndex =
static_cast<int>(deg / step);
204 lowIndex = std::min(lowIndex, parameters.orientations - 1);
205 const int highIndex = (lowIndex + 1) % parameters.orientations;
221 if (mask.has_value())
224 <<
"At least one element has to be valid. Here, all elements are masked out!";
228 Grid::value_type::Index row = 0;
229 Grid::value_type::Index col = 0;
233 float minVal = std::numeric_limits<float>::max();
235 for (
int r = 0; r < size.x(); r++)
237 for (
int c = 0;
c < size.y();
c++)
239 if (mask.has_value())
241 if (not mask.value()(r,
c))
248 for (
const auto& g : grid)
251 const float currentVal = g(r,
c);
252 if (currentVal < minVal)
262 const auto& g = grid[rotation.
index];
264 const float currentVal = g(r,
c);
265 if (currentVal < minVal)
276 return {.value = minVal, .index = {row, col}, .position =
toPositionGlobal({row, col})};
295 if (rotation.has_value())
297 return grid.at(rotation->index)(v.index.x(), v.index.y()) <= 0.F;
302 return std::any_of(grid.begin(),
304 [&v](
const auto& grid2d)
305 { return grid2d(v.index.x(), v.index.y()) <= 0.F; });
311 const float clearance)
const
320 return grid.at(low.index)(v.index.x(), v.index.y()) > clearance and
321 grid.at(high.index)(v.index.x(), v.index.y()) > clearance;
324 std::optional<Costmap3D::Vertex>
327 const float maxDistance,
328 const float minClearance)
const
339 const Eigen::MatrixXf combined =
340 (grid.at(low.index).cwiseMin(grid.at(high.index)).array() - minClearance)
344 const Costmap combinedCostmap{combined,
347 .cellSize = parameters.cellSize,
349 .sceneBoundsMargin = parameters.sceneBoundsMargin,
356 if (not vertex.has_value())
361 return Vertex{.index = vertex->index, .position = vertex->position};
364 const std::optional<Costmap3D::Mask>&
370 std::optional<Costmap3D::Mask>&
379 return grid.at(rot_index)(
index.x(),
index.y());
390 if (rot_index < 0 || rot_index > this->grid.size() - 1)
393 <<
" but it is not within allowed range (0 .. "
394 << this->grid.size() - 1 <<
")";
398 return grid.at(rot_index)(
index.x(),
index.y());
409 return value(v.index, r.index);
421 .min = sceneBounds.min + parameters.cellSize * Eigen::Vector2f{
min.cast<
float>()},
422 .max = sceneBounds.min + parameters.cellSize * Eigen::Vector2f{
max.cast<
float>()}};
427 for (
auto& grid2d : grid)
435 if (mask.has_value())
445 sceneBounds.min = newSceneBounds.min;
446 sceneBounds.max = newSceneBounds.max;
448 ARMARX_VERBOSE <<
"New scene bounds: " << sceneBounds.min <<
" and " << sceneBounds.max;
454 if (not mask.has_value())
457 return size.y() * size.x();
460 return mask->array().cast<
int>().sum();
474 const auto y =
index.y();
477 for (
int r = 1; r < 10; r++)
479 for (
int i = -r; i <= r; i++)
481 for (
int j = -r; j <= r; j++)
496 std::pair<Costmap3D::Index, Costmap3D::Index>
499 if (not mask.has_value())
505 << mask->cast<
float>().sum() / mask->size();
513 for (
int x = 0;
x < size.x();
x++)
515 for (
int y = 0; y < size.y(); y++)
517 if (mask.value()(
x, y))
519 min.x() = std::min(
min.x(),
x);
520 min.y() = std::min(
min.y(), y);
521 max.x() = std::max(
max.x(),
x);
522 max.y() = std::max(
max.y(), y);
533 return {grid[0].rows(), grid[0].cols()};
542 .cellSize = parameters.cellSize,
544 .sceneBoundsMargin = parameters.sceneBoundsMargin,
550 c.getMutableMask() =
c.getGrid().array() > 0.0F;
558 const std::optional<Mask>& mask)
566 for (
int rotIdx = 0; rotIdx < parameters.orientations; rotIdx++)
568 ARMARX_DEBUG <<
"Converting to Euclidean SDF for rotation index " << rotIdx;
580 if (not mask.has_value())
582 ARMARX_DEBUG <<
"Costmap3D::convertToPseudoSDF: No mask present, nothing to do.";
586 const Eigen::MatrixXf& grid2d = grid[rotationIndex];
593 cv::Mat mask8U(ny, nx, CV_8U);
594 for (
int x = 0;
x < nx; ++
x)
596 for (
int y = 0; y < ny; ++y)
598 bool isFree = mask.value()(
x, y) && grid2d(
x, y) > 0.0f;
599 mask8U.at<uchar>(y,
x) = isFree ? 255 : 0;
606 cv::distanceTransform(mask8U, distImage, cv::DIST_L2, cv::DIST_MASK_PRECISE, CV_32F);
608 const float cellSize = parameters.cellSize;
612 for (
int x = 0;
x < nx; ++
x)
614 for (
int y = 0; y < ny; ++y)
616 if (grid2d(
x, y) <= 0.0f)
618 float distPixels = distImage.at<
float>(y,
x);
619 grid[rotationIndex](
x, y) = -distPixels * cellSize;
624 ARMARX_DEBUG <<
"Converted orientation " << rotationIndex
625 <<
" to true Euclidean SDF. Range: [" << grid[rotationIndex].minCoeff() <<
", "
626 << grid[rotationIndex].maxCoeff() <<
"]";
std::optional< Vertex > findClosestCollisionFreeVertex(const Position &position, float maxDistance) const
Find the closest collision-free position to a given position.
std::pair< Rotation, Rotation > bracketingRotationsFromDegrees(RotationDegrees degrees) const
The two orientation slices bracketing the given continuous rotation.
void convertToPseudoSDF()
Costmap costmapForOrientation(RotationIndex rotationIndex, bool initializeMask=false) const
bool isWithinRange(const Index &index) const noexcept
const core::Pose2D & origin() const
const SceneBounds & getLocalSceneBounds() const noexcept
Position toPositionGlobal(const Index &index) const
Index findClosestValidIndex(const Index &index)
void cutToValidBoundingBox()
const Grid & getGrid() const
static Costmap3D WithSameDimsAs(const Costmap3D &other, const Grid &grid, const std::optional< Mask > &mask=std::nullopt)
Create a Costmap3D with the same dimensions and parameters as the given one.
std::optional< Vertex > findClosestCollisionFreeVertex(const Position &position, RotationDegrees orientationDeg, float maxDistance, float minClearance=0.F) const
Find the closest position where the robot fits at the given continuous orientation with more than min...
Costmap3D(const Grid &grid, const Parameters ¶meters, const SceneBounds &sceneBounds, const std::optional< Mask > &mask=std::nullopt, const core::Pose2D &origin=core::Pose2D::Identity())
static constexpr Rotation ALL_ORIENTATIONS
bool isInCollision(const Position &p, std::optional< Rotation > rotation=std::nullopt) const
const std::optional< Mask > & getMask() const noexcept
std::size_t numberOfValidElements() const
bool isMaskedOut(const Index &index) const noexcept
Rotation rotationFromIndex(RotationIndex index) const
Rotation closestRotationFromDegrees(RotationDegrees degrees) const
Position toPositionLocal(const Index &index) const
std::optional< Costmap3D::Mask > & getMutableMask() noexcept
void convertToPseudoSDFForRotation(RotationIndex rot_index)
std::vector< Eigen::MatrixXf > Grid
std::optional< float > value(const Index &index, const RotationIndex rot_index) const
std::pair< Index, Index > getValidBoundingBox() const
Vertex toVertex(const Position &globalPosition) const
bool isValid(const Index &index) const noexcept
checks whether the cell is masked out
Optimum optimum(Rotation rotation=ALL_ORIENTATIONS) const
const Parameters & params() const noexcept
bool isFreeWithClearance(const Position &p, RotationDegrees orientationDeg, float clearance=0.F) const
Whether the robot fits at the given position and continuous orientation with more than the given obst...
float value_ignore_mask(const Index &index, const RotationIndex rot_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_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_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.
float rotationFrom0To360(float degrees)
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)