GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: include/pinocchio/algorithm/center-of-mass.hxx Lines: 179 184 97.3 %
Date: 2024-04-26 13:14:21 Branches: 179 410 43.7 %

Line Branch Exec Source
1
//
2
// Copyright (c) 2015-2020 CNRS INRIA
3
//
4
5
#ifndef __pinocchio_algorithm_center_of_mass_hxx__
6
#define __pinocchio_algorithm_center_of_mass_hxx__
7
8
#include "pinocchio/algorithm/check.hpp"
9
#include "pinocchio/multibody/visitor.hpp"
10
#include "pinocchio/algorithm/kinematics.hpp"
11
#include "pinocchio/algorithm/jacobian.hpp"
12
13
/// @cond DEV
14
15
namespace pinocchio
16
{
17
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
18
2
  inline Scalar computeTotalMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model)
19
  {
20
2
    Scalar m = Scalar(0);
21
56
    for(JointIndex i=1; i<(JointIndex)(model.njoints); ++i)
22
    {
23
54
      m += model.inertias[i].mass();
24
    }
25
2
    return m;
26
  }
27
28
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
29
1
  inline Scalar computeTotalMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
30
                                 DataTpl<Scalar,Options,JointCollectionTpl> & data)
31
  {
32
1
    data.mass[0] = computeTotalMass(model);
33
1
    return data.mass[0];
34
  }
35
36
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
37
1
  inline void computeSubtreeMasses(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
38
                                   DataTpl<Scalar,Options,JointCollectionTpl> & data)
39
  {
40
1
    data.mass[0] = Scalar(0);
41
42
    // Forward Step
43
28
    for(JointIndex i=1; i<(JointIndex)(model.njoints); ++i)
44
    {
45
27
      data.mass[i] = model.inertias[i].mass();
46
    }
47
48
    // Backward Step
49
28
    for(JointIndex i=(JointIndex)(model.njoints-1); i>0; --i)
50
    {
51
27
      const JointIndex & parent = model.parents[i];
52
27
      data.mass[parent] += data.mass[i];
53
    }
54
1
  }
55
56
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename ConfigVectorType>
57
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Vector3 &
58
899
  centerOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
59
               DataTpl<Scalar,Options,JointCollectionTpl> & data,
60
               const Eigen::MatrixBase<ConfigVectorType> & q,
61
               const bool computeSubtreeComs)
62
  {
63
899
    forwardKinematics(model,data,q.derived());
64
65
899
    centerOfMass(model,data,POSITION,computeSubtreeComs);
66
899
    return data.com[0];
67
  }
68
69
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename ConfigVectorType, typename TangentVectorType>
70
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Vector3 &
71
44
  centerOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
72
               DataTpl<Scalar,Options,JointCollectionTpl> & data,
73
               const Eigen::MatrixBase<ConfigVectorType> & q,
74
               const Eigen::MatrixBase<TangentVectorType> & v,
75
               const bool computeSubtreeComs)
76
  {
77
44
    forwardKinematics(model,data,q.derived(),v.derived());
78
79
44
    centerOfMass(model,data,VELOCITY,computeSubtreeComs);
80
44
    return data.com[0];
81
  }
82
83
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename ConfigVectorType, typename TangentVectorType1, typename TangentVectorType2>
84
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Vector3 &
85
3
  centerOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
86
               DataTpl<Scalar,Options,JointCollectionTpl> & data,
87
               const Eigen::MatrixBase<ConfigVectorType> & q,
88
               const Eigen::MatrixBase<TangentVectorType1> & v,
89
               const Eigen::MatrixBase<TangentVectorType2> & a,
90
               const bool computeSubtreeComs)
91
  {
92
3
    forwardKinematics(model,data,q,v,a);
93
94
3
    centerOfMass(model,data,ACCELERATION,computeSubtreeComs);
95
3
    return data.com[0];
96
  }
