GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/dyna_com.cpp Lines: 0 169 0.0 %
Date: 2023-12-03 14:15:28 Branches: 0 394 0.0 %

Line Branch Exec Source
1
/**
2
 * @file
3
 * @copyright Copyright (c) 2022, LAAS-CNRS, Toward, PalRobotics
4
 * @brief
5
 */
6
7
#include "aig/dyna_com.hpp"
8
9
#include <algorithm>
10
#include <cctype>
11
#include <eiquadprog/eiquadprog.hpp>
12
#include <pinocchio/algorithm/center-of-mass.hpp>
13
#include <pinocchio/algorithm/centroidal.hpp>
14
#include <pinocchio/algorithm/frames.hpp>
15
#include <pinocchio/parsers/urdf.hpp>
16
17
#include "aig/contact6d.hpp"
18
19
namespace aig {
20
21
DynaCoM::DynaCoM() {}
22
DynaCoM::DynaCoM(const DynaCoMSettings settings) { initialize(settings); }
23
24
void DynaCoM::initialize(const DynaCoMSettings settings) {
25
  // Copy the settings internally.
26
  settings_ = settings;
27
28
  // Check if the urdf and the srdf file exists or not.
29
  bool urdf_file_exists = false;
30
  {
31
    std::ifstream f(settings_.urdf.c_str());
32
    urdf_file_exists = f.good();
33
  }
34
35
  // Build the robot model.
36
  if (urdf_file_exists) {
37
    pinocchio::urdf::buildModel(settings_.urdf,
38
                                pinocchio::JointModelFreeFlyer(), model_);
39
  } else if (settings_.urdf != "") {
40
    pinocchio::urdf::buildModelFromXML(
41
        settings_.urdf, pinocchio::JointModelFreeFlyer(), model_);
42
  } else {
43
    throw std::runtime_error("DynaCoM::DynaCoM(): settings_.urdf is empty");
44
  }
45
  // Build pinocchio cache.
46
  data_ = pinocchio::Data(model_);
47
48
  mass_ = 0.0;
49
  for (size_t k = 0; k < model_.inertias.size(); ++k) {
50
    mass_ += model_.inertias[k].mass();
51
  }
52
  weight_ = mass_ * model_.gravity981;
53
  S_ << 0, -1, 1, 0;
54
  Sz_.setZero();
55
  Sz_.diagonal().segment<4>(2) << 1, 1, 1, 1;
56
57
  // matrix_sizes
58
  uni_rows_ = 0;
59
  fri_rows_ = 0;
60
  cols_ = 0;
61
  newton_euler_b_.resize(6);
62
}
63
64
const Eigen::Matrix<double, 6, 6> DynaCoM::toWorldCoPWrench(
65
    pinocchio::SE3 pose) {
66
  /**
67
   * To compute any CoP, we need some surface. We compute the full
68
   * robot CoP considering always a horizontal surface. So, just
69
   * the vertical forces produce pressure on such surface.
70
   * This method generates the adjoint matrix needed to transform
71
   * local forces in some frame pose to a world wrench composed by
72
   * the vertical force and the torque. Lateral forces are discarted
73
   * because they do not contribute on this CoP.
74
   * Still, notice that the lateral forces have an effect that is
75
   * accounted in the non-linear effects.
76
   */
77
78
  soMs_ = pinocchio::SE3(pose.rotation(), Eigen::Vector3d::Zero());
79
  soXs_ = soMs_.toActionMatrixInverse().transpose();
80
  oMso_ = pinocchio::SE3(Eigen::Matrix3d::Identity(), pose.translation());
81
  oXso_ = oMso_.toActionMatrixInverse().transpose();
82
83
  return oXso_ * Sz_ * soXs_;
84
}
85
86
void DynaCoM::computeDynamics(const Eigen::VectorXd &posture,
87
                              const Eigen::VectorXd &velocity,
88
                              const Eigen::VectorXd &acceleration,
89
                              const Eigen::Matrix<double, 6, 1> &externalWrench,
90
                              bool flatHorizontalGround) {
91
  /**
92
   * @brief The external wrench is supposed to be expressed
93
   * in the frame of the Center of mass.
94
   *
95
   * @brief The option flatHorizontalGround assumes that supporting contacts
96
   * where previously defined.
97
   *
98
   * TODO: In the case when flatHorizontalGround = True, still, we could
99
   * remove the assumption of horizontal ground by including the lateral force
100
   * produced by the lateral components of the groundNormalReaction.
101
   *
102
   * For this we should know what is the direction normal to the ground. Then,
103
   * we could change the flag name by `bool flatGround`. The normal direction
104
   * can be obtained from the feet frames (both are the same in a flatGround)
105
   *
106
   */
107
108
  pinocchio::computeCentroidalMomentumTimeVariation(model_, data_, posture,
109
                                                    velocity, acceleration);
110
111
  acom_ = data_.dhg.linear() / mass_;
112
  dL_ = data_.dhg.angular();
113
  L_ = data_.hg.angular();
114
115
  groundCoMForce_ = data_.dhg.linear() - weight_ - externalWrench.head<3>();
116
  groundCoMTorque_ = dL_ - externalWrench.tail<3>();
117
118
  if (flatHorizontalGround)
119
    cop_ =
120
        data_.com[0].head<2>() + (S_ * groundCoMTorque_.head<2>() -
121
                                  groundCoMForce_.head<2>() * data_.com[0](2)) /
122
                                     (groundCoMForce_(2));
123
124
  else {
125
    distributeForce(groundCoMForce_, groundCoMTorque_, data_.com[0]);
126
    // @TODO: IT could happen that the expected groundCoMwrench is not feasible,
127
    // in such case, we should get the clossest possible and recompute the
128
    // centroidal motion accordingly. So, groundCoMForce_(2) in the following
129
    // should be updated.
130
    // The case when no contact is active should also be managed.
131
132
    CoPTorque_ = Eigen::Vector3d::Zero();
133
    for (std::string name : active_contact6ds_) {
134
      std::shared_ptr<Contact6D> &contact = known_contact6ds_[name];
135
      CoPTorque_ +=
136
          (toWorldCoPWrench(contact->getPose()) * contact->appliedForce())
137
              .segment<3>(3);
138
    }
139
    cop_ = S_ * CoPTorque_.head<2>() / groundCoMForce_(2);
140
  }
141
}
142
143
void DynaCoM::computeNL(const double &w, const Eigen::VectorXd &posture,
144
                        const Eigen::VectorXd &velocity,
145
                        const Eigen::VectorXd &acceleration,
146
                        const Eigen::Matrix<double, 6, 1> &externalWrench,
147
                        bool flatHorizontalGround) {
148
  computeDynamics(posture, velocity, acceleration, externalWrench,
149
                  flatHorizontalGround);
150
  computeNL(w);
151
}
152
153
void DynaCoM::computeNL(const double &w) {
154
  /**
155
   * In this function form, computeDynamics is supposed to have been called
156
   * before.
157
   */
158
  n_ = acom_.head<2>() / (w * w) - data_.com[0].head<2>() + cop_;
159
}
160
161
// Contact management
162
// //////////////////////////////////////////////////////////////////
163
164
void DynaCoM::addContact6d(const std::shared_ptr<Contact6D> &contact,
165
                           const std::string &name, const bool active) {
166
  contact->setFrameID(model_.getFrameId(contact->getSettings().frame_name));
167
  contact->setPose(data_.oMf[contact->getFrameID()]);
168
169
  known_contact6ds_.insert(
170
      std::pair<std::string, std::shared_ptr<Contact6D>>(name, contact));
171
172
  addSizes(known_contact6ds_[name]);
173
  if (active) activateContact6d(name);
174
}
175
176
void DynaCoM::removeContact6d(const std::string &name) {
177
  knownID_ = known_contact6ds_.find(name);
178
  if (knownID_ != known_contact6ds_.end()) {
179
    removeSizes(known_contact6ds_[name]);
180
    deactivateContact6d(name);
181
    known_contact6ds_.erase(name);
182
  }
183
}
184
185
void DynaCoM::addSizes(const std::shared_ptr<Contact6D> &contact) {
186
  uni_rows_ += contact->uni_rows();
187
  fri_rows_ += contact->fri_rows();
188
  cols_ += contact->cols();
189
190
  resizeMatrices();
191
}
192
193
void DynaCoM::removeSizes(const std::shared_ptr<Contact6D> &contact) {
194
  uni_rows_ -= contact->uni_rows();
195
  fri_rows_ -= contact->fri_rows();
196
  cols_ -= contact->cols();
197
198
  resizeMatrices();
199
}
200
201
void DynaCoM::resizeMatrices() {
202
  unilaterality_A_.resize(uni_rows_, cols_);
203
  unilaterality_b_.resize(uni_rows_);
204
  friction_A_.resize(fri_rows_, cols_);
205
  friction_b_.resize(fri_rows_);
206
  regularization_A_.resize(cols_);
207
  regularization_b_.resize(cols_);
208
  newton_euler_A_.resize(6, cols_);
209
}
210
211
void DynaCoM::activateContact6d(const std::string &name) {
212
  activeID_ =
213
      std::find(active_contact6ds_.begin(), active_contact6ds_.end(), name);
214
  knownID_ = known_contact6ds_.find(name);
215
216
  if (activeID_ == active_contact6ds_.end()) {
217
    if (knownID_ != known_contact6ds_.end()) {
218
      active_contact6ds_.push_back(name);
219
      std::cout << "activated contact " << name << std::endl;
220
      return;
221
    } else {
222
      std::cout << "no contact called " << name << " was defined" << std::endl;
223
      return;
224
    }
225
  }
226
  std::cout << name << " was already active" << std::endl;
227
}
228
229
void DynaCoM::deactivateContact6d(const std::string &name) {
230
  activeID_ =
231
      std::find(active_contact6ds_.begin(), active_contact6ds_.end(), name);
232
  if (activeID_ != active_contact6ds_.end()) {
233
    known_contact6ds_[name]->deactivate();
234
    active_contact6ds_.erase(activeID_);
235
    std::cout << "deactivated contact " << name << std::endl;
236
    return;
237
  }
238
  std::cout << name << " was not active" << std::endl;
239
}
240
241
void DynaCoM::buildMatrices(const Eigen::Vector3d &groundCoMForce,
242
                            const Eigen::Vector3d &groundCoMTorque,
243
                            const Eigen::Vector3d &CoM) {
244
  size_t uni_r, fri_r, cols;
245
246
  uni_i_ = 0;
247
  fri_i_ = 0;
248
  j_ = 0;
249
  for (std::string name : active_contact6ds_) {
250
    std::shared_ptr<Contact6D> &contact = known_contact6ds_[name];
251
252
    uni_r = contact->uni_rows();
253
    fri_r = contact->fri_rows();
254
    cols = contact->cols();
255
256
    unilaterality_A_.block(uni_i_, j_, uni_r, cols) << contact->uni_A();
257
    unilaterality_A_.block(0, j_, uni_i_, cols).setZero();
258
    unilaterality_A_.block(uni_i_, 0, uni_r, j_).setZero();
259
    unilaterality_b_.segment(uni_i_, uni_r) << contact->uni_b();
260
261
    friction_A_.block(fri_i_, j_, fri_r, cols) << contact->fri_A();
262
    friction_A_.block(0, j_, fri_i_, cols).setZero();
263
    friction_A_.block(fri_i_, 0, fri_r, j_).setZero();
264
    friction_b_.segment(fri_i_, fri_r) << contact->fri_b();
265
266
    regularization_A_.segment(j_, cols) << contact->reg_A();
267
    regularization_b_.segment(j_, cols) << contact->reg_b();
268
269
    contact->updateNewtonEuler(CoM, pinocchio::updateFramePlacement(
270
                                        model_, data_, contact->getFrameID()));
271
    newton_euler_A_.block(0, j_, 6, cols) << contact->NE_A();
272
273
    uni_i_ += uni_r;
274
    fri_i_ += fri_r;
275
    j_ += cols;
276
  }
277
  newton_euler_b_ << groundCoMForce, groundCoMTorque;
278
}
279
280
// void DynaCoM::solveQP() {
281
//   H_.resize(j_, j_);
282
//   g_.resize(j_);
283
//   C_.resize(uni_i_ + fri_i_, j_);
284
//   u_.resize(uni_i_ + fri_i_);
285
//   l_.resize(uni_i_ + fri_i_);
286
//   A_.resize(6, j_);
287
288
//  H_.setZero();
289
//  g_.setZero();
290
//  H_.diagonal() << (regularization_A_.cwiseAbs2()).segment(0, j_);
291
//  C_ << unilaterality_A_.block(0, 0, uni_i_, j_),
292
//      friction_A_.block(0, 0, fri_i_, j_);
293
294
//  u_.setZero();
295
//  l_.setConstant(-inf_);
296
//  A_ << newton_euler_A_.block(0, 0, 6, j_);
297
//  b_ << newton_euler_b_;
298
//  // Initialization of QP solver
299
//  // std::cout << "matrix H:\n " << H_ << std::endl;
300
//  // std::cout << "matrix A:\n " << A_ << std::endl;
301
//  // std::cout << "matrix b:\n " << b_ << std::endl;
302
//  // std::cout << "matrix C:\n " << C_ << std::endl;
303
304
//  // std::cout<<"In solveQP, matrices made, starting proxQP"<<std::endl;
305
//  proxsuite::proxqp::dense::isize dim = j_;
306
//  proxsuite::proxqp::dense::isize n_eq(6);
307
//  proxsuite::proxqp::dense::isize n_in(fri_i_ + uni_i_);
308
//  proxsuite::proxqp::dense::QP<double> qp(dim, n_eq, n_in);
309
310
//  qp.init(H_, g_, A_, b_, C_, l_,
311
//          u_);  //,std::nullopt,std::nullopt,std::nullopt
312
//  qp.solve();
313
314
//  F_.resize(j_);
315
//  F_ << qp.results.x;
316
//  // Check results
317
//  // std::cout << "solution:\n " << F_ << std::endl;
318
//}
319
320
void DynaCoM::solveQP() {
321
  /* eiquadprog formulation :
322
  min 0.5 * x G x + g0 x
323
  s.t.
324
  CE^T x + ce0 = 0
325
  CI^T x + ci0 >= 0
326
  */
327
328
  /* proxsuite formulation :
329
  min 0.5 * x H x + g^T x
330
  s.t.
331
  A x = b
332
  l <= C x <= u
333
  */
334
335
  int dim(static_cast<int>(j_));  // number of variables
336
  int n_eq(6);                    // number of equality constraints
337
  int n_ineq(
338
      static_cast<int>(fri_i_ + uni_i_));  // number of inequalities constraints
339
340
  F_.resize(dim);
341
  G_.resize(dim, dim);
342
  g0_.resize(dim);
343
  CE_.resize(dim, n_eq);
344
  ce0_.resize(n_eq);
345
  C_.resize(n_ineq, dim);
346
  CI_.resize(dim, n_ineq);
347
  ci0_.resize(n_ineq);
348
349
  G_.setZero();
350
  G_.diagonal() << (regularization_A_.cwiseAbs2()).segment(0, dim);
351
  g0_.setZero();
352
353
  CE_ << newton_euler_A_.block(0, 0, 6, dim).transpose();
354
  ce0_ << -newton_euler_b_;
355
356
  C_ << unilaterality_A_.block(0, 0, static_cast<int>(uni_i_), dim),
357
      friction_A_.block(0, 0, static_cast<int>(fri_i_), dim);
358
  CI_ = -C_.transpose();
359
  ci0_.setZero();
360
361
  //  std::cout<<"G "<<std::endl<<G_<<std::endl;
362
  //  std::cout<<"g0 "<<std::endl<<g0_<<std::endl;
363
  //  std::cout<<"CE "<<std::endl<<CE_<<std::endl;
364
  //  std::cout<<"ce "<<std::endl<<ce0_<<std::endl;
365
  //  std::cout<<"CI "<<std::endl<<CI_<<std::endl;
366
  //  std::cout<<"ci "<<std::endl<<ci0_<<std::endl;
367
368
  activeSetSize_ = 0;
369
  // const double precision =
370
  eiquadprog::solvers::solve_quadprog(G_, g0_, CE_, ce0_, CI_, ci0_, F_,
371
                                      activeSet_, activeSetSize_);
372
  // std::cout<<"DynaCom::SolveQP, finished with precision =
373
  // "<<precision<<std::endl;
374
}
375
376
void DynaCoM::distribute() {
377
  Eigen::Index n, i = 0;
378
  for (auto contact_pair : known_contact6ds_) {
379
    if (std::find(active_contact6ds_.begin(), active_contact6ds_.end(),
380
                  contact_pair.first) == active_contact6ds_.end()) {
381
      // Contact not active
382
      contact_pair.second->applyForce(Eigen::Matrix<double, 6, 1>::Zero());
383
    } else {
384
      // contact is active
385
      n = static_cast<int>(contact_pair.second->cols());
386
      contact_pair.second->applyForce(F_.segment(i, n));
387
      i += n;
388
    }
389
  }
390
}
391
392
void DynaCoM::distributeForce(const Eigen::Vector3d &groundCoMForce,
393
                              const Eigen::Vector3d &groundCoMTorque,
394
                              const Eigen::Vector3d &CoM) {
395
  /**
396
   *
397
   *  Make sure that the data of the dynaCoM
398
   * class has been updated to the correct robot
399
   * posture before executing this distribution.
400
   *
401
   * //@TODO: if the list of active contacts is empty, no force or torque
402
   * can be applied. Manage such case. We change the arguments? we throw
403
   * error? Check.
404
   *
405
   * */
406
407
  buildMatrices(groundCoMForce, groundCoMTorque, CoM);
408
  // std::cout<<"it is at least building the matrices"<<std::endl;
409
  solveQP();
410
  // std::cout<<"it is actually solving the QP"<<std::endl;
411
  distribute();
412
  // std::cout<<"it even distribute the forces"<<std::endl;
413
  // std::cout << "Distributed" << std::endl;
414
}
415
416
}  // namespace aig