1111 #include " pinocchio/algorithm/aba-second-order-derivatives.hpp"
1212#endif // PINOCCHIO_LSP
1313
14+ #include " pinocchio/spatial/act-on-set.hpp"
15+
1416namespace pinocchio
1517{
1618
19+ // Forward sweep used by ComputeABASecondOrderDerivatives to set up the
20+ // articulated-body-style inner-product recursion: forward kinematics,
21+ // CRBA composite inertia, and the spatial force corresponding to the
22+ // input acceleration `a`. Gravity is deliberately zeroed at the base so
23+ // the result of the subsequent backward sweep is (dM/dq)*a only -- no
24+ // gravity contribution.
25+ template <
26+ typename Scalar,
27+ int Options,
28+ template <typename , int > class JointCollectionTpl ,
29+ typename ConfigVectorType,
30+ typename InputAccType>
31+ struct ComputeABASecondOrderDerivativesInnerForwardStep
32+ : public fusion::JointUnaryVisitorBase<ComputeABASecondOrderDerivativesInnerForwardStep<
33+ Scalar,
34+ Options,
35+ JointCollectionTpl,
36+ ConfigVectorType,
37+ InputAccType>>
38+ {
39+ typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
40+ typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
41+
42+ typedef boost::fusion::vector<
43+ const Model &,
44+ Data &,
45+ const ConfigVectorType &,
46+ const InputAccType &>
47+ ArgsType;
48+
49+ template <typename JointModel>
50+ static void algo (
51+ const JointModelBase<JointModel> & jmodel,
52+ JointDataBase<typename JointModel::JointDataDerived> & jdata,
53+ const Model & model,
54+ Data & data,
55+ const Eigen::MatrixBase<ConfigVectorType> & q,
56+ const Eigen::MatrixBase<InputAccType> & a)
57+ {
58+ typedef typename Model::JointIndex JointIndex;
59+ typedef typename Data::Motion Motion;
60+ typedef typename Data::Inertia Inertia;
61+
62+ const JointIndex i = jmodel.id ();
63+ const JointIndex parent = model.parents [i];
64+ Motion & oa = data.oa [i];
65+
66+ jmodel.calc (jdata.derived (), q.derived ());
67+ data.liMi [i] = model.jointPlacements [i] * jdata.M ();
68+
69+ if (parent > 0 )
70+ {
71+ data.oMi [i] = data.oMi [parent] * data.liMi [i];
72+ oa = data.oa [parent];
73+ }
74+ else
75+ {
76+ data.oMi [i] = data.liMi [i];
77+ oa.setZero (); // zero gravity: result is (dM/dq)*a, not (dM/dq)*a + dh/dq
78+ }
79+
80+ typedef
81+ typename SizeDepType<JointModel::NV >::template ColsReturn<typename Data::Matrix6x>::Type
82+ ColsBlock;
83+ ColsBlock J_cols = jmodel.jointCols (data.J );
84+ ColsBlock ddJ_cols = jmodel.jointCols (data.ddJ );
85+
86+ J_cols.noalias () = data.oMi [i].act (jdata.S ());
87+ motionSet::motionAction (oa, J_cols, ddJ_cols);
88+ oa += data.oMi [i].act (jdata.S () * jmodel.jointVelocitySelector (a));
89+
90+ Inertia & oY = data.oYcrb [i];
91+ oY = data.oMi [i].act (model.inertias [i]);
92+ data.of [i] = oY * oa;
93+ }
94+ };
95+
96+ // Backward sweep companion to ComputeABASecondOrderDerivativesInnerForwardStep.
97+ // Fills `dM_a_dq(j, k) = d/dq_k ( M(q) * a )_j` using `Ftmp1` and `Ftmp3`
98+ // as accumulation workspace (each sized 6 x model.nv).
99+ template <
100+ typename Scalar,
101+ int Options,
102+ template <typename , int > class JointCollectionTpl ,
103+ typename FtmpType,
104+ typename OutputMatrixType>
105+ struct ComputeABASecondOrderDerivativesInnerBackwardStep
106+ : public fusion::JointUnaryVisitorBase<ComputeABASecondOrderDerivativesInnerBackwardStep<
107+ Scalar,
108+ Options,
109+ JointCollectionTpl,
110+ FtmpType,
111+ OutputMatrixType>>
112+ {
113+ typedef ModelTpl<Scalar, Options, JointCollectionTpl> Model;
114+ typedef DataTpl<Scalar, Options, JointCollectionTpl> Data;
115+
116+ typedef boost::fusion::
117+ vector<const Model &, Data &, FtmpType &, FtmpType &, OutputMatrixType &>
118+ ArgsType;
119+
120+ template <typename JointModel>
121+ static void algo (
122+ const JointModelBase<JointModel> & jmodel,
123+ const Model & model,
124+ Data & data,
125+ FtmpType & Ftmp1,
126+ FtmpType & Ftmp3,
127+ OutputMatrixType & dM_a_dq)
128+ {
129+ typedef typename Model::JointIndex JointIndex;
130+
131+ const JointIndex i = jmodel.id ();
132+ const JointIndex parent = model.parents [i];
133+
134+ typedef
135+ typename SizeDepType<JointModel::NV >::template ColsReturn<typename Data::Matrix6x>::Type
136+ ColsBlock;
137+ ColsBlock J_cols = jmodel.jointCols (data.J );
138+ ColsBlock ddJ_cols = jmodel.jointCols (data.ddJ );
139+ ColsBlock tmp1 = jmodel.jointCols (Ftmp1);
140+ ColsBlock tmp3 = jmodel.jointCols (Ftmp3);
141+
142+ const Eigen::Index joint_idx = (Eigen::Index)jmodel.idx_v ();
143+ const Eigen::Index joint_dofs = (Eigen::Index)jmodel.nv ();
144+ const Eigen::Index subtree_dofs = (Eigen::Index)data.nvSubtree [i];
145+ const Eigen::Index successor_idx = joint_idx + joint_dofs;
146+ const Eigen::Index successor_dofs = subtree_dofs - joint_dofs;
147+
148+ typename Data::Inertia & oYcrb = data.oYcrb [i];
149+ motionSet::inertiaAction (oYcrb, J_cols, tmp1);
150+ motionSet::inertiaAction<ADDTO >(oYcrb, ddJ_cols, tmp3);
151+ motionSet::act<ADDTO >(J_cols, data.of [i], tmp3);
152+
153+ if (successor_dofs > 0 )
154+ dM_a_dq.block (joint_idx, successor_idx, joint_dofs, successor_dofs).noalias () =
155+ J_cols.transpose () * Ftmp3.middleCols (successor_idx, successor_dofs);
156+
157+ dM_a_dq.block (joint_idx, joint_idx, subtree_dofs, joint_dofs).noalias () =
158+ Ftmp1.middleCols (joint_idx, subtree_dofs).transpose () * ddJ_cols;
159+
160+ if (parent > 0 )
161+ {
162+ data.oYcrb [parent] += data.oYcrb [i];
163+ data.of [parent] += data.of [i];
164+ }
165+ }
166+ };
167+
17168 template <
18169 typename Scalar,
19170 int Options,
@@ -36,132 +187,122 @@ namespace pinocchio
36187 const Tensor3 & d2ddq_dqdv,
37188 const Tensor4 & d2ddq_dtaudq)
38189 {
39- assert (q.size () == model.nq && " The joint configuration vector is not of right size " );
40- assert (v.size () == model.nv && " The joint velocity vector is not of right size " );
41- assert (tau.size () == model.nv && " The joint torque vector is not of right size " );
42- assert (d2ddq_dqdq.dimension (0 ) == model.nv );
43- assert (d2ddq_dqdq.dimension (1 ) == model.nv );
44- assert (d2ddq_dqdq.dimension (2 ) == model.nv );
45- assert (d2ddq_dvdv.dimension (0 ) == model.nv );
46- assert (d2ddq_dvdv.dimension (1 ) == model.nv );
47- assert (d2ddq_dvdv.dimension (2 ) == model.nv );
48- assert (d2ddq_dqdv.dimension (0 ) == model.nv );
49- assert (d2ddq_dqdv.dimension (1 ) == model.nv );
50- assert (d2ddq_dqdv.dimension (2 ) == model.nv );
51- assert (d2ddq_dtaudq.dimension (0 ) == model.nv );
52- assert (d2ddq_dtaudq.dimension (1 ) == model.nv );
53- assert (d2ddq_dtaudq.dimension (2 ) == model.nv );
190+ PINOCCHIO_CHECK_ARGUMENT_SIZE (q.size (), model.nq );
191+ PINOCCHIO_CHECK_ARGUMENT_SIZE (v.size (), model.nv );
192+ PINOCCHIO_CHECK_ARGUMENT_SIZE (tau.size (), model.nv );
193+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdq.dimension (0 ), model.nv );
194+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdq.dimension (1 ), model.nv );
195+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdq.dimension (2 ), model.nv );
196+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dvdv.dimension (0 ), model.nv );
197+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dvdv.dimension (1 ), model.nv );
198+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dvdv.dimension (2 ), model.nv );
199+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdv.dimension (0 ), model.nv );
200+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdv.dimension (1 ), model.nv );
201+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dqdv.dimension (2 ), model.nv );
202+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dtaudq.dimension (0 ), model.nv );
203+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dtaudq.dimension (1 ), model.nv );
204+ PINOCCHIO_CHECK_ARGUMENT_SIZE (d2ddq_dtaudq.dimension (2 ), model.nv );
54205 assert (model.check (data) && " data is not consistent with model." );
206+ assert (model.check (MimicChecker ()) && " Function does not support mimic joints" );
55207
56- typedef typename DataTpl <Scalar, Options, JointCollectionTpl>::MatrixXs MatrixXs ;
57- typedef typename DataTpl<Scalar, Options, JointCollectionTpl>::VectorXs VectorXs ;
58- typedef typename DataTpl<Scalar, Options, JointCollectionTpl>::Tensor3x Tensor3x ;
59- typedef Eigen::Map<MatrixXs> MatrixMap ;
60- typedef Eigen::Map< const MatrixXs> ConstMatrixMap ;
61- typedef Eigen::Map<VectorXs> VectorMap ;
62- typedef Eigen::Map< const VectorXs> ConstVectorMap ;
208+ typedef ModelTpl <Scalar, Options, JointCollectionTpl> Model ;
209+ typedef DataTpl<Scalar, Options, JointCollectionTpl> Data ;
210+ typedef typename Data::MatrixXs MatrixXs ;
211+ typedef typename Data::VectorXs VectorXs ;
212+ typedef typename Data::Matrix6x Matrix6x ;
213+ typedef typename Data::Tensor3x Tensor3x ;
214+ typedef typename Model::JointIndex JointIndex ;
63215
64216 const Eigen::Index nv = model.nv ;
65217 const Eigen::Index nv2 = nv * nv;
66218
67- // 1. First-order ABA derivatives: data.ddq_dq , data.ddq_dv and data.Minv
68- // (upper triangular). Also leaves data.ddq filled .
219+ // 1) First-order ABA derivatives: fills data.ddq , data.ddq_dq, data.ddq_dv
220+ // and (upper triangle of) data.Minv .
69221 computeABADerivatives (model, data, q.derived (), v.derived (), tau.derived ());
70-
71- // computeABADerivatives only fills the upper triangle of Minv (which is
72- // symmetric). Symmetrize so it can be used directly as the outer multiplier.
73222 data.Minv .template triangularView <Eigen::StrictlyLower>() =
74223 data.Minv .transpose ().template triangularView <Eigen::StrictlyLower>();
75224
76- // 2. Second-order RNEA derivatives, evaluated at (q, v, qdd ). The 4th
77- // output d2tau_dadq carries the pages of dM/dq.
225+ // 2) Second-order RNEA derivatives at (q, v, ddq ). d2tau_dadq holds the
226+ // pages of dM/dq.
78227 ComputeRNEASecondOrderDerivatives (
79228 model, data, q.derived (), v.derived (), data.ddq , data.d2tau_dqdq , data.d2tau_dvdv ,
80229 data.d2tau_dqdv , data.d2tau_dadq );
81230
82- // 3. Pre-compute the three inner products used by the chain-rule formulas.
83- // Index conventions are kept consistent with the rest of the SO API:
84- // prodq(n, a, b) = (M_q_a * ddq_dq.col(b))_n
85- // prodv(n, a, b) = (M_q_a * ddq_dv.col(b))_n
86- // prodqdd(n, c, u) = (M_q_u * Minv)(n, c)
87- // where M_q_u(n, m) = d2tau_dadq(n, m, u) is the u-th page of dM/dq.
88- Tensor3x prodq (nv, nv, nv);
89- Tensor3x prodv (nv, nv, nv);
90- Tensor3x prodqdd (nv, nv, nv);
231+ // 3) Inner-Term: for each column w of (ddq_dq, ddq_dv, Minv), compute
232+ // (dM/dq) * column and store as page w of the corresponding tensor.
233+ typedef ComputeABASecondOrderDerivativesInnerForwardStep<
234+ Scalar, Options, JointCollectionTpl, ConfigVectorType, VectorXs>
235+ InnerPass1;
236+ typedef ComputeABASecondOrderDerivativesInnerBackwardStep<
237+ Scalar, Options, JointCollectionTpl, Matrix6x, MatrixXs>
238+ InnerPass2;
91239
92- for (Eigen::Index u = 0 ; u < nv; ++u)
93- {
94- ConstMatrixMap Mq_u (data.d2tau_dadq .data () + u * nv2, nv, nv);
240+ Tensor3x prodq (nv, nv, nv), prodv (nv, nv, nv), prodqdd (nv, nv, nv);
241+ Matrix6x Ftmp1 = Matrix6x::Zero (6 , nv);
242+ Matrix6x Ftmp3 = Matrix6x::Zero (6 , nv);
243+ MatrixXs inner_out (nv, nv);
244+
245+ auto fill_inner = [&](const auto & input_columns, Tensor3x & out_tensor) {
95246 for (Eigen::Index w = 0 ; w < nv; ++w)
96247 {
97- VectorMap prodq_uw (prodq.data () + (w * nv + u) * nv, nv); // prodq(:, u, w)
98- VectorMap prodv_uw (prodv.data () + (w * nv + u) * nv, nv); // prodv(:, u, w)
99- prodq_uw.noalias () = Mq_u * data.ddq_dq .col (w);
100- prodv_uw.noalias () = Mq_u * data.ddq_dv .col (w);
248+ VectorXs a_input = input_columns.col (w);
249+ for (JointIndex i = 1 ; i < (JointIndex)model.njoints ; ++i)
250+ InnerPass1::run (
251+ model.joints [i], data.joints [i],
252+ typename InnerPass1::ArgsType (model, data, q.derived (), a_input));
253+
254+ Ftmp1.setZero ();
255+ Ftmp3.setZero ();
256+ inner_out.setZero ();
257+ for (JointIndex i = (JointIndex)(model.njoints - 1 ); i > 0 ; --i)
258+ InnerPass2::run (
259+ model.joints [i],
260+ typename InnerPass2::ArgsType (model, data, Ftmp1, Ftmp3, inner_out));
261+
262+ Eigen::Map<MatrixXs>(out_tensor.data () + w * nv2, nv, nv) = inner_out;
101263 }
102- MatrixMap prodqdd_u (prodqdd.data () + u * nv2, nv, nv); // prodqdd page u
103- prodqdd_u.noalias () = Mq_u * data.Minv ;
104- }
264+ };
105265
106- // 4. Assemble the inner block term_in of size (nv, 4 * nv * nv). For each
107- // outer index u and inner index i, four nv-vectors are written:
108- // (4u ) i : d2tau_dqdq(:, i, u) + prodq(:, u, i) + prodq(:, i, u)
109- // (4u + 1) i : d2tau_dvdv(:, i, u)
110- // (4u + 2) i : d2tau_dqdv(:, i, u) + prodv(:, i, u)
111- // (4u + 3) i : prodqdd(:, i, u)
112- //
113- // The 2nd-dim slice of a Tensor3x is not contiguous in memory, so we
114- // use column-by-column maps for the prodq cross term.
266+ fill_inner (data.ddq_dq , prodq);
267+ fill_inner (data.ddq_dv , prodv);
268+ fill_inner (data.Minv , prodqdd);
269+
270+ // 4) Outer-Term: build term_in (nv x 4*nv*nv) and take a single dense
271+ // -M^{-1} * term_in product. Singh, Russell & Wensing (2023),
272+ // Fig. 14b: at practical robotics sizes the BLAS-3 GEMM beats the
273+ // AZA recursion on the Outer-Term.
115274 MatrixXs term_in (nv, 4 * nv2);
116275 for (Eigen::Index u = 0 ; u < nv; ++u)
117276 {
118- const Scalar * d2tau_dqdq_page = data.d2tau_dqdq .data () + u * nv2;
119- const Scalar * d2tau_dvdv_page = data.d2tau_dvdv .data () + u * nv2;
120- const Scalar * d2tau_dqdv_page = data.d2tau_dqdv .data () + u * nv2;
121- const Scalar * prodv_page = prodv.data () + u * nv2;
122- const Scalar * prodqdd_page = prodqdd.data () + u * nv2;
123-
124277 for (Eigen::Index i = 0 ; i < nv; ++i)
125278 {
126- // prodq(:, u, i) is the contiguous column at offset (i * nv + u) * nv
127- ConstVectorMap prodq_u_i (prodq.data () + (i * nv + u) * nv, nv);
128- // prodq(:, i, u) is the contiguous column at offset (u * nv + i) * nv
129- ConstVectorMap prodq_i_u (prodq.data () + (u * nv + i) * nv, nv);
130-
131- ConstVectorMap d2tau_dqdq_iu (d2tau_dqdq_page + i * nv, nv);
132- ConstVectorMap d2tau_dvdv_iu (d2tau_dvdv_page + i * nv, nv);
133- ConstVectorMap d2tau_dqdv_iu (d2tau_dqdv_page + i * nv, nv);
134- ConstVectorMap prodv_iu (prodv_page + i * nv, nv);
135- ConstVectorMap prodqdd_iu (prodqdd_page + i * nv, nv);
136-
137- term_in.col ((4 * u) * nv + i) = d2tau_dqdq_iu + prodq_u_i + prodq_i_u;
138- term_in.col ((4 * u + 1 ) * nv + i) = d2tau_dvdv_iu;
139- term_in.col ((4 * u + 2 ) * nv + i) = d2tau_dqdv_iu + prodv_iu;
140- term_in.col ((4 * u + 3 ) * nv + i) = prodqdd_iu;
279+ for (Eigen::Index n = 0 ; n < nv; ++n)
280+ {
281+ term_in (n, (4 * u + 0 ) * nv + i) =
282+ data.d2tau_dqdq (n, i, u) + prodq (n, u, i) + prodq (n, i, u);
283+ term_in (n, (4 * u + 1 ) * nv + i) = data.d2tau_dvdv (n, i, u);
284+ term_in (n, (4 * u + 2 ) * nv + i) = data.d2tau_dqdv (n, i, u) + prodv (n, i, u);
285+ term_in (n, (4 * u + 3 ) * nv + i) = prodqdd (n, u, i);
286+ }
141287 }
142288 }
143289
144- // 5. Outer DMM: term_out = -Minv * term_in. Single dense matrix product.
145290 MatrixXs term_out (nv, 4 * nv2);
146291 term_out.noalias () = -data.Minv * term_in;
147292
148- // 6. Scatter term_out columns back into the four output tensors.
149- Tensor1 & d2ddq_dqdq_ = const_cast <Tensor1 &>(d2ddq_dqdq);
150- Tensor2 & d2ddq_dvdv_ = const_cast <Tensor2 &>(d2ddq_dvdv);
151- Tensor3 & d2ddq_dqdv_ = const_cast <Tensor3 &>(d2ddq_dqdv);
152- Tensor4 & d2ddq_dtaudq_ = const_cast <Tensor4 &>(d2ddq_dtaudq);
153-
293+ // 5) Scatter term_out into the four output tensors.
154294 for (Eigen::Index u = 0 ; u < nv; ++u)
155295 {
156- MatrixMap dqdq_u (d2ddq_dqdq_.data () + u * nv2, nv, nv);
157- MatrixMap dvdv_u (d2ddq_dvdv_.data () + u * nv2, nv, nv);
158- MatrixMap dqdv_u (d2ddq_dqdv_.data () + u * nv2, nv, nv);
159- MatrixMap dtdq_u (d2ddq_dtaudq_.data () + u * nv2, nv, nv);
160-
161- dqdq_u = term_out.middleCols ((4 * u) * nv, nv);
162- dvdv_u = term_out.middleCols ((4 * u + 1 ) * nv, nv);
163- dqdv_u = term_out.middleCols ((4 * u + 2 ) * nv, nv);
164- dtdq_u = term_out.middleCols ((4 * u + 3 ) * nv, nv);
296+ for (Eigen::Index i = 0 ; i < nv; ++i)
297+ {
298+ for (Eigen::Index n = 0 ; n < nv; ++n)
299+ {
300+ const_cast <Tensor1 &>(d2ddq_dqdq)(n, i, u) = term_out (n, (4 * u + 0 ) * nv + i);
301+ const_cast <Tensor2 &>(d2ddq_dvdv)(n, i, u) = term_out (n, (4 * u + 1 ) * nv + i);
302+ const_cast <Tensor3 &>(d2ddq_dqdv)(n, i, u) = term_out (n, (4 * u + 2 ) * nv + i);
303+ const_cast <Tensor4 &>(d2ddq_dtaudq)(n, i, u) = term_out (n, (4 * u + 3 ) * nv + i);
304+ }
305+ }
165306 }
166307 }
167308
0 commit comments