GCC Code Coverage Report


Directory: ./
File: include/pinocchio/algorithm/delassus.hxx
Date: 2025-02-12 21:03:38
Exec Total Coverage
Lines: 0 308 0.0%
Branches: 0 974 0.0%

Line Branch Exec Source
1 //
2 // Copyright (c) 2020-2024 INRIA CNRS
3 // Copyright (c) 2023 KU Leuven
4 //
5
6 #ifndef __pinocchio_algorithm_contact_delassus_hxx__
7 #define __pinocchio_algorithm_contact_delassus_hxx__
8
9 #include "pinocchio/algorithm/check.hpp"
10 #include "pinocchio/algorithm/contact-info.hpp"
11 #include "pinocchio/algorithm/model.hpp"
12 #include "pinocchio/multibody/fwd.hpp"
13
14 namespace pinocchio
15 {
16
17 template<
18 typename Scalar,
19 int Options,
20 template<typename, int> class JointCollectionTpl,
21 typename ConfigVectorType>
22 struct ComputeOSIMForwardStep
23 : public fusion::JointUnaryVisitorBase<
24 ComputeOSIMForwardStep<Scalar, Options, JointCollectionTpl, ConfigVectorType>>
25 {
26 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
27 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
28
29 typedef boost::fusion::vector<const Model &, Data &, const ConfigVectorType &> ArgsType;
30
31 template<typename JointModel>
32 static void algo(
33 const pinocchio::JointModelBase<JointModel> & jmodel,
34 pinocchio::JointDataBase<typename JointModel::JointDataDerived> & jdata,
35 const Model & model,
36 Data & data,
37 const Eigen::MatrixBase<ConfigVectorType> & q)
38 {
39 typedef typename Model::JointIndex JointIndex;
40
41 const JointIndex i = jmodel.id();
42 jmodel.calc(jdata.derived(), q.derived());
43
44 const JointIndex parent = model.parents[i];
45 data.liMi[i] = model.jointPlacements[i] * jdata.M();
46 if (parent > 0)
47 data.oMi[i] = data.oMi[parent] * data.liMi[i];
48 else
49 data.oMi[i] = data.liMi[i];
50
51 jmodel.jointCols(data.J) = data.oMi[i].act(jdata.S());
52 data.oYcrb[i] = data.oMi[i].act(model.inertias[i]);
53 data.oYaba[i] = data.oYcrb[i].matrix();
54 }
55 };
56
57 template<typename Scalar, int Options, template<typename, int> class JointCollectionTpl>
58 struct ComputeOSIMBackwardStep
59 : public fusion::JointUnaryVisitorBase<
60 ComputeOSIMBackwardStep<Scalar, Options, JointCollectionTpl>>
61 {
62 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
63 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
64
65 typedef boost::fusion::vector<const Model &, Data &> ArgsType;
66
67 template<typename JointModel>
68 static void algo(
69 const JointModelBase<JointModel> & jmodel,
70 JointDataBase<typename JointModel::JointDataDerived> & jdata,
71 const Model & model,
72 Data & data)
73 {
74 typedef typename Model::JointIndex JointIndex;
75 typedef typename Data::Inertia Inertia;
76 typedef typename Data::Matrix6 Matrix6;
77 typedef typename Data::Matrix6x Matrix6x;
78
79 const JointIndex i = jmodel.id();
80 const JointIndex parent = model.parents[i];
81 typename Inertia::Matrix6 & Ia = data.oYaba[i];
82
83 typedef typename SizeDepType<JointModel::NV>::template ColsReturn<Matrix6x>::Type ColBlock;
84 ColBlock Jcols = jmodel.jointCols(data.J);
85
86 jdata.U().noalias() = Ia * Jcols;
87 jdata.StU().noalias() = Jcols.transpose() * jdata.U();
88
89 // Account for the rotor inertia contribution
90 jdata.StU().diagonal() += jmodel.jointVelocitySelector(model.armature);
91
92 internal::PerformStYSInversion<Scalar>::run(jdata.StU(), jdata.Dinv());
93
94 jdata.UDinv().noalias() =
95 Jcols
96 * jdata.Dinv().transpose(); //@justin can we remove the transpose since Dinv() is symmetric?
97 data.oK[i].noalias() = jdata.UDinv() * Jcols.transpose();
98 data.oL[i].noalias() = -jdata.UDinv() * jdata.U().transpose();
99 data.oL[i] += Matrix6::Identity();
100
101 if (parent > 0)
102 {
103 jdata.UDinv().noalias() = jdata.U() * jdata.Dinv();
104 data.oYaba[parent] += Ia;
105 data.oYaba[parent].noalias() -= jdata.UDinv() * jdata.U().transpose();
106 }
107 }
108 };
109
110 template<
111 typename Scalar,
112 int Options,
113 template<typename, int> class JointCollectionTpl,
114 typename ConfigVectorType,
115 class ModelAllocator,
116 class DataAllocator,
117 typename MatrixType>
118 void computeDelassusMatrix(
119 const ModelTpl<Scalar, Options, JointCollectionTpl> & model,
120 DataTpl<Scalar, Options, JointCollectionTpl> & data,
121 const Eigen::MatrixBase<ConfigVectorType> & q,
122 const std::vector<RigidConstraintModelTpl<Scalar, Options>, ModelAllocator> & contact_models,
123 std::vector<RigidConstraintDataTpl<Scalar, Options>, DataAllocator> & contact_data,
124 const Eigen::MatrixBase<MatrixType> & delassus_,
125 const Scalar mu)
126 {
127 assert(model.check(data) && "data is not consistent with model.");
128 PINOCCHIO_CHECK_ARGUMENT_SIZE(
129 q.size(), model.nq, "The joint configuration vector is not of right size");
130 PINOCCHIO_CHECK_INPUT_ARGUMENT(
131 check_expression_if_real<Scalar>(mu >= Scalar(0)), "mu has to be positive");
132 PINOCCHIO_CHECK_ARGUMENT_SIZE(
133 contact_models.size(), contact_data.size(), "contact models and data size are not the same");
134
135 MatrixType & delassus = delassus_.const_cast_derived();
136 const size_t constraint_total_size = getTotalConstraintSize(contact_models);
137 PINOCCHIO_CHECK_ARGUMENT_SIZE(delassus_.rows(), (Eigen::DenseIndex)constraint_total_size);
138 PINOCCHIO_CHECK_ARGUMENT_SIZE(delassus_.cols(), (Eigen::DenseIndex)constraint_total_size);
139
140 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
141 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
142
143 typedef typename Model::JointIndex JointIndex;
144 typedef typename Model::SE3 SE3;
145 typedef typename Model::IndexVector IndexVector;
146 typedef RigidConstraintModelTpl<Scalar, Options> RigidConstraintModel;
147 typedef RigidConstraintDataTpl<Scalar, Options> RigidConstraintData;
148
149 typedef ComputeOSIMForwardStep<Scalar, Options, JointCollectionTpl, ConfigVectorType> Pass1;
150 for (JointIndex i = 1; i < (JointIndex)model.njoints; ++i)
151 {
152 Pass1::run(
153 model.joints[i], data.joints[i], typename Pass1::ArgsType(model, data, q.derived()));
154 }
155
156 for (size_t k = 0; k < contact_models.size(); ++k)
157 {
158 const RigidConstraintModel & cmodel = contact_models[k];
159 RigidConstraintData & cdata = contact_data[k];
160
161 const JointIndex joint1_id = cmodel.joint1_id;
162
163 // Compute relative placement between the joint and the contact frame
164 SE3 & oMc = cdata.oMc1;
165 oMc = data.oMi[joint1_id] * cmodel.joint1_placement; // contact placement
166
167 typedef typename Data::Inertia Inertia;
168 typedef typename Inertia::Symmetric3 Symmetric3;
169
170 // Add contact inertia to the joint articulated inertia
171 Symmetric3 S(Symmetric3::Zero());
172 if (cmodel.type == CONTACT_6D)
173 S.setDiagonal(Symmetric3::Vector3::Constant(mu));
174
175 const Inertia contact_inertia(mu, oMc.translation(), S);
176 data.oYaba[joint1_id] += contact_inertia.matrix();
177 }
178
179 typedef ComputeOSIMBackwardStep<Scalar, Options, JointCollectionTpl> Pass2;
180 for (JointIndex i = (JointIndex)model.njoints - 1; i > 0; --i)
181 {
182 Pass2::run(model.joints[i], data.joints[i], typename Pass2::ArgsType(model, data));
183 }
184
185 Eigen::DenseIndex current_row_id = 0;
186 for (size_t k = 0; k < contact_models.size(); ++k)
187 {
188 typedef typename RigidConstraintData::VectorOfMatrix6 VectorOfMatrix6;
189 const RigidConstraintModel & cmodel = contact_models[k];
190 RigidConstraintData & cdata = contact_data[k];
191
192 const JointIndex joint1_id = cmodel.joint1_id;
193 const SE3 & oMc1 = cdata.oMc1;
194 const IndexVector & support1 = model.supports[joint1_id];
195
196 {
197 VectorOfMatrix6 & propagators = cdata.extended_motion_propagators_joint1;
198 VectorOfMatrix6 & lambdas = cdata.lambdas_joint1;
199 switch (cmodel.type)
200 {
201 case CONTACT_3D: {
202 oMc1.toActionMatrixInverse(propagators.back());
203 propagators.back().template bottomRows<3>().setZero();
204 for (size_t j = support1.size() - 1; j > 1; --j)
205 {
206 lambdas[j].template leftCols<3>().noalias() =
207 data.oK[(size_t)support1[j]] * propagators[j].template topRows<3>().transpose();
208 propagators[j - 1].template topRows<3>().noalias() =
209 propagators[j].template topRows<3>() * data.oL[(size_t)support1[j]];
210 }
211 lambdas[1].template leftCols<3>().noalias() =
212 data.oK[(size_t)support1[1]] * propagators[1].template topRows<3>().transpose();
213
214 for (size_t j = 2; j < support1.size(); ++j)
215 {
216 lambdas[j].template leftCols<3>().noalias() +=
217 data.oL[(size_t)support1[j]] * lambdas[j - 1].template leftCols<3>();
218 }
219 break;
220 }
221 case CONTACT_6D: {
222 oMc1.toActionMatrixInverse(propagators.back());
223 for (size_t j = support1.size() - 1; j > 1; --j)
224 {
225 lambdas[j].noalias() = data.oK[(size_t)support1[j]] * propagators[j].transpose();
226 propagators[j - 1].noalias() = propagators[j] * data.oL[(size_t)support1[j]];
227 }
228 lambdas[1].noalias() = data.oK[(size_t)support1[1]] * propagators[1].transpose();
229
230 for (size_t j = 2; j < support1.size(); ++j)
231 {
232 lambdas[j].noalias() += data.oL[(size_t)support1[j]] * lambdas[j - 1];
233 }
234 break;
235 }
236 default: {
237 assert(false && "must never happened");
238 break;
239 }
240 }
241 }
242
243 // Fill the delassus matrix block-wise
244 {
245 const int size = cmodel.size();
246
247 const VectorOfMatrix6 & propagators = cdata.extended_motion_propagators_joint1;
248 const VectorOfMatrix6 & lambdas = cdata.lambdas_joint1;
249
250 Eigen::DenseIndex current_row_id_other = 0;
251 for (size_t i = 0; i < k; ++i)
252 {
253 const RigidConstraintModel & cmodel_other = contact_models[i];
254 const RigidConstraintData & cdata_other = contact_data[i];
255 const int size_other = cmodel_other.size();
256 const IndexVector & support1_other = model.supports[cmodel_other.joint1_id];
257
258 const VectorOfMatrix6 & propagators_other =
259 cdata_other.extended_motion_propagators_joint1;
260
261 size_t id_in_support1, id_in_support1_other;
262 findCommonAncestor(
263 model, joint1_id, cmodel_other.joint1_id, id_in_support1, id_in_support1_other);
264
265 delassus.block(current_row_id_other, current_row_id, size_other, size).noalias() =
266 propagators_other[id_in_support1_other].topRows(size_other)
267 * lambdas[id_in_support1].leftCols(size);
268
269 current_row_id_other += size_other;
270 }
271
272 assert(current_row_id_other == current_row_id && "current row indexes do not match.");
273 delassus.block(current_row_id, current_row_id, size, size).noalias() =
274 propagators.back().topRows(size) * lambdas.back().leftCols(size);
275 current_row_id += size;
276 }
277 }
278 assert(
279 current_row_id == delassus.rows()
280 && "current row indexes do not the number of rows in the Delassus matrix.");
281 }
282
283 template<
284 typename Scalar,
285 int Options,
286 template<typename, int> class JointCollectionTpl,
287 class Allocator>
288 inline void initPvDelassus(
289 const ModelTpl<Scalar, Options, JointCollectionTpl> & model,
290 DataTpl<Scalar, Options, JointCollectionTpl> & data,
291 const std::vector<RigidConstraintModelTpl<Scalar, Options>, Allocator> & contact_models)
292 {
293 std::fill(data.constraints_supported_dim.begin(), data.constraints_supported_dim.end(), 0);
294 for (std::size_t i = 0; i < contact_models.size(); ++i)
295 {
296 const RigidConstraintModelTpl<Scalar, Options> & contact_model = contact_models[i];
297 const JointIndex & joint_id = contact_model.joint1_id;
298 switch (contact_model.reference_frame)
299 {
300 case LOCAL:
301 if (contact_model.type == CONTACT_6D)
302 data.constraints_supported_dim[joint_id] += 6;
303 else if (contact_model.type == CONTACT_3D)
304 data.constraints_supported_dim[joint_id] += 3;
305 else
306 assert(false && "Must never happen");
307 break;
308 case WORLD:
309 assert(false && "WORLD not implemented");
310 break;
311 case LOCAL_WORLD_ALIGNED:
312 assert(false && "LOCAL_WORLD_ALIGNED not implemented");
313 break;
314 default:
315 assert(false && "Must never happen");
316 break;
317 }
318 data.accumulation_descendant[joint_id] = joint_id;
319
320 data.constraints_on_joint[joint_id].push_back(i);
321 }
322
323 // Running backprop to get the count of constraints
324 for (JointIndex i = (JointIndex)model.njoints - 1; i > 0; --i)
325 {
326 const JointIndex & parent = model.parents[i];
327 data.constraints_supported_dim[parent] += data.constraints_supported_dim[i];
328 for (std::size_t constraint : data.constraints_on_joint[i])
329 data.constraints_supported[i].insert(constraint);
330 for (std::size_t constraint : data.constraints_supported[i])
331 data.constraints_supported[parent].insert(
332 constraint); // For loops, will need to use a set data-structure
333 }
334 // Assign accumulation descendants for branching nodes
335 for (JointIndex i = (JointIndex)model.njoints - 1; i > 0; --i)
336 {
337 const JointIndex & parent = model.parents[i];
338 if (data.constraints_supported_dim[i] > 0)
339 {
340 data.joints_supporting_constraints.push_back(i);
341 data.accumulation_descendant[parent] = data.accumulation_descendant[i];
342 if (data.constraints_supported_dim[parent] > data.constraints_supported_dim[i])
343 {
344 data.accumulation_descendant[parent] = parent;
345 }
346 }
347 }
348
349 for (JointIndex i = 1; i < (JointIndex)model.njoints; i++)
350 {
351 if (data.accumulation_descendant[i] == i)
352 data.accumulation_joints.push_back(i);
353 }
354 // Assign accumulation ancestors
355 for (JointIndex i = (JointIndex)model.njoints - 1; i > 0; --i)
356 {
357 const JointIndex & parent = model.parents[i];
358 if (data.accumulation_descendant[parent] == parent)
359 {
360 if (data.accumulation_descendant[i] != 0)
361 data.accumulation_ancestor[data.accumulation_descendant[i]] = parent;
362 }
363 }
364 }
365
366 template<typename Scalar, int Options, template<typename, int> class JointCollectionTpl>
367 JointIndex findGreatestCommonAncestor(
368 const ModelTpl<Scalar, Options, JointCollectionTpl> & model,
369 const DataTpl<Scalar, Options, JointCollectionTpl> & data,
370 JointIndex joint1_id,
371 JointIndex joint2_id,
372 size_t & index_ancestor_in_support1,
373 size_t & index_ancestor_in_support2)
374 {
375 PINOCCHIO_CHECK_INPUT_ARGUMENT(
376 joint1_id < (JointIndex)model.njoints, "joint1_id is not valid.");
377 PINOCCHIO_CHECK_INPUT_ARGUMENT(
378 joint2_id < (JointIndex)model.njoints, "joint2_id is not valid.");
379
380 if (joint1_id == 0 || joint2_id == 0)
381 {
382 index_ancestor_in_support1 = index_ancestor_in_support2 = 0;
383 return 0;
384 }
385
386 index_ancestor_in_support1 = 0;
387 index_ancestor_in_support2 = 0;
388
389 if (data.constraints_supported[joint1_id].size() > 1)
390 index_ancestor_in_support1++;
391 if (data.constraints_supported[joint2_id].size() > 1)
392 index_ancestor_in_support2++;
393
394 while (joint1_id != joint2_id)
395 {
396 if (joint1_id > joint2_id)
397 {
398 joint1_id = data.accumulation_ancestor[joint1_id];
399 index_ancestor_in_support1++;
400 }
401 else
402 {
403 joint2_id = data.accumulation_ancestor[joint2_id];
404 index_ancestor_in_support2++;
405 }
406 }
407
408 index_ancestor_in_support1--;
409 index_ancestor_in_support2--;
410
411 return joint1_id;
412 }
413
414 template<typename Scalar, int Options, template<typename, int> class JointCollectionTpl>
415 struct ComputePvDelassusBackwardStep
416 : public fusion::JointUnaryVisitorBase<
417 ComputeOSIMBackwardStep<Scalar, Options, JointCollectionTpl>>
418 {
419 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
420 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
421
422 typedef boost::fusion::vector<const Model &, Data &> ArgsType;
423
424 template<typename JointModel>
425 static void algo(
426 const JointModelBase<JointModel> & jmodel,
427 JointDataBase<typename JointModel::JointDataDerived> & jdata,
428 const Model & model,
429 Data & data)
430 {
431 typedef typename Model::JointIndex JointIndex;
432 typedef typename Data::Inertia Inertia;
433 typedef typename Data::Matrix6x Matrix6x;
434
435 const JointIndex i = jmodel.id();
436 const JointIndex parent = model.parents[i];
437 typename Inertia::Matrix6 & Ia = data.oYaba[i];
438
439 typedef typename SizeDepType<JointModel::NV>::template ColsReturn<Matrix6x>::Type ColBlock;
440 ColBlock Jcols = jmodel.jointCols(data.J);
441
442 jdata.U().noalias() = Ia * Jcols;
443 jdata.StU().noalias() = Jcols.transpose() * jdata.U();
444
445 // Account for the rotor inertia contribution
446 jdata.StU().diagonal() += jmodel.jointVelocitySelector(model.armature);
447
448 internal::PerformStYSInversion<Scalar>::run(jdata.StU(), jdata.Dinv());
449
450 jdata.UDinv().noalias() = Jcols * jdata.Dinv().transpose();
451 data.oL[i].setIdentity();
452 data.oL[i].noalias() -= jdata.UDinv() * jdata.U().transpose();
453
454 if (parent > 0)
455 {
456 data.oYaba[parent].noalias() = Ia - jdata.UDinv() * jdata.U().transpose();
457 }
458 }
459 };
460
461 template<
462 typename Scalar,
463 int Options,
464 template<typename, int> class JointCollectionTpl,
465 typename ConfigVectorType,
466 class ModelAllocator,
467 class DataAllocator,
468 typename MatrixType>
469 void computePvDelassusMatrix(
470 const ModelTpl<Scalar, Options, JointCollectionTpl> & model,
471 DataTpl<Scalar, Options, JointCollectionTpl> & data,
472 const Eigen::MatrixBase<ConfigVectorType> & q,
473 const std::vector<RigidConstraintModelTpl<Scalar, Options>, ModelAllocator> & contact_models,
474 std::vector<RigidConstraintDataTpl<Scalar, Options>, DataAllocator> & contact_data,
475 const Eigen::MatrixBase<MatrixType> & delassus_,
476 const Scalar mu)
477 {
478 assert(model.check(data) && "data is not consistent with model.");
479 PINOCCHIO_CHECK_ARGUMENT_SIZE(
480 q.size(), model.nq, "The joint configuration vector is not of right size");
481 PINOCCHIO_CHECK_INPUT_ARGUMENT(
482 check_expression_if_real<Scalar>(mu >= Scalar(0)), "mu has to be positive");
483 PINOCCHIO_CHECK_ARGUMENT_SIZE(
484 contact_models.size(), contact_data.size(), "contact models and data size are not the same");
485
486 MatrixType & delassus = delassus_.const_cast_derived();
487 const size_t constraint_total_size = getTotalConstraintSize(contact_models);
488 PINOCCHIO_CHECK_ARGUMENT_SIZE(delassus_.rows(), (Eigen::DenseIndex)constraint_total_size);
489 PINOCCHIO_CHECK_ARGUMENT_SIZE(delassus_.cols(), (Eigen::DenseIndex)constraint_total_size);
490
491 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
492
493 typename Data::Vector6 scratch_pad_vector = Data::Vector6::Zero();
494 typename Data::Vector6 scratch_pad_vector2 = Data::Vector6::Zero();
495
496 typename Data::Matrix6 scratch_pad1;
497 typename Data::Matrix6 scratch_pad2;
498
499 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
500
501 typedef typename Model::JointIndex JointIndex;
502 typedef typename Model::SE3 SE3;
503 typedef RigidConstraintModelTpl<Scalar, Options> RigidConstraintModel;
504 typedef RigidConstraintDataTpl<Scalar, Options> RigidConstraintData;
505
506 typedef ComputeOSIMForwardStep<Scalar, Options, JointCollectionTpl, ConfigVectorType> Pass1;
507 for (JointIndex i = 1; i < (JointIndex)model.njoints; ++i)
508 {
509 Pass1::run(
510 model.joints[i], data.joints[i], typename Pass1::ArgsType(model, data, q.derived()));
511 }
512
513 for (size_t k = 0; k < contact_models.size(); ++k)
514 {
515 const RigidConstraintModel & cmodel = contact_models[k];
516 RigidConstraintData & cdata = contact_data[k];
517
518 const JointIndex joint1_id = cmodel.joint1_id;
519
520 // Compute relative placement between the joint and the contact frame
521 SE3 & oMc = cdata.oMc1;
522 oMc = data.oMi[joint1_id] * cmodel.joint1_placement; // contact placement
523
524 typedef typename Data::Inertia Inertia;
525 typedef typename Inertia::Symmetric3 Symmetric3;
526
527 // Add contact inertia to the joint articulated inertia
528 Symmetric3 S(Symmetric3::Zero());
529 if (cmodel.type == CONTACT_6D)
530 S.setDiagonal(Symmetric3::Vector3::Constant(mu));
531
532 const Inertia contact_inertia(mu, oMc.translation(), S);
533 data.oYaba[joint1_id] += contact_inertia.matrix();
534
535 typedef typename RigidConstraintData::VectorOfMatrix6 VectorOfMatrix6;
536
537 VectorOfMatrix6 & propagators = cdata.extended_motion_propagators_joint1;
538 switch (cmodel.type)
539 {
540 case CONTACT_3D: {
541 oMc.toActionMatrixInverse(propagators[0]);
542 propagators[0].template bottomRows<3>().setZero();
543 break;
544 }
545 case CONTACT_6D: {
546 oMc.toActionMatrixInverse(propagators[0]);
547 break;
548 }
549
550 default: {
551 assert(false && "must never happen");
552 break;
553 }
554 }
555 }
556
557 // Initialize motion propagator and spatial_inv_inertia at all the accumulation joints
558 for (JointIndex i : data.accumulation_joints)
559 {
560
561 if (data.constraints_supported[i].size() == 1)
562 {
563 size_t constraint = data.constraints_on_joint[i][0];
564 data.extended_motion_propagator[i] =
565 contact_data[constraint].extended_motion_propagators_joint1[0];
566 }
567 else
568 {
569 data.extended_motion_propagator[i].setIdentity();
570 }
571 data.spatial_inv_inertia[i].setZero();
572 }
573
574 typedef ComputePvDelassusBackwardStep<Scalar, Options, JointCollectionTpl> Pass2;
575 for (JointIndex i = (JointIndex)model.njoints - 1; i > 0; --i)
576 {
577 Pass2::run(model.joints[i], data.joints[i], typename Pass2::ArgsType(model, data));
578
579 if (data.constraints_supported_dim[i] > 0)
580 {
581 size_t ad_i = data.accumulation_descendant[i];
582 int nv = model.joints[i].nv();
583 // propagate the spatial inverse inertia
584 if (nv == 1)
585 {
586 // Optimization for single DoF joints
587 if (data.constraints_supported_dim[ad_i] != 3)
588 {
589 // When propagating 6D constraints
590 scratch_pad_vector.noalias() =
591 data.extended_motion_propagator[i] * model.joints[i].jointCols(data.J);
592 scratch_pad_vector2.noalias() = scratch_pad_vector * data.joints[i].Dinv().coeff(0, 0);
593 data.spatial_inv_inertia[ad_i].noalias() +=
594 scratch_pad_vector2 * scratch_pad_vector.transpose();
595 }
596 else
597 {
598 // Propagating 3D constraints
599 scratch_pad_vector.template topRows<3>().noalias() =
600 data.extended_motion_propagator[i].template topRows<3>()
601 * model.joints[i].jointCols(data.J);
602 scratch_pad_vector2.template topRows<3>().noalias() =
603 scratch_pad_vector.template topRows<3>() * data.joints[i].Dinv().coeff(0, 0);
604 data.spatial_inv_inertia[ad_i].template topLeftCorner<3, 3>().noalias() +=
605 scratch_pad_vector2.template topRows<3>()
606 * scratch_pad_vector.template topRows<3>().transpose();
607 }
608 }
609 else if (nv == 6)
610 {
611 scratch_pad1.noalias() =
612 data.extended_motion_propagator[i] * model.joints[i].jointCols(data.J);
613 scratch_pad2.noalias() = scratch_pad1 * data.joints[i].Dinv();
614 data.spatial_inv_inertia[ad_i].noalias() += scratch_pad2 * scratch_pad1.transpose();
615 }
616 else if (nv > 1)
617 {
618 // Joints with more than 1 DoF
619 scratch_pad1.leftCols(nv).noalias() =
620 data.extended_motion_propagator[i] * model.joints[i].jointCols(data.J);
621 scratch_pad2.leftCols(nv).noalias() = scratch_pad1.leftCols(nv) * data.joints[i].Dinv();
622 data.spatial_inv_inertia[ad_i].noalias() +=
623 scratch_pad2.leftCols(nv) * scratch_pad1.leftCols(nv).transpose();
624 }
625 else
626 {
627 assert(false && "must never happen");
628 }
629 // propagate the EMP
630 const JointIndex parent = model.parents[i];
631 size_t par_ad_i = data.accumulation_descendant[parent];
632 if (parent > 0 && par_ad_i == ad_i)
633 {
634
635 if (data.constraints_supported_dim[ad_i] != 3)
636 {
637 data.extended_motion_propagator[parent].noalias() =
638 data.extended_motion_propagator[i] * data.oL[i];
639 }
640 else
641 {
642 data.extended_motion_propagator[parent].template topRows<3>().noalias() =
643 data.extended_motion_propagator[i].template topRows<3>() * data.oL[i];
644 }
645 }
646 else if (par_ad_i != ad_i) // TODO: optimize for 3D constraints
647 {
648 data.extended_motion_propagator2[ad_i].noalias() =
649 data.extended_motion_propagator[i] * data.oL[i];
650 }
651 }
652 }
653
654 for (JointIndex i : data.accumulation_joints)
655 {
656
657 size_t an_i = data.accumulation_ancestor[i];
658 if (an_i != 0) // TODO: optimize for 3D constraints. Not very important here.
659 {
660 scratch_pad1.noalias() =
661 data.extended_motion_propagator2[i] * data.spatial_inv_inertia[an_i];
662 data.spatial_inv_inertia[i].noalias() +=
663 scratch_pad1 * data.extended_motion_propagator2[i].transpose();
664 }
665 }
666
667 typedef typename RigidConstraintData::VectorOfMatrix6 VectorOfMatrix6;
668 // compute EMP in the reverse direction
669 for (size_t k = 0; k < contact_models.size(); ++k)
670 {
671 VectorOfMatrix6 & cemp = contact_data[k].extended_motion_propagators_joint1;
672 JointIndex curr_joint = contact_models[k].joint1_id;
673
674 JointIndex an_i;
675
676 if (data.constraints_supported[curr_joint].size() == 1)
677 {
678 cemp[0] = data.extended_motion_propagator2[curr_joint];
679 an_i = data.accumulation_ancestor[curr_joint];
680 }
681 else
682 {
683 an_i = curr_joint;
684 }
685
686 size_t counter = 1;
687 while (an_i > 1)
688 {
689 if (contact_models[k].size() != 3)
690 cemp[counter].noalias() = cemp[counter - 1] * data.extended_motion_propagator2[an_i];
691 else
692 cemp[counter].template topRows<3>().noalias() =
693 cemp[counter - 1].template topRows<3>() * data.extended_motion_propagator2[an_i];
694 curr_joint = an_i;
695 an_i = data.accumulation_ancestor[curr_joint];
696 counter++;
697 }
698 }
699
700 Eigen::DenseIndex current_row_id = 0;
701 for (size_t k = 0; k < contact_models.size(); ++k)
702 {
703 const RigidConstraintModel & cmodel = contact_models[k];
704 RigidConstraintData & cdata = contact_data[k];
705 const VectorOfMatrix6 & cemp = cdata.extended_motion_propagators_joint1;
706
707 const JointIndex joint1_id = cmodel.joint1_id;
708
709 // Fill the delassus matrix block-wise
710 {
711 const int size = cmodel.size();
712
713 Eigen::DenseIndex current_row_id_other = 0;
714 for (size_t i = 0; i < k; ++i)
715 {
716 RigidConstraintData & cdata_other = contact_data[i];
717 const RigidConstraintModel & cmodel_other = contact_models[i];
718 const int size_other = cmodel_other.size();
719 const JointIndex joint1_id_other = cmodel_other.joint1_id;
720
721 const VectorOfMatrix6 & cemp_other = cdata_other.extended_motion_propagators_joint1;
722
723 size_t id_in_support1, id_in_support1_other, gca;
724 gca = findGreatestCommonAncestor(
725 model, data, joint1_id, joint1_id_other, id_in_support1, id_in_support1_other);
726
727 scratch_pad1.noalias() = cemp_other[id_in_support1_other] * data.spatial_inv_inertia[gca];
728 if (size == 6)
729 {
730 if (size_other == 6)
731 delassus.template block<6, 6>(current_row_id_other, current_row_id).noalias() =
732 scratch_pad1 * cemp[id_in_support1].transpose();
733 else if (size_other == 3)
734 delassus.template block<3, 6>(current_row_id_other, current_row_id).noalias() =
735 scratch_pad1.template topRows<3>()
736 * cemp[id_in_support1].template topRows<6>().transpose();
737 }
738 else if (size == 3)
739 {
740 if (size_other == 6)
741 delassus.template block<6, 3>(current_row_id_other, current_row_id).noalias() =
742 scratch_pad1.template topRows<6>()
743 * cemp[id_in_support1].template topRows<3>().transpose();
744 else if (size_other == 3)
745 delassus.template block<3, 3>(current_row_id_other, current_row_id).noalias() =
746 scratch_pad1.template topRows<3>()
747 * cemp[id_in_support1].template topRows<3>().transpose();
748 }
749 else
750 {
751 delassus.block(current_row_id_other, current_row_id, size_other, size).noalias() =
752 scratch_pad1.topRows(size_other) * cemp[id_in_support1].topRows(size).transpose();
753 }
754 current_row_id_other += size_other;
755 }
756
757 assert(current_row_id_other == current_row_id && "current row indexes do not match.");
758 if (
759 data.constraints_supported_dim[joint1_id] > 6
760 || data.constraints_supported[joint1_id].size() > 1)
761 {
762 scratch_pad1.noalias() = cemp[0] * data.spatial_inv_inertia[joint1_id];
763 delassus.block(current_row_id, current_row_id, size, size).noalias() =
764 scratch_pad1.topRows(size) * cemp[0].topRows(size).transpose();
765 }
766 else
767 {
768 if (size == 6)
769 delassus.template block<6, 6>(current_row_id, current_row_id) =
770 data.spatial_inv_inertia[joint1_id];
771 else if (size == 3)
772 delassus.template block<3, 3>(current_row_id, current_row_id) =
773 data.spatial_inv_inertia[joint1_id].template topLeftCorner<3, 3>();
774 else
775 delassus.block(current_row_id, current_row_id, size, size) =
776 data.spatial_inv_inertia[joint1_id].topLeftCorner(size, size);
777 }
778 current_row_id += size;
779 }
780 }
781 assert(
782 current_row_id == delassus.rows()
783 && "current row indexes do not the number of rows in the Delassus matrix.");
784 }
785
786 template<
787 typename Scalar,
788 int Options,
789 template<typename, int> class JointCollectionTpl,
790 typename ConfigVectorType,
791 class ModelAllocator,
792 class DataAllocator,
793 typename MatrixType>
794 void computeDampedDelassusMatrixInverse(
795 const ModelTpl<Scalar, Options, JointCollectionTpl> & model,
796 DataTpl<Scalar, Options, JointCollectionTpl> & data,
797 const Eigen::MatrixBase<ConfigVectorType> & q,
798 const std::vector<RigidConstraintModelTpl<Scalar, Options>, ModelAllocator> & contact_models,
799 std::vector<RigidConstraintDataTpl<Scalar, Options>, DataAllocator> & contact_data,
800 const Eigen::MatrixBase<MatrixType> & damped_delassus_inverse_,
801 const Scalar mu,
802 const bool scaled,
803 const bool Pv)
804 {
805 PINOCCHIO_CHECK_INPUT_ARGUMENT(
806 check_expression_if_real<Scalar>(mu >= Eigen::NumTraits<Scalar>::dummy_precision()),
807 "mu is too small.");
808
809 const Scalar mu_inv = Scalar(1) / mu;
810 MatrixType & damped_delassus_inverse = damped_delassus_inverse_.const_cast_derived();
811
812 const Scalar mu_inv_square = mu_inv * mu_inv;
813 assert(
814 check_expression_if_real<Scalar>(mu_inv_square != std::numeric_limits<Scalar>::infinity())
815 && "mu_inv**2 is equal to infinity.");
816
817 if (Pv)
818 computePvDelassusMatrix(
819 model, data, q, contact_models, contact_data, damped_delassus_inverse, mu_inv);
820 else
821 computeDelassusMatrix(
822 model, data, q, contact_models, contact_data, damped_delassus_inverse, mu_inv);
823
824 damped_delassus_inverse *= -mu_inv;
825 damped_delassus_inverse.diagonal().array() += Scalar(1);
826 if (!scaled)
827 damped_delassus_inverse *= mu_inv;
828 }
829
830 } // namespace pinocchio
831
832 #endif // ifndef __pinocchio_algorithm_contact_delassus_hxx__
833