Skip to content

Commit 50e174f

Browse files
authored
Merge pull request #1 from simeon-ned/feat/log_cholesky_parameters
Feat/log cholesky parameters
2 parents be4a836 + 79b35f0 commit 50e174f

File tree

5 files changed

+316
-3
lines changed

5 files changed

+316
-3
lines changed

cmake

include/pinocchio/bindings/python/spatial/inertia.hpp

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,12 @@ namespace pinocchio
3939
typedef typename Inertia::Matrix3 Matrix3;
4040
typedef typename Inertia::Vector6 Vector6;
4141
typedef typename Inertia::Matrix6 Matrix6;
42+
typedef typename Inertia::Matrix4 Matrix4;
43+
typedef typename Inertia::Matrix10 Matrix10;
44+
typedef typename Inertia::Vector10 Vector10;
4245

4346
typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1, Options> VectorXs;
47+
typedef Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic, Options> MatrixXs;
4448
typedef MotionTpl<Scalar, Options> Motion;
4549
typedef ForceTpl<Scalar, Options> Force;
4650

@@ -57,7 +61,6 @@ namespace pinocchio
5761
&InertiaPythonVisitor::makeFromMCI, bp::default_call_policies(),
5862
bp::args("mass", "lever", "inertia")),
5963
"Initialize from mass, lever and 3d inertia.")
60-
6164
.def(bp::init<>(bp::arg("self"), "Default constructor."))
6265
.def(bp::init<const Inertia &>((bp::arg("self"), bp::arg("clone")), "Copy constructor"))
6366

@@ -124,6 +127,17 @@ namespace pinocchio
124127
(Matrix6(Inertia::*)(const MotionDense<Motion> &) const)
125128
& Inertia::template variation<Motion>,
126129
bp::args("self", "v"), "Returns the time derivative of the inertia.")
130+
.def(
131+
"LogcholToDynamicParameters", &InertiaPythonVisitor::LogcholToDynamicParameters_proxy,
132+
bp::args("log_cholesky"),
133+
"Converts logarithmic Cholesky parameters directly to theta parameters.")
134+
.staticmethod("LogcholToDynamicParameters")
135+
.def(
136+
"calculateLogCholeskyJacobian",
137+
&InertiaPythonVisitor::calculateLogCholeskyJacobian_proxy, bp::args("log_cholesky"),
138+
"Calculates the Jacobian of the dynamic parameters with respect to the log-Cholesky "
139+
"parameters.")
140+
.staticmethod("calculateLogCholeskyJacobian")
127141

128142
#ifndef PINOCCHIO_PYTHON_SKIP_COMPARISON_OPERATIONS
129143
.def(bp::self == bp::self)
@@ -165,7 +179,32 @@ namespace pinocchio
165179
"I_{xy}, I_{yy}, I_{xz}, I_{yz}, I_{zz}]^T "
166180
"where I = I_C + mS^T(c)S(c) and I_C has its origin at the barycenter.")
167181
.staticmethod("FromDynamicParameters")
168-
182+
.def(
183+
"toPseudoInertia", &InertiaPythonVisitor::toPseudoInertia_proxy, bp::arg("self"),
184+
"Converts the inertia to a pseudo inertia matrix."
185+
"\nThe returned 4x4 pseudo inertia matrix has the form:"
186+
"\n[[ -0.5*I_xx + 0.5*I_yy + 0.5*I_zz, -I_xy, -I_xz, mr_x],"
187+
"\n [ -I_xy, 0.5*I_xx - 0.5*I_yy + 0.5*I_zz, -I_yz, mr_y],"
188+
"\n [ -I_xz, -I_yz, 0.5*I_xx + 0.5*I_yy - 0.5*I_zz, mr_z],"
189+
"\n [ mr_x, mr_y, mr_z, m ]].")
190+
.def(
191+
"FromPseudoInertia", &Inertia::FromPseudoInertia, bp::args("pseudo_inertia"),
192+
"Builds an inertia matrix from a 4x4 pseudo inertia matrix."
193+
"\nThe parameters are given as"
194+
"\npseudo_inertia = [[ -0.5*I_xx + 0.5*I_yy + 0.5*I_zz, -I_xy, -I_xz, mr_x],"
195+
"\n [ -I_xy, 0.5*I_xx - 0.5*I_yy + 0.5*I_zz, -I_yz, mr_y],"
196+
"\n [ -I_xz, -I_yz, 0.5*I_xx + 0.5*I_yy - 0.5*I_zz, mr_z],"
197+
"\n [ mr_x, mr_y, mr_z, m ]].")
198+
.staticmethod("FromPseudoInertia")
199+
.def(
200+
"FromLogCholeskyParameters", &Inertia::template FromLogCholeskyParameters<VectorXs>,
201+
bp::args("log_cholesky"),
202+
"Builds an InertiaTpl from log Cholesky parameters."
203+
"\nThe parameters are given as log_cholesky = [alpha, d_1, d_2, d_3, s_{12}, s_{23}, "
204+
"s_{13}, t_1, t_2, t_3] "
205+
"\nThe returned InertiaTpl object is constructed from the provided log Cholesky "
206+
"parameters.")
207+
.staticmethod("FromLogCholeskyParameters")
169208
.def(
170209
"FromSphere", &Inertia::FromSphere, bp::args("mass", "radius"),
171210
"Returns the Inertia of a sphere defined by a given mass and radius.")
@@ -236,6 +275,21 @@ namespace pinocchio
236275
return self.toDynamicParameters();
237276
}
238277

