CostmapBuilder.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 "CostmapBuilder.h"
23 
24 #include <algorithm>
25 #include <cstddef>
26 #include <functional>
27 #include <limits>
28 #include <optional>
29 #include <string>
30 #include <vector>
31 
32 #include <VirtualRobot/CollisionDetection/CollisionChecker.h>
33 #include <VirtualRobot/CollisionDetection/CollisionModel.h>
34 #include <VirtualRobot/Nodes/RobotNode.h> // IWYU pragma: keep
35 #include <VirtualRobot/Robot.h> // IWYU pragma: keep
36 #include <VirtualRobot/SceneObjectSet.h>
37 #include <VirtualRobot/VirtualRobot.h>
38 
44 
51 
53 {
54 
55 
57  const VirtualRobot::SceneObjectSetPtr& obstacles,
58  const std::vector<VirtualRobot::RobotPtr>& articulatedObjects,
59  const std::vector<Room>& rooms,
60  const Costmap::Parameters& parameters,
61  const std::string& robotCollisonModelName,
62  const CostmapBuilderParams& builderParameters) :
63  robot(robot),
64  obstacles(obstacles),
66  rooms(rooms),
67  parameters(parameters),
68  robotCollisionModelName(robotCollisonModelName),
69  builderParameters(builderParameters)
70  {
71  ARMARX_CHECK_NOT_NULL(robot) << "Robot must be set";
72  ARMARX_CHECK_NOT_NULL(obstacles);
73  ARMARX_CHECK(robot->hasRobotNode(robotCollisionModelName));
74  }
75 
76  Costmap
78  {
80  [](const armarx::core::time::Duration& duration)
81  { ARMARX_INFO << "Creating costmap took " << duration; });
82 
83  // We do not restrict the costmap to rooms here. Instead, we only update the costmap
84  // inside the rooms below (if this is requested).
85 
86  {
88  [](const armarx::core::time::Duration& duration)
89  { ARMARX_INFO << "Filling costmap took " << duration; });
90 
91  ARMARX_VERBOSE << "Filling grid with size (" << costmap.getGrid().rows() << "/"
92  << costmap.getGrid().cols() << ") and resolution "
93  << costmap.params().cellSize;
94 
95  if (builderParameters.restrictToRooms)
96  {
97  ARMARX_INFO << "Restricting to rooms";
98  extendGridCosts(costmap, rooms);
99  }
100  else
101  {
102  extendGridCosts(costmap, {});
103  }
104 
105  ARMARX_VERBOSE << "Filled grid";
106  }
107 
108  initializeMask(costmap);
109  ARMARX_VERBOSE << "Initialized mask";
110 
111  return costmap;
112  }
113 
114  Costmap
116  {
118  [](const armarx::core::time::Duration& duration)
119  { ARMARX_INFO << "Creating costmap took " << duration; });
120 
121  ARMARX_INFO << articulatedObjects.size() << " articulated objects";
122  ARMARX_INFO << obstacles->getSize() << " objects";
123 
124  const auto sceneBounds = computeSceneBounds(obstacles,
125  articulatedObjects,
126  init,
127  rooms,
128  parameters.sceneBoundsMargin,
129  builderParameters.restrictToRooms);
130  const auto grid = createUniformGrid(sceneBounds, parameters);
131 
132  ARMARX_VERBOSE << "Creating grid";
133  Costmap costmap(grid, parameters, sceneBounds);
134 
135  if (builderParameters.restrictToRooms)
136  {
137  initializeEmptyMask(costmap);
138 
140  ARMARX_VERBOSE << "Restricting to rooms:";
141  for (const auto& room : rooms)
142  {
143  ARMARX_VERBOSE << " - " << room.name;
144  }
145 
146  invalidateOutsideRooms(rooms, costmap);
147 
148  ARMARX_VERBOSE << "Restricted to rooms. Fraction of valid elements: "
149  << costmap.mask->cast<float>().sum() / costmap.mask->size();
150  }
151 
152  {
154  [](const armarx::core::time::Duration& duration)
155  { ARMARX_INFO << "Filling costmap took " << duration; });
156 
157  ARMARX_VERBOSE << "Filling grid with size (" << costmap.getGrid().rows() << "/"
158  << costmap.getGrid().cols() << ") and resolution "
159  << costmap.params().cellSize;
160  fillGridCosts(costmap);
161  ARMARX_VERBOSE << "Filled grid";
162  }
163 
164  initializeMask(costmap);
165  ARMARX_VERBOSE << "Initialized mask";
166 
167  return costmap;
168  }
169 
170  void
172  {
173  costmap.mask = costmap.grid.array() > 0.F;
174 
175  ARMARX_VERBOSE << "Initializing mask: Fraction of valid elements: "
176  << costmap.mask->cast<float>().sum() / costmap.mask->size();
177  }
178 
179  void
180  CostmapBuilder::initializeEmptyMask(Costmap& costmap)
181  {
182  costmap.mask = costmap.grid.array() >= -42.F; // FIXME: magic number, just same size
183  costmap.mask->setOnes();
184 
185  ARMARX_VERBOSE << "Initializing empty mask: Fraction of valid elements: "
186  << costmap.mask->cast<float>().sum() / costmap.mask->size();
187  }
188 
189  Eigen::MatrixXf
191  const Costmap::Parameters& parameters)
192  {
193  ARMARX_TRACE;
194 
195  ARMARX_VERBOSE << "Scene bounds are " << sceneBounds.min << " and " << sceneBounds.max;
196 
197  //+1 for explicit rounding up
198  size_t c_x = (sceneBounds.max.x() - sceneBounds.min.x()) / parameters.cellSize + 1;
199  size_t c_y = (sceneBounds.max.y() - sceneBounds.min.y()) / parameters.cellSize + 1;
200 
201  ARMARX_VERBOSE << "Grid size: " << c_x << ", " << c_y;
202 
203  ARMARX_VERBOSE << "Resetting grid";
204  Eigen::MatrixXf grid(c_x, c_y);
205  grid.setZero();
206 
207  return grid;
208  }
209 
210  float
211  CostmapBuilder::computeCost(const Costmap::Position& position,
212  VirtualRobot::RobotPtr& collisionRobot,
213  const VirtualRobot::CollisionModelPtr& robotCollisionModel)
214  {
215  ARMARX_TRACE;
216  ARMARX_CHECK_NOT_NULL(collisionRobot);
217  ARMARX_CHECK_NOT_NULL(robotCollisionModel);
218  ARMARX_CHECK_NOT_NULL(VirtualRobot::CollisionChecker::getGlobalCollisionChecker());
219  ARMARX_CHECK_NOT_NULL(obstacles);
220  // ARMARX_CHECK_GREATER(obstacles->getSize(), 0);
221 
222  const core::Pose globalPose(Eigen::Translation3f(conv::to3D(position)));
223  collisionRobot->setGlobalPose(globalPose.matrix());
224 
225  // distance to non-articulated objects
226  float distanceNonArticulated = std::numeric_limits<float>::max();
227  if (obstacles->getSize() > 0)
228  {
229  distanceNonArticulated =
230  VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
231  robotCollisionModel, obstacles);
232  }
233 
234  // distance to articulated objects
235  float distanceArticulatedMin = std::numeric_limits<float>::max();
236  for (const auto& articulatedObject : articulatedObjects)
237  {
238  for (const auto& colModel : articulatedObject->getCollisionModels())
239  {
240  const float distanceArticulated =
241  VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
242  robotCollisionModel, colModel);
243  distanceArticulatedMin = std::min(distanceArticulated, distanceArticulatedMin);
244  }
245  }
246 
247  return std::min(distanceNonArticulated, distanceArticulatedMin);
248 
249 
250  // Eigen::Vector3f P1;
251  // Eigen::Vector3f P2;
252  // int id1, id2;
253 
254  // float minDistance = std::numeric_limits<float>::max();
255 
256  // // TODO omp...
257  // for (size_t i = 0; i < obstacles->getSize(); i++)
258  // {
259  // // cheap collision check
260  // // VirtualRobot::BoundingBox obstacleBbox =
261  // // obstacles->getSceneObject(i)->getCollisionModel()->getBoundingBox(true);
262  // // if (not intersects(robotBbox, obstacleBbox))
263  // // {
264  // // continue;
265  // // }
266 
267  // // precise collision check
268  // const float dist =
269  // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->calculateDistance(
270  // robotCollisionModel,
271  // obstacles->getSceneObject(i)->getCollisionModel(),
272  // P1,
273  // P2,
274  // &id1,
275  // &id2);
276 
277  // // check if objects collide
278  // if ((dist <= parameters.cellSize / 2) or
279  // VirtualRobot::CollisionChecker::getGlobalCollisionChecker()->checkCollision(
280  // robotCollisionModel, obstacles->getSceneObject(i)->getCollisionModel()))
281  // {
282  // minDistance = 0;
283  // break;
284  // }
285 
286  // minDistance = std::min(minDistance, dist);
287  // }
288  // // return n->position.x() >= sceneBounds.min.x() && n->position.x() <= sceneBounds.max.x() &&
289  // // n->position.y() >= sceneBounds.min.y() && n->position.y() <= sceneBounds.max.y();
290 
291  // return minDistance;
292  }
293 
294  void
295  CostmapBuilder::fillGridCosts(Costmap& costmap)
296  {
297  applyFnToCostmap(costmap,
298  [&](VirtualRobot::RobotPtr& collisionRobot,
299  VirtualRobot::CollisionModelPtr& robotCollisionModel,
300  const std::optional<float> maskOpt,
301  const float cost,
302  const Costmap::Index& index) -> float
303  {
304  const auto position = costmap.toPositionGlobal(index);
305 
306  // consider mask if available and skip if not valid
307  if (maskOpt.has_value())
308  {
309  if (not maskOpt.value())
310  {
311  return 0.F;
312  }
313  }
314 
315  return computeCost(position, collisionRobot, robotCollisionModel);
316  });
317  }
318 
319  void
320  CostmapBuilder::extendGridCosts(Costmap& costmap, const std::vector<Room>& rooms)
321  {
322  applyFnToCostmap(costmap,
323  [&](VirtualRobot::RobotPtr& collisionRobot,
324  VirtualRobot::CollisionModelPtr& robotCollisionModel,
325  const std::optional<float> maskOpt,
326  const float cost,
327  const Costmap::Index& index) -> float
328  {
329  const auto position = costmap.toPositionGlobal(index);
330 
331  // consider mask if available and skip if not valid
332  if (maskOpt.has_value())
333  {
334  if (not maskOpt.value())
335  {
336  return std::min(0.F, costmap.grid(index.x(), index.y()));
337  }
338  }
339 
340  if (cost < 0.F)
341  {
342  return cost;
343  }
344 
345  if (not rooms.empty())
346  {
347  if (std::none_of(rooms.begin(),
348  rooms.end(),
349  [&position](const Room& room) -> bool
350  { return room.isInside(position); }))
351  {
352  return cost;
353  }
354  }
355 
356  return std::min(
357  cost, computeCost(position, collisionRobot, robotCollisionModel));
358  });
359  }
360 
361  void
362  CostmapBuilder::applyFnToCostmap(Costmap& costmap,
363  const std::function<float(VirtualRobot::RobotPtr&,
364  VirtualRobot::CollisionModelPtr&,
365  const std::optional<float>,
366  const float,
367  const Costmap::Index&)>& fn)
368  {
369  VirtualRobot::RobotPtr collisionRobot;
370  VirtualRobot::CollisionModelPtr robotCollisionModel;
371 
372  if (costmap.mask.has_value())
373  {
374  ARMARX_VERBOSE << "Costmap provides mask.";
375  }
376 
377  const std::size_t c_x = costmap.grid.rows();
378  const std::size_t c_y = costmap.grid.cols();
379 
380  robot->setUpdateVisualization(false);
381 
382 #pragma omp parallel for schedule(static) private(collisionRobot, \
383  robotCollisionModel) default(shared)
384  for (unsigned int x = 0; x < c_x; x++)
385  {
386  // initialize if needed
387  if (collisionRobot == nullptr)
388  {
389 #pragma omp critical
390  {
391  ARMARX_DEBUG << "Copying robot";
392  ARMARX_CHECK_NOT_NULL(robot);
393  collisionRobot =
394  robot->clone("collision_robot_" + std::to_string(omp_get_thread_num()));
395 
396  // replace the standard collision model by our custom one
397  collisionRobot->setPrimitiveApproximationModel({"navigation"}, false);
398 
399  collisionRobot->setUpdateVisualization(false);
400  ARMARX_DEBUG << "Copying done";
401  }
402 
403  ARMARX_CHECK_NOT_NULL(collisionRobot);
404  ARMARX_CHECK(collisionRobot->hasRobotNode(robotCollisionModelName));
405 
406  ARMARX_CHECK(collisionRobot);
407  const auto collisionRobotNode =
408  collisionRobot->getRobotNode(robotCollisionModelName);
409  ARMARX_CHECK_NOT_NULL(collisionRobotNode);
410 
411  robotCollisionModel = collisionRobotNode->getCollisionModel();
412  ARMARX_CHECK(robotCollisionModel) << "Collision model not available. "
413  "Make sure that you load the robot correctly!";
414 
415  // scale collision model
416  robotCollisionModel->scale(builderParameters.collisionModelScaleFactor);
417  }
418 
419  ARMARX_CHECK_NOT_NULL(collisionRobot);
420  ARMARX_CHECK_NOT_NULL(robotCollisionModel);
421 
422  for (unsigned int y = 0; y < c_y; y++)
423  {
424  const Costmap::Index index{x, y};
425 
426  const auto maskVal = costmap.mask.has_value()
427  ? std::make_optional(costmap.mask.value()(x, y))
428  : std::nullopt;
429  costmap.grid(x, y) =
430  fn(collisionRobot, robotCollisionModel, maskVal, costmap.grid(x, y), index);
431  }
432  }
433  }
434 
435 } // namespace armarx::navigation::algorithms
ARMARX_VERBOSE
#define ARMARX_VERBOSE
Definition: Logging.h:187
armarx::navigation::algorithms::Costmap::Index
Eigen::Array2i Index
Definition: Costmap.h:54
armarx::navigation::core::Pose
Eigen::Isometry3f Pose
Definition: basic_types.h:31
util.h
index
uint8_t index
Definition: EtherCATFrame.h:59
basic_types.h
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::objpose::util::articulatedObjects
objpose::ObjectPoseSeq articulatedObjects(objpose::ObjectPoseSeq objects)
Definition: util.cpp:82
trace.h
armarx::navigation::algorithms::CostmapBuilder::CostmapBuilder
CostmapBuilder(const VirtualRobot::RobotPtr &robot, const VirtualRobot::SceneObjectSetPtr &obstacles, const std::vector< VirtualRobot::RobotPtr > &articulatedObjects, const std::vector< Room > &rooms, const Costmap::Parameters &parameters, const std::string &robotCollisonModelName, const CostmapBuilderParams &builderParameters)
Definition: CostmapBuilder.cpp:56
Duration.h
armarx::navigation::algorithms::computeSceneBounds
SceneBounds computeSceneBounds(const VirtualRobot::SceneObjectSetPtr &obstacles, const std::vector< VirtualRobot::RobotPtr > &articulatedObjects, const SceneBounds &init, const std::vector< Room > &rooms, const float margin, const bool restrictToRooms)
Definition: util.cpp:62
armarx::navigation::algorithms
This file is part of ArmarX.
Definition: aron_conversions.cpp:25
armarx::rooms
Brief description of class rooms.
Definition: rooms.h:38
ARMARX_CHECK_NOT_EMPTY
#define ARMARX_CHECK_NOT_EMPTY(c)
Definition: ExpressionException.h:224
armarx::navigation::algorithms::CostmapBuilder::createUniformGrid
static Eigen::MatrixXf createUniformGrid(const SceneBounds &sceneBounds, const Costmap::Parameters &parameters)
Definition: CostmapBuilder.cpp:190
armarx::navigation::algorithms::CostmapBuilder::CostmapBuilderParams::restrictToRooms
bool restrictToRooms
Definition: CostmapBuilder.h:45
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
Costmap.h
armarx::navigation::algorithms::CostmapBuilder::extend
Costmap extend(Costmap costmap)
Definition: CostmapBuilder.cpp:77
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:77
armarx::navigation::algorithms::Costmap::params
const Parameters & params() const noexcept
Definition: Costmap.cpp:237
ARMARX_DEBUG
#define ARMARX_DEBUG
Definition: Logging.h:184
armarx::navigation::algorithms::Costmap::getGrid
const Grid & getGrid() const
Definition: Costmap.cpp:243
armarx::navigation::algorithms::invalidateOutsideRooms
void invalidateOutsideRooms(const std::vector< Room > &rooms, Costmap &costmap)
Definition: util.cpp:439
armarx::navigation::algorithms::Costmap::Position
Eigen::Vector2f Position
Definition: Costmap.h:55
max
T max(T t1, T t2)
Definition: gdiam.h:51
armarx::navigation::algorithms::SceneBounds
Definition: types.h:29
armarx::to_string
const std::string & to_string(const std::string &s)
Definition: StringHelpers.h:41
armarx::navigation::algorithms::CostmapBuilder::initializeMask
static void initializeMask(Costmap &costmap)
Definition: CostmapBuilder.cpp:171
armarx::navigation::algorithms::CostmapBuilder::CostmapBuilderParams
Definition: CostmapBuilder.h:43
ExpressionException.h
armarx::navigation::algorithms::SceneBounds::min
Eigen::Vector2f min
Definition: types.h:31
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:181
std
Definition: Application.h:66
armarx::core::time::ScopedStopWatch
Measures the time this stop watch was inside the current scope.
Definition: ScopedStopWatch.h:31
armarx::navigation::algorithms::CostmapBuilder::create
Costmap create(const SceneBounds &init=SceneBounds())
Definition: CostmapBuilder.cpp:115
armarx::core::time::Duration
Represents a duration.
Definition: Duration.h:16
armarx::navigation::algorithms::Costmap::Parameters::cellSize
float cellSize
How big each cell is in the uniform grid.
Definition: Costmap.h:30
F
Definition: ExportDialogControllerTest.cpp:18
armarx::navigation::conv::to3D
std::vector< Eigen::Vector3f > to3D(const std::vector< Eigen::Vector2f > &v)
Definition: eigen.cpp:14
armarx::navigation::algorithms::Costmap::Parameters
Definition: Costmap.h:24
eigen.h
Logging.h
min
T min(T t1, T t2)
Definition: gdiam.h:44
armarx::navigation::algorithms::Costmap::Parameters::sceneBoundsMargin
float sceneBoundsMargin
Definition: Costmap.h:36
ScopedStopWatch.h
Room.h
types.h
armarx::navigation::algorithms::SceneBounds::max
Eigen::Vector2f max
Definition: types.h:32
armarx::navigation::algorithms::Costmap
Definition: Costmap.h:16
VirtualRobot::RobotPtr
std::shared_ptr< class Robot > RobotPtr
Definition: Bus.h:19