Skip to content

Commit 00a2ec9

Browse files
author
ipuch
committed
tests(joint-ellipsoid): Sdot as finite difference
1 parent 298fd21 commit 00a2ec9

File tree

4 files changed

+109
-36
lines changed

4 files changed

+109
-36
lines changed

bindings/python/parsers/graph/expose-edges.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ namespace pinocchio
3636
const int JointSphericalZYX::nq;
3737
const int JointSphericalZYX::nv;
3838

39+
const int JointEllipsoid::nq;
40+
const int JointEllipsoid::nv;
41+
3942
const int JointTranslation::nq;
4043
const int JointTranslation::nv;
4144

@@ -117,6 +120,15 @@ namespace pinocchio
117120
.def_readonly("nq", &JointEllipsoid::nq, "Number of configuration variables.")
118121
.def_readonly("nv", &JointEllipsoid::nv, "Number of tangent variables.");
119122

123+
bp::class_<JointEllipsoid>(
124+
"JointEllipsoid", "Represents an ellipsoidal joint.",
125+
bp::init<>(bp::args("self", "radius_a", "radius_b", "radius_c"), "Constructor with radii."))
126+
.def_readwrite("radius_a", &JointEllipsoid::radius_a, "Semi-axis length in the x direction.")
127+
.def_readwrite("radius_b", &JointEllipsoid::radius_b, "Semi-axis length in the y direction.")
128+
.def_readwrite("radius_c", &JointEllipsoid::radius_c, "Semi-axis length in the z direction.")
129+
.def_readonly("nq", &JointEllipsoid::nq, "Number of configuration variables.")
130+
.def_readonly("nv", &JointEllipsoid::nv, "Number of tangent variables.");
131+
120132
bp::class_<JointTranslation>(
121133
"JointTranslation", "Represents a translation joint.",
122134
bp::init<>(bp::args("self"), "Default constructor."))

include/pinocchio/multibody/joint/joint-ellipsoid.hpp

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#include "pinocchio/math/matrix.hpp"
1313
#include "pinocchio/spatial/inertia.hpp"
1414
#include "pinocchio/spatial/skew.hpp"
15-
#include "pinocchio/multibody/joint-motion-subspace.hpp" // because we are dense.
15+
#include "pinocchio/multibody/joint-motion-subspace.hpp"
1616