97
98
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
99
  const typename DataTpl<Scalar,Options,JointCollectionTpl>::Vector3 &
100
946
  centerOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
101
               DataTpl<Scalar,Options,JointCollectionTpl> & data,
102
               KinematicLevel kinematic_level,
103
               const bool computeSubtreeComs)
104
  {
105
946
    assert(model.check(data) && "data is not consistent with model.");
106

946
    PINOCCHIO_CHECK_INPUT_ARGUMENT(kinematic_level >= 0 && kinematic_level <= 2);
107
108
    typedef ModelTpl<Scalar,Options,JointCollectionTpl> Model;
109
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
110
111
    typedef typename Model::JointIndex JointIndex;
112
113
    typedef typename Data::SE3 SE3;
114
    typedef typename Data::Motion Motion;
115
    typedef typename Data::Inertia Inertia;
116
117
946
    const bool do_position = (kinematic_level >= POSITION);
118
946
    const bool do_velocity = (kinematic_level >= VELOCITY);
119
946
    const bool do_acceleration = (kinematic_level >= ACCELERATION);
120
121
946
    data.mass[0] = 0;
122
946
    if(do_position)
123
946
      data.com[0].setZero();
124
946
    if(do_velocity)
125
47
      data.vcom[0].setZero();
126
946
    if(do_acceleration)
127
3
      data.acom[0].setZero();
128
129
    // Forward Step
130
26574
    for(JointIndex i=1; i<(JointIndex)(model.njoints); ++i)
131
    {
132
25628
      const typename Inertia::Scalar  & mass = model.inertias[i].mass();
133
25628
      const typename SE3::Vector3 & lever = model.inertias[i].lever();
134
135
25628
      const Motion & v = data.v[i];
136
25628
      const Motion & a = data.a[i];
137
138
25628
      data.mass[i] = mass;
139
140
25628
      if(do_position)
141

25628
        data.com[i].noalias()  = mass * lever;
142
143
25628
      if(do_velocity)
144



1343
        data.vcom[i].noalias() = mass * (v.angular().cross(lever) + v.linear());
145
146
25628
      if(do_acceleration)
147




164
        data.acom[i].noalias() = mass * (a.angular().cross(lever) + a.linear())
148
82
                     + v.angular().cross(data.vcom[i]); // take into accound the coriolis part of the acceleration
149
    }
150
151
    // Backward Step
152
26574
    for(JointIndex i=(JointIndex)(model.njoints-1); i>0; --i)
153
    {
154
25628
      const JointIndex & parent = model.parents[i];
155
25628
      const SE3 & liMi = data.liMi[i];
156
157
25628
      data.mass[parent] += data.mass[i];
158
159
25628
      if(do_position)
160


25628
        data.com[parent] += (liMi.rotation()*data.com[i]
161
25628
                         + data.mass[i] * liMi.translation());
162
163
25628
      if(do_velocity)
164
1343
        data.vcom[parent] += liMi.rotation()*data.vcom[i];
165
166
25628
      if(do_acceleration)
167
82
        data.acom[parent] += liMi.rotation()*data.acom[i];
168
169
25628
      if(computeSubtreeComs)
170
      {
171
25600
        if(do_position)
172
25600
          data.com[i] /= data.mass[i];
173
25600
        if(do_velocity)
174
1315
          data.vcom[i] /= data.mass[i];
175
25600
        if(do_acceleration)
176
82
          data.acom[i] /= data.mass[i];
177
      }
178
    }
179
180
946
    if(do_position)
181
946
      data.com[0] /= data.mass[0];
182
946
    if(do_velocity)
183
47
      data.vcom[0] /= data.mass[0];
184
946
    if(do_acceleration)
185
3
      data.acom[0] /= data.mass[0];
186
187
946
    return data.com[0];
188
  }
189
190
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
191
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Vector3 &
192
3
  getComFromCrba(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
193
                 DataTpl<Scalar,Options,JointCollectionTpl> & data)
