1616namespace pinocchio
1717{
1818
19- // Forward sweep used by ComputeABASecondOrderDerivatives to set up the
20- // articulated-body-style inner-product recursion: forward kinematics, the
21- // body spatial inertia oYcrb[i] (composited into a CRBA-style composite
22- // inertia by the backward sweep), and the spatial force oY * oa
23- // corresponding to the input acceleration `a` propagated through the chain.
24- // Gravity is deliberately zeroed at the base so the result of the
25- // subsequent backward sweep is (dM/dq)*a only -- no gravity contribution.
19+ // Forward sweep used by ComputeABASecondOrderDerivatives to propagate the
20+ // input acceleration `a` through the chain. Only acceleration-dependent
21+ // quantities are recomputed per call:
22+ // - data.oa[i]: spatial acceleration with zero gravity at the root, so
23+ // the backward sweep yields (dM/dq)*a only (no gravity contribution).
24+ // - ddJ_cols = oa x J_cols.
25+ // - data.of[i] = oYcrb_leaf[i] * oa, the spatial force at body i.
26+ // - data.oYcrb[i] is reset to oYcrb_leaf[i], since the backward sweep
27+ // composites oYcrb in place and we need the leaf values at the start
28+ // of each per-column iteration.
29+ // q-dependent quantities (data.oMi, data.liMi, data.J, data.joints[i] with
30+ // a valid jdata.S(), and the world-frame leaf inertia oYcrb_leaf cached by
31+ // the caller) are assumed to already be set by the prior computeABADerivatives
32+ // and ComputeRNEASecondOrderDerivatives calls, so we deliberately skip
33+ // jmodel.calc and the liMi/oMi/J_cols updates that a standard ABA forward
34+ // sweep would perform.
2635 template <
2736 typename Scalar,
2837 int Options,
2938 template <typename , int > class JointCollectionTpl ,
30- typename ConfigVectorType,
3139 typename InputAccType>
3240 struct ComputeABASecondOrderDerivativesInnerForwardStep
3341 : public fusion::JointUnaryVisitorBase<ComputeABASecondOrderDerivativesInnerForwardStep<
3442 Scalar,
3543 Options,
3644 JointCollectionTpl,
37- ConfigVectorType,
3845 InputAccType>>
3946 {
4047 typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
4148 typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
49+ typedef typename Data::Inertia Inertia;
50+ typedef std::vector<Inertia> InertiaVector;
4251
4352 typedef boost::fusion::
44- vector<const Model &, Data &, const ConfigVectorType &, const InputAccType &>
53+ vector<const Model &, Data &, const InputAccType &, const InertiaVector &>
4554 ArgsType;
4655
4756 template <typename JointModel>
@@ -50,44 +59,32 @@ namespace pinocchio
5059 JointDataBase<typename JointModel::JointDataDerived> & jdata,
5160 const Model & model,
5261 Data & data,
53- const Eigen::MatrixBase<ConfigVectorType > & q ,
54- const Eigen::MatrixBase<InputAccType> & a )
62+ const Eigen::MatrixBase<InputAccType > & a ,
63+ const InertiaVector & oYcrb_leaf )
5564 {
5665 typedef typename Model::JointIndex JointIndex;
5766 typedef typename Data::Motion Motion;
58- typedef typename Data::Inertia Inertia;
5967
6068 const JointIndex i = jmodel.id ();
6169 const JointIndex parent = model.parents [i];
6270 Motion & oa = data.oa [i];
6371
64- jmodel.calc (jdata.derived (), q.derived ());
65- data.liMi [i] = model.jointPlacements [i] * jdata.M ();
66-
6772 if (parent > 0 )
68- {
69- data.oMi [i] = data.oMi [parent] * data.liMi [i];
7073 oa = data.oa [parent];
71- }
7274 else
73- {
74- data.oMi [i] = data.liMi [i];
7575 oa.setZero (); // zero gravity: result is (dM/dq)*a, not (dM/dq)*a + dh/dq
76- }
7776
7877 typedef
7978 typename SizeDepType<JointModel::NV >::template ColsReturn<typename Data::Matrix6x>::Type
8079 ColsBlock;
8180 ColsBlock J_cols = jmodel.jointCols (data.J );
8281 ColsBlock ddJ_cols = jmodel.jointCols (data.ddJ );
8382
84- J_cols.noalias () = data.oMi [i].act (jdata.S ());
8583 motionSet::motionAction (oa, J_cols, ddJ_cols);
8684 oa += data.oMi [i].act (jdata.S () * jmodel.jointVelocitySelector (a));
8785
88- Inertia & oY = data.oYcrb [i];
89- oY = data.oMi [i].act (model.inertias [i]);
90- data.of [i] = oY * oa;
86+ data.oYcrb [i] = oYcrb_leaf[i];
87+ data.of [i] = data.oYcrb [i] * oa;
9188 }
9289 };
9390
@@ -216,9 +213,7 @@ namespace pinocchio
216213 // 1) First-order ABA derivatives: fills data.ddq, data.ddq_dq, data.ddq_dv
217214 // and (upper triangle of) data.Minv.
218215 computeABADerivatives (model, data, q.derived (), v.derived (), tau.derived ());
219- // Symmetrize Minv: computeABADerivatives only fills the upper triangle, but
220- // the dense GEMM in step 4 and the inner-term sweep for the dtaudq block
221- // (step 3, fill_inner(data.Minv, prodqdd)) both require the full matrix.
216+ // Symmetrize Minv: computeABADerivatives only fills the upper triangle
222217 data.Minv .template triangularView <Eigen::StrictlyLower>() =
223218 data.Minv .transpose ().template triangularView <Eigen::StrictlyLower>();
224219
@@ -230,13 +225,26 @@ namespace pinocchio
230225
231226 // 3) Inner-Term: for each column w of (ddq_dq, ddq_dv, Minv), compute
232227 // (dM/dq) * column and store as page w of the corresponding tensor.
228+ //
229+ // The per-column forward sweep below is the acceleration-only pass; it
230+ // reuses the q-dependent kinematics (data.oMi, data.J, data.joints[i])
231+ // already populated by computeABADerivatives and SO RNEA, and the leaf
232+ // body inertias cached in oYcrb_leaf right below.
233233 typedef ComputeABASecondOrderDerivativesInnerForwardStep<
234- Scalar, Options, JointCollectionTpl, ConfigVectorType, VectorXs>
234+ Scalar, Options, JointCollectionTpl, VectorXs>
235235 InnerPass1;
236236 typedef ComputeABASecondOrderDerivativesInnerBackwardStep<
237237 Scalar, Options, JointCollectionTpl, Matrix6x, MatrixXs>
238238 InnerPass2;
239239
240+ // Cache the world-frame leaf body inertia once. SO RNEA leaves
241+ // data.oYcrb in its composited state, and the inner-term backward sweep
242+ // mutates data.oYcrb again per call, so we restore the leaf values at
243+ // the start of each per-column forward pass.
244+ typename InnerPass1::InertiaVector oYcrb_leaf ((std::size_t )model.njoints );
245+ for (JointIndex i = 1 ; i < (JointIndex)model.njoints ; ++i)
246+ oYcrb_leaf[i] = data.oMi [i].act (model.inertias [i]);
247+
240248 // TODO: preallocate these scratch buffers (three nv-cubed tensors, two
241249 // 6-by-nv matrices, one nv-by-nv matrix, plus the two nv-by-4*nv^2
242250 // matrices in step 4) as Data fields to avoid the per-call allocations
@@ -254,7 +262,7 @@ namespace pinocchio
254262 for (JointIndex i = 1 ; i < (JointIndex)model.njoints ; ++i)
255263 InnerPass1::run (
256264 model.joints [i], data.joints [i],
257- typename InnerPass1::ArgsType (model, data, q. derived (), a_input ));
265+ typename InnerPass1::ArgsType (model, data, a_input, oYcrb_leaf ));
258266
259267 Ftmp1.setZero ();
260268 Ftmp3.setZero ();
0 commit comments