GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/contact_generation/reachability.cc Lines: 104 357 29.1 %
Date: 2024-02-02 12:21:48 Branches: 144 1056 13.6 %

Line Branch Exec Source
1
#include <fstream>
2
#include <hpp/bezier-com-traj/common_solve_methods.hh>
3
#include <hpp/bezier-com-traj/solve.hh>
4
#include <hpp/rbprm/contact_generation/reachability.hh>
5
#include <hpp/rbprm/rbprm-fullbody.hh>
6
#include <hpp/rbprm/stability/stability.hh>
7
#include <hpp/util/timer.hh>
8
#include <iostream>
9
10
#ifndef QHULL
11
#define QHULL 0
12
#endif
13
14
#ifndef STAT_TIMINGS
15
#define STAT_TIMINGS 0
16
#endif
17
18
#ifndef FULL_TIME_SAMPLING
19
#define FULL_TIME_SAMPLING 0
20
#endif
21
22
namespace hpp {
23
namespace rbprm {
24
namespace reachability {
25
26
using centroidal_dynamics::Matrix3;
27
using centroidal_dynamics::Vector3;
28
29
// helper method used to print vectors of string
30
std::ostream& operator<<(std::ostream& os,
31
                         const std::vector<std::string>& vect) {
32
  for (std::vector<std::string>::const_iterator sit = vect.begin();
33
       sit != vect.end(); ++sit) {
34
    os << *sit << " ; ";
35
  }
36
  return os;
37
}
38
39
void printQHullFile(const std::pair<MatrixXX, VectorX>& Ab, fcl::Vec3f intPoint,
40
                    const std::string& fileName, bool clipZ = false) {
41
  std::ofstream file;
42
  using std::endl;
43
  std::string path("/local/fernbach/qhull/constraints_obj/");
44
  path.append(fileName);
45
  hppDout(notice, "print to file : " << path);
46
  file.open(path.c_str(), std::ios::out | std::ios::trunc);
47
  file << "3 1" << endl;
48
  file << "\t " << intPoint[0] << "\t" << intPoint[1] << "\t" << intPoint[2]
49
       << endl;
50
  file << "4" << endl;
51
  clipZ ? file << Ab.first.rows() + 2 << endl : file << Ab.first.rows() << endl;
52
  for (size_type i = 0; i < Ab.first.rows(); ++i) {
53
    file << "\t" << Ab.first(i, 0) << "\t" << Ab.first(i, 1) << "\t"
54
         << Ab.first(i, 2) << "\t" << -Ab.second[i] - 0.005 << endl;
55
  }
56
  if (clipZ) {
57
    file << "\t" << 0 << "\t" << 0 << "\t" << 1. << "\t" << -3. << endl;
58
    file << "\t" << 0 << "\t" << 0 << "\t" << -1. << "\t" << -1. << endl;
59
  }
60
  file.close();
61
}
62
63
// Method to print (in hppDout) the inequalities express as halfspace, in a
64
// format readable by qHull use qHalf FP | qConvex Ft      to use them
65
void printQHull(const std::pair<MatrixXX, VectorX>& Ab,
66
                fcl::Vec3f intPoint = fcl::Vec3f::Zero(),
67
                const std::string& fileName = std::string(),
68
                bool clipZ = false) {
69
  using std::endl;
70
  std::stringstream ss;
71
  ss << "qHull Output : use qhalf FP | qconvex Ft " << endl;
72
  ss << "3 1" << endl;
73
  ss << "\t " << intPoint[0] << "\t" << intPoint[1] << "\t" << intPoint[2]
74
     << endl;
75
  ss << "4" << endl;
76
  for (size_type i = 0; i < Ab.first.rows(); ++i) {
77
    ss << "\t" << Ab.first(i, 0) << "\t" << Ab.first(i, 1) << "\t"
78
       << Ab.first(i, 2) << "\t" << -Ab.second[i] - 0.001 << endl;
79
  }
80
  hppDout(notice, ss.str());
81
  if (!fileName.empty()) printQHullFile(Ab, intPoint, fileName, clipZ);
82
}
83
84
48
std::pair<MatrixXX, VectorX> stackConstraints(
85
    const std::pair<MatrixXX, VectorX>& Ab,
86
    const std::pair<MatrixXX, VectorX>& Cd) {
87

48
  size_t numIneq = Ab.first.rows() + Cd.first.rows();
88
96
  MatrixXX M(numIneq, 3);
89
96
  VectorX n(numIneq);
90

48
  M.block(0, 0, Ab.first.rows(), 3) = Ab.first;
91


48
  M.block(Ab.first.rows(), 0, Cd.first.rows(), 3) = Cd.first;
92

48
  n.segment(0, Ab.first.rows()) = Ab.second;
93


48
  n.segment(Ab.first.rows(), Cd.first.rows()) = Cd.second;
94
96
  return std::make_pair(M, n);
95
}
96
97
/**
98
 * @brief computeDistanceCost cost that minimize || x - c ||
99
 * @param c0 target point (the point that we want to be the closest from)
100
 * @return the matrices H and g that express the cost
101
 */
102
16
std::pair<MatrixXX, VectorX> computeDistanceCost(const fcl::Vec3f& c0) {
103

32
  MatrixXX H = Matrix3::Identity();
104

32
  VectorX g = -c0;
105
32
  return std::make_pair(H, g);
106
}
107
108
16
bool intersectionExist(const std::pair<MatrixXX, VectorX>& Ab,
109
                       const fcl::Vec3f& c, fcl::Vec3f& c_out) {
110
  hppDout(notice, "Call solveur solveIntersection");
111
  hppDout(notice, "init = " << c);
112
  bezier_com_traj::ResultData res =
113

32
      bezier_com_traj::solve(Ab, computeDistanceCost(c), c);
114
16
  c_out = res.x;
115
  hppDout(notice, "success Solveur solveIntersection = " << res.success_);
116
  hppDout(notice,
117
          "x = [" << c_out[0] << "," << c_out[1] << "," << c_out[2] << "]");
118
32
  return res.success_;
119
}
120
121
32
std::pair<MatrixXX, VectorX> computeStabilityConstraints(
122
    const centroidal_dynamics::Equilibrium& contactPhase,
123
    const fcl::Vec3f& int_point, const fcl::Vec3f& acc) {
124
  // gravity vector
125
  hppDout(notice, "Compute stability constraints");
126
  hppDout(notice, "With acceleration = " << acc);
127
32
  const Vector3& g = contactPhase.m_gravity;
128

32
  const Matrix3 gSkew = bezier_com_traj::skew(g);
129

32
  const Matrix3 accSkew = bezier_com_traj::skew(acc);
130
  // compute GIWC
131
64
  centroidal_dynamics::MatrixXX Hrow;
132
64
  VectorX h;
133
  centroidal_dynamics::LP_status status =
134
32
      contactPhase.getPolytopeInequalities(Hrow, h);
135
32
  if (status != centroidal_dynamics::LP_STATUS_OPTIMAL) {
136
    hppDout(warning, "getPolytopeInequalities failed, lp status : " + status);
137
    MatrixXX A(MatrixXX::Zero(0, 0));
138
    VectorX b(VectorX::Zero(0));
139
    return std::make_pair(A, b);
140
  }
141
  // hppDout(notice,"Hrow : \n"<<Hrow);
142

64
  MatrixXX H = -Hrow;
143

32
  H.rowwise().normalize();
144
32
  int dimH = (int)(H.rows());
145
  hppDout(notice, "Dim H rows : " << dimH << " ; col : " << H.cols());
146
  // hppDout(notice,"H : \n"<<H);
147

64
  MatrixXX mH = contactPhase.m_mass * H;
148
  // constraints : mH[:,3:6] g^  x <= h + mH[:,0:3]g
149
  // A = mH g^
150
  // b = h + mHg
151
152


64
  MatrixXX A(mH.block(0, 3, dimH, 3) * (gSkew - accSkew));
153


64
  VectorX b(h + mH.block(0, 0, dimH, 3) * (g - acc));
154
  /* hppDout(notice,"Stability constraints matrices : ");
155
   hppDout(notice,"Interior point : \n"<<int_point);
156
   hppDout(notice,"A = \n"<<A);
157
   hppDout(notice,"b = \n"<<b);*/
158
#if QHULL
159
  hppDout(notice, "Stability constraints qHull : ");
160
  printQHull(std::make_pair(A, b), int_point);
161
#else
162
  (void)int_point;  // silent the unused parameter warning in case QHULL 0
163
#endif
164
32
  return std::make_pair(A, b);
165
}
166
167
32
centroidal_dynamics::Equilibrium computeContactConeForState(
168
    const RbPrmFullBodyPtr_t& fullbody, State& state, bool& success) {
169
  hppStartBenchmark(REACHABLE_CALL_CENTROIDAL);
170
  centroidal_dynamics::Equilibrium contactCone(
171
32
      fullbody->device_->name(), fullbody->device_->mass(), 4,
172
32
      centroidal_dynamics::SOLVER_LP_QPOASES, true, 1000, false);
173
32
  centroidal_dynamics::EquilibriumAlgorithm alg =
174
      centroidal_dynamics::EQUILIBRIUM_ALGORITHM_PP;
175
  try {
176
32
    stability::setupLibrary(
177
        fullbody, state, contactCone, alg, fullbody->getFriction(), 0.05,
178
        0.05);  // 0.01 : 'safe' support zone, under the flexibility
179
32
    success = true;
180
  } catch (std::runtime_error e) {
181
    hppDout(notice, "Error in setupLibrary : " << e.what());
182
    success = false;
183
  }
184
  hppStopBenchmark(REACHABLE_CALL_CENTROIDAL);
185
  hppDisplayBenchmark(REACHABLE_CALL_CENTROIDAL);
186
64
  return contactCone;
187
}
188
189
32
std::pair<MatrixXX, VectorX> computeStabilityConstraintsForState(
190
    const RbPrmFullBodyPtr_t& fullbody, State& state, bool& success,
191
    const fcl::Vec3f& acc) {
192
  hppDout(notice, "contact order : ");
193
  hppDout(notice, "  " << state.contactOrder_.front());
194
  centroidal_dynamics::Equilibrium cone(
195
64
      computeContactConeForState(fullbody, state, success));
196
32
  std::pair<MatrixXX, VectorX> Ab;
197
32
  if (success) {
198
32
    Ab = computeStabilityConstraints(
199

32
        cone, state.contactPositions_.at(state.contactOrder_.front()), acc);
200


32
    if (Ab.first.cols() == 0 || Ab.first.rows() == 0) success = false;
201
  } else {
202
    MatrixXX A(MatrixXX::Zero(0, 0));
203
    VectorX b(VectorX::Zero(0));
204
    Ab = std::make_pair(A, b);
205
  }
206
64
  return Ab;
207
}
208
209
32
std::pair<MatrixXX, VectorX> computeConstraintsForState(
210
    const RbPrmFullBodyPtr_t& fullbody, State& state, bool& success,
211
    const fcl::Vec3f& acc) {
212
  std::pair<MatrixXX, VectorX> Ab =
213
64
      computeStabilityConstraintsForState(fullbody, state, success, acc);
214
32
  if (success)
215
    return stackConstraints(
216

32
        computeKinematicsConstraintsForState(fullbody, state), Ab);
217
  else
218
    return Ab;
219
}
220
221
Result isReachableIntermediate(const RbPrmFullBodyPtr_t& fullbody,
222
                               State& previous, State& intermediate,
223
                               State& next) {
224
  // TODO
225
  hppDout(notice, "isReachableIntermadiate :");
226
  std::vector<std::string> contactsNames = next.contactVariations(previous);
227
  hppDout(notice, "Contact variations : " << contactsNames);
228
  Result resBreak, resCreate;
229
  Result res;
230
  std::pair<MatrixXX, VectorX> Ab, Cd;
231
  resBreak = isReachable(fullbody, previous, intermediate);
232
  resCreate = isReachable(fullbody, intermediate, next);
233
  hppDout(notice, "isReachableIntermediate : ");
234
  hppDout(notice, "resBreak status    : " << resBreak.status);
235
  hppDout(notice, "resCreation status : " << resCreate.status);
236
#if QHULL
237
  hppDout(notice, "constraint for contact break : ");
238
  printQHull(resBreak.constraints_, resBreak.x, "constraints_break.txt");
239
  hppDout(notice, "constraint for contact creation : ");
240
  printQHull(resCreate.constraints_, resCreate.x, "constraints_create.txt");
241
#endif
242
  if (resBreak.success() && resCreate.success()) {
243
    res.status = REACHABLE;
244
    res.x = (resBreak.x + resCreate.x) / 2.;
245
    res.xBreak_ = resBreak.x;
246
    res.xCreate_ = resCreate.x;
247
    hppDout(notice, "reachable intermediate success, x = " << res.x);
248
// only for test, it take time to compute :
249
#if QHULL
250
    hppDout(notice, "constraint for intersection : ");
251
    printQHull(stackConstraints(resBreak.constraints_, resCreate.constraints_),
252
               res.x, "constraints.txt");
253
#endif
254
  } else if (resBreak.status == UNREACHABLE &&
255
             resCreate.status == UNREACHABLE) {
256
    res.status = UNREACHABLE;
257
  } else if (!resBreak.success()) {
258
    res.status = CONTACT_BREAK_FAILED;
259
  } else if (!resCreate.success()) {
260
    res.status = CONTACT_CREATION_FAILED;
261
  }
262
  return res;
263
}
264
265
16
Result isReachable(const RbPrmFullBodyPtr_t& fullbody, State& previous,
266
                   State& next, const fcl::Vec3f& acc,
267
                   bool useIntermediateState) {
268
  hppStartBenchmark(IS_REACHABLE);
269
16
  assert(previous.nbContacts > 0 &&
270
         "Reachability : previous state have less than 1 contact.");
271
16
  assert(next.nbContacts > 0 &&
272
         "Reachability : next state have less than 1 contact.");
273
32
  std::vector<std::string> contactsCreation, contactsBreak;
274
16
  next.contactBreaks(previous, contactsBreak);
275
16
  next.contactCreations(previous, contactsCreation);
276
  hppDout(notice, "IsReachable called : for configuration :  r(["
277
                      << pinocchio::displayConfig(previous.configuration_)
278
                      << "])");
279
  hppDout(notice, "contact for previous : " << previous.nbContacts);
280
  hppDout(notice, "contact for next     : " << next.nbContacts);
281
  hppDout(notice, "Contacts break : " << contactsBreak);
282
  hppDout(notice, "contacts creation : " << contactsCreation);
283
284
#if QHULL == 0
285

16
  if (contactsCreation.size() <= 0 && contactsBreak.size() <= 0) {
286
    hppDout(notice, "No contact variation, abort.");
287
    return Result(NO_CONTACT_VARIATION);
288
  }
289
#endif
290
291

16
  if (contactsCreation.size() > 1 || contactsBreak.size() > 1) {
292
    hppDout(notice, "Too many contact variation, abort.");
293
    return Result(TOO_MANY_CONTACTS_VARIATION);
294
  }
295
32
  State intermediate;
296

16
  if (contactsBreak.size() == 1 && contactsCreation.size() == 1) {
297

16
    if (next.contactVariations(previous).size() ==
298
        1) {  // there is 1 contact repositionning between previous and next
299
      // we need to create the intermediate state, and call is reachable for the
300
      // 3 states.
301

16
      intermediate = State(previous);
302
16
      intermediate.RemoveContact(contactsBreak[0]);
303
      hppDout(notice,
304
              "Contact repositionning between the 2 states, create "
305
              "intermediate state");
306
16
      if (useIntermediateState) {
307
        hppDout(notice, "call isReachableIntermediate.");
308
        return isReachableIntermediate(fullbody, previous, intermediate, next);
309
      }
310
    } else {
311
      hppDout(notice,
312
              "Contact break and creation are different. You need to call "
313
              "isReachable with 2 adjacent states");
314
      return Result(TOO_MANY_CONTACTS_VARIATION);
315
    }
316
  }
317
318
  bool success;
319
  bool successCone;
320
32
  Result res;
321


32
  std::pair<MatrixXX, VectorX> Ab, K_p, K_n, A_p, A_n;
322

16
  if (contactsBreak.size() == 1 && contactsCreation.size() == 1) {
323
16
    A_p = computeConstraintsForState(fullbody, previous, successCone, acc);
324
16
    if (!successCone) {
325
      hppDout(warning,
326
              "Unable to compute computeStabilityConstraintsForState.");
327
      return Result(UNABLE_TO_COMPUTE);
328
    }
329
16
    A_n = computeConstraintsForState(fullbody, next, successCone, acc);
330
16
    if (!successCone) {
331
      hppDout(warning,
332
              "Unable to compute computeStabilityConstraintsForState.");
333
      return Result(UNABLE_TO_COMPUTE);
334
    }
335
16
    Ab = stackConstraints(A_p, A_n);
336
  }
337
  // there is only one contact creation OR (exclusive) break between the two
338
  // states test C_p \inter C_n (ie : A_p \inter K_p \inter A_n \inter K_n),
339
  // with simplifications du to relations between the constraints :
340
  else if (contactsBreak.size() >
341
           0) {  // next have one less contact than previous
342
    hppDout(notice, "Contact break between previous and next state");
343
    // A_n \inside A_p, thus  A_p is redunbdant
344
    // K_p \inside K_n, thus  K_n is redunbdant
345
    // So, we only need to test A_n \inter K_p
346
    // TODO : K_p can be given as parameter to avoid re-computation
347
    // std::pair<MatrixXX,VectorX> A_n =
348
    // computeStabilityConstraintsForState(fullbody,next);
349
    // std::pair<MatrixXX,VectorX> K_p =
350
    // computeKinematicsConstraintsForState(fullbody,previous); Ab =
351
    // stackConstraints(A_n,K_p); develloped computation, needed to display the
352
    // differents constraints :
353
    hppStartBenchmark(REACHABLE_STABILITY);
354
    A_n = computeStabilityConstraintsForState(fullbody, next, successCone, acc);
355
    if (!successCone) {
356
      hppDout(warning,
357
              "Unable to compute computeStabilityConstraintsForState.");
358
      return Result(UNABLE_TO_COMPUTE);
359
    }
360
361
    hppStopBenchmark(REACHABLE_STABILITY);
362
    hppDisplayBenchmark(REACHABLE_STABILITY);
363
    hppStartBenchmark(REACHABLE_KINEMATIC);
364
    K_p = computeKinematicsConstraintsForState(fullbody, previous);
365
    hppStopBenchmark(REACHABLE_KINEMATIC);
366
    hppDisplayBenchmark(REACHABLE_KINEMATIC);
367
    hppStartBenchmark(REACHABLE_STACK);
368
    Ab = stackConstraints(A_n, K_p);
369
    hppStopBenchmark(REACHABLE_STACK);
370
    hppDisplayBenchmark(REACHABLE_STACK);
371
  } else {  // next have one more contact than previous
372
    hppDout(notice, "Contact creation between previous and next");
373
    // A_p \inside A_n, thus A_n is redunbdant
374
    // K_n \inside K_p ; and K_n = K_p \inter K_n^m (where K_n^m is the
375
    // kinematic constraint for the moving limb at state n) we use K_n^m,
376
    // because C_p can be given as parameter to avoid re-computation So, we only
377
    // need to test C_n \inter K_p_m std::pair<MatrixXX,VectorX> C_p   =
378
    // computeConstraintsForState(fullbody,previous);
379
    // std::pair<MatrixXX,VectorX> K_n_m =
380
    // computeKinematicsConstraintsForLimb(fullbody,previous,contactsCreation[0]);
381
    // // kinematic constraint only for the moving contact for state previous Ab
382
    // = stackConstraints(C_p,K_n_m);
383
    hppStartBenchmark(REACHABLE_STABILITY);
384
    A_p = computeStabilityConstraintsForState(fullbody, previous, successCone,
385
                                              acc);
386
    if (!successCone) {
387
      hppDout(warning,
388
              "Unable to compute computeStabilityConstraintsForState.");
389
      return Result(UNABLE_TO_COMPUTE);
390
    }
391
    hppStopBenchmark(REACHABLE_STABILITY);
392
    hppDisplayBenchmark(REACHABLE_STABILITY);
393
    // K_p = computeKinematicsConstraintsForState(fullbody,previous);
394
    hppStartBenchmark(REACHABLE_KINEMATIC);
395
    K_n = computeKinematicsConstraintsForState(fullbody, next);
396
    hppStopBenchmark(REACHABLE_KINEMATIC);
397
    hppDisplayBenchmark(REACHABLE_KINEMATIC);
398
    hppStartBenchmark(REACHABLE_STACK);
399
    Ab = stackConstraints(A_p, K_n);
400
    hppStopBenchmark(REACHABLE_STACK);
401
    hppDisplayBenchmark(REACHABLE_STACK);
402
  }
403
404
  // compute COM positions :
405
16
  pinocchio::Computation_t flag = fullbody->device_->computationFlag();
406
16
  pinocchio::Computation_t newflag = static_cast<pinocchio::Computation_t>(
407
      pinocchio::JOINT_POSITION | pinocchio::JACOBIAN | pinocchio::COM);
408
16
  fullbody->device_->controlComputation(newflag);
409

16
  fullbody->device_->currentConfiguration(previous.configuration_);
410
16
  fullbody->device_->computeForwardKinematics();
411

16
  fcl::Vec3f com_previous = fullbody->device_->positionCenterOfMass();
412

16
  fullbody->device_->currentConfiguration(next.configuration_);
413
16
  fullbody->device_->computeForwardKinematics();
414

16
  fcl::Vec3f com_next = fullbody->device_->positionCenterOfMass();
415
16
  fullbody->device_->controlComputation(flag);
416
417
  // compute the position in the middle of the most constrained support polygon
418
  // (used for the cost function):
419
32
  State smaller_state;
420

16
  if (contactsBreak.size() == 1 && contactsCreation.size() == 1) {
421
16
    smaller_state = intermediate;
422
  } else if (contactsBreak.size() >
423
             0) {  // next have the smaller support polygone
424
    smaller_state = next;
425
  } else {  // previous have the smaller support polygon
426
    smaller_state = previous;
427
  }
428

16
  fcl::Vec3f c_robust = fcl::Vec3f::Zero();
429
16
  for (std::map<std::string, fcl::Vec3f>::const_iterator cit =
430
16
           smaller_state.contactPositions_.begin();
431
48
       cit != smaller_state.contactPositions_.end(); ++cit) {
432
16
    fcl::Transform3f jointT(smaller_state.contactRotation_.at(cit->first),
433
32
                            cit->second);
434
    fcl::Vec3f position =
435

16
        jointT.transform(fullbody->GetLimb(cit->first)->offset_);
436
16
    c_robust += position;
437
  }
438
16
  c_robust /= (fcl::FCL_REAL)smaller_state.contactPositions_.size();
439

16
  c_robust[2] = (com_previous[2] + com_next[2]) / 2.;
440
441
16
  fcl::Vec3f x;
442
  hppStartBenchmark(QP_REACHABLE);
443
16
  success = intersectionExist(Ab, c_robust, x);
444
  hppStopBenchmark(QP_REACHABLE);
445
  hppDisplayBenchmark(QP_REACHABLE);
446
16
  if (success) {
447
13
    res.status = REACHABLE;
448
    hppDout(notice, "REACHABLE");
449
  } else {
450
3
    res.status = UNREACHABLE;
451
    hppDout(notice, "UNREACHABLE");
452
  }
453
16
  res.x = x;
454
16
  res.constraints_ = Ab;
455
  hppStopBenchmark(IS_REACHABLE);
456
  hppDisplayBenchmark(IS_REACHABLE);
457
458
// compute interior point to display with qHull (only required for display and
459
// debug)
460
#if QHULL
461
  Vector3 int_pt_kin;
462
  fcl::Vec3f int_pt_stab;
463
  if (res.success()) {
464
    int_pt_stab = x;
465
    int_pt_kin = x;
466
  } else {
467
    if (contactsBreak.size() > 0) {
468
      int_pt_kin = com_previous;
469
      intersectionExist(A_n, (com_previous + com_next) / 2., int_pt_stab);
470
    } else {
471
      int_pt_kin = com_next;
472
      intersectionExist(A_p, (com_previous + com_next) / 2., int_pt_stab);
473
    }
474
  }
475
476
  if (contactsBreak.size() > 0) {
477
    hppDout(notice, "Stability constraint for state i+1 :");
478
    printQHull(A_n, int_pt_stab, "stability.txt", true);
479
    hppDout(notice, "Kinematics constraint for state i :");
480
    printQHull(K_p, int_pt_kin, "kinematics.txt");
481
  } else {
482
    hppDout(notice, "Stability constraint for state i :");
483
    printQHull(A_p, int_pt_stab, "stability.txt", true);
484
    hppDout(notice, "Kinematics constraint for state i+1 :");
485
    printQHull(K_n, int_pt_kin, "kinematics.txt");
486
  }
487
  hppDout(notice, "Intersection of constraints :");
488
  printQHull(Ab, int_pt_stab, "constraints.txt");
489
490
  if (contactsCreation.size() <= 0 && contactsBreak.size() <= 0) {
491
    hppDout(notice, "No contact variation, abort.");
492
    return Result(NO_CONTACT_VARIATION);
493
  }
494
#endif
495
496
16
  return res;
497
}
498
499
void printTimingFile(std::ofstream& file, const VectorX& timings, bool success,
500
                     bool quasiStaticSuccess) {
501
  using std::endl;
502
  file << timings[0] << " " << timings[1] << " " << timings[2] << " "
503
       << (success ? "1 " : "0 ") << (quasiStaticSuccess ? "1" : "0") << endl;
504
}
505
506
Result isReachableDynamic(const RbPrmFullBodyPtr_t& fullbody, State& previous,
507
                          State& next, bool tryQuasiStatic,
508
                          std::vector<double> timings, int numPointsPerPhases) {
509
  Result res;
510
  std::vector<std::string> contactsCreation, contactsBreak;
511
  next.contactBreaks(previous, contactsBreak);
512
  next.contactCreations(previous, contactsCreation);
513
  hppDout(notice, "IsReachableDynamic called : ");
514
  hppDout(notice, "Between configuration : "
515
                      << pinocchio::displayConfig(previous.configuration_));
516
  hppDout(notice, "and     configuration : "
517
                      << pinocchio::displayConfig(next.configuration_));
518
  assert(
519
      fullbody->device_->extraConfigSpace().dimension() >= 6 &&
520
      "You need to set at least 6 extraDOF to use the reachability methods.");
521
  if (previous.configuration_.head<3>() == next.configuration_.head<3>()) {
522
    hppDout(notice, "Same root position, unable to compute");
523
    return Result(SAME_ROOT_POSITION);
524
  }
525
  hppDout(notice, "Contacts break : " << contactsBreak);
526
  hppDout(notice, "contacts creation : " << contactsCreation);
527
  if (contactsCreation.size() <= 0 && contactsBreak.size() <= 0) {
528
    hppDout(notice, "No contact variation, abort.");
529
    return Result(NO_CONTACT_VARIATION);
530
  }
531
  if ((contactsBreak.size() + contactsCreation.size()) > 2 ||
532
      contactsBreak.size() > 1 || contactsCreation.size() > 1) {
533
    hppDout(notice, "More than 2 contact change");
534
    return Result(TOO_MANY_CONTACTS_VARIATION);
535
  }
536
  if (contactsBreak.size() == 0) {
537
    hppDout(notice,
538
            "Only contact creation. State Next have more contact than state "
539
            "Previous");
540
    if (previous.nbContacts <= 1) {
541
      hppDout(
542
          notice,
543
          "Previous is in single support. Unable to compute");  // FIXME : maybe
544
                                                                // compute in
545
                                                                // quasiStatic
546
      return Result(UNABLE_TO_COMPUTE);
547
    }
548
  }
549
  if (contactsCreation.size() == 0) {
550
    hppDout(
551
        notice,
552
        "Only contact break. State Next have less contact than state Previous");
553
    if (next.nbContacts <= 1) {
554
      hppDout(notice,
555
              "Next is in single support. Unable to compute");  // FIXME : maybe
556
                                                                // compute in
557
                                                                // quasiStatic
558
      return Result(UNABLE_TO_COMPUTE);
559
    }
560
  }
561
562
  // build intermediate state :
563
  State mid(previous);  // build intermediate state
564
  if (contactsBreak.size() > 0) {
565
    mid.RemoveContact(contactsBreak[0]);
566
  }
567
568
#if STAT_TIMINGS
569
  // outputs timings results in file :
570
  std::ofstream file;
571
  std::string path("/local/fernbac/bench_iros18/bench/timing_hrp2_darpa.log");
572
  hppDout(notice, "print to file : " << path);
573
  file.open(path.c_str(), std::ios_base::app);
574
  file << "# new pair of states" << std::endl;
575
#endif
576
  bool quasiStaticSucces(false);
577
578
  // compute COM positions :
579
  pinocchio::Computation_t flag = fullbody->device_->computationFlag();
580
  pinocchio::Computation_t newflag = static_cast<pinocchio::Computation_t>(
581
      pinocchio::JOINT_POSITION | pinocchio::JACOBIAN | pinocchio::COM);
582
  fullbody->device_->controlComputation(newflag);
583
  fullbody->device_->currentConfiguration(previous.configuration_);
584
  fullbody->device_->computeForwardKinematics();
585
  fcl::Vec3f com_previous = fullbody->device_->positionCenterOfMass();
586
  fullbody->device_->currentConfiguration(next.configuration_);
587
  fullbody->device_->computeForwardKinematics();
588
  fcl::Vec3f com_next = fullbody->device_->positionCenterOfMass();
589
  fullbody->device_->controlComputation(flag);
590
591
  if (tryQuasiStatic) {
592
    hppDout(notice, "Try quasi-static reachability : ");
593
    Result quasiStaticResult =
594
        isReachableIntermediate(fullbody, previous, mid, next);
595
    if (quasiStaticResult.success()) {
596
      hppDout(notice, "REACHABLE in quasi-static");
597
      quasiStaticResult.status = QUASI_STATIC;
598
      // build a Bezier curve of order 2 :
599
      std::vector<Vector3> wps;
600
      wps.push_back(com_previous);
601
      wps.push_back(quasiStaticResult.x);
602
      wps.push_back(com_next);
603
      bezier_Ptr bezierCurve = bezier_Ptr(new bezier_t(wps.begin(), wps.end()));
604
      quasiStaticResult.path_ = BezierPath::create(
605
          fullbody->device_, bezierCurve, previous.configuration_,
606
          next.configuration_, core::interval_t(0., 1));
607
      quasiStaticSucces = true;
608
#if !STAT_TIMINGS
609
      return quasiStaticResult;
610
#endif
611
    } else {
612
      hppDout(notice, "UNREACHABLE in quasi-static");
613
      // return Result(REACHABLE); // testing
614
    }
615
  }
616
617
  hppStartBenchmark(IS_REACHABLE_DYNAMIC);
618
  hppStartBenchmark(COMPUTE_DOUBLE_DESCRIPTION);
619
620
  // build ProblemData from states object and call solveOneStep()
621
  bezier_com_traj::ProblemData pData;
622
  bool successCone(true), successConeCurrent;
623
  // build contactPhases :
624
  bezier_com_traj::ContactData previousData, nextData, midData;
625
  std::pair<MatrixX3, VectorX> Ab =
626
      computeKinematicsConstraintsForState(fullbody, previous);
627
  previousData.Kin_ = Ab.first;
628
  previousData.kin_ = Ab.second;
629
  centroidal_dynamics::Equilibrium conePrevious =
630
      computeContactConeForState(fullbody, previous, successConeCurrent);
631
  successCone = successCone && successConeCurrent;
632
  previousData.contactPhase_ = &conePrevious;
633
  if (contactsBreak.size() > 0) {
634
    Ab = computeKinematicsConstraintsForState(fullbody, mid);
635
    midData.Kin_ = Ab.first;
636
    midData.kin_ = Ab.second;
637
  }
638
  centroidal_dynamics::Equilibrium coneMid =
639
      computeContactConeForState(fullbody, mid, successConeCurrent);
640
  successCone = successCone && successConeCurrent;
641
  midData.contactPhase_ = &coneMid;
642
  Ab = computeKinematicsConstraintsForState(fullbody, next);
643
  nextData.Kin_ = Ab.first;
644
  nextData.kin_ = Ab.second;
645
  centroidal_dynamics::Equilibrium coneNext =
646
      computeContactConeForState(fullbody, next, successConeCurrent);
647
  successCone = successCone && successConeCurrent;
648
  nextData.contactPhase_ = &coneNext;
649
650
  hppStopBenchmark(COMPUTE_DOUBLE_DESCRIPTION);
651
  hppDisplayBenchmark(COMPUTE_DOUBLE_DESCRIPTION);
652
653
  if (!successCone) {
654
    return Result(UNABLE_TO_COMPUTE);
655
  }
656
657
  pData.contacts_.push_back(previousData);
658
  if (contactsBreak.size() == 1 && contactsCreation.size() == 1) {
659
    hppDout(notice, "Contact break AND creation, create intermediate state");
660
    pData.contacts_.push_back(midData);
661
  }
662
  pData.contacts_.push_back(nextData);
663
  pData.constraints_.flag_ =
664
      bezier_com_traj::INIT_POS | bezier_com_traj::INIT_VEL |
665
      bezier_com_traj::INIT_ACC | bezier_com_traj::END_ACC |
666
      bezier_com_traj::END_VEL | bezier_com_traj::END_POS;
667
  pData.constraints_.constraintAcceleration_ = true;
668
  pData.constraints_.maxAcceleration_ = 10.;
669
  pData.constraints_.reduce_h_ = 1e-3;
670
  // set init/goal values :
671
  pData.c0_ = com_previous;
672
  pData.c1_ = com_next;
673
  size_t id_velocity = fullbody->device_->configSize() -
674
                       fullbody->device_->extraConfigSpace().dimension();
675
  pData.dc0_ = previous.configuration_.segment<3>(id_velocity);
676
  pData.dc1_ = next.configuration_.segment<3>(id_velocity);
677
  pData.ddc0_ =
678
      previous.configuration_.segment<3>(id_velocity + 3);  // unused for now
679
  pData.ddc1_ = next.configuration_.segment<3>(id_velocity + 3);
680
  // pData.dc0_ = fcl::Vec3f::Zero();
681
  // pData.dc1_ = fcl::Vec3f::Zero();
682
  pData.ddc0_ = fcl::Vec3f::Zero();
683
  pData.ddc1_ = fcl::Vec3f::Zero();
684
  hppDout(notice, "Build pData : ");
685
  hppDout(notice, "c0 = " << pData.c0_.transpose());
686
  hppDout(notice, "c1 = " << pData.c1_.transpose());
687
  hppDout(notice, "dc0 = " << pData.dc0_.transpose());
688
  hppDout(notice, "dc1 = " << pData.dc1_.transpose());
689
  hppDout(notice, "ddc0 = " << pData.ddc0_.transpose());
690
  hppDout(notice, "ddc1 = " << pData.ddc1_.transpose());
691
  assert((pData.contacts_.size() == 2 || pData.contacts_.size() == 3) &&
692
         "Error in computation of contact constraints, must be only 2 or 3 "
693
         "phases.");
694
695
  // compute initial guess :
696
  // average of all contact point for the state with the less contacts, z =
697
  // average of the CoM heigh between previous and next
698
  State lessContacts;
699
  if (contactsBreak.size() == 1 && contactsCreation.size() == 1)
700
    lessContacts = mid;
701
  else if (contactsBreak.size() == 1)
702
    lessContacts = next;
703
  else if (contactsCreation.size() == 1)
704
    lessContacts = previous;
705
  else
706
    lessContacts = previous;
707
708
  VectorX current_timings;
709
  double total_time = 0;
710
  bool timing_provided(false);
711
  int t_id = 1;
712
#if !FULL_TIME_SAMPLING
713
  MatrixXX timings_matrix;
714
#endif
715
  hppDout(notice, " timings provided size :  " << timings.size());
716
  if (timings.size() != pData.contacts_.size()) {
717
    // build timing vector
718
    // TODO : retrieve timing found by planning ?? how ?? (pass it as argument
719
    // or store it inside the states ?)
720
    hppDout(notice, "Contact break and creation. Use hardcoded timing matrice");
721
#if FULL_TIME_SAMPLING
722
    const double time_increment = 0.05;
723
    const double min_SS = 0.6;
724
    const double max_SS = 1.6;
725
    const double min_DS = 0.3;
726
    const double max_DS = 1.5;
727
    if (contactsBreak.size() == 1 && contactsCreation.size() == 1) {
728
      current_timings = VectorX(3);
729
      // current_timings<<0.6,0.4,0.6; //hrp2
730
      // total_time = 1.6;
731
      current_timings << min_DS, min_SS, min_DS;  // hrp2
732
    } else {
733
      hppDout(notice, "Only two phases.");
734
      current_timings = VectorX(2);
735
      current_timings << 1., 1.;
736
    }
737
#else
738
    timings_matrix =
739
        MatrixXX(18, 3);  // contain the timings of the first 3 phases, the
740
                          // total time and the discretization step
741
    timings_matrix << 0.6, 1.2, 0.6, 1., 1.2, 1., 0.8, 0.7, 0.8, 0.3, 0.6, 0.3,
742
        0.5, 0.6, 0.5, 0.6, 0.6, 0.6, 0.8, 0.6, 0.8, 0.3, 0.8, 0.3, 0.3, 0.7,
743
        0.3, 0.6, 0.8, 0.6, 1, 0.7, 1,  // found with script
744
        1.5, 0.7, 1, 0.8, 0.8, 0.8,     // script good
745
        1., 0.6, 1., 1.2, 0.6, 1.2, 1.5, 0.6, 1.5, 0.1, 0.2, 0.1, 0.2, 0.3, 0.2;
746
    current_timings =
747
        timings_matrix.block(0, 0, 1, pData.contacts_.size()).transpose();
748
#endif
749
    total_time = 0;
750
    for (size_t i = 0; i < pData.contacts_.size(); ++i) {
751
      total_time += current_timings[i];
752
    }
753
  } else {
754
    hppDout(notice, "Timing vector is provided");
755
    current_timings = VectorX(timings.size());
756
    for (size_t i = 0; i < timings.size(); ++i) {
757
      current_timings[i] = timings[i];
758
      total_time += timings[i];
759
    }
760
    timing_provided = true;
761
  }
762
763
  // pData.representation_ = bezier_com_traj::FORCE;
764
765
  // loop over all possible timings :
766
  bool success(false);
767
  bool no_timings_left(false);
768
  bezier_com_traj::ResultDataCOMTraj resBezier;
769
#if STAT_TIMINGS
770
  while (!no_timings_left) {
771
#else
772
  while (!success && !no_timings_left) {
773
#endif
774
    // call solveur :
775
    hppDout(notice, "Try with timings : " << current_timings.transpose());
776
    hppDout(notice, "Call solveOneStep");
777
    hppStartBenchmark(SOLVE_TRANSITION_ONE_STEP);
778
    if (numPointsPerPhases > 0) {
779
      hppDout(notice, "Call computeCOMTraj discretized");
780
      resBezier = bezier_com_traj::computeCOMTrajFixedSize(
781
          pData, current_timings, numPointsPerPhases);
782
    } else {
783
      hppDout(notice, "Call computeCOMTraj continuous");
784
      resBezier = bezier_com_traj::computeCOMTraj(pData, current_timings);
785
    }
786
787
    hppStopBenchmark(SOLVE_TRANSITION_ONE_STEP);
788
    hppDisplayBenchmark(SOLVE_TRANSITION_ONE_STEP);
789
790
    // wrap the result :
791
    if (resBezier.success_) {
792
      hppDout(notice, "REACHABLE");
793
      res.status = REACHABLE;
794
      bezier_Ptr bezierCurve = bezier_Ptr(new bezier_t(resBezier.c_of_t_));
795
      hppDout(notice,
796
              "Resulting bezier curve have timing : " << bezierCurve->max());
797
      for (bezier_t::cit_point_t wpit = bezierCurve->waypoints().begin();
798
           wpit != bezierCurve->waypoints().end(); ++wpit) {
799
        hppDout(notice, "with waypoint : " << (*wpit).transpose());
800
      }
801
      // replace extra dof in next.configuration to fit the final velocity and
802
      // acceleration found :
803
      next.configuration_.segment<3>(id_velocity) = resBezier.dc1_;
804
      next.configuration_.segment<3>(id_velocity + 3) = resBezier.ddc1_;
805
      hppDout(notice, "new final configuration : "
806
                          << pinocchio::displayConfig(next.configuration_));
807
      res.path_ = BezierPath::create(
808
          fullbody->device_, bezierCurve, previous.configuration_,
809
          next.configuration_, core::interval_t(0., total_time));
810
      hppDout(notice, "position of the waypoint : " << resBezier.x.transpose());
811
      hppDout(notice, "With timings : " << current_timings.transpose());
812
      hppDout(notice, "total time = " << total_time);
813
      hppDout(notice, "new final velocity : " << resBezier.dc1_.transpose());
814
      res.timings_ = current_timings;
815
      res.pathPerPhases_.push_back(BezierPath::create(
816
          fullbody->device_, bezierCurve, previous.configuration_,
817
          next.configuration_, core::interval_t(0., current_timings[0])));
818
      res.pathPerPhases_.push_back(BezierPath::create(
819
          fullbody->device_, bezierCurve, previous.configuration_,
820
          next.configuration_,
821
          core::interval_t(current_timings[0],
822
                           current_timings[0] + current_timings[1])));
823
      if (current_timings.size() > 2)
824
        res.pathPerPhases_.push_back(BezierPath::create(
825
            fullbody->device_, bezierCurve, previous.configuration_,
826
            next.configuration_,
827
            core::interval_t(current_timings[0] + current_timings[1],
828
                             total_time)));
829
      success = true;
830
    } else {
831
      hppDout(notice, "UNREACHABLE");
832
      res.status = UNREACHABLE;
833
    }
834
#if STAT_TIMINGS
835
    // print result in file :
836
    printTimingFile(file, current_timings, res.success(), quasiStaticSucces);
837
#else
838
    (void)quasiStaticSucces;  // silent warning
839
#endif
840
    // build the new timing vector :
841
842
    if (!timing_provided) {
843
#if FULL_TIME_SAMPLING
844
      current_timings[0] += time_increment;
845
      if (current_timings[0] > max_DS) {
846
        current_timings[0] = min_DS;
847
        current_timings[1] += time_increment;
848
        if (current_timings[1] > max_SS) {
849
          if (current_timings.size() == 3) {
850
            current_timings[1] = min_SS;
851
            current_timings[2] += time_increment;
852
            if (current_timings[2] > max_DS) {
853
              no_timings_left = true;
854
            }
855
          } else {
856
            no_timings_left = true;
857
          }
858
        }
859
      }
860
#else
861
      current_timings =
862
          timings_matrix.block(t_id, 0, 1, pData.contacts_.size()).transpose();
863
      t_id++;
864
      if (t_id == timings_matrix.rows()) no_timings_left = true;
865
#endif
866
      total_time = 0;
867
      for (size_t i = 0; i < pData.contacts_.size(); ++i) {
868
        total_time += current_timings[i];
869
      }
870
    } else {
871
      no_timings_left = true;
872
    }
873
  }
874
875
  hppStopBenchmark(IS_REACHABLE_DYNAMIC);
876
  hppDisplayBenchmark(IS_REACHABLE_DYNAMIC);
877
878
  delete pData.contacts_[0].contactPhase_;
879
  delete pData.contacts_[1].contactPhase_;
880
  if (pData.contacts_.size() > 2) delete pData.contacts_[2].contactPhase_;
881
882
#if STAT_TIMINGS
883
  file.close();
884
  return Result(REACHABLE);
885
#endif
886
887
  if (!success) {
888
    hppDout(notice, "No valid timings found, always UNREACHABLE");
889
  }
890
891
  if (success && tryQuasiStatic) {
892
    hppDout(notice, "ONLY REACHABLE IN DYNAMIC !!!");
893
  }
894
  hppDout(notice, "Between configuration : r(["
895
                      << pinocchio::displayConfig(previous.configuration_)
896
                      << "])");
897
  hppDout(notice, "and     configuration : r(["
898
                      << pinocchio::displayConfig(next.configuration_) << "])");
899
900
  return res;
901
}
902
903
}  // namespace reachability
904
}  // namespace rbprm
905
}  // namespace hpp