1717
namespace pinocchio
1818
{
@@ -452,28 +452,23 @@ namespace pinocchio
452452
const Scalar c1s2 = c1 * s2;
453453

454454
Scalar S_11, S_21, S_31, S_12, S_22, S_32;
455-
// S_11 = c1 * (-radius_b * c0 * (c0 * s2 + c2 * s0 * s1) - radius_c * s0 * (- c0 * c2 * s1 +
456-
// s0 * s2));
455+
457456
S_11 = dndoty_dqdot0 * radius_b * (c0 * s2 + c2 * s0 * s1)
458457
+ dndotz_dqdot0 * radius_c * (-c0 * c2 * s1 + s0 * s2);
459-
// S_21 = c1 * (radius_b * c0 * (- c0 * c2 + s0 * s1 * s2) - radius_c * s0 * (c0 * s1 * s2 +
460-
// c2 * s0));
458+
461459
S_21 = -dndoty_dqdot0 * radius_b * (-c0 * c2 + s0 * s1 * s2)
462460
+ dndotz_dqdot0 * radius_c * (c0 * s1 * s2 + c2 * s0);
463-
// S_31 = c0 * c1 * c1 * s0 * (radius_b - radius_c);
461+
464462
S_31 = c1 * (-dndoty_dqdot0 * radius_b * s0 + dndotz_dqdot0 * radius_c * c0);
465463

466-
// S_12 = radius_a * c1c2 * c1 + radius_b * s0 * s1 * (c0 * s2 + c2 * s0 * s1) - radius_c * c0
467-
// * s1 * (- c0 * c2 * s1 + s0 * s2);
468464
S_12 = dndotx_dqdot1 * radius_a * c1 * c2
469465
+ dndoty_dqdot1 * radius_b * (c0 * s2 + c2 * s0 * s1)
470466
+ dndotz_dqdot1 * radius_c * (-c0 * c2 * s1 + s0 * s2);
471-
// S_22 = - radius_a * c1s2 * c1 - radius_b * s0 * s1 * (- c0 * c2 + s0 * s1 * s2) - radius_c
472-
// * c0 * s1 * (c0 * s1 * s2 + c2 * s0);
467+
473468
S_22 = -dndotx_dqdot1 * radius_a * c1 * s2
474469
- dndoty_dqdot1 * radius_b * (-c0 * c2 + s0 * s1 * s2)
475470
+ dndotz_dqdot1 * radius_c * (c0 * s1 * s2 + c2 * s0);
476-
// S_32 = c1 * s1 * (radius_a - radius_b * s0 * s0 - radius_c * c0 * c0);
471+
477472
S_32 = dndotx_dqdot1 * radius_a * s1 - dndoty_dqdot1 * radius_b * c1 * s0
478473
+ dndotz_dqdot1 * radius_c * c0 * c1;
479474

unittest/joint-ellipsoid.cpp

Lines changed: 90 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,63 @@ void addJointAndBody(
3636
model.appendBodyToJoint(idx, Y);
3737
}
3838

39+
/// \brief Compute Sdot (motion subspace derivative) via finite differences
40+
template<typename JointModel>
41+
Eigen::Matrix<double, 6, JointModel::NV> finiteDiffSdot(
42+
const JointModel & jmodel,
43+
typename JointModel::JointDataDerived & jdata,
44+
const typename JointModel::ConfigVector_t & q,
45+
const typename JointModel::TangentVector_t & v,
46+
double eps = 1e-8)
47+
{
48+
typedef typename LieGroup<JointModel>::type LieGroupType;
49+
typedef typename JointModel::ConfigVector_t ConfigVector_t;
50+
typedef typename JointModel::TangentVector_t TangentVector_t;
51+
52+
const Eigen::DenseIndex nv = jmodel.nv();
53+
54+
Eigen::Matrix<double, 6, JointModel::NV> Sdot_fd;
55+
Sdot_fd.setZero();
56+
57+
ConfigVector_t q_integrated(q);
58+
TangentVector_t v_integrate(nv);
59+
v_integrate.setZero();
60+
61+
for (Eigen::DenseIndex k = 0; k < nv; ++k)
62+
{
63+
// Integrate along kth direction
64+
v_integrate[k] = eps;
65+
q_integrated = LieGroupType().integrate(q, v_integrate);
66+
67+
// Compute S at q + eps * e_k
68+
jmodel.calc(jdata, q_integrated);
69+
const Eigen::Matrix<double, 6, JointModel::NV> S_plus = jdata.S.matrix();
70+
71+
// Integrate in negative direction
72+
v_integrate[k] = -eps;
73+
q_integrated = LieGroupType().integrate(q, v_integrate);
74+
75+
// Compute S at q - eps * e_k
76+
jmodel.calc(jdata, q_integrated);
77+
const Eigen::Matrix<double, 6, JointModel::NV> S_minus = jdata.S.matrix();
78+
79+
// Compute dS/dq_k via central differences
80+
Eigen::Matrix<double, 6, JointModel::NV> dS_dqk = (S_plus - S_minus) / (2.0 * eps);
81+
82+
// Accumulate: Sdot += (dS/dq_k) * v_k
83+
Sdot_fd += dS_dqk * v[k];
84+
85+
// Reset
86+
v_integrate[k] = 0.;
87+
}
88+
89+
return Sdot_fd;
90+
}
91+
92+
// ============================================================================
93+
// Test Suite
94+
// ============================================================================
95+
3996
BOOST_AUTO_TEST_SUITE(JointEllipsoid)
4097

4198
BOOST_AUTO_TEST_CASE(vsSphericalZYX)
@@ -71,31 +128,8 @@ BOOST_AUTO_TEST_CASE(vsSphericalZYX)
71128
// Extract XYZ Euler angles from the rotation matrix
72129
// For XYZ convention: R = Rx(x) * Ry(y) * Rz(z)
73130
// We need to solve for x, y, z
74-
75131
Eigen::Vector3d q_e;
76-
77-
// From the Ellipsoid rotation matrix structure:
78-
// R(0,2) = sin(y)
79-
// R(1,2) = -sin(x)*cos(y)
80-
// R(2,2) = cos(x)*cos(y)
81-
82-
double sy = R(0, 2);
83-
q_e(1) = std::asin(sy); // y angle
84-
85-
double cy = std::cos(q_e(1));
86-
87-
if (std::abs(cy) > 1e-6)
88-
{
89-
// Not at singularity
90-
q_e(0) = std::atan2(-R(1, 2) / cy, R(2, 2) / cy); // x angle
91-
q_e(2) = std::atan2(-R(0, 1) / cy, R(0, 0) / cy); // z angle
92-
}
93-
else
94-
{
95-
// Gimbal lock - choose x = 0
96-
q_e(0) = 0.0;
97-
q_e(2) = std::atan2(R(1, 0), R(1, 1));
98-
}
132+
q_e = R.eulerAngles(0, 1, 2); // XYZ convention
99133

100134
// Get the motion subspace matrices (which give us the Jacobians)
101135
JointModelSphericalZYX jmodel_s;
@@ -291,4 +325,36 @@ BOOST_AUTO_TEST_CASE(vsCompositeTxTyTzRxRyRz)
291325
BOOST_CHECK(aAbaEllipsoid.isApprox(qddot_ellipsoid));
292326
}
293327

