GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/tools/device.cpp Lines: 184 302 60.9 %
Date: 2023-03-13 12:09:37 Branches: 273 924 29.5 %

Line Branch Exec Source
1
/*
2
 * Copyright 2010,
3
 * Nicolas Mansard, Olivier Stasse, François Bleibel, Florent Lamiraux
4
 *
5
 * CNRS
6
 *
7
 */
8
9
/* --------------------------------------------------------------------- */
10
/* --- INCLUDE --------------------------------------------------------- */
11
/* --------------------------------------------------------------------- */
12
13
/* SOT */
14
#define ENABLE_RT_LOG
15
16
#include "sot/core/device.hh"
17
18
#include <iostream>
19
#include <sot/core/debug.hh>
20
#include <sot/core/macros.hh>
21
using namespace std;
22
23
#include <dynamic-graph/all-commands.h>
24
#include <dynamic-graph/factory.h>
25
#include <dynamic-graph/linear-algebra.h>
26
#include <dynamic-graph/real-time-logger.h>
27
28
#include <Eigen/Geometry>
29
#include <pinocchio/multibody/liegroup/special-euclidean.hpp>
30
#include <sot/core/matrix-geometry.hh>
31
using namespace dynamicgraph::sot;
32
using namespace dynamicgraph;
33
34
const std::string Device::CLASS_NAME = "Device";
35
36
/* --------------------------------------------------------------------- */
37
/* --- CLASS ----------------------------------------------------------- */
38
/* --------------------------------------------------------------------- */
39
40
2000
void Device::integrateRollPitchYaw(Vector &state, const Vector &control,
41
                                   double dt) {
42
  using Eigen::AngleAxisd;
43
  using Eigen::QuaternionMapd;
44
  using Eigen::Vector3d;
45
46
  typedef pinocchio::SpecialEuclideanOperationTpl<3, double> SE3;
47

2000
  Eigen::Matrix<double, 7, 1> qin, qout;
48

2000
  qin.head<3>() = state.head<3>();
49
50

2000
  QuaternionMapd quat(qin.tail<4>().data());
51


2000
  quat = AngleAxisd(state(5), Vector3d::UnitZ()) *
52


4000
         AngleAxisd(state(4), Vector3d::UnitY()) *
53


4000
         AngleAxisd(state(3), Vector3d::UnitX());
54
55


2000
  SE3().integrate(qin, control.head<6>() * dt, qout);
56
57
  // Update freeflyer pose
58

2000
  ffPose_.translation() = qout.head<3>();
59

2000
  state.head<3>() = qout.head<3>();
60
61


2000
  ffPose_.linear() = QuaternionMapd(qout.tail<4>().data()).toRotationMatrix();
62


2000
  state.segment<3>(3) = ffPose_.linear().eulerAngles(2, 1, 0).reverse();
63
2000
}
64
65
const MatrixHomogeneous &Device::freeFlyerPose() const { return ffPose_; }
66
67
4
Device::~Device() {
68
20
  for (unsigned int i = 0; i < 4; ++i) {
69
16
    delete forcesSOUT[i];
70
  }
71
}
72
73
2
Device::Device(const std::string &n)
74
    : Entity(n),
75
      state_(6),
76
      sanityCheck_(true),
77
      controlInputType_(CONTROL_INPUT_ONE_INTEGRATION),
78
4
      controlSIN(NULL, "Device(" + n + ")::input(double)::control"),
79
4
      attitudeSIN(NULL, "Device(" + n + ")::input(vector3)::attitudeIN"),
80
4
      zmpSIN(NULL, "Device(" + n + ")::input(vector3)::zmp"),
81
4
      stateSOUT("Device(" + n + ")::output(vector)::state")
82
      //,attitudeSIN(NULL,"Device::input(matrixRot)::attitudeIN")
83
      ,
84
4
      velocitySOUT("Device(" + n + ")::output(vector)::velocity"),
85
4
      attitudeSOUT("Device(" + n + ")::output(matrixRot)::attitude"),
86
4
      motorcontrolSOUT("Device(" + n + ")::output(vector)::motorcontrol"),
87
4
      previousControlSOUT("Device(" + n + ")::output(vector)::previousControl"),