278+
static Matrix4 toPseudoInertia_proxy(const Inertia & self)
279+
{
280+
return self.toPseudoInertia();
281+
}
282+
283+
static VectorXs LogcholToDynamicParameters_proxy(const VectorXs & log_cholesky)
284+
{
285+
return Inertia::LogcholToDynamicParameters(log_cholesky);
286+
}
287+
288+
static MatrixXs calculateLogCholeskyJacobian_proxy(const VectorXs & log_cholesky)
289+
{
290+
return Inertia::calculateLogCholeskyJacobian(log_cholesky);
291+
}
292+
239293
static Inertia *
240294
makeFromMCI(const Scalar & mass, const Vector3 & lever, const Matrix3 & inertia)
241295
{

include/pinocchio/spatial/inertia.hpp

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ namespace pinocchio
241241
typedef Eigen::Matrix<T, 3, 3, U> Matrix3;
242242
typedef Eigen::Matrix<T, 4, 4, U> Matrix4;
243243
typedef Eigen::Matrix<T, 6, 6, U> Matrix6;
244+
typedef Eigen::Matrix<T, 10, 10, U> Matrix10;
244245
typedef Matrix6 ActionMatrix_t;
245246
typedef Vector3 Angular_t;
246247
typedef Vector3 Linear_t;
@@ -271,6 +272,7 @@ namespace pinocchio
271272

272273
typedef typename Symmetric3::AlphaSkewSquare AlphaSkewSquare;
273274
typedef typename Eigen::Matrix<Scalar, 10, 1, Options> Vector10;
275+
typedef typename Eigen::Matrix<Scalar, 10, 10, Options> Matrix10;
274276

275277
// Constructors
276278
InertiaTpl()
@@ -565,6 +567,220 @@ namespace pinocchio
565567
mass, lever, Symmetric3(params.template segment<6>(4)) + AlphaSkewSquare(mass, lever));
566568
}
567569