328+
BOOST_AUTO_TEST_CASE(testSdotFiniteDifferences)
329+
{
330+
using namespace pinocchio;
331+
332+
// Ellipsoid parameters
333+
double radius_a = 2.0;
334+
double radius_b = 1.5;
335+
double radius_c = 1.0;
336+
337+
JointModelEllipsoid jmodel(radius_a, radius_b, radius_c);
338+
jmodel.setIndexes(0, 0, 0);
339+
340+
JointDataEllipsoid jdata = jmodel.createData();
341+
342+
// Test configuration and velocity
343+
typedef JointModelEllipsoid::ConfigVector_t ConfigVector_t;
344+
typedef JointModelEllipsoid::TangentVector_t TangentVector_t;
345+
typedef LieGroup<JointModelEllipsoid>::type LieGroupType;
346+
347+
ConfigVector_t q = LieGroupType().random();
348+
TangentVector_t v = TangentVector_t::Random();
349+
350+
// Compute analytical Sdot
351+
const double eps = 1e-8;
352+
jmodel.calc(jdata, q, v);
353+
const Eigen::Matrix<double, 6, 3> Sdot_ref = jdata.Sdot.matrix();
354+
355+
// Compute Sdot via finite differences using helper function
356+
const Eigen::Matrix<double, 6, 3> Sdot_fd = finiteDiffSdot(jmodel, jdata, q, v, eps);
357+
358+
BOOST_CHECK(Sdot_ref.isApprox(Sdot_fd, sqrt(eps)));
359+
}
294360
BOOST_AUTO_TEST_SUITE_END()

unittest/model-graph.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ BOOST_AUTO_TEST_CASE(test_reverse_spherical_zyx)
617617
d_f.oMf[m_forward.getFrameId("body1", pinocchio::BODY)]));
618618
}
619619

620-
/// @brief test if reversing of a composite joint is correct.
620+
/// @brief test if reversing of an ellipsoid joint is correct.
621621
BOOST_AUTO_TEST_CASE(test_reverse_ellipsoid)
622622
{
623623
using namespace pinocchio::graph;

0 commit comments

Comments
 (0)