hfields.cpp
Go to the documentation of this file.
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Copyright (c) 2021, INRIA
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above
14  * copyright notice, this list of conditions and the following
15  * disclaimer in the documentation and/or other materials provided
16  * with the distribution.
17  * * Neither the name of Open Source Robotics Foundation nor the names of its
18  * contributors may be used to endorse or promote products derived
19  * from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  */
34 
37 #define BOOST_TEST_MODULE FCL_HEIGHT_FIELDS
38 #include <boost/test/included/unit_test.hpp>
39 #include <boost/filesystem.hpp>
40 
41 #include "fcl_resources/config.h"
42 
43 #include <hpp/fcl/collision.h>
44 #include <hpp/fcl/hfield.h>
45 #include <hpp/fcl/math/transform.h>
49 
50 #include <hpp/fcl/collision.h>
51 
52 #include "utility.h"
53 #include <iostream>
54 
55 using namespace hpp::fcl;
56 
57 template <typename BV>
58 void test_constant_hfields(const Eigen::DenseIndex nx,
59  const Eigen::DenseIndex ny,
60  const FCL_REAL min_altitude,
61  const FCL_REAL max_altitude) {
62  const FCL_REAL x_dim = 1., y_dim = 2.;
63  const MatrixXf heights = MatrixXf::Constant(ny, nx, max_altitude);
64 
65  HeightField<BV> hfield(x_dim, y_dim, heights, min_altitude);
66 
67  BOOST_CHECK(hfield.getXDim() == x_dim);
68  BOOST_CHECK(hfield.getYDim() == y_dim);
69 
70  const VecXf& x_grid = hfield.getXGrid();
71  BOOST_CHECK(x_grid[0] == -x_dim / 2.);
72  BOOST_CHECK(x_grid[nx - 1] == x_dim / 2.);
73 
74  const VecXf& y_grid = hfield.getYGrid();
75  BOOST_CHECK(y_grid[0] == y_dim / 2.);
76  BOOST_CHECK(y_grid[ny - 1] == -y_dim / 2.);
77 
78  // Test local AABB
79  hfield.computeLocalAABB();
80 
81  for (Eigen::DenseIndex i = 0; i < nx; ++i) {
82  for (Eigen::DenseIndex j = 0; j < ny; ++j) {
83  Vec3f point(x_grid[i], y_grid[j], heights(j, i));
84  BOOST_CHECK(hfield.aabb_local.contain(point));
85  }
86  }
87 
88  // Test clone
89  {
90  HeightField<BV>* hfield_clone = hfield.clone();
91  hfield_clone->computeLocalAABB();
92  BOOST_CHECK(*hfield_clone == hfield);
93 
94  delete hfield_clone;
95  }
96 
97  // Build equivalent object
98  const Box equivalent_box(x_dim, y_dim, max_altitude - min_altitude);
99  const Transform3f box_placement(
100  Matrix3f::Identity(), Vec3f(0., 0., (max_altitude + min_altitude) / 2.));
101 
102  // Test collision
103  const Sphere sphere(1.);
104  static const Transform3f IdTransform;
105 
106  const Box box(Vec3f::Ones());
107 
109 
110  // No collision case
111  {
112  const FCL_REAL eps_no_collision = +0.1 * (max_altitude - min_altitude);
113  M_sphere.setTranslation(
114  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
115  M_box.setTranslation(
116  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
117  CollisionRequest request;
118 
119  CollisionResult result;
120  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
121 
122  BOOST_CHECK(!result.isCollision());
123 
124  CollisionResult result_check_sphere;
125  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
126  request, result_check_sphere);
127 
128  BOOST_CHECK(!result_check_sphere.isCollision());
129 
130  CollisionResult result_check_box;
131  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
132  result_check_box);
133 
134  BOOST_CHECK(!result_check_box.isCollision());
135  }
136 
137  // Collision case
138  {
139  const FCL_REAL eps_collision = -0.1 * (max_altitude - min_altitude);
140  M_sphere.setTranslation(
141  Vec3f(0., 0., max_altitude + sphere.radius + eps_collision));
142  M_box.setTranslation(
143  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_collision));
145  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
146 
147  CollisionResult result;
148  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
149 
150  BOOST_CHECK(result.isCollision());
151 
152  CollisionResult result_check;
153  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
154  request, result_check);
155 
156  BOOST_CHECK(result_check.isCollision());
157 
158  CollisionResult result_check_box;
159  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
160  result_check_box);
161 
162  BOOST_CHECK(result_check_box.isCollision());
163  }
164 
165  // Update height
166  hfield.updateHeights(
167  MatrixXf::Constant(ny, nx, max_altitude / 2.)); // We change nothing
168 
169  // No collision case
170  {
171  const FCL_REAL eps_no_collision = +0.1 * (max_altitude - min_altitude);
172  M_sphere.setTranslation(
173  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
174  M_box.setTranslation(
175  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
176  CollisionRequest request;
177 
178  CollisionResult result;
179  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
180 
181  BOOST_CHECK(!result.isCollision());
182 
183  CollisionResult result_check_sphere;
184  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
185  request, result_check_sphere);
186 
187  BOOST_CHECK(!result_check_sphere.isCollision());
188 
189  CollisionResult result_check_box;
190  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
191  result_check_box);
192 
193  BOOST_CHECK(!result_check_box.isCollision());
194  }
195 
196  // Collision case
197  {
198  const FCL_REAL eps_collision = -0.1 * (max_altitude - min_altitude);
199  M_sphere.setTranslation(
200  Vec3f(0., 0., max_altitude + sphere.radius + eps_collision));
201  M_box.setTranslation(
202  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_collision));
204  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
205 
206  CollisionResult result;
207  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
208 
209  BOOST_CHECK(!result.isCollision());
210 
211  CollisionResult result_check;
212  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
213  request, result_check);
214 
215  BOOST_CHECK(result_check.isCollision());
216 
217  CollisionResult result_check_box;
218  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
219  result_check_box);
220 
221  BOOST_CHECK(result_check_box.isCollision());
222  }
223 
224  // Restore height
225  hfield.updateHeights(
226  MatrixXf::Constant(ny, nx, max_altitude)); // We change nothing
227 
228  // Collision case
229  {
230  const FCL_REAL eps_collision = -0.1 * (max_altitude - min_altitude);
231  M_sphere.setTranslation(
232  Vec3f(0., 0., max_altitude + sphere.radius + eps_collision));
233  M_box.setTranslation(
234  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_collision));
236  request; //(CONTACT | DISTANCE_LOWER_BOUND, (size_t)((nx-1)*(ny-1)));
237 
238  CollisionResult result;
239  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
240 
241  BOOST_CHECK(result.isCollision());
242 
243  CollisionResult result_check;
244  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
245  request, result_check);
246 
247  BOOST_CHECK(result_check.isCollision());
248 
249  CollisionResult result_check_box;
250  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
251  result_check_box);
252 
253  BOOST_CHECK(result_check_box.isCollision());
254  }
255 }
256 
257 BOOST_AUTO_TEST_CASE(building_constant_hfields) {
258  const FCL_REAL max_altitude = 1., min_altitude = 0.;
259 
260  test_constant_hfields<OBBRSS>(2, 2, min_altitude,
261  max_altitude); // Simple case
262  test_constant_hfields<OBBRSS>(20, 2, min_altitude, max_altitude);
263  test_constant_hfields<OBBRSS>(100, 100, min_altitude, max_altitude);
264  // test_constant_hfields<OBBRSS>(1000,1000,min_altitude,max_altitude);
265 
266  test_constant_hfields<AABB>(2, 2, min_altitude, max_altitude); // Simple case
267  test_constant_hfields<AABB>(20, 2, min_altitude, max_altitude);
268  test_constant_hfields<AABB>(100, 100, min_altitude, max_altitude);
269 }
270 
271 template <typename BV>
272 void test_negative_security_margin(const Eigen::DenseIndex nx,
273  const Eigen::DenseIndex ny,
274  const FCL_REAL min_altitude,
275  const FCL_REAL max_altitude) {
276  const FCL_REAL x_dim = 1., y_dim = 2.;
277  const MatrixXf heights = MatrixXf::Constant(ny, nx, max_altitude);
278 
279  HeightField<BV> hfield(x_dim, y_dim, heights, min_altitude);
280 
281  // Build equivalent object
282  const Box equivalent_box(x_dim, y_dim, max_altitude - min_altitude);
283  const Transform3f box_placement(
284  Matrix3f::Identity(), Vec3f(0., 0., (max_altitude + min_altitude) / 2.));
285 
286  // Test collision
287  const Sphere sphere(1.);
288  static const Transform3f IdTransform;
289 
290  const Box box(Vec3f::Ones());
291 
293 
294  // No collision case
295  {
296  const FCL_REAL eps_no_collision = +0.1 * (max_altitude - min_altitude);
297  M_sphere.setTranslation(
298  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
299  M_box.setTranslation(
300  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
301  CollisionRequest request;
302 
303  CollisionResult result;
304  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
305 
306  BOOST_CHECK(!result.isCollision());
307 
308  CollisionResult result_check_sphere;
309  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
310  request, result_check_sphere);
311 
312  BOOST_CHECK(!result_check_sphere.isCollision());
313 
314  CollisionResult result_check_box;
315  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
316  result_check_box);
317 
318  BOOST_CHECK(!result_check_box.isCollision());
319  }
320 
321  // Collision case - positive security_margin
322  {
323  const FCL_REAL eps_no_collision = +0.1 * (max_altitude - min_altitude);
324  M_sphere.setTranslation(
325  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
326  M_box.setTranslation(
327  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
328  CollisionRequest request;
329  request.security_margin = eps_no_collision + 1e-6;
330 
331  CollisionResult result;
332  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
333 
334  BOOST_CHECK(result.isCollision());
335 
336  CollisionResult result_check_sphere;
337  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
338  request, result_check_sphere);
339 
340  BOOST_CHECK(result_check_sphere.isCollision());
341 
342  CollisionResult result_check_box;
343  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
344  result_check_box);
345 
346  BOOST_CHECK(result_check_box.isCollision());
347  }
348 
349  // Collision case
350  {
351  const FCL_REAL eps_no_collision = -0.1 * (max_altitude - min_altitude);
352  M_sphere.setTranslation(
353  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
354  M_box.setTranslation(
355  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
356  CollisionRequest request;
357 
358  CollisionResult result;
359  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
360 
361  BOOST_CHECK(result.isCollision());
362 
363  CollisionResult result_check_sphere;
364  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
365  request, result_check_sphere);
366 
367  BOOST_CHECK(result_check_sphere.isCollision());
368 
369  CollisionResult result_check_box;
370  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
371  result_check_box);
372 
373  BOOST_CHECK(result_check_box.isCollision());
374  }
375 
376  // No collision case - negative security_margin
377  {
378  const FCL_REAL eps_no_collision = -0.1 * (max_altitude - min_altitude);
379  M_sphere.setTranslation(
380  Vec3f(0., 0., max_altitude + sphere.radius + eps_no_collision));
381  M_box.setTranslation(
382  Vec3f(0., 0., max_altitude + box.halfSide[2] + eps_no_collision));
383  CollisionRequest request;
384  request.security_margin = eps_no_collision - 1e-4;
385 
386  CollisionResult result;
387  collide(&hfield, IdTransform, &sphere, M_sphere, request, result);
388 
389  BOOST_CHECK(!result.isCollision());
390 
391  CollisionResult result_check_sphere;
392  collide(&equivalent_box, IdTransform * box_placement, &sphere, M_sphere,
393  request, result_check_sphere);
394 
395  BOOST_CHECK(!result_check_sphere.isCollision());
396 
397  CollisionResult result_check_box;
398  collide(&equivalent_box, IdTransform * box_placement, &box, M_box, request,
399  result_check_box);
400 
401  BOOST_CHECK(!result_check_box.isCollision());
402  }
403 }
404 
405 BOOST_AUTO_TEST_CASE(negative_security_margin) {
406  const FCL_REAL max_altitude = 1., min_altitude = 0.;
407 
408  // test_negative_security_margin<OBBRSS>(100, 100, min_altitude,
409  // max_altitude);
410  test_negative_security_margin<AABB>(100, 100, min_altitude, max_altitude);
411 }
412 
413 BOOST_AUTO_TEST_CASE(hfield_with_square_hole) {
414  const Eigen::DenseIndex nx = 100, ny = 100;
415 
416  typedef AABB BV;
417  const MatrixXf X =
418  Eigen::RowVectorXd::LinSpaced(nx, -1., 1.).replicate(ny, 1);
419  const MatrixXf Y = Eigen::VectorXd::LinSpaced(ny, 1., -1.).replicate(1, nx);
420 
421  const FCL_REAL dim_square = 0.5;
422 
423  const Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> hole =
424  (X.array().abs() < dim_square) && (Y.array().abs() < dim_square);
425 
426  const MatrixXf heights =
427  MatrixXf::Ones(ny, nx) - hole.cast<double>().matrix();
428 
429  const HeightField<BV> hfield(2., 2., heights, -10.);
430 
431  Sphere sphere(0.48);
432  const Transform3f sphere_pos(Vec3f(0., 0., 0.5));
433  const Transform3f hfield_pos;
434 
435  const CollisionRequest request;
436 
437  CollisionResult result;
438  {
439  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
440 
441  BOOST_CHECK(!result.isCollision());
442  }
443 
444  sphere.radius = 0.51;
445 
446  {
447  CollisionResult result;
448  const Sphere sphere2(0.51);
449  collide(&hfield, hfield_pos, &sphere2, sphere_pos, request, result);
450 
451  BOOST_CHECK(result.isCollision());
452  }
453 }
454 
455 BOOST_AUTO_TEST_CASE(hfield_with_circular_hole) {
456  const Eigen::DenseIndex nx = 100, ny = 100;
457 
458  // typedef OBBRSS BV; TODO(jcarpent): OBBRSS does not work (compile in Debug
459  // mode), as the overlap of OBBRSS is not satisfactory yet.
460  typedef AABB BV;
461  const MatrixXf X =
462  Eigen::RowVectorXd::LinSpaced(nx, -1., 1.).replicate(ny, 1);
463  const MatrixXf Y = Eigen::VectorXd::LinSpaced(ny, 1., -1.).replicate(1, nx);
464 
465  const FCL_REAL dim_hole = 1;
466 
467  const Eigen::Array<bool, Eigen::Dynamic, Eigen::Dynamic> hole =
468  (X.array().square() + Y.array().square() <= dim_hole);
469 
470  const MatrixXf heights =
471  MatrixXf::Ones(ny, nx) - hole.cast<double>().matrix();
472 
473  const HeightField<BV> hfield(2., 2., heights, -10.);
474 
475  BOOST_CHECK(hfield.getXGrid()[0] == -1.);
476  BOOST_CHECK(hfield.getXGrid()[nx - 1] == +1.);
477 
478  BOOST_CHECK(hfield.getYGrid()[0] == +1.);
479  BOOST_CHECK(hfield.getYGrid()[ny - 1] == -1.);
480 
481  Sphere sphere(0.975);
482  const Transform3f sphere_pos(Vec3f(0., 0., 1.));
483  const Transform3f hfield_pos;
484 
485  {
486  CollisionResult result;
487  CollisionRequest request;
488  request.security_margin = 0.;
489  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
490 
491  BOOST_CHECK(!result.isCollision());
492  }
493 
494  {
495  CollisionResult result;
496  CollisionRequest request;
497  request.security_margin = 0.01;
498  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
499 
500  BOOST_CHECK(!result.isCollision());
501  }
502 
503  {
504  CollisionResult result;
505  CollisionRequest request;
506  request.security_margin = 1. - sphere.radius;
507  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
508 
509  BOOST_CHECK(result.isCollision());
510  }
511 
512  {
513  CollisionResult result;
514  CollisionRequest request;
515  request.security_margin = -0.005;
516  collide(&hfield, hfield_pos, &sphere, sphere_pos, request, result);
517 
518  BOOST_CHECK(!result.isCollision());
519  }
520 }
Vec3f halfSide
box side half-length
Simple transform class used locally by InterpMotion.
Definition: transform.h:54
FCL_REAL getYDim() const
Returns the dimension of the Height Field along the Y direction.
Definition: hfield.h:239
Eigen::Matrix< FCL_REAL, Eigen::Dynamic, Eigen::Dynamic > MatrixXf
Definition: data_types.h:71
void setTranslation(const Eigen::MatrixBase< Derived > &T_)
set transform from translation
Definition: transform.h:142
virtual HeightField< BV > * clone() const
Clone *this into a new CollisionGeometry.
Definition: hfield.h:246
Eigen::Matrix< FCL_REAL, Eigen::Dynamic, 1 > VecXf
Definition: data_types.h:67
BOOST_AUTO_TEST_CASE(building_constant_hfields)
Definition: hfields.cpp:257
FCL_REAL getXDim() const
Returns the dimension of the Height Field along the X direction.
Definition: hfield.h:237
void test_negative_security_margin(const Eigen::DenseIndex nx, const Eigen::DenseIndex ny, const FCL_REAL min_altitude, const FCL_REAL max_altitude)
Definition: hfields.cpp:272
void computeLocalAABB()
Compute the AABB for the HeightField, used for broad-phase collision.
Definition: hfield.h:253
const VecXf & getXGrid() const
Returns a const reference of the grid along the X direction.
Definition: hfield.h:229
Data structure depicting a height field given by the base grid dimensions and the elevation along the...
Definition: hfield.h:182
request to the collision algorithm
const VecXf & getYGrid() const
Returns a const reference of the grid along the Y direction.
Definition: hfield.h:231
AABB aabb_local
AABB in local coordinate, used for tight AABB when only translation transform.
double FCL_REAL
Definition: data_types.h:65
Center at zero point, axis aligned box.
A class describing the AABB collision structure, which is a box in 3D space determined by two diagona...
Definition: BV/AABB.h:54
FCL_REAL radius
Radius of the sphere.
Center at zero point sphere.
void updateHeights(const MatrixXf &new_heights)
Update Height Field height.
Definition: hfield.h:265
HPP_FCL_DLLAPI std::size_t collide(const CollisionObject *o1, const CollisionObject *o2, const CollisionRequest &request, CollisionResult &result)
Main collision interface: given two collision objects, and the requirements for contacts, including num of max contacts, whether perform exhaustive collision (i.e., returning returning all the contact points), whether return detailed contact information (i.e., normal, contact point, depth; otherwise only contact primitive id is returned), this function performs the collision between them. Return value is the number of contacts generated between the two objects.
bool contain(const Vec3f &p) const
Check whether the AABB contains a point.
Definition: BV/AABB.h:101
bool isCollision() const
return binary collision result
Eigen::Matrix< FCL_REAL, 3, 1 > Vec3f
Definition: data_types.h:66
void test_constant_hfields(const Eigen::DenseIndex nx, const Eigen::DenseIndex ny, const FCL_REAL min_altitude, const FCL_REAL max_altitude)
Definition: hfields.cpp:58
FCL_REAL security_margin
Distance below which objects are considered in collision. See Collision.


hpp-fcl
Author(s):
autogenerated on Fri Jun 2 2023 02:39:01