570+
/**
571+
* Converts the inertia to a pseudo inertia matrix.
572+
*
573+
* @return A 4x4 pseudo inertia matrix.
574+
*/
575+
Matrix4 toPseudoInertia() const
576+
{
577+
Vector10 dynamic_params = toDynamicParameters();
578+
Scalar m = dynamic_params[0];
579+
Vector3 h = dynamic_params.template segment<3>(1);
580+
Matrix3 I_bar;
581+
I_bar << dynamic_params[4], dynamic_params[5], dynamic_params[7], dynamic_params[5],
582+
dynamic_params[6], dynamic_params[8], dynamic_params[7], dynamic_params[8],
583+
dynamic_params[9];
584+
585+
Matrix3 Sigma = 0.5 * I_bar.trace() * Matrix3::Identity() - I_bar;
586+
Matrix4 pseudo_inertia = Matrix4::Zero();
587+
pseudo_inertia.template block<3, 3>(0, 0) = Sigma;
588+
pseudo_inertia.template block<3, 1>(0, 3) = h;
589+
pseudo_inertia.template block<1, 3>(3, 0) = h.transpose();
590+
pseudo_inertia(3, 3) = m;
591+
592+
return pseudo_inertia;
593+
}
594+
595+
/**
596+
* Builds an inertia matrix from a 4x4 pseudo inertia matrix.
597+
*
598+
* @param[in] pseudo_inertia A 4x4 pseudo inertia matrix.
599+
*
600+
* @return An InertiaTpl object constructed from the provided pseudo inertia matrix.
601+
*/
602+
static InertiaTpl FromPseudoInertia(const Matrix4 & pseudo_inertia)
603+
{
604+
Scalar m = pseudo_inertia(3, 3);
605+
Vector3 h = pseudo_inertia.template block<3, 1>(0, 3);
606+
Matrix3 Sigma = pseudo_inertia.template block<3, 3>(0, 0);
607+
Matrix3 I_bar = Sigma.trace() * Matrix3::Identity() - Sigma;
608+
609+
Vector10 dynamic_params;
610+
dynamic_params[0] = m; /* */
611+
dynamic_params.template segment<3>(1) = h;
612+
dynamic_params[4] = I_bar(0, 0);
613+
dynamic_params[5] = I_bar(0, 1);
614+
dynamic_params[6] = I_bar(1, 1);
615+
dynamic_params[7] = I_bar(0, 2);
616+
dynamic_params[8] = I_bar(1, 2);
617+
dynamic_params[9] = I_bar(2, 2);
618+
619+
return FromDynamicParameters(dynamic_params);
620+
}
621+
622+
/**
623+
* Converts logarithmic Cholesky parameters directly to dynamic parameters.
624+
*
625+
* @param[in] log_cholesky A 10-dimensional vector containing logarithmic Cholesky parameters.
626+
* The parameters are given as
627+
* \f$ \eta = [\alpha, d_1, d_2, d_3, s_{12}, s_{23}, s_{13}, t_1, t_2, t_3] \f$
628+
*
629+
* @return A 10-dimensional vector containing mass, first moments, and inertia tensor
630+
* components. The parameters are given as \f$ \theta = [m, mc_x, mc_y, mc_z, I_{xx}, I_{xy},
631+
* I_{yy}, I_{xz}, I_{yz}, I_{zz}] \f$
632+
*/
633+
template<typename Vector10Like>
634+
static Vector10 LogcholToDynamicParameters(const Eigen::MatrixBase<Vector10Like> & log_cholesky)
635+
{
636+
PINOCCHIO_ASSERT_MATRIX_SPECIFIC_SIZE(Vector10Like, log_cholesky, 10, 1);
637+
using Scalar = typename Vector10Like::Scalar;
638+
Vector10 dynamic_params;
639+
640+
const Scalar alpha = log_cholesky[0];
641+
const Scalar d1 = log_cholesky[1];
642+
const Scalar d2 = log_cholesky[2];
643+
const Scalar d3 = log_cholesky[3];
644+
const Scalar s12 = log_cholesky[4];
645+
const Scalar s23 = log_cholesky[5];
646+
const Scalar s13 = log_cholesky[6];
647+
const Scalar t1 = log_cholesky[7];
648+
const Scalar t2 = log_cholesky[8];
649+
const Scalar t3 = log_cholesky[9];
650+
651+
const Scalar exp_d1 = math::exp(d1);
652+
const Scalar exp_d2 = math::exp(d2);
653+
const Scalar exp_d3 = math::exp(d3);
654+
655+
dynamic_params[0] = 1;
656+
dynamic_params[1] = t1;
657+
dynamic_params[2] = t2;
658+
dynamic_params[3] = t3;
659+
dynamic_params[4] = s23 * s23 + t2 * t2 + t3 * t3 + exp_d2 * exp_d2 + exp_d3 * exp_d3;
660+
dynamic_params[5] = -s12 * exp_d2 - s13 * s23 - t1 * t2;
661+
dynamic_params[6] =
662+
s12 * s12 + s13 * s13 + t1 * t1 + t3 * t3 + exp_d1 * exp_d1 + exp_d3 * exp_d3;
663+
dynamic_params[7] = -s13 * exp_d3 - t1 * t3;
664+
dynamic_params[8] = -s23 * exp_d3 - t2 * t3;
665+
dynamic_params[9] =
666+
s12 * s12 + s13 * s13 + s23 * s23 + t1 * t1 + t2 * t2 + exp_d1 * exp_d1 + exp_d2 * exp_d2;
667+
668+
const Scalar exp_2_alpha = math::exp(2 * alpha);
669+
dynamic_params *= exp_2_alpha;
670+
671+
return dynamic_params;
672+
}
673+
674+
/**
675+
* Builds an Inertia from log Cholesky parameters.
676+
*
677+
* @param[in] log_cholesky A 10-dimensional vector containing logarithmic Cholesky parameters.
678+
* The parameters are given as
679+
* \f$ \eta = [\alpha, d_1, d_2, d_3, s_{12}, s_{23}, s_{13}, t_1, t_2, t_3] \f$
680+
*
681+
* @return An Inertia object constructed from the provided log Cholesky parameters.
682+
*/
683+
template<typename Vector10Like>
684+
static InertiaTpl
685+
FromLogCholeskyParameters(const Eigen::MatrixBase<Vector10Like> & log_cholesky)
686+
{
687+
PINOCCHIO_ASSERT_MATRIX_SPECIFIC_SIZE(Vector10Like, log_cholesky, 10, 1);
688+
Vector10 dynamic_params = LogcholToDynamicParameters(log_cholesky);
689+
return FromDynamicParameters(dynamic_params);
690+
}
691+
692+
/**
693+
* Calculates the Jacobian of the dynamic parameters with respect to the log-Cholesky
694+
* parameters.
695+
*
696+
* @param[in] log_cholesky A 10-dimensional vector containing the log-Cholesky parameters.
697+
*
698+
* @return A 10x10 matrix containing the Jacobian of dynamic parameters with respect to
699+
* log-Cholesky parameters.
700+
*/
701+
static Matrix10 calculateLogCholeskyJacobian(const Vector10 & log_cholesky)
702+
{
703+
Matrix10 jacobian = Matrix10::Zero();
704+
const Scalar alpha = log_cholesky[0];
705+
const Scalar d1 = log_cholesky[1];
706+
const Scalar d2 = log_cholesky[2];
707+
const Scalar d3 = log_cholesky[3];
708+
const Scalar s12 = log_cholesky[4];
709+
const Scalar s23 = log_cholesky[5];
710+
const Scalar s13 = log_cholesky[6];
711+
const Scalar t1 = log_cholesky[7];
712+
const Scalar t2 = log_cholesky[8];
713+
const Scalar t3 = log_cholesky[9];
714+
715+
const Scalar exp_2alpha = math::exp(2 * alpha);
716+
const Scalar exp_2d1 = math::exp(2 * d1);
717+
const Scalar exp_2d2 = math::exp(2 * d2);
718+
const Scalar exp_2d3 = math::exp(2 * d3);
719+
const Scalar exp_d1 = math::exp(d1);
720+
const Scalar exp_d2 = math::exp(d2);
721+
const Scalar exp_d3 = math::exp(d3);
722+
723+
jacobian(0, 0) = 2 * exp_2alpha;
724+
725+
jacobian(1, 0) = 2 * t1 * exp_2alpha;
726+
jacobian(1, 7) = exp_2alpha;
727+
728+
jacobian(2, 0) = 2 * t2 * exp_2alpha;
729+
jacobian(2, 8) = exp_2alpha;
730+
731+
jacobian(3, 0) = 2 * t3 * exp_2alpha;
732+
jacobian(3, 9) = exp_2alpha;
733+
734+
jacobian(4, 0) = 2 * (s23 * s23 + t2 * t2 + t3 * t3 + exp_2d2 + exp_2d3) * exp_2alpha;
735+
jacobian(4, 2) = 2 * exp_2alpha * exp_2d2;
736+
jacobian(4, 3) = 2 * exp_2alpha * exp_2d3;
737+
jacobian(4, 5) = 2 * s23 * exp_2alpha;
738+
jacobian(4, 8) = 2 * t2 * exp_2alpha;
739+
jacobian(4, 9) = 2 * t3 * exp_2alpha;
740+
741+
jacobian(5, 0) = -2 * (s12 * exp_d2 + s13 * s23 + t1 * t2) * exp_2alpha;
742+
jacobian(5, 2) = -s12 * exp_2alpha * exp_d2;
743+
jacobian(5, 4) = -exp_2alpha * exp_d2;
744+
jacobian(5, 5) = -s13 * exp_2alpha;
745+
jacobian(5, 6) = -s23 * exp_2alpha;
746+
jacobian(5, 7) = -t2 * exp_2alpha;
747+
jacobian(5, 8) = -t1 * exp_2alpha;
748+
749+
jacobian(6, 0) =
750+
2 * (s12 * s12 + s13 * s13 + t1 * t1 + t3 * t3 + exp_2d1 + exp_2d3) * exp_2alpha;
751+
jacobian(6, 1) = 2 * exp_2alpha * exp_2d1;
752+
jacobian(6, 3) = 2 * exp_2alpha * exp_2d3;
753+
jacobian(6, 4) = 2 * s12 * exp_2alpha;
754+
jacobian(6, 6) = 2 * s13 * exp_2alpha;
755+
jacobian(6, 7) = 2 * t1 * exp_2alpha;
756+
jacobian(6, 9) = 2 * t3 * exp_2alpha;
757+
758+
jacobian(7, 0) = -2 * (s13 * exp_d3 + t1 * t3) * exp_2alpha;
759+
jacobian(7, 3) = -s13 * exp_2alpha * exp_d3;
760+
jacobian(7, 6) = -exp_2alpha * exp_d3;
761+
jacobian(7, 7) = -t3 * exp_2alpha;
762+
jacobian(7, 9) = -t1 * exp_2alpha;
763+
764+
jacobian(8, 0) = -2 * (s23 * exp_d3 + t2 * t3) * exp_2alpha;
765+
jacobian(8, 3) = -s23 * exp_2alpha * exp_d3;
766+
jacobian(8, 5) = -exp_2alpha * exp_d3;
767+
jacobian(8, 8) = -t3 * exp_2alpha;
768+
jacobian(8, 9) = -t2 * exp_2alpha;
769+
770+
jacobian(9, 0) = 2
771+
* (s12 * s12 + s13 * s13 + s23 * s23 + t1 * t1 + t2 * t2 + exp_2d1 + exp_2d2)
772+
* exp_2alpha;
773+
jacobian(9, 1) = 2 * exp_2alpha * exp_2d1;
774+
jacobian(9, 2) = 2 * exp_2alpha * exp_2d2;
775+
jacobian(9, 4) = 2 * s12 * exp_2alpha;
776+
jacobian(9, 5) = 2 * s23 * exp_2alpha;
777+
jacobian(9, 6) = 2 * s13 * exp_2alpha;
778+
jacobian(9, 7) = 2 * t1 * exp_2alpha;
779+
jacobian(9, 8) = 2 * t2 * exp_2alpha;
780+
781+
return jacobian;
782+
}
783+
568784
// Arithmetic operators
569785
InertiaTpl & __equl__(const InertiaTpl & clone)
570786
{

unittest/python/bindings_inertia.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,40 @@ def test_dynamic_parameters(self):
111111
I2 = pin.Inertia.FromDynamicParameters(v)
112112
self.assertApprox(I2, I)
113113

114+
def test_pseudo_inertia(self):
115+
I = pin.Inertia.Random()
116+
pseudo = I.toPseudoInertia()
117+
self.assertTrue(np.all(np.linalg.eigvals(pseudo) > 0))
118+
I_back = pin.Inertia.FromPseudoInertia(pseudo)
119+
self.assertApprox(I_back.mass, I.mass)
120+
self.assertApprox(I_back.lever, I.lever)
121+
self.assertApprox(I_back.inertia, I.inertia)
122+
123+
def test_log_cholesky(self):
124+
eta = np.random.randn(10)
125+
alpha, d1, d2, d3, s12, s23, s13, t1, t2, t3 = eta
126+
# Compute the exponential terms
127+
exp_alpha = np.exp(alpha)
128+
exp_d1 = np.exp(d1)
129+
exp_d2 = np.exp(d2)
130+
exp_d3 = np.exp(d3)
131+
132+
# Create the matrix U
133+
U = exp_alpha * np.array(
134+
[
135+
[exp_d1, s12, s13, t1],
136+
[0, exp_d2, s23, t2],
137+
[0, 0, exp_d3, t3],
138+
[0, 0, 0, 1],
139+
]
140+
)
141+
pseudo_chol = U @ U.T
142+
143+
inertia = pin.Inertia.FromLogCholeskyParameters(eta)
144+
pseudo = inertia.toPseudoInertia()
145+
self.assertTrue(np.all(np.linalg.eigvals(pseudo) > 0))
146+
self.assertApprox(pseudo, pseudo_chol)
147+
114148
def test_array(self):
115149
I = pin.Inertia.Random()
116150
I_array = np.array(I)

unittest/spatial.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,15 @@ BOOST_AUTO_TEST_CASE(test_Inertia)
631631
Symmetric3(3.79705882, 0., 3.79705882, 0., 0., 1.81176471).matrix(),
632632
1e-5)); // Computed with hppfcl::Capsule
633633

634+
// Test Inertia From Pseudo Inertia and backward
635+
I1 = Inertia::FromCapsule(1., 2., 3);
636+
BOOST_CHECK(I1.isApprox(Inertia::FromPseudoInertia(I1.toPseudoInertia())));
637+
638+
// Test Inertia From LogCholesky parameters and backward
639+
Inertia::Vector10 logChol = Inertia::Vector10::Random();
640+
I1 = Inertia::FromLogCholeskyParameters(logChol);
641+
BOOST_CHECK(I1.isApprox(Inertia::FromDynamicParameters(I1.toDynamicParameters())));
642+
634643
// Copy operator
635644
Inertia aI_copy(aI);
636645
BOOST_CHECK(aI_copy == aI);

0 commit comments

Comments
 (0)