GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/rbprm-shooter.cc Lines: 193 202 95.5 %
Date: 2024-02-02 12:21:48 Branches: 193 336 57.4 %

Line Branch Exec Source
1
// Copyright (c) 2014, LAAS-CNRS
2
// Authors: Steve Tonneau (steve.tonneau@laas.fr)
3
//
4
// This file is part of hpp-rbprm.
5
// hpp-rbprm is free software: you can redistribute it
6
// and/or modify it under the terms of the GNU Lesser General Public
7
// License as published by the Free Software Foundation, either version
8
// 3 of the License, or (at your option) any later version.
9
//
10
// hpp-rbprm is distributed in the hope that it will be
11
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
// General Lesser Public License for more details.  You should have
14
// received a copy of the GNU Lesser General Public License along with
15
// hpp-rbprm. If not, see <http://www.gnu.org/licenses/>.
16
17
#include <hpp/fcl/BVH/BVH_model.h>
18
#include <hpp/fcl/collision_object.h>
19
20
#include <Eigen/Geometry>
21
#include <hpp/core/collision-validation-report.hh>
22
#include <hpp/core/collision-validation.hh>
23
#include <hpp/core/configuration-shooter/uniform.hh>
24
#include <hpp/core/problem.hh>
25
#include <hpp/pinocchio/configuration.hh>
26
#include <hpp/pinocchio/liegroup-element.hh>
27
#include <hpp/pinocchio/liegroup-space.hh>
28
#include <hpp/rbprm/rbprm-shooter.hh>
29
#include <hpp/rbprm/rbprm-validation-report.hh>
30
#include <hpp/util/timer.hh>
31
#include <pinocchio/algorithm/joint-configuration.hpp>
32
33
namespace hpp {
34
using namespace core;
35
using namespace fcl;
36
namespace {
37
static const int SIZE_EULER = 6;
38
typedef fcl::BVHModel<OBBRSS> BVHModelOB;
39
typedef fcl::shared_ptr<const BVHModelOB> BVHModelOBConst_Ptr_t;
40
41
47226
BVHModelOBConst_Ptr_t GetModel(
42
    const pinocchio::FclConstCollisionObjectPtr_t object) {
43

47226
  if (object->collisionGeometry()->getNodeType() != BV_OBBRSS) {
44
    hppDout(warning,
45
            "Collision geometry in shooter is not a BV_OBBRSS, cannot get the "
46
            "model.");
47
    return BVHModelOBConst_Ptr_t();
48
  }
49
  // assert(object->collisionGeometry()->getNodeType() == BV_OBBRSS);
50
  const BVHModelOBConst_Ptr_t model =
51
94452
      static_pointer_cast<const BVHModelOB>(object->collisionGeometry());
52
  // assert(model->getModelType() == BVH_MODEL_TRIANGLES);
53
47226
  if (model->getModelType() != BVH_MODEL_TRIANGLES) {
54
    hppDout(warning, "Collision model is not of type BVH_MODEL_TRIANGLES.");
55
    return BVHModelOBConst_Ptr_t();
56
  }
57
47226
  return model;
58
}
59
60
174
double TriangleArea(rbprm::TrianglePoints& tri) {
61
  double a, b, c;
62
174
  a = (tri.p1 - tri.p2).norm();
63
174
  b = (tri.p2 - tri.p3).norm();
64
174
  c = (tri.p3 - tri.p1).norm();
65
174
  double s = 0.5 * (a + b + c);
66
174
  return sqrt(s * (s - a) * (s - b) * (s - c));
67
}
68
69
48395
std::vector<double> getTranslationBounds(
70
    const pinocchio::RbPrmDevicePtr_t robot) {
71
96790
  const JointPtr_t root = robot->Device::rootJoint();
72
48395
  std::vector<double> res;
73
193580
  for (std::size_t i = 0; i < 3; ++i) {
74

145185
    if (root->isBounded(i)) {
75

145185
      res.push_back(root->lowerBound(i));
76

145185
      res.push_back(root->upperBound(i));
77
    } else {
78
      res.push_back(-std::numeric_limits<double>::max());
79
      res.push_back(std::numeric_limits<double>::max());
80
    }
81
  }
82
96790
  return res;
83
}
84
85
1132
void SetConfigTranslation(const pinocchio::RbPrmDevicePtr_t robot,
86
                          Configuration_t& config, const Vec3f& translation) {
87
2264
  std::vector<double> bounds = getTranslationBounds(robot);
88
4528
  for (std::size_t i = 0; i < 3; ++i) {
89
3396
    config(i) =
90
6792
        std::min(bounds[2 * i + 1], std::max(bounds[2 * i], translation[i]));
91
  }
92
1132
}
93
94
47263
void Translate(const pinocchio::RbPrmDevicePtr_t robot, Configuration_t& config,
95
               const Vec3f& translation) {
96
  // bound to positions limits
97
94526
  std::vector<double> bounds = getTranslationBounds(robot);
98
189052
  for (int i = 0; i < 3; ++i) {
99
283578
    config(i) = std::min(bounds[2 * i + 1],
100

425367
                         std::max(bounds[2 * i], config(i) + translation[i]));
101
  }
102
47263
}
103
104
/*void SampleRotationRec(ConfigurationPtr_t config, JointVector_t& jv,
105
std::size_t& current)
106
{
107
    JointPtr_t joint = jv[current++];
108
    std::size_t rank = joint->rankInConfiguration ();
109
    const hpp::pinocchio::LiegroupSpacePtr_t jm = joint->configurationSpace();
110
    jm->neutral().setNeutral(); setRandom ();
111
    hpp::pinocchio::LiegroupSpace toto;
112
    //toto/
113
    joint->jointModel()-> configuration ()->uniformlySample (rank, *config);
114
    if(current<jv.size())
115
        SampleRotationRec(config,jv,current);
116
}*/
117
118
1132
void SampleRotation(const std::vector<double>& so3, Configuration_t& config) {
119
1132
  if (so3.empty()) return;
120
1132
  assert(so3.size() == SIZE_EULER);
121
1132
  Eigen::Vector3d rot;
122
4528
  for (int i = 0; i < 6; i += 2) {
123
3396
    rot[i / 2] = so3[i] + (so3[i + 1] - so3[i]) * rand() / RAND_MAX;
124
    // std::cout << "rot i " << rot [i/2] << " i " << i/2 << std::endl;
125
  }
126
127


1132
  Eigen::Quaterniond qt = Eigen::AngleAxisd(rot(0), Eigen::Vector3d::UnitZ()) *
128

1132
                          Eigen::AngleAxisd(rot(1), Eigen::Vector3d::UnitY()) *
129


2264
                          Eigen::AngleAxisd(rot(2), Eigen::Vector3d::UnitX());
130
1132
  std::size_t rank = 3;
131
  /*for(std::size_t i = 0; i <4; ++i)
132
  {
133
      config(rank+i) = qt.coeffs()(i);
134
  }*/
135

1132
  config.segment<4>(rank) = qt.coeffs();
136
}
137
138
/*void SampleRotation(pinocchio::DevicePtr_t so3, ConfigurationPtr_t config,
139
JointVector_t& jv)
140
{
141
    std::size_t id = 1;
142
    if(so3->rootJoint())
143
    {
144
        Eigen::Matrix <value_type, 3, 1> confso3;
145
        id+=1;
146
        pinocchio::JointPtr_t joint = so3->rootJoint();
147
        for(int i =0; i <3; ++i)
148
        {
149
            joint->configuration()->uniformlySample (i, confso3);
150
            joint = joint->numberChildJoints() > 0 ? joint->childJoint(0) : 0;
151
        }
152
        Eigen::Quaterniond qt = Eigen::AngleAxisd(confso3(0),
153
Eigen::Vector3d::UnitZ())
154
          * Eigen::AngleAxisd(confso3(1), Eigen::Vector3d::UnitY())
155
          * Eigen::AngleAxisd(confso3(2), Eigen::Vector3d::UnitX());
156
        std::size_t rank = 3;
157
        (*config)(rank+0) = qt.w();
158
        (*config)(rank+1) = qt.x();
159
        (*config)(rank+2) = qt.y();
160
        (*config)(rank+3) = qt.z();
161
    }
162
    if(id < jv.size())
163
        SampleRotationRec(config,jv,id);
164
}*/
165
166
/*std::vector<double> initSo3()
167
{
168
    std::vector<double> res;
169
    int sign = -1;
170
    for (int i =0; i<6; ++i)
171
    {
172
        res.push_back(sign * std::numeric_limits<double>::infinity());
173
        sign *= -1;
174
    }
175
    //DevicePtr_t so3Robot = pinocchio::Device::create("so3Robot");
176
    //so3Robot->rootJoint(0);
177
    return res;
178
}*/
179
180
37
void seRotationtLimits(std::vector<double>& so3Robot,
181
                       const std::vector<double>& limitszyx) {
182
37
  assert(SIZE_EULER == limitszyx.size());
183
37
  so3Robot = limitszyx;
184
  /*pinocchio::Joint* previous = so3Robot->rootJoint();
185
  if(previous == 0)
186
  {
187
      // init joints
188
      previous = new pinocchio::jointRotation::Bounded(fcl::Transform3f());
189
      pinocchio::Joint * jy = new
190
  pinocchio::jointRotation::Bounded(fcl::Transform3f()); pinocchio::Joint * jx =
191
  new pinocchio::jointRotation::Bounded(fcl::Transform3f());
192
      so3Robot->rootJoint(previous);
193
      previous->addChildJoint (jy);
194
      jy->addChildJoint (jx);
195
      previous->name("so3z");
196
      jy->name("so3y");
197
      jx->name("so3x");
198
  }
199
  // set limits pinocchio::JointPtr_t
200
  assert(limitszyx.size() == 6);
201
  int i = 0;
202
  pinocchio::JointPtr_t current = previous;
203
  for(std::vector<double>::const_iterator cit = limitszyx.begin();
204
      cit != limitszyx.end(); ++cit, ++i)
205
  {
206
      if(i % 2 == 0)
207
      {
208
          current->lowerBound(0, *cit);
209
      }
210
      else
211
      {
212
          current->upperBound(0, *cit);
213
          current = current->numberChildJoints() > 0 ? current->childJoint(0) :
214
  0;
215
      }
216
  }*/
217
37
}
218
219
}  // namespace
220
221
namespace rbprm {
222
223
37
RbPrmShooterPtr_t RbPrmShooter::create(
224
    const pinocchio::RbPrmDevicePtr_t& robot,
225
    const ObjectStdVector_t& geometries, const affMap_t& affordances,
226
    const std::vector<std::string>& filter,
227
    const std::map<std::string, std::vector<std::string> >& affFilters,
228
    const std::size_t shootLimit, const std::size_t displacementLimit) {
229
37
  unsigned int seed = (unsigned int)(time(NULL));
230
37
  srand(seed);
231
  hppDout(notice, "&&&&&& SEED = " << seed);
232
  RbPrmShooter* ptr =
233
      new RbPrmShooter(robot, geometries, affordances, filter, affFilters,
234
37
                       shootLimit, displacementLimit);
235
236
37
  RbPrmShooterPtr_t shPtr(ptr);
237
37
  ptr->init(shPtr);
238
37
  return shPtr;
239
}
240
241
37
void RbPrmShooter::init(const RbPrmShooterPtr_t& self) {
242
37
  ConfigurationShooter::init(self);
243
37
  weak_ = self;
244
37
}
245
246
37
void RbPrmShooter::BoundSO3(const std::vector<double>& limitszyx) {
247
37
  seRotationtLimits(eulerSo3_, limitszyx);
248
37
}
249
250
/**
251
 * @brief getUsedSurfaces produce a list of CollisionObject from the affordances
252
 * list : use all objects corresponding to at least one affordance filter set.
253
 * @param affordances
254
 * @param affFilters
255
 * @return
256
 */
257
37
hpp::core::ObjectStdVector_t getUsedSurfaces(
258
    const affMap_t& affordances,
259
    const std::map<std::string, std::vector<std::string> >& affFilters) {
260
37
  core::ObjectStdVector_t surfaces;
261
74
  std::set<std::string> addedTypes;
262
  hppDout(notice, "Begin getUsedSurfaces from affordances");
263
82
  for (std::map<std::string, std::vector<std::string> >::const_iterator
264
37
           itFilter = affFilters.begin();
265
201
       itFilter != affFilters.end(); ++itFilter) {  // for each roms
266
    hppDout(notice, "For rom : " << itFilter->first);
267
82
    for (std::vector<std::string>::const_iterator itType =
268
82
             itFilter->second.begin();
269
246
         itType != itFilter->second.end(); ++itType) {
270
      hppDout(notice, "aff type : " << *itType);
271

127
      if (addedTypes.empty() ||
272

127
          (addedTypes.find(*itType) == addedTypes.end())) {
273
        hppDout(notice,
274
                "new type of affordance, add corresponding collision objects "
275
                "to the list");
276
37
        addedTypes.insert(*itType);
277

37
        if (affordances.map.find(*itType) != affordances.map.end()) {
278
37
          affMap_t::const_iterator itAff = affordances.map.find(*itType);
279
87
          for (AffordanceObjects_t::const_iterator itObj =
280
37
                   itAff->second.begin();
281
211
               itObj != itAff->second.end(); ++itObj) {
282
87
            surfaces.push_back(itObj->second);
283
          }
284
        }
285
      }
286
    }
287
  }
288
  hppDout(notice, "final size of surfaces list : " << surfaces.size());
289
74
  return surfaces;
290
}
291
292
// TODO: outward
293
294
37
RbPrmShooter::RbPrmShooter(
295
    const pinocchio::RbPrmDevicePtr_t& robot,
296
    const hpp::core::ObjectStdVector_t& geometries, const affMap_t& affordances,
297
    const std::vector<std::string>& filter,
298
    const std::map<std::string, std::vector<std::string> >& affFilters,
299
37
    const std::size_t shootLimit, const std::size_t displacementLimit)
300
    : shootLimit_(shootLimit),
301
      displacementLimit_(displacementLimit),
302
      filter_(filter),
303
      weights_(),
304
      triangles_(),
305
      robot_(robot),
306
37
      validator_(rbprm::RbPrmValidation::create(robot_, filter, affFilters,
307
                                                affordances, geometries)),
308
      uniformShooter_(core::configurationShooter::Uniform::create(robot)),
309

37
      ratioWeighted_(0.3) {
310
469
  for (hpp::core::ObjectStdVector_t::const_iterator cit = geometries.begin();
311
469
       cit != geometries.end(); ++cit) {
312
432
    validator_->addObstacle(*cit);
313
  }
314

37
  this->InitWeightedTriangles(getUsedSurfaces(affordances, affFilters));
315
37
}
316
317
37
void RbPrmShooter::InitWeightedTriangles(
318
    const core::ObjectStdVector_t& geometries) {
319
37
  double sum = 0;
320
124
  for (core::ObjectStdVector_t::const_iterator objit = geometries.begin();
321
211
       objit != geometries.end(); ++objit) {
322
87
    const pinocchio::FclConstCollisionObjectPtr_t colObj = (*objit)->fcl();
323
174
    BVHModelOBConst_Ptr_t model = GetModel(colObj);  // TODO NOT TRIANGLES
324
261
    for (int i = 0; i < model->num_tris; ++i) {
325
174
      TrianglePoints tri;
326
174
      Triangle fcltri = model->tri_indices[i];
327

174
      tri.p1 = colObj->getRotation() * model->vertices[fcltri[0]] +
328
348
               colObj->getTranslation();
329

174
      tri.p2 = colObj->getRotation() * model->vertices[fcltri[1]] +
330
348
               colObj->getTranslation();
331

174
      tri.p3 = colObj->getRotation() * model->vertices[fcltri[2]] +
332
348
               colObj->getTranslation();
333
174
      double weight = TriangleArea(tri);
334
      hppDout(notice, "Area of triangle = " << weight);
335
174
      sum += weight;
336
174
      weights_.push_back(weight);
337

174
      fcl::Vec3f normal = (tri.p2 - tri.p1).cross(tri.p3 - tri.p1);
338
174
      normal.normalize();
339

174
      triangles_.push_back(std::make_pair(normal, tri));
340
    }
341
  }
342
37
  double previousWeight = 0;
343
  hppDout(notice, "Sum of all areas of triangles : " << sum);
344
211
  for (std::vector<double>::iterator wit = weights_.begin();
345
211
       wit != weights_.end(); ++wit) {
346
174
    previousWeight += (*wit) / sum;
347
174
    (*wit) = previousWeight;
348
    hppDout(notice, "current weight = " << previousWeight);
349
  }
350
  hppDout(notice, "number of triangle for the shooter : " << triangles_.size());
351
37
}
352
353
697
const RbPrmShooter::T_TriangleNormal& RbPrmShooter::RandomPointIntriangle()
354
    const {
355
697
  return triangles_[rand() % triangles_.size()];
356
}
357
358
297
const RbPrmShooter::T_TriangleNormal& RbPrmShooter::WeightedTriangle() const {
359
297
  double r = ((double)rand() / (RAND_MAX));
360
297
  std::vector<T_TriangleNormal>::const_iterator trit = triangles_.begin();
361
1770
  for (std::vector<double>::const_iterator wit = weights_.begin();
362
3243
       wit != weights_.end(); ++wit, ++trit) {
363
1770
    if (*wit >= r) return *trit;
364
  }
365
  return triangles_[triangles_.size() - 1];  // not supposed to happen
366
}
367
368
1132
void RbPrmShooter::randConfigAtPos(const pinocchio::RbPrmDevicePtr_t robot,
369
                                   const std::vector<double>& eulerSo3,
370
                                   Configuration_t& config,
371
                                   const Vec3f p) const {
372
1132
  uniformShooter_->shoot(config);
373
1132
  SetConfigTranslation(robot, config, p);
374
1132
  SampleRotation(eulerSo3, config);
375
1132
}
376
377
47139
fcl::Vec3f normalFromTriangleContact(
378
    const Contact& c, hpp::core::CollisionObjectConstPtr_t colObj) {
379
47139
  int i = c.b2;
380
47139
  TrianglePoints tri;
381

94278
  BVHModelOBConst_Ptr_t model = GetModel(colObj->fcl());  // TODO NOT TRIANGLES
382
47139
  fcl::Vec3f normal;
383
47139
  if (model) {
384
47139
    Triangle fcltri = model->tri_indices[i];
385

47139
    tri.p1 = colObj->fcl()->getRotation() * model->vertices[fcltri[0]] +
386

94278
             colObj->fcl()->getTranslation();
387

47139
    tri.p2 = colObj->fcl()->getRotation() * model->vertices[fcltri[1]] +
388

94278
             colObj->fcl()->getTranslation();
389

47139
    tri.p3 = colObj->fcl()->getRotation() * model->vertices[fcltri[2]] +
390

94278
             colObj->fcl()->getTranslation();
391

47139
    normal = (tri.p2 - tri.p1).cross(tri.p3 - tri.p1);
392
  } else {
393
    hppDout(warning,
394
            "In shooter : cannot get contact normal, use z axis by default.");
395
    normal = fcl::Vec3f(0, 0, 1);
396
  }
397
94278
  return normal.normalized();
398
}
399
400
431
void RbPrmShooter::impl_shoot(hpp::core::Configuration_t& config) const {
401
  hppDout(notice, "!!! Random shoot");
402
  HPP_DEFINE_TIMECOUNTER(SHOOT_COLLISION);
403
431
  uniformShooter_->shoot(config);
404
431
  std::size_t limit = shootLimit_;
405
431
  bool found(false);
406

1425
  while (limit > 0 && !found) {
407
    // pick one triangle randomly
408
994
    const T_TriangleNormal* sampled(0);
409
994
    double r = ((double)rand() / (RAND_MAX));
410
994
    if (r > ratioWeighted_)
411
697
      sampled = &RandomPointIntriangle();
412
    else
413
297
      sampled = &WeightedTriangle();
414
994
    const TrianglePoints& tri = sampled->second;
415
    // http://stackoverflow.com/questions/4778147/sample-random-point-in-triangle
416
    double r1, r2;
417
994
    r1 = ((double)rand() / (RAND_MAX));
418
994
    r2 = ((double)rand() / (RAND_MAX));
419


994
    Vec3f p = (1 - sqrt(r1)) * tri.p1 + (sqrt(r1) * (1 - r2)) * tri.p2 +
420

1988
              (sqrt(r1) * r2) * tri.p3;
421
994
    const Vec3f& n = sampled->first;
422
423
    // set configuration position to sampled point
424

994
    randConfigAtPos(robot_, eulerSo3_, config, p);
425
    // rotate and translate randomly until valid configuration found or
426
    // no obstacle is reachable
427

994
    ValidationReportPtr_t reportShPtr(new RbprmValidationReport);
428
994
    std::size_t limitDis = displacementLimit_;
429
994
    Vec3f lastDirection = n;
430

48564
    while (!found && limitDis > 0) {
431
      HPP_START_TIMECOUNTER(SHOOT_COLLISION);
432
47671
      found = validator_->validate(config, reportShPtr, filter_);
433
      RbprmValidationReportPtr_t report =
434
47671
          dynamic_pointer_cast<RbprmValidationReport>(reportShPtr);
435

47671
      bool valid = found || !report->trunkInCollision;
436
      HPP_STOP_TIMECOUNTER(SHOOT_COLLISION);
437
438
47671
      if (valid & !found) {
439
        // try to rotate to reach rom
440

253
        for (; limitDis > 0 && !found && valid; --limitDis) {
441
          // SampleRotation(eulerSo3_, config);
442

138
          randConfigAtPos(robot_, eulerSo3_, config, p);
443
          HPP_START_TIMECOUNTER(SHOOT_COLLISION);
444
138
          found = validator_->validate(config, reportShPtr, filter_);
445
          HPP_STOP_TIMECOUNTER(SHOOT_COLLISION);
446
138
          if (!found) {
447

124
            Translate(robot_, config,
448

248
                      -lastDirection * 0.2 * ((double)rand() / (RAND_MAX)));
449
          }
450
          HPP_START_TIMECOUNTER(SHOOT_COLLISION);
451
138
          found = validator_->validate(config, reportShPtr, filter_);
452
138
          report = dynamic_pointer_cast<RbprmValidationReport>(reportShPtr);
453

138
          valid = found || !report->trunkInCollision;
454
          // found = validator_->validate(config, filter_);
455
          HPP_STOP_TIMECOUNTER(SHOOT_COLLISION);
456
        }
457
115
        if (!found) break;
458
47556
      } else if (!valid)  // move out of collision
459
      {
460
        // retrieve Contact information
461
47139
        report = dynamic_pointer_cast<RbprmValidationReport>(reportShPtr);
462

94278
        lastDirection = normalFromTriangleContact(report->result.getContact(0),
463
94278
                                                  report->object2);
464

94278
        Translate(
465
47139
            robot_, config,
466
            lastDirection *
467

47139
                (std::abs(report->result.getContact(0).penetration_depth) +
468
                 0.03));
469
47139
        limitDis--;
470
      }
471
    }
472
994
    limit--;
473
  }
474
431
  if (!found) std::cout << "no config found" << std::endl;
475
  hppDout(info, "shoot : " << pinocchio::displayConfig(config));
476
  HPP_DISPLAY_TIMECOUNTER(SHOOT_COLLISION);
477
431
}
478
479
void RbPrmShooter::sampleExtraDOF(bool sampleExtraDOF) {
480
  uniformShooter_->sampleExtraDOF(sampleExtraDOF);
481
}
482
483
3
HPP_START_PARAMETER_DECLARATION(RbprmShooter)
484


3
Problem::declareParameter(core::ParameterDescription(
485
    core::Parameter::FLOAT, "RbprmShooter/ratioWeighted",
486
    "The ratio used to select a random triangle with a weight proportional to "
487
    "it's area. "
488
    "Otherwise the triangles are choosed uniformly. ",
489
6
    core::Parameter(0.3)));
490
3
HPP_END_PARAMETER_DECLARATION(RbprmShooter)
491
492
}  // namespace rbprm
493
}  // namespace hpp