88
4
      ZMPPreviousControllerSOUT("Device(" + n +
89
                                ")::output(vector)::zmppreviouscontroller"),
90
4
      robotState_("Device(" + n + ")::output(vector)::robotState"),
91
4
      robotVelocity_("Device(" + n + ")::output(vector)::robotVelocity"),
92
4
      pseudoTorqueSOUT("Device(" + n + ")::output(vector)::ptorque")
93
94
      ,
95
      ffPose_(),
96


















50
      forceZero6(6) {
97
2
  forceZero6.fill(0);
98
  /* --- SIGNALS --- */
99
10
  for (int i = 0; i < 4; ++i) {
100
8
    withForceSignals[i] = false;
101
  }
102
4
  forcesSOUT[0] =
103


2
      new Signal<Vector, int>("Device(" + n + ")::output(vector6)::forceRLEG");
104
4
  forcesSOUT[1] =
105


2
      new Signal<Vector, int>("Device(" + n + ")::output(vector6)::forceLLEG");
106
4
  forcesSOUT[2] =
107


2
      new Signal<Vector, int>("Device(" + n + ")::output(vector6)::forceRARM");
108
4
  forcesSOUT[3] =
109


2
      new Signal<Vector, int>("Device(" + n + ")::output(vector6)::forceLARM");
110
111
4
  signalRegistration(
112


4
      controlSIN << stateSOUT << robotState_ << robotVelocity_ << velocitySOUT
113


2
                 << attitudeSOUT << attitudeSIN << zmpSIN << *forcesSOUT[0]
114

2
                 << *forcesSOUT[1] << *forcesSOUT[2] << *forcesSOUT[3]
115

2
                 << previousControlSOUT << pseudoTorqueSOUT << motorcontrolSOUT
116

2
                 << ZMPPreviousControllerSOUT);
117
2
  state_.fill(.0);
118
2
  stateSOUT.setConstant(state_);
119
120

2
  velocity_.resize(state_.size());
121
2
  velocity_.setZero();
122
2
  velocitySOUT.setConstant(velocity_);
123
124
  /* --- Commands --- */
125
  {
126
2
    std::string docstring;
127
    /* Command setStateSize. */
128
    docstring =
129
        "\n"
130
        "    Set size of state vector\n"
131
2
        "\n";
132

2
    addCommand("resize", new command::Setter<Device, unsigned int>(
133

2
                             *this, &Device::setStateSize, docstring));
134
    docstring =
135
        "\n"
136
        "    Set state vector value\n"
137
2
        "\n";
138

2
    addCommand("set", new command::Setter<Device, Vector>(
139

2
                          *this, &Device::setState, docstring));
140
141
    docstring =
142
        "\n"
143
        "    Set velocity vector value\n"
144
2
        "\n";
145

2
    addCommand("setVelocity", new command::Setter<Device, Vector>(
146

2
                                  *this, &Device::setVelocity, docstring));
147
148
2
    void (Device::*setRootPtr)(const Matrix &) = &Device::setRoot;
149

4
    docstring = command::docCommandVoid1("Set the root position.",
150
2
                                         "matrix homogeneous");
151

2
    addCommand("setRoot",
152
2
               command::makeCommandVoid1(*this, setRootPtr, docstring));
153
154
    /* Second Order Integration set. */
155
    docstring =
156
        "\n"
157
        "    Set the position calculous starting from  \n"
158
        "    acceleration measure instead of velocity \n"
159
2
        "\n";
160
161

2
    addCommand("setSecondOrderIntegration",
162
2
               command::makeCommandVoid0(
163
                   *this, &Device::setSecondOrderIntegration, docstring));
164
165
    /* Display information */
166
    docstring =
167
        "\n"
168
        "    Display information on device  \n"
169
2
        "\n";
170

2
    addCommand("display", command::makeCommandVoid0(*this, &Device::cmdDisplay,
171
                                                    docstring));
172
173
    /* SET of control input type. */
174
    docstring =
175
        "\n"
176
        "    Set the type of control input which can be  \n"
177
        "    acceleration, velocity, or position\n"
178
2
        "\n";
179
180

2
    addCommand("setControlInputType",
181
               new command::Setter<Device, string>(
182

2
                   *this, &Device::setControlInputType, docstring));
183
184
    docstring =
185
        "\n"
186
        "    Enable/Disable sanity checks\n"
187
2
        "\n";
188

2
    addCommand("setSanityCheck",
189
               new command::Setter<Device, bool>(*this, &Device::setSanityCheck,
190

2
                                                 docstring));
191
192

2
    addCommand("setPositionBounds",
193
2
               command::makeCommandVoid2(
194
                   *this, &Device::setPositionBounds,
195


4
                   command::docCommandVoid2("Set robot position bounds",
196
                                            "vector: lower bounds",
197
                                            "vector: upper bounds")));
198
199

2
    addCommand("setVelocityBounds",
200
2
               command::makeCommandVoid2(
201
                   *this, &Device::setVelocityBounds,
202


4
                   command::docCommandVoid2("Set robot velocity bounds",
203
                                            "vector: lower bounds",
204
                                            "vector: upper bounds")));
205
206

2
    addCommand("setTorqueBounds",
207
2
               command::makeCommandVoid2(
208
                   *this, &Device::setTorqueBounds,
209


4
                   command::docCommandVoid2("Set robot torque bounds",
210
                                            "vector: lower bounds",
211
                                            "vector: upper bounds")));
212
213

2
    addCommand("getTimeStep",
214
2
               command::makeDirectGetter(
215
                   *this, &this->timestep_,
216

4
                   command::docDirectGetter("Time step", "double")));
217
  }
218
2
}
219
220
1
void Device::setStateSize(const unsigned int &size) {
221
1
  state_.resize(size);
222
1
  state_.fill(.0);
223
1
  stateSOUT.setConstant(state_);
224
1
  previousControlSOUT.setConstant(state_);
225
1
  pseudoTorqueSOUT.setConstant(state_);
226
1
  motorcontrolSOUT.setConstant(state_);
227
228
1
  Device::setVelocitySize(size);
229
230
2
  Vector zmp(3);
231
1
  zmp.fill(.0);
232
1
  ZMPPreviousControllerSOUT.setConstant(zmp);
233
1
}
234
235
2
void Device::setVelocitySize(const unsigned int &size) {
236
2
  velocity_.resize(size);
237
2
  velocity_.fill(.0);
238
2
  velocitySOUT.setConstant(velocity_);
239
2
}
240
241
1
void Device::setState(const Vector &st) {
242
1
  if (sanityCheck_) {
243
1
    const Vector::Index &s = st.size();
244
    SOT_CORE_DISABLE_WARNING_PUSH
245
    SOT_CORE_DISABLE_WARNING_FALLTHROUGH
246

1
    switch (controlInputType_) {
247
      case CONTROL_INPUT_TWO_INTEGRATION:
248
        dgRTLOG()
249
            << "Sanity check for this control is not well supported. "
250
               "In order to make it work, use pinocchio and the contact forces "
251
               "to estimate the joint torques for the given acceleration.\n";
252
        if (s != lowerTorque_.size() || s != upperTorque_.size()) {
253
          std::ostringstream os;
254
          os << "dynamicgraph::sot::Device::setState: upper and/or lower torque"
255
                "bounds do not match state size. Input State size = "
256
             << st.size() << ", lowerTorque_.size() = " << lowerTorque_.size()
257
             << ", upperTorque_.size() = " << upperTorque_.size()
258
             << ". Set them first with setTorqueBounds.";
259
          throw std::invalid_argument(os.str().c_str());
260
          // fall through
261
        }
262
      case CONTROL_INPUT_ONE_INTEGRATION:
263

1
        if (s != lowerVelocity_.size() || s != upperVelocity_.size()) {
264
          std::ostringstream os;
265
          os << "dynamicgraph::sot::Device::setState: upper and/or lower "
266
                "velocity"
267
                " bounds do not match state size. Input State size = "
268
             << st.size()
269
             << ", lowerVelocity_.size() = " << lowerVelocity_.size()
270
             << ", upperVelocity_.size() = " << upperVelocity_.size()
271
             << ". Set them first with setVelocityBounds.";
272
          throw std::invalid_argument(os.str().c_str());
273
        }
274
        // fall through
275
      case CONTROL_INPUT_NO_INTEGRATION:
276
1
        break;
277
      default:
278
        throw std::invalid_argument("Invalid control mode type.");
279
    }
280
    SOT_CORE_DISABLE_WARNING_POP
281
  }
282
1
  state_ = st;
283
1
  stateSOUT.setConstant(state_);
284
1
  motorcontrolSOUT.setConstant(state_);
285
1
}
286
287
1
void Device::setVelocity(const Vector &vel) {
288
1
  velocity_ = vel;
289
1
  velocitySOUT.setConstant(velocity_);
290
1
}
291
292
void Device::setRoot(const Matrix &root) {
293
  Eigen::Matrix4d _matrix4d(root);
294
  MatrixHomogeneous _root(_matrix4d);
295
  setRoot(_root);
296
}
297
298
void Device::setRoot(const MatrixHomogeneous &worldMwaist) {
299
  VectorRollPitchYaw r = (worldMwaist.linear().eulerAngles(2, 1, 0)).reverse();
300
  Vector q = state_;
301
  q = worldMwaist.translation();  // abusive ... but working.
302
  for (unsigned int i = 0; i < 3; ++i) q(i + 3) = r(i);
303
}
304
305
void Device::setSecondOrderIntegration() {
306
  controlInputType_ = CONTROL_INPUT_TWO_INTEGRATION;
307
  velocity_.resize(state_.size());
308
  velocity_.setZero();
309
  velocitySOUT.setConstant(velocity_);
310
}
311
312
void Device::setNoIntegration() {
313
  controlInputType_ = CONTROL_INPUT_NO_INTEGRATION;
314
  velocity_.resize(state_.size());
315
  velocity_.setZero();
316
  velocitySOUT.setConstant(velocity_);
317
}
318
319
void Device::setControlInputType(const std::string &cit) {
320
  for (int i = 0; i < CONTROL_INPUT_SIZE; i++)
321
    if (cit == ControlInput_s[i]) {
322
      controlInputType_ = (ControlInput)i;
323
      sotDEBUG(25) << "Control input type: " << ControlInput_s[i] << endl;
324
      return;
325
    }
326
  sotDEBUG(25) << "Unrecognized control input type: " << cit << endl;
327
}
328
329
void Device::setSanityCheck(const bool &enableCheck) {
330
  if (enableCheck) {
331
    const Vector::Index &s = state_.size();
332
    SOT_CORE_DISABLE_WARNING_PUSH
333
    SOT_CORE_DISABLE_WARNING_FALLTHROUGH
334
    switch (controlInputType_) {
335
      case CONTROL_INPUT_TWO_INTEGRATION:
336
        dgRTLOG()
337
            << "Sanity check for this control is not well supported. "
338
               "In order to make it work, use pinocchio and the contact forces "
339
               "to estimate the joint torques for the given acceleration.\n";
340
        if (s != lowerTorque_.size() || s != upperTorque_.size())
341
          throw std::invalid_argument(
342
              "Upper and/or lower torque bounds "
343
              "do not match state size. Set them first with setTorqueBounds");
344
        // fall through
345
      case CONTROL_INPUT_ONE_INTEGRATION:
346
        if (s != lowerVelocity_.size() || s != upperVelocity_.size())
347
          throw std::invalid_argument(
348
              "Upper and/or lower velocity bounds "
349
              "do not match state size. Set them first with setVelocityBounds");
350
        // fall through
351
      case CONTROL_INPUT_NO_INTEGRATION:
352
        if (s != lowerPosition_.size() || s != upperPosition_.size())
353
          throw std::invalid_argument(
354
              "Upper and/or lower position bounds "
355
              "do not match state size. Set them first with setPositionBounds");
356
        break;
357
      default:
358
        throw std::invalid_argument("Invalid control mode type.");
359
    }
360
    SOT_CORE_DISABLE_WARNING_POP
361
  }
362
  sanityCheck_ = enableCheck;
363
}
364
365
1
void Device::setPositionBounds(const Vector &lower, const Vector &upper) {
366
2
  std::ostringstream oss;
367

1
  if (lower.size() != state_.size()) {
368
    oss << "Lower bound size should be " << state_.size() << ", got "
369
        << lower.size();
370
    throw std::invalid_argument(oss.str());
371
  }
372

1
  if (upper.size() != state_.size()) {
373
    oss << "Upper bound size should be " << state_.size() << ", got "
374
        << upper.size();
375
    throw std::invalid_argument(oss.str());
376
  }
377
1
  lowerPosition_ = lower;
378
1
  upperPosition_ = upper;
379
1
}
380
381
1
void Device::setVelocityBounds(const Vector &lower, const Vector &upper) {
382
2
  std::ostringstream oss;
383

1
  if (lower.size() != velocity_.size()) {
384
    oss << "Lower bound size should be " << velocity_.size() << ", got "
385
        << lower.size();
386
    throw std::invalid_argument(oss.str());
387
  }
388

1
  if (upper.size() != velocity_.size()) {
389
    oss << "Upper bound size should be " << velocity_.size() << ", got "
390
        << upper.size();
391
    throw std::invalid_argument(oss.str());
392
  }
393
1
  lowerVelocity_ = lower;
394
1
  upperVelocity_ = upper;
395
1
}
396
397
void Device::setTorqueBounds(const Vector &lower, const Vector &upper) {
398
  // TODO I think the torque bounds size are state_.size()-6...
399
  std::ostringstream oss;
400
  if (lower.size() != state_.size()) {
401
    oss << "Lower bound size should be " << state_.size() << ", got "
402
        << lower.size();
403
    throw std::invalid_argument(oss.str());
404
  }
405
  if (upper.size() != state_.size()) {
406
    oss << "Lower bound size should be " << state_.size() << ", got "
407
        << upper.size();
408
    throw std::invalid_argument(oss.str());
409
  }
410
  lowerTorque_ = lower;
411
  upperTorque_ = upper;
412
}
413
414
2000
void Device::increment(const double &dt) {
415
2000
  int time = stateSOUT.getTime();
416
  sotDEBUG(25) << "Time : " << time << std::endl;
417
418
  // Run Synchronous commands and evaluate signals outside the main
419
  // connected component of the graph.
420
  try {
421
2000
    periodicCallBefore_.run(time + 1);
422
  } catch (std::exception &e) {
423
    dgRTLOG() << "exception caught while running periodical commands (before): "
424
              << e.what() << std::endl;
425
  } catch (const char *str) {
426
    dgRTLOG() << "exception caught while running periodical commands (before): "
427
              << str << std::endl;
428
  } catch (...) {
429
    dgRTLOG() << "unknown exception caught while"
430
              << " running periodical commands (before)" << std::endl;
431
  }
432
433
  /* Force the recomputation of the control. */
434
2000
  controlSIN(time);
435
  sotDEBUG(25) << "u" << time << " = " << controlSIN.accessCopy() << endl;
436
437
  /* Integration of numerical values. This function is virtual. */
438
2000
  integrate(dt);
439
  sotDEBUG(25) << "q" << time << " = " << state_ << endl;
440
441
  /* Position the signals corresponding to sensors. */
442
2000
  stateSOUT.setConstant(state_);
443
2000
  stateSOUT.setTime(time + 1);
444
  // computation of the velocity signal
445
2000
  if (controlInputType_ == CONTROL_INPUT_TWO_INTEGRATION) {
446
    velocitySOUT.setConstant(velocity_);
447
    velocitySOUT.setTime(time + 1);
448
2000
  } else if (controlInputType_ == CONTROL_INPUT_ONE_INTEGRATION) {
449

2000
    velocitySOUT.setConstant(controlSIN.accessCopy());
450
2000
    velocitySOUT.setTime(time + 1);
451
  }
452
10000
  for (int i = 0; i < 4; ++i) {
453

8000
    if (!withForceSignals[i]) forcesSOUT[i]->setConstant(forceZero6);
454
  }
455
4000
  Vector zmp(3);
456
2000
  zmp.fill(.0);
457
2000
  ZMPPreviousControllerSOUT.setConstant(zmp);
458
459
  // Run Synchronous commands and evaluate signals outside the main
460
  // connected component of the graph.
461
  try {
462
2000
    periodicCallAfter_.run(time + 1);
463
  } catch (std::exception &e) {
464
    dgRTLOG() << "exception caught while running periodical commands (after): "
465
              << e.what() << std::endl;
466
  } catch (const char *str) {
467
    dgRTLOG() << "exception caught while running periodical commands (after): "
468
              << str << std::endl;
469
  } catch (...) {
470
    dgRTLOG() << "unknown exception caught while"
471
              << " running periodical commands (after)" << std::endl;
472
  }
473
474
  // Others signals.
475
2000
  motorcontrolSOUT.setConstant(state_);
476
2000
}
477
478
// Return true if it saturates.
479
152000
inline bool saturateBounds(double &val, const double &lower,
480
                           const double &upper) {
481
152000
  assert(lower <= upper);
482
152000
  if (val < lower) {
483
    val = lower;
484
    return true;
485
  }
486
152000
  if (upper < val) {
487
    val = upper;
488
    return true;
489
  }
490
152000
  return false;
491
}
492
493
#define CHECK_BOUNDS(val, lower, upper, what)                                \
494
  for (int i = 0; i < val.size(); ++i) {                                     \
495
    double old = val(i);                                                     \
496
    if (saturateBounds(val(i), lower(i), upper(i))) {                        \
497
      std::ostringstream oss;                                                \
498
      oss << "Robot " what " bound violation at DoF " << i << ": requested " \
499
          << old << " but set " << val(i) << '\n';                           \
500
      SEND_ERROR_STREAM_MSG(oss.str());                                      \
501
    }                                                                        \
502
  }