194
  {
195
    PINOCCHIO_UNUSED_VARIABLE(model);
196
197
3
    assert(model.check(data) && "data is not consistent with model.");
198
3
    return data.com[0] = data.liMi[1].act(data.Ycrb[1].lever());
199
  }
200
201
  /* --- JACOBIAN ---------------------------------------------------------- */
202
  /* --- JACOBIAN ---------------------------------------------------------- */
203
  /* --- JACOBIAN ---------------------------------------------------------- */
204
205
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename Matrix3x>
206
  struct JacobianCenterOfMassBackwardStep
207
  : public fusion::JointUnaryVisitorBase< JacobianCenterOfMassBackwardStep<Scalar,Options,JointCollectionTpl,Matrix3x> >
208
  {
209
    typedef ModelTpl<Scalar,Options,JointCollectionTpl> Model;
210
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
211
212
    typedef boost::fusion::vector<const Model &,
213
                                  Data &,
214
                                  const Eigen::MatrixBase<Matrix3x> &,
215
                                  const bool &
216
                                  > ArgsType;
217
218
    template<typename JointModel>
219
546
    static void algo(const JointModelBase<JointModel> & jmodel,
220
                     JointDataBase<typename JointModel::JointDataDerived> & jdata,
221
                     const Model & model,
222
                     Data & data,
223
                     const Eigen::MatrixBase<Matrix3x> & Jcom,
224
                     const bool & computeSubtreeComs)
225
    {
226
546
      const JointIndex & i      = (JointIndex) jmodel.id();
227
546
      const JointIndex & parent = model.parents[i];
228
229
546
      data.com[parent]  += data.com[i];
230
546
      data.mass[parent] += data.mass[i];
231
232
      typedef typename Data::Matrix6x Matrix6x;
233
      typedef typename SizeDepType<JointModel::NV>::template ColsReturn<Matrix6x>::Type ColBlock;
234
235
546
      Matrix3x & Jcom_ = PINOCCHIO_EIGEN_CONST_CAST(Matrix3x,Jcom);
236
237
546
      ColBlock Jcols = jmodel.jointCols(data.J);
238

546
      Jcols = data.oMi[i].act(jdata.S());
239
240

1152
      for(Eigen::DenseIndex col_id = 0; col_id < jmodel.nv(); ++col_id)
241
      {
242
        jmodel.jointCols(Jcom_).col(col_id)
243





606
        = data.mass[i] * Jcols.col(col_id).template segment<3>(Motion::LINEAR)
244
606
        - data.com[i].cross(Jcols.col(col_id).template segment<3>(Motion::ANGULAR));
245
      }
246
247
546
      if(computeSubtreeComs)
248
546
        data.com[i] /= data.mass[i];
249
    }
250
251
  };
252
253
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename ConfigVectorType>
254
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Matrix3x &
255
4
  jacobianCenterOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
256
                       DataTpl<Scalar,Options,JointCollectionTpl> & data,
257
                       const Eigen::MatrixBase<ConfigVectorType> & q,
258
                       const bool computeSubtreeComs)
259
  {
260
4
    forwardKinematics(model, data, q);
261
4
    return jacobianCenterOfMass(model, data, computeSubtreeComs);
262
  }
263
264
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
265
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Matrix3x &
266
4
  jacobianCenterOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
267
                       DataTpl<Scalar,Options,JointCollectionTpl> & data,
268
                       const bool computeSubtreeComs)
