GCC Code Coverage Report


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