503
504
2000
void Device::integrate(const double &dt) {
505
2000
  const Vector &controlIN = controlSIN.accessCopy();
506
507

2000
  if (sanityCheck_ && controlIN.hasNaN()) {
508
    dgRTLOG() << "Device::integrate: Control has NaN values: " << '\n'
509
              << controlIN.transpose() << '\n';
510
    return;
511
  }
512
513
2000
  if (controlInputType_ == CONTROL_INPUT_NO_INTEGRATION) {
514
    state_.tail(controlIN.size()) = controlIN;
515
    return;
516
  }
517
518

2000
  if (vel_control_.size() == 0) vel_control_ = Vector::Zero(controlIN.size());
519
520
  // If control size is state size - 6, integrate joint angles,
521
  // if control and state are of same size, integrate 6 first degrees of
522
  // freedom as a translation and roll pitch yaw.
523
524
2000
  if (controlInputType_ == CONTROL_INPUT_TWO_INTEGRATION) {
525
    // TODO check acceleration
526
    // Position increment
527
    vel_control_ = velocity_.tail(controlIN.size()) + (0.5 * dt) * controlIN;
528
    // Velocity integration.
529
    velocity_.tail(controlIN.size()) += controlIN * dt;
530
  } else {
531
2000
    vel_control_ = controlIN;
532
  }
533
534
  // Velocity bounds check
535
2000
  if (sanityCheck_) {
536







78000
    CHECK_BOUNDS(velocity_, lowerVelocity_, upperVelocity_, "velocity");
537
  }
538
539
2000
  if (vel_control_.size() == state_.size()) {
540
    // Freeflyer integration
541
2000
    integrateRollPitchYaw(state_, vel_control_, dt);
542
    // Joints integration
543


2000
    state_.tail(state_.size() - 6) += vel_control_.tail(state_.size() - 6) * dt;
544
  } else {
545
    // Position integration
546
    state_.tail(controlIN.size()) += vel_control_ * dt;
547
  }
548
549
  // Position bounds check
550
2000
  if (sanityCheck_) {
551







78000
    CHECK_BOUNDS(state_, lowerPosition_, upperPosition_, "position");
552
  }
553
}
554
555
/* --- DISPLAY ------------------------------------------------------------ */
556
557
1
void Device::display(std::ostream &os) const {
558
1
  os << name << ": " << state_ << endl
559
1
     << "sanityCheck: " << sanityCheck_ << endl
560
1
     << "controlInputType:" << controlInputType_ << endl;
561
1
}
562
563
1
void Device::cmdDisplay() {
564
1
  std::cout << name << ": " << state_ << endl
565
1
            << "sanityCheck: " << sanityCheck_ << endl
566
1
            << "controlInputType:" << controlInputType_ << endl;
567
1
}