269
  {
270
4
    assert(model.check(data) && "data is not consistent with model.");
271
272
    typedef ModelTpl<Scalar,Options,JointCollectionTpl> Model;
273
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
274
275
    typedef typename Model::JointIndex JointIndex;
276
277
    typedef typename Data::Matrix3x Matrix3x;
278
    typedef typename Data::SE3 SE3;
279
    typedef typename Data::Inertia Inertia;
280
281
4
    data.com[0].setZero();
282
4
    data.mass[0] = Scalar(0);
283
284
112
    for(JointIndex i=1; i<(JointIndex)(model.njoints); ++i)
285
    {
286
108
      const typename Inertia::Scalar & mass = model.inertias[i].mass();
287
108
      const typename SE3::Vector3 & lever = model.inertias[i].lever();
288
289
108
      data.mass[i] = mass;
290

108
      data.com[i].noalias() = mass*data.oMi[i].act(lever);
291
    }
292
293
    // Backward step
294
    typedef JacobianCenterOfMassBackwardStep<Scalar,Options,JointCollectionTpl,Matrix3x> Pass2;
295
112
    for(JointIndex i= (JointIndex) (model.njoints-1); i>0; --i)
296
    {
297
108
      Pass2::run(model.joints[i],data.joints[i],
298
108
                 typename Pass2::ArgsType(model,data,data.Jcom,computeSubtreeComs));
299
    }
300
301
4
    data.com[0] /= data.mass[0];
302
4
    data.Jcom /=  data.mass[0];
303
304
4
    return data.Jcom;
305
  }
306
307
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename Matrix3x>
308
  struct JacobianSubtreeCenterOfMassBackwardStep
309
  : public fusion::JointUnaryVisitorBase< JacobianSubtreeCenterOfMassBackwardStep<Scalar,Options,JointCollectionTpl,Matrix3x> >
310
  {
311
    typedef ModelTpl<Scalar,Options,JointCollectionTpl> Model;
312
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
313
314
    typedef boost::fusion::vector<const Model &,
315
    Data &,
316
    const JointIndex &,
317
    const Eigen::MatrixBase<Matrix3x> &
318
    > ArgsType;
319
320
    template<typename JointModel>
321
222
    static void algo(const JointModelBase<JointModel> & jmodel,
322
                     JointDataBase<typename JointModel::JointDataDerived> & jdata,
323
                     const Model & model,
324
                     Data & data,
325
                     const JointIndex & subtree_root_id,
326
                     const Eigen::MatrixBase<Matrix3x> & Jcom)
327
    {
328
      PINOCCHIO_UNUSED_VARIABLE(model);
329
330
222
      const JointIndex & i      = (JointIndex) jmodel.id();
331
332
      typedef typename Data::Matrix6x Matrix6x;
333
      typedef typename SizeDepType<JointModel::NV>::template ColsReturn<Matrix6x>::Type ColBlock;
334
335
222
      Matrix3x & Jcom_ = PINOCCHIO_EIGEN_CONST_CAST(Matrix3x,Jcom);
336
337
222
      ColBlock Jcols = jmodel.jointCols(data.J);
338

222
      Jcols = data.oMi[i].act(jdata.S());
339
340

704
      for(Eigen::DenseIndex col_id = 0; col_id < jmodel.nv(); ++col_id)
341
      {
342
        jmodel.jointCols(Jcom_).col(col_id)
343




482
        = Jcols.col(col_id).template segment<3>(Motion::LINEAR)
344
482
        - data.com[subtree_root_id].cross(Jcols.col(col_id).template segment<3>(Motion::ANGULAR));
345
      }
346
    }
347
348
  };
349
350
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename ConfigVectorType, typename Matrix3xLike>
351
  inline void
352
  jacobianSubtreeCenterOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
353
                              DataTpl<Scalar,Options,JointCollectionTpl> & data,
354
                              const Eigen::MatrixBase<ConfigVectorType> & q,
355
                              const JointIndex & rootSubtreeId,
356
                              const Eigen::MatrixBase<Matrix3xLike> & res)
357
  {
358
    forwardKinematics(model, data, q);
359
    jacobianSubtreeCenterOfMass(model, data, rootSubtreeId, res);
360
  }
361
362
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename Matrix3xLike>
363
  inline void
364
28
  jacobianSubtreeCenterOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
365
                              DataTpl<Scalar,Options,JointCollectionTpl> & data,
