util.cpp
Go to the documentation of this file.
1 /**
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @author Fabian Reister ( fabian dot reister at kit dot edu )
17  * @date 2022
18  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
19  * GNU General Public License
20  */
21 
22 #include "util.h"
23 
24 #include <algorithm>
25 #include <cstddef>
26 #include <iterator>
27 
28 #include <opencv2/core.hpp>
29 #include <opencv2/core/eigen.hpp>
30 #include <opencv2/imgproc.hpp>
31 
32 #include <SimoxUtility/algorithm/apply.hpp>
33 #include <VirtualRobot/BoundingBox.h>
34 #include <VirtualRobot/CollisionDetection/CollisionModel.h>
35 #include <VirtualRobot/Random.h>
36 #include <VirtualRobot/SceneObjectSet.h>
37 #include <VirtualRobot/Workspace/WorkspaceGrid.h>
38 
41 
46 #include <range/v3/all.hpp>
47 
48 
50 {
51 
52 
53  SceneBounds
54  computeSceneBounds(const VirtualRobot::SceneObjectSetPtr& obstacles, const SceneBounds& init)
55  {
56  SceneBounds bounds = init;
57 
58  ARMARX_CHECK_NOT_NULL(obstacles);
59  ARMARX_CHECK(not obstacles->getCollisionModels().empty());
60 
61  for (size_t i = 0; i < obstacles->getCollisionModels().size(); i++)
62  {
63  VirtualRobot::BoundingBox bb = obstacles->getCollisionModels()[i]->getBoundingBox();
64  bounds.min.x() = std::min(bb.getMin().x(), bounds.min.x());
65  bounds.min.y() = std::min(bb.getMin().y(), bounds.min.y());
66  bounds.max.x() = std::max(bb.getMax().x(), bounds.max.x());
67  bounds.max.y() = std::max(bb.getMax().y(), bounds.max.y());
68  }
69 
70  return bounds;
71  }
72 
73 
74  SceneBounds
75  toSceneBounds(const VirtualRobot::WorkspaceGrid::Extends& extends)
76  {
77  return {.min = Eigen::Vector2f{extends.minX, extends.minY},
78  .max = Eigen::Vector2f{extends.maxX, extends.maxY}};
79  }
80 
81  SceneBounds
82  merge(const std::vector<SceneBounds>& sceneBounds)
83  {
84  SceneBounds bounds;
85 
86  const auto expandBounds = [&bounds](const SceneBounds& sb)
87  {
88  bounds.min.x() = std::min(bounds.min.x(), sb.min.x());
89  bounds.min.y() = std::min(bounds.min.y(), sb.min.y());
90 
91  bounds.max.x() = std::max(bounds.max.x(), sb.max.x());
92  bounds.max.y() = std::max(bounds.max.y(), sb.max.y());
93 
94  return bounds;
95  };
96 
97  ranges::for_each(sceneBounds, expandBounds);
98 
99  return bounds;
100  }
101 
102 
103  Costmap
104  toCostmap(const VirtualRobot::WorkspaceGrid& workspaceGrid)
105  {
106  const Costmap::Grid grid(workspaceGrid.getCells().x, workspaceGrid.getCells().y);
107  const SceneBounds sceneBounds = toSceneBounds(workspaceGrid.getExtends());
108 
109  const Costmap::Parameters parameters{.binaryGrid = false,
110  .cellSize = workspaceGrid.getDiscretizeSize()};
111 
112  return {grid, parameters, sceneBounds};
113  }
114 
115 
116  // Costmap
117  // mergeUnaligned(const std::vector<Costmap>& costmaps,
118  // const std::vector<float>& weights,
119  // float cellSize,
120  // const Eigen::Array2f& offset)
121  // {
122  // ARMARX_TRACE;
123 
124  // ARMARX_CHECK_NOT_EMPTY(costmaps);
125  // ARMARX_CHECK_EQUAL(costmaps.size(), weights.size());
126 
127  // // if unset, use the smallest cell size
128  // if (cellSize < 0)
129  // {
130  // const auto compCellSize = [](const Costmap& a, const Costmap& b) -> bool
131  // { return a.params().cellSize < b.params().cellSize; };
132 
133  // cellSize = ranges::min_element(costmaps, compCellSize)->params().cellSize;
134  // }
135 
136  // // scale each costmap
137  // std::vector<Costmap> scaledCostmaps =
138  // simox::alg::apply(costmaps,
139  // [&cellSize](const Costmap& costmap) -> Costmap
140  // { return scaleCostmap(costmap, cellSize); });
141 
142  // // merge costmaps into one
143  // ARMARX_VERBOSE << "Merging " << scaledCostmaps.size() << " costmaps";
144 
145  // // - combine scene bounds
146  // std::vector<SceneBounds> sceneBoundsAll = simox::alg::apply(
147  // scaledCostmaps, [](const Costmap& costmap) { return costmap.getSceneBounds(); });
148 
149  // SceneBounds sceneBounds = merge(sceneBoundsAll);
150  // sceneBounds.min.x() -= 0.5 * cellSize;
151  // sceneBounds.max.x() -= 0.5 * cellSize;
152  // sceneBounds.min.y() -= cellSize;
153  // sceneBounds.max.y() -= cellSize;
154 
155 
156  // sceneBounds.min.x() += offset.x();
157  // sceneBounds.max.x() += offset.x();
158  // sceneBounds.min.y() += offset.y();
159  // sceneBounds.max.y() += offset.y();
160 
161  // // - create grid
162  // const auto largeGrid = CostmapBuilder::createUniformGrid(
163  // sceneBounds, Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize});
164 
165  // Costmap largeCostmap(
166  // largeGrid, Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize}, sceneBounds);
167 
168  // // TODO save each combo individually
169 
170  // static int i = 0;
171  // // - add all costmaps to this one
172  // ranges::for_each(ranges::views::zip(scaledCostmaps, weights),
173  // [&largeCostmap](const auto& p)
174  // {
175  // ARMARX_INFO << "adding to costmap";
176  // const auto& [costmap, weight] = p;
177  // largeCostmap.add(costmap, weight);
178 
179  // armarx::navigation::algorithms::save(
180  // costmap, "/tmp/costmap" + std::to_string(i++));
181  // });
182 
183  // armarx::navigation::algorithms::save(largeCostmap,
184  // "/tmp/large-costmap" + std::to_string(i++));
185 
186  // return largeCostmap;
187  // }
188 
189  void
190  checkSameSize(const std::vector<Costmap>& costmaps)
191  {
192  ARMARX_CHECK_NOT_EMPTY(costmaps);
193 
194  const auto assertSameSize = [&costmaps](const Costmap& costmap)
195  {
196  ARMARX_CHECK_EQUAL(costmap.getGrid().rows(), costmaps.front().getGrid().rows());
197  ARMARX_CHECK_EQUAL(costmap.getGrid().rows(), costmaps.front().getGrid().rows());
198  };
199 
200  ranges::for_each(costmaps, assertSameSize);
201  }
202 
203 
204  Costmap
205  mergeAligned(const std::vector<Costmap>& costmaps, CostmapMergeMode mode)
206  {
207  ARMARX_CHECK_NOT_EMPTY(costmaps);
208  checkSameSize(costmaps);
209 
210 
211  // average mode: fixed weight
212  if (mode == CostmapMergeMode::AVERAGE)
213  {
214  const std::vector<float> weights(costmaps.size(), 1.F / costmaps.size());
215  return mergeAligned(costmaps, weights);
216  }
217 
218  const auto grid = CostmapBuilder::createUniformGrid(costmaps.front().getLocalSceneBounds(),
219  costmaps.front().params());
220 
221  Costmap mergedCostmap(
222  grid, costmaps.front().params(), costmaps.front().getLocalSceneBounds());
223 
224  const auto addMode = [&]() -> Costmap::AddMode
225  {
226  Costmap::AddMode addMode{};
227 
228  switch (mode)
229  {
231  ARMARX_CHECK(false) << "This case should already be handled.";
232  break;
234  addMode = Costmap::AddMode::MIN;
235  break;
237  addMode = Costmap::AddMode::MAX;
238  break;
239  }
240 
241  return addMode;
242  }();
243 
244  ranges::for_each(costmaps,
245  [&mergedCostmap, &addMode](const auto& costmap)
246  { mergedCostmap.add(costmap, addMode); });
247 
248  return mergedCostmap;
249  }
250 
251  Costmap
252  mergeAligned(const std::vector<Costmap>& costmaps, const std::vector<float>& weights)
253  {
254  ARMARX_CHECK_EQUAL(costmaps.size(), weights.size());
255  ARMARX_CHECK_NOT_EMPTY(costmaps);
256  checkSameSize(costmaps);
257 
258 
259  const auto grid = CostmapBuilder::createUniformGrid(costmaps.front().getLocalSceneBounds(),
260  costmaps.front().params());
261 
262  Costmap mergedCostmap(
263  grid, costmaps.front().params(), costmaps.front().getLocalSceneBounds());
264 
265  // foreach pair (costmap, weight): add it to there merged costmap
266  ranges::for_each(ranges::views::zip(costmaps, weights),
267  [&mergedCostmap](const auto& p)
268  {
269  const auto& [costmap, weight] = p;
270  mergedCostmap.add(costmap, weight);
271  });
272 
273  return mergedCostmap;
274  }
275 
276  Costmap
277  scaleCostmap(const Costmap& costmap, float cellSize)
278  {
279  const float scale = costmap.params().cellSize / cellSize;
280  ARMARX_VERBOSE << "Scaling grid by a factor of " << scale;
281 
282  cv::Mat src;
283  cv::eigen2cv(costmap.getGrid(), src);
284 
285  cv::Mat dst;
286  cv::resize(src, dst, cv::Size{0, 0}, scale, scale);
287 
288  Eigen::MatrixXf scaledGrid;
289  cv::cv2eigen(dst, scaledGrid);
290 
291 
292  std::optional<Costmap::Mask> scaledMask;
293  if (costmap.getMask().has_value())
294  {
295  ARMARX_TRACE;
296  ARMARX_VERBOSE << "Scaling mask by a factor of " << scale;
297 
298  // just an int large enough to check if resizing causes
299  // issues on boundary to invalid regions
300  constexpr int someInt = 100;
301 
303  someInt * costmap.getMask().value().cast<std::uint8_t>();
304 
305  cv::Mat maskMat;
306  cv::eigen2cv(mask, maskMat);
307 
308  cv::Mat maskScaled;
309  cv::resize(maskMat, maskScaled, cv::Size{0, 0}, scale, scale);
310 
311  // only those points are valid where the original value '10' is preserved.
312  // all other points are boundary points to the invalid area
313  // cv::threshold(maskMat, maskMat, 10.9, 1, cv::THRESH_BINARY);
314 
315  Eigen::MatrixXi scaledMaskInt; //(scaledGrid.rows(), scaledGrid.cols());
316  cv::cv2eigen(maskScaled, scaledMaskInt);
317 
318  Costmap::Mask m = scaledMaskInt.array() >= someInt;
319 
320  scaledMask = m;
321  }
322 
323  ARMARX_VERBOSE << "Original size (" << costmap.getGrid().rows() << ", "
324  << costmap.getGrid().cols() << ")";
325  ARMARX_VERBOSE << "Resized to (" << scaledGrid.rows() << ", " << scaledGrid.cols() << ")";
326 
327  return {scaledGrid,
328  Costmap::Parameters{.binaryGrid = false, .cellSize = cellSize},
329  costmap.getLocalSceneBounds(),
330  scaledMask};
331  }
332 
333 
334  std::optional<core::Pose2D>
336  {
337  const auto sizeX = costmap.getGrid().cols();
338  const auto sizeY = costmap.getGrid().rows();
339 
340  constexpr std::size_t maxIterations = 1000;
341 
342  // sample a valid pose in the costmap
343 
344  for (std::size_t iteration = 0; iteration < maxIterations; iteration++)
345  {
346  const float iX = VirtualRobot::RandomFloat() * static_cast<float>(sizeX);
347  const float iY = VirtualRobot::RandomFloat() * static_cast<float>(sizeY);
348 
349  algorithms::Costmap::Index idx(iX, iY);
350 
351  if (not costmap.isValid(idx))
352  {
353  continue;
354  }
355 
357  pose.translation() = costmap.toPositionGlobal(idx);
358  return pose;
359  }
360 
361  ARMARX_ERROR << "Failed to sample pose in costmap!";
362  return std::nullopt;
363  }
364 } // 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::CostmapMergeMode
CostmapMergeMode
Defines how the.
Definition: util.h:55
armarx::navigation::algorithms::Costmap::AddMode::MAX
@ MAX
util.h
armarx::navigation::algorithms::merge
SceneBounds merge(const std::vector< SceneBounds > &sceneBounds)
Definition: util.cpp:82
CostmapBuilder.h
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
armarx::navigation::algorithms::CostmapMergeMode::AVERAGE
@ AVERAGE
armarx::navigation::algorithms
This file is part of ArmarX.
Definition: aron_conversions.cpp:13
armarx::navigation::algorithms::checkSameSize
void checkSameSize(const std::vector< Costmap > &costmaps)
Definition: util.cpp:190
armarx::navigation::algorithms::Costmap::Grid
Eigen::MatrixXf Grid
Definition: Costmap.h:46
armarx::navigation::algorithms::mergeAligned
Costmap mergeAligned(const std::vector< Costmap > &costmaps, CostmapMergeMode mode)
Definition: util.cpp:205
ARMARX_CHECK_NOT_EMPTY
#define ARMARX_CHECK_NOT_EMPTY(c)
Definition: ExpressionException.h:224
persistence.h
armarx::navigation::algorithms::Costmap::toPositionGlobal
Position toPositionGlobal(const Index &index) const
Definition: Costmap.cpp:71
armarx::navigation::algorithms::CostmapBuilder::createUniformGrid
static Eigen::MatrixXf createUniformGrid(const SceneBounds &sceneBounds, const Costmap::Parameters &parameters)
Definition: CostmapBuilder.cpp:88
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
GfxTL::Identity
void Identity(MatrixXX< N, N, T > *a)
Definition: MatrixXX.h:523
armarx::navigation::algorithms::Costmap::Parameters::binaryGrid
bool binaryGrid
Definition: Costmap.h:24
armarx::navigation::algorithms::Costmap::AddMode::MIN
@ MIN
armarx::navigation::algorithms::Costmap::params
const Parameters & params() const noexcept
Definition: Costmap.cpp:230
armarx::navigation::algorithms::scaleCostmap
Costmap scaleCostmap(const Costmap &costmap, float cellSize)
Definition: util.cpp:277
armarx::navigation::algorithms::Costmap::getGrid
const Grid & getGrid() const
Definition: Costmap.cpp:236
armarx::conversions::cv2eigen
Eigen::Vector2f cv2eigen(const cv::Point2f &pt)
Definition: opencv_eigen.h:47
armarx::navigation::algorithms::computeSceneBounds
SceneBounds computeSceneBounds(const VirtualRobot::SceneObjectSetPtr &obstacles, const SceneBounds &init)
Definition: util.cpp:54
max
T max(T t1, T t2)
Definition: gdiam.h:48
armarx::navigation::algorithms::SceneBounds
Definition: types.h:29
ARMARX_ERROR
#define ARMARX_ERROR
Definition: Logging.h:189
ExpressionException.h
armarx::navigation::algorithms::CostmapMergeMode::MAX
@ MAX
armarx::navigation::algorithms::CostmapMergeMode::MIN
@ MIN
armarx::navigation::algorithms::SceneBounds::min
Eigen::Vector2f min
Definition: types.h:31
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::sampleValidPositionInMap
std::optional< core::Pose2D > sampleValidPositionInMap(const algorithms::Costmap &costmap)
Definition: util.cpp:335
armarx::navigation::algorithms::Costmap::Parameters
Definition: Costmap.h:21
Eigen::Matrix
Definition: EigenForwardDeclarations.h:27
armarx::navigation::algorithms::toSceneBounds
SceneBounds toSceneBounds(const VirtualRobot::WorkspaceGrid::Extends &extends)
Definition: util.cpp:75
armarx::navigation::algorithms::toCostmap
Costmap toCostmap(const VirtualRobot::WorkspaceGrid &workspaceGrid)
Definition: util.cpp:104
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
types.h
armarx::navigation::algorithms::SceneBounds::max
Eigen::Vector2f max
Definition: types.h:32
armarx::navigation::algorithms::Costmap
Definition: Costmap.h:13
armarx::conversions::eigen2cv
cv::Point2f eigen2cv(const Eigen::Vector2f &pt)
Definition: opencv_eigen.h:30