366
                              const JointIndex & rootSubtreeId,
367
                              const Eigen::MatrixBase<Matrix3xLike> & res)
368
  {
369
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
370
    typedef ModelTpl<Scalar,Options,JointCollectionTpl> Model;
371
372

28
    assert(model.check(data) && "data is not consistent with model.");
373

28
    PINOCCHIO_CHECK_INPUT_ARGUMENT((int)rootSubtreeId < model.njoints, "Invalid joint id.");
374







28
    PINOCCHIO_CHECK_ARGUMENT_SIZE(res.rows(), 3, "the resulting matrix does not have the right size.");
375







28
    PINOCCHIO_CHECK_ARGUMENT_SIZE(res.cols(), model.nv, "the resulting matrix does not have the right size.");
376
377
28
    Matrix3xLike & Jcom_subtree = PINOCCHIO_EIGEN_CONST_CAST(Matrix3xLike,res);
378
379
    typedef typename Data::SE3 SE3;
380
    typedef typename Data::Inertia Inertia;
381
382
    typedef typename Model::IndexVector IndexVector;
383
28
    const IndexVector & subtree = model.subtrees[rootSubtreeId];
384
385
28
    const bool computeSubtreeComs = true;
386
387
28
    if(rootSubtreeId == 0)
388
    {
389
1
      data.mass[0] = 0;
390
1
      data.com[0].setZero();
391
    }
392
393
193
    for(size_t k = 0; k < subtree.size(); ++k)
394
    {
395
165
      const JointIndex joint_id = subtree[k];
396
165
      const typename Inertia::Scalar & mass = model.inertias[joint_id].mass();
397
165
      const typename SE3::Vector3 & lever = model.inertias[joint_id].lever();
398
399
165
      data.mass[joint_id] = mass;
400

165
      data.com[joint_id] = mass*data.oMi[joint_id].act(lever);
401
    }
402
403
    // Backward step
404
    typedef JacobianCenterOfMassBackwardStep<Scalar,Options,JointCollectionTpl,Matrix3xLike> Pass2;
405
193
    for(Eigen::DenseIndex k = (Eigen::DenseIndex)subtree.size() - 1; k >= 0; --k)
406
    {
407
165
      const JointIndex joint_id = subtree[(size_t)k];
408

165
      Pass2::run(model.joints[joint_id],data.joints[joint_id],
409
                 typename Pass2::ArgsType(model,data,Jcom_subtree,computeSubtreeComs));
410
    }
411
412

28
    PINOCCHIO_CHECK_INPUT_ARGUMENT(data.mass[rootSubtreeId] > 0., "The mass of the subtree is not positive.");
413
28
    const Scalar mass_inv_subtree = Scalar(1)/data.mass[rootSubtreeId];
414
28
    typename Data::Vector3 & com_subtree = data.com[rootSubtreeId];
415
    if(!computeSubtreeComs)
416
      com_subtree *= mass_inv_subtree;
417
418
28
    if(rootSubtreeId == 0)
419
    {
420
1
      Jcom_subtree *= mass_inv_subtree;
421
1
      return; // skip the rest
422
    }
423
424
27
    const int idx_v = model.joints[rootSubtreeId].idx_v();
425
27
    const int nv_subtree = data.nvSubtree[rootSubtreeId];
426
427

27
    Jcom_subtree.middleCols(idx_v,nv_subtree) *= mass_inv_subtree;
428
429
    // Second backward step
430
    typedef JacobianSubtreeCenterOfMassBackwardStep<Scalar,Options,JointCollectionTpl,Matrix3xLike> Pass3;
431
138
    for(JointIndex parent = model.parents[rootSubtreeId];
432
138
        parent > 0;
433
111
        parent = model.parents[parent])
434
    {
435

111
      Pass3::run(model.joints[parent],data.joints[parent],
436
                 typename Pass3::ArgsType(model,data,rootSubtreeId,Jcom_subtree));
437
    }
438
  }
439
440
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl, typename Matrix3xLike>
441
  inline void
442
27
  getJacobianSubtreeCenterOfMass(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
443
                                 const DataTpl<Scalar,Options,JointCollectionTpl> & data,
444
                                 const JointIndex & rootSubtreeId,
445
                                 const Eigen::MatrixBase<Matrix3xLike> & res)
446
  {
447
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
448
449

27
    assert(model.check(data) && "data is not consistent with model.");
450

27
    PINOCCHIO_CHECK_INPUT_ARGUMENT(((int)rootSubtreeId < model.njoints), "Invalid joint id.");
451







27
    PINOCCHIO_CHECK_ARGUMENT_SIZE(res.rows(), 3, "the resulting matrix does not have the right size.");
452







27
    PINOCCHIO_CHECK_ARGUMENT_SIZE(res.cols(), model.nv, "the resulting matrix does not have the right size.");
453
454
27
    Matrix3xLike & Jcom_subtree = PINOCCHIO_EIGEN_CONST_CAST(Matrix3xLike,res);
455
456
27
    if(rootSubtreeId == 0)
457
    {
458
      Jcom_subtree = data.Jcom;
459
      return;
460
    }
461
462
27
    const int idx_v = model.joints[rootSubtreeId].idx_v();
463
27
    const int nv_subtree = data.nvSubtree[rootSubtreeId];
464
465
27
    const Scalar mass_ratio = data.mass[0] / data.mass[rootSubtreeId];
466
    Jcom_subtree.middleCols(idx_v,nv_subtree)
467


27
    = mass_ratio * data.Jcom.middleCols(idx_v,nv_subtree);
468
469
27
    const typename Data::Vector3 & com_subtree = data.com[rootSubtreeId];
470
471
268
    for(int parent = data.parents_fromRow[(size_t)idx_v];
472
268
        parent >= 0;
473
241
        parent = data.parents_fromRow[(size_t)parent])
474
    {
475
241
      typename Data::Matrix6x::ConstColXpr Jcol = data.J.col(parent);
476



241
      Jcom_subtree.col(parent).noalias() = Jcol.template segment<3>(Motion::LINEAR) - com_subtree.cross(Jcol.template segment<3>(Motion::ANGULAR));
477
    }
478
  }
479
480
  template<typename Scalar, int Options, template<typename,int> class JointCollectionTpl>
481
  inline const typename DataTpl<Scalar,Options,JointCollectionTpl>::Matrix3x &
482
7
  getJacobianComFromCrba(const ModelTpl<Scalar,Options,JointCollectionTpl> & model,
483
                         DataTpl<Scalar,Options,JointCollectionTpl> & data)
484
  {
485
    PINOCCHIO_UNUSED_VARIABLE(model);
486

7
    assert(model.check(data) && "data is not consistent with model.");
487
488
    typedef DataTpl<Scalar,Options,JointCollectionTpl> Data;
489
    typedef typename Data::SE3 SE3;
490
491
7
    const SE3 & oM1 = data.liMi[1];
492
493
    // Extract the total mass of the system.
494
7
    data.mass[0] = data.M(0,0);
495
496
    // As the 6 first rows of M*a are a wrench, we just need to multiply by the
497
    // relative rotation between the first joint and the world
498


7
    const typename SE3::Matrix3 oR1_over_m (oM1.rotation() / data.M(0,0));
499
500
    // I don't know why, but the colwise multiplication is much more faster
501
    // than the direct Eigen multiplication
502
231
    for(long k=0; k<model.nv;++k)
503



224
      data.Jcom.col(k).noalias() = oR1_over_m * data.M.template topRows<3>().col(k);
504
505
7
    return data.Jcom;
506
  }
507
508
} // namespace pinocchio
509
510
/// @endcond
511
512
#endif // ifndef __pinocchio_algorithm_center_of_mass_hxx__