rbdlsim/3rdparty/rbdl/tests/LoopConstraintsTests.cc

2311 lines
69 KiB
C++

#include "rbdl_tests.h"
#include "rbdl/rbdl.h"
#include <cassert>
#include "Fixtures.h"
#include "PendulumModels.h"
using namespace std;
using namespace RigidBodyDynamics;
using namespace RigidBodyDynamics::Math;
const double TEST_PREC = 1.0e-11;
// Reduce an angle to the (-pi, pi] range.
static double inRange(double angle) {
while(angle > M_PI) {
angle -= 2. * M_PI;
}
while(angle <= -M_PI) {
angle += 2. * M_PI;
}
return angle;
}
struct FourBarLinkage {
FourBarLinkage()
: model()
, cs()
, q()
, qd()
, qdd()
, tau()
, l1(2.)
, l2(2.)
, m1(2.)
, m2(2.)
, idB1(0)
, idB2(0)
, idB3(0)
, idB4(0)
, idB5(0)
, X_p(Xtrans(Vector3d(l2, 0., 0.)))
, X_s(Xtrans(Vector3d(0., 0., 0.))) {
Body link1 = Body(m1, Vector3d(0.5 * l1, 0., 0.)
, Vector3d(0., 0., m1 * l1 * l1 / 3.));
Body link2 = Body(m2, Vector3d(0.5 * l2, 0., 0.)
, Vector3d(0., 0., m2 * l2 * l2 / 3.));
Vector3d vector3d_zero = Vector3d::Zero();
Body virtual_body(0., vector3d_zero, vector3d_zero);
Joint joint_rev_z(JointTypeRevoluteZ);
idB1 = model.AddBody(0 , Xtrans(Vector3d(0., 0., 0.)),
joint_rev_z, link1);
idB2 = model.AddBody(idB1, Xtrans(Vector3d(l1, 0., 0.)),
joint_rev_z, link2);
idB3 = model.AddBody(0 , Xtrans(Vector3d(0., 0., 0.)),
joint_rev_z, link1);
idB4 = model.AddBody(idB3, Xtrans(Vector3d(l1, 0., 0.)),
joint_rev_z, link2);
idB5 = model.AddBody(idB4, Xtrans(Vector3d(l2, 0., 0.)),
joint_rev_z
, virtual_body);
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0,0,0,1,0,0), true, 0.1);
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0,0,0,0,1,0), true, 0.1);
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0,0,1,0,0,0), true, 0.1);
cs.Bind(model);
q = VectorNd::Zero(model.dof_count);
qd = VectorNd::Zero(model.dof_count);
qdd = VectorNd::Zero(model.dof_count);
tau = VectorNd::Zero(model.dof_count);
}
Model model;
ConstraintSet cs;
VectorNd q;
VectorNd qd;
VectorNd qdd;
VectorNd tau;
double l1;
double l2;
double m1;
double m2;
unsigned int idB1;
unsigned int idB2;
unsigned int idB3;
unsigned int idB4;
unsigned int idB5;
SpatialTransform X_p;
SpatialTransform X_s;
};
struct FloatingFourBarLinkage {
FloatingFourBarLinkage()
: model()
, cs()
, q()
, qd()
, qdd()
, tau()
, l1(2.)
, l2(2.)
, m1(2.)
, m2(2.)
, idB0(0)
, idB1(0)
, idB2(0)
, idB3(0)
, idB4(0)
, idB5(0)
, X_p(Xtrans(Vector3d(l2, 0., 0.)))
, X_s(Xtrans(Vector3d(0., 0., 0.))) {
Body link1 = Body(m1, Vector3d(0.5 * l1, 0., 0.)
, Vector3d(0., 0., m1 * l1 * l1 / 3.));
Body link2 = Body(m2, Vector3d(0.5 * l2, 0., 0.)
, Vector3d(0., 0., m2 * l2 * l2 / 3.));
Vector3d vector3d_zero = Vector3d::Zero();
Body virtual_body(0., vector3d_zero, vector3d_zero);
Joint joint_trans(JointTypeTranslationXYZ);
Joint joint_rev_z(JointTypeRevoluteZ);
idB0 = model.AddBody(0, Xtrans(Vector3d(0., 0., 0.)), joint_trans
, virtual_body);
idB1 = model.AddBody(idB0, Xtrans(Vector3d(0., 0., 0.)),
joint_rev_z, link1);
idB2 = model.AddBody(idB1, Xtrans(Vector3d(l1, 0., 0.)),
joint_rev_z, link2);
idB3 = model.AddBody(idB0, Xtrans(Vector3d(0., 0., 0.)),
joint_rev_z, link1);
idB4 = model.AddBody(idB3, Xtrans(Vector3d(l1, 0., 0.)),
joint_rev_z, link2);
idB5 = model.AddBody(idB4, Xtrans(Vector3d(l2, 0., 0.)),
joint_rev_z
, virtual_body);
cs.AddContactConstraint(idB0, Vector3d::Zero(), Vector3d(1.,0.,0.));
cs.AddContactConstraint(idB0, Vector3d::Zero(), Vector3d(0.,1.,0.));
cs.AddContactConstraint(idB0, Vector3d::Zero(), Vector3d(0.,0.,1.));
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0.,0.,0.,1.,0.,0.), true, 0.1);
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0.,0.,0.,0.,1.,0.), true, 0.1);
cs.AddLoopConstraint(idB2, idB5, X_p, X_s,
SpatialVector(0.,0.,1.,0.,0.,0.), true, 0.1);
cs.Bind(model);
q = VectorNd::Zero(model.dof_count);
qd = VectorNd::Zero(model.dof_count);
qdd = VectorNd::Zero(model.dof_count);
tau = VectorNd::Zero(model.dof_count);
}
Model model;
ConstraintSet cs;
VectorNd q;
VectorNd qd;
VectorNd qdd;
VectorNd tau;
double l1;
double l2;
double m1;
double m2;
unsigned int idB0;
unsigned int idB1;
unsigned int idB2;
unsigned int idB3;
unsigned int idB4;
unsigned int idB5;
SpatialTransform X_p;
SpatialTransform X_s;
};
struct SliderCrank3D {
SliderCrank3D()
: model()
, cs()
, q()
, qd()
, id_p(0)
, id_s(0)
, X_p()
, X_s() {
double slider_mass = 5.;
double slider_height = 0.1;
double crank_link1_mass = 3.;
double crank_link1_length = 1.;
double crank_link2_mass = 1.;
double crank_link2_radius = 0.2;
double crank_link2_length = 3.;
double crank_link1_height = crank_link2_length - crank_link1_length
+ slider_height;
Body slider(slider_mass, Vector3d::Zero(), Vector3d(1., 1., 1.));
Body crankLink1(crank_link1_mass
, Vector3d(0.5 * crank_link1_length, 0., 0.)
, Vector3d(0., 0.
, crank_link1_mass * crank_link1_length * crank_link1_length / 3.));
Body crankLink2(crank_link2_mass
, Vector3d(0.5 * crank_link2_length, 0., 0.)
, Vector3d(crank_link2_mass * crank_link2_radius * crank_link2_radius/2.
, crank_link2_mass * (3. * crank_link2_radius * crank_link2_radius
+ crank_link2_length * crank_link2_length) / 12.
, crank_link2_mass * (3. * crank_link2_radius * crank_link2_radius
+ crank_link2_length * crank_link2_length) / 12.));
Joint joint_rev_z(JointTypeRevoluteZ);
Joint joint_sphere(JointTypeEulerZYX);
Joint joint_prs_x(SpatialVector(0.,0.,0.,1.,0.,0.));
id_p = model.AddBody(0
, SpatialTransform()
, joint_prs_x, slider);
unsigned int id_b1 = model.AddBody(0
, Xroty(-0.5*M_PI) * Xtrans(Vector3d(0., 0., crank_link1_height))
, joint_rev_z, crankLink1);
id_s = model.AddBody(id_b1
, Xroty(M_PI) * Xtrans(Vector3d(crank_link1_length, 0., 0.))
, joint_sphere, crankLink2);
X_p = Xtrans(Vector3d(0., 0., slider_height));
X_s = SpatialTransform(roty(-0.5 * M_PI),
Vector3d(crank_link2_length, 0, 0));
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,1,0,0), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,0,1,0), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,0,0,1), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,1,0,0,0), true, 0.1);
cs.Bind(model);
q = VectorNd::Zero(model.dof_count);
qd = VectorNd::Zero(model.dof_count);
qdd = VectorNd::Zero(model.dof_count);
tau = VectorNd::Zero(model.dof_count);
Matrix3d rot_ps
= (CalcBodyWorldOrientation(model,q,id_p).transpose()*X_p.E).transpose()
* CalcBodyWorldOrientation(model,q,id_s).transpose()*X_s.E;
REQUIRE (rot_ps(0,0) - 1. < TEST_PREC);
REQUIRE (rot_ps(1,1) - 1. < TEST_PREC);
REQUIRE (rot_ps(2,2) - 1. < TEST_PREC);
REQUIRE (rot_ps(0,1) < TEST_PREC);
REQUIRE (rot_ps(0,2) < TEST_PREC);
REQUIRE (rot_ps(1,0) < TEST_PREC);
REQUIRE (rot_ps(1,2) < TEST_PREC);
REQUIRE (rot_ps(2,0) < TEST_PREC);
REQUIRE (rot_ps(2,1) < TEST_PREC);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)
- CalcBodyToBaseCoordinates(model, q, id_s, X_s.r)).norm() < TEST_PREC);
}
Model model;
ConstraintSet cs;
VectorNd q;
VectorNd qd;
VectorNd qdd;
VectorNd tau;
unsigned int id_p;
unsigned int id_s;
SpatialTransform X_p;
SpatialTransform X_s;
};
struct SliderCrank3DSphericalJoint {
SliderCrank3DSphericalJoint()
: model()
, cs()
, q()
, qd()
, id_p(0)
, id_s(0)
, X_p()
, X_s() {
double slider_mass = 5.;
double slider_height = 0.1;
double crank_link1_mass = 3.;
double crank_link1_length = 1.;
double crank_link2_mass = 1.;
double crank_link2_radius = 0.2;
double crank_link2_length = 3.;
double crank_link1_height = crank_link2_length - crank_link1_length
+ slider_height;
Body slider(slider_mass, Vector3d::Zero(), Vector3d(1., 1., 1.));
Body crankLink1(crank_link1_mass
, Vector3d(0.5 * crank_link1_length, 0., 0.)
, Vector3d(0., 0.
, crank_link1_mass * crank_link1_length * crank_link1_length / 3.));
Body crankLink2(crank_link2_mass
, Vector3d(0.5 * crank_link2_length, 0., 0.)
, Vector3d(crank_link2_mass * crank_link2_radius * crank_link2_radius/2.
, crank_link2_mass * (3. * crank_link2_radius * crank_link2_radius
+ crank_link2_length * crank_link2_length) / 12.
, crank_link2_mass * (3. * crank_link2_radius * crank_link2_radius
+ crank_link2_length * crank_link2_length) / 12.));
Joint joint_rev_z(JointTypeRevoluteZ);
Joint joint_sphere(JointTypeSpherical);
Joint joint_prs_x(SpatialVector(0.,0.,0.,1.,0.,0.));
id_p = model.AddBody(0
, SpatialTransform()
, joint_prs_x, slider);
unsigned int id_b1 = model.AddBody(0
, Xroty(-0.5*M_PI) * Xtrans(Vector3d(0., 0., crank_link1_height))
, joint_rev_z, crankLink1);
id_s = model.AddBody(id_b1
, Xroty(M_PI) * Xtrans(Vector3d(crank_link1_length, 0., 0.))
, joint_sphere, crankLink2);
X_p = Xtrans(Vector3d(0., 0., slider_height));
X_s = SpatialTransform(roty(-0.5 * M_PI),Vector3d(crank_link2_length,0,0));
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,1,0,0), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,0,1,0), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,0,0,0,1), true, 0.1);
cs.AddLoopConstraint(id_p, id_s, X_p, X_s,
SpatialVector(0,0,1,0,0,0), true, 0.1);
cs.Bind(model);
q = VectorNd::Zero(model.q_size);
qd = VectorNd::Zero(model.dof_count);
qdd = VectorNd::Zero(model.dof_count);
tau = VectorNd::Zero(model.dof_count);
Matrix3d rot_ps
= (CalcBodyWorldOrientation(model,q,id_p).transpose()*X_p.E).transpose()
* CalcBodyWorldOrientation(model,q,id_s).transpose()*X_s.E;
REQUIRE (rot_ps(0,0) - 1. < TEST_PREC);
REQUIRE (rot_ps(1,1) - 1. < TEST_PREC);
REQUIRE (rot_ps(2,2) - 1. < TEST_PREC);
REQUIRE (rot_ps(0,1) < TEST_PREC);
REQUIRE (rot_ps(0,2) < TEST_PREC);
REQUIRE (rot_ps(1,0) < TEST_PREC);
REQUIRE (rot_ps(1,2) < TEST_PREC);
REQUIRE (rot_ps(2,0) < TEST_PREC);
REQUIRE (rot_ps(2,1) < TEST_PREC);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)
- CalcBodyToBaseCoordinates(model, q, id_s, X_s.r)).norm() < TEST_PREC);
}
Model model;
ConstraintSet cs;
VectorNd q;
VectorNd qd;
VectorNd qdd;
VectorNd tau;
unsigned int id_p;
unsigned int id_s;
SpatialTransform X_p;
SpatialTransform X_s;
};
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageConstraintErrors", "") {
VectorNd err = VectorNd::Zero(cs.size());
Vector3d pos1;
Vector3d pos2;
Vector3d posErr;
Matrix3d rot_p;
double angleErr;
// Test in zero position.
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT(Vector3d (0., 0., 0.), AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = M_PI * 3 / 4;
q[1] = -M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = 0.;
angleErr = sin(-0.5 * M_PI);
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
REQUIRE (std::fabs(posErr[1]) < TEST_PREC);
REQUIRE (std::fabs(posErr[2]) < TEST_PREC);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (Vector3d (posErr[0], 0., angleErr), AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = 0.;
q[1] = 0.;
q[2] = M_PI + 0.1;
q[3] = 0.;
q[4] = 0.;
angleErr = sin(-q[0] - q[1] + q[2] + q[3] + q[4]);
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT(Vector3d (posErr[0], posErr[1], angleErr), AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = 0.8;
q[1] = -0.4;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = 0.;
angleErr = sin(-q[0] - q[1] + q[2] + q[3] + q[4]);
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT(Vector3d (posErr[0], posErr[1], angleErr), AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageConstraintJacobian", "") {
MatrixNd G(MatrixNd::Zero(cs.size(), q.size()));
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
// Zero Q configuration, both arms of the 4-bar laying on the x-axis
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = -1.;
qd[1] = -1.;
qd[2] = -1.;
qd[3] = -1.;
qd[4] = 0.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Both arms of the 4-bar laying on the y-axis
q[0] = 0.5 * M_PI;
q[1] = 0.;
q[2] = 0.5 * M_PI;
q[3] = 0.;
q[4] = 0.;
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = -1.;
qd[1] = -1.;
qd[2] = -1.;
qd[3] = -1.;
qd[4] = 0.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Arms symmetric wrt y axis.
q[0] = M_PI * 3 / 4;
q[1] = -0.5 * M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = q[0] + q[1] - q[2] - q[3];
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = -1.;
qd[1] = -1.;
qd[2] = -2.;
qd[3] = +1.;
qd[4] = -1.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageConstraintsVelocityErrors", "") {
VectorNd errd(VectorNd::Zero(cs.size()));
VectorNd errdRef(VectorNd::Zero(cs.size()));
MatrixNd G(cs.size(), model.dof_count);
// Arms symmetric wrt y axis.
q[0] = M_PI * 3 / 4;
q[1] = -0.5 * M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = q[0] + q[1] - q[2] - q[3];
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = -1.;
qd[1] = -1.;
qd[2] = -2.;
qd[3] = +1.;
qd[4] = -1.;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
// Invalid velocities.
qd[0] = -1.;
qd[1] = -1.;
qd[2] = 0.;
qd[3] = 0.;
qd[4] = 0.;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
CalcConstraintsJacobian(model, q, cs, G);
errdRef = G * qd;
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageQAssembly", "") {
VectorNd weights(q.size());
VectorNd err(cs.size());
VectorNd errRef(VectorNd::Zero(cs.size()));
weights[0] = 1.;
weights[1] = 0.;
weights[2] = 1.;
weights[3] = 0.;
weights[4] = 0.;
VectorNd qRef = VectorNd::Zero(q.size());
qRef[0] = M_PI * 3 / 4;
qRef[1] = -0.5 * M_PI;
qRef[2] = M_PI - qRef[0];
qRef[3] = -qRef[1];
qRef[4] = qRef[0] + qRef[1] - qRef[2] - qRef[3];
REQUIRE (qRef[0] + qRef[1] - qRef[2] - qRef[3] - qRef[4] == 0.);
bool success;
// Feasible initial guess.
VectorNd qInit = VectorNd::Zero(q.size());
qInit = qRef;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT (inRange(q[0] + q[1]), IsClose(inRange(q[2] + q[3] + q[4]), TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[0], IsClose(q[0], TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[2], IsClose(q[2], TEST_PREC, TEST_PREC));
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[0] = qRef[0];
qInit[1] = qRef[1];
qInit[2] = qRef[2];
qInit[3] = qRef[3];
qInit[4] = qRef[4] + 0.05;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE(success);
REQUIRE_THAT (CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT (inRange(q[0] + q[1]), IsClose(inRange(q[2] + q[3] + q[4]), TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[0], IsClose(q[0], TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[2], IsClose(q[2], TEST_PREC, TEST_PREC));
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[0] = qRef[0] - 0.2;
qInit[1] = qRef[1] - 0.;
qInit[2] = qRef[2] + 0.1;
qInit[3] = qRef[3] - 0.03;
qInit[4] = qRef[4] + 0.05;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE (success);
REQUIRE_THAT (CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT (inRange(q[0] + q[1]), IsClose(inRange(q[2] + q[3] + q[4]), TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[0], IsClose(q[0], TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[2], IsClose(q[2], TEST_PREC, TEST_PREC));
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[0] = qRef[0] + 0.01;
qInit[1] = qRef[1] + 0.02;
qInit[2] = qRef[2] - 0.03;
qInit[3] = qRef[3] - 0.02;
qInit[4] = qRef[4] + 0.01;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE (success);
REQUIRE_THAT (CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT (inRange(q[0] + q[1]), IsClose(inRange(q[2] + q[3] + q[4]), TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[0], IsClose(q[0], TEST_PREC, TEST_PREC));
REQUIRE_THAT (qInit[2], IsClose(q[2], TEST_PREC, TEST_PREC));
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageQDotAssembly", "") {
VectorNd weights(q.size());
weights[0] = 1.;
weights[1] = 0.;
weights[2] = 1.;
weights[3] = 0.;
weights[4] = 0.;
q[0] = M_PI * 3 / 4;
q[1] = -0.5 * M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = q[0] + q[1] - q[2] - q[3];
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
VectorNd qdInit = VectorNd::Zero(q.size());
qdInit[0] = 0.01;
qdInit[1] = 0.5;
qdInit[2] = -0.7;
qdInit[3] = -0.5;
qdInit[4] = 0.3;
CalcAssemblyQDot(model, q, qdInit, cs, qd, weights);
MatrixNd G(MatrixNd::Zero(cs.size(), q.size()));
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (CalcPointVelocity6D(model, q, qd, idB2, X_p.r), AllCloseVector(CalcPointVelocity6D(model, q, qd, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
REQUIRE_THAT (qdInit[0], IsClose(qd[0], TEST_PREC, TEST_PREC));
REQUIRE_THAT (qdInit[2], IsClose(qd[2], TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_TestFourBarLinkageForwardDynamics", "") {
VectorNd qddDirect;
VectorNd qddNullSpace;
cs.SetSolver(LinearSolverColPivHouseholderQR);
#ifndef RBDL_USE_SIMPLE_MATH
// SimpleMath has no solver that can solve the system in this configuration.
// Configuration 1.
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = 0.;
qd[4] = 0.;
REQUIRE (qd[0] + qd[1] - qd[2] - qd[3] - qd[4] == 0.);
REQUIRE ((CalcPointVelocity(model, q, qd, idB2, X_p.r)
- CalcPointVelocity(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
tau[0] = 1.;
tau[1] = -2.;
tau[2] = 3.;
tau[3] = -5.;
tau[4] = 7.;
qddDirect = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qddDirect);
REQUIRE_THAT (CalcPointAcceleration6D(model, q, qd, qddDirect, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddDirect, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
qddNullSpace = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qddNullSpace);
REQUIRE_THAT(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
#endif
// Configuration 2.
q[0] = M_PI * 3 / 4;
q[1] = -0.5 * M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = q[0] + q[1] - q[2] - q[3];
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = -1.;
qd[1] = -1.;
qd[2] = -2.;
qd[3] = +1.;
qd[4] = -1.;
REQUIRE (qd[0] + qd[1] - qd[2] - qd[3] - qd[4] == 0.);
REQUIRE ((CalcPointVelocity(model, q, qd, idB2, X_p.r)
- CalcPointVelocity(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
tau[0] = 1.;
tau[1] = -2.;
tau[2] = 3.;
tau[3] = -5.;
tau[4] = 7.;
qddDirect = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qddDirect);
REQUIRE_THAT(CalcPointAcceleration6D(model, q, qd, qddDirect, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddDirect, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
qddNullSpace = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qddNullSpace);
REQUIRE_THAT(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
// Note:
// The Range Space Sparse method can't be used because the H matrix has a 0
// on the diagonal and the LTL factorization tries to divide by 0.
// Note:
// LinearSolverPartialPivLU does not work because the A matrix in the dynamic
// system is not invertible.
// Note:
// LinearSolverHouseholderQR sometimes does not work well when the system is
// in a singular configuration.
}
TEST_CASE_METHOD (FourBarLinkage, __FILE__"_FourBarLinkageImpulse", "") {
VectorNd qdPlusDirect(qd.size());
VectorNd qdPlusRangeSpaceSparse(qd.size());
VectorNd qdPlusNullSpace(qd.size());
VectorNd errd(cs.size());
q[0] = M_PI * 3 / 4;
q[1] = -0.5 * M_PI;
q[2] = M_PI - q[0];
q[3] = -q[1];
q[4] = q[0] + q[1] - q[2] - q[3];
REQUIRE (q[0] + q[1] - q[2] - q[3] - q[4] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
cs.v_plus[0] = 1.;
cs.v_plus[1] = 2.;
cs.v_plus[2] = 3.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errd);
REQUIRE_THAT(cs.v_plus, AllCloseVector(errd, TEST_PREC, TEST_PREC));
cs.v_plus[0] = 0.;
cs.v_plus[1] = 0.;
cs.v_plus[2] = 0.;
qd[0] = 1.;
qd[1] = 2.;
qd[2] = 3.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errd);
REQUIRE_THAT(cs.v_plus, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DConstraintErrors", "") {
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
Vector3d pos_p;
Vector3d pos_s;
Matrix3d rot_p;
Matrix3d rot_s;
Matrix3d rot_ps;
Vector3d rotationVec;
// Test in zero position.
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in another configurations.
q[0] = 0.4;
q[1] = 0.25 * M_PI;
q[2] = -0.25 * M_PI;
q[3] = 0.01;
q[4] = 0.01;
CalcConstraintsPositionError(model, q, cs, err);
pos_p = CalcBodyToBaseCoordinates(model, q, id_p, X_p.r);
pos_s = CalcBodyToBaseCoordinates(model, q, id_s, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, id_p).transpose() * X_p.E;
rot_s = CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
rot_ps = rot_p.transpose() * rot_s;
rotationVec = - 0.5 * Vector3d
( rot_ps(1,2) - rot_ps(2,1)
, rot_ps(2,0) - rot_ps(0,2)
, rot_ps(0,1) - rot_ps(1,0));
errRef.block<3,1>(0,0) = pos_s - pos_p;
errRef[3] = rotationVec[2];
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DConstraintJacobian", "") {
MatrixNd G(MatrixNd::Zero(cs.size(), q.size()));
// Test in zero position.
G.setZero();
CalcConstraintsJacobian(model, q, cs, G);
VectorNd errRef(VectorNd::Zero(cs.size()));
VectorNd err = G * qd;
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DConstraintsVelocityErrors", "") {
VectorNd errd(VectorNd::Zero(cs.size()));
VectorNd errdRef(VectorNd::Zero(cs.size()));
MatrixNd G(cs.size(), model.dof_count);
VectorNd qWeights(q.size());
VectorNd qInit(q.size());
bool success = false;
// Compute assembled configuration.
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
qInit[2] = -0.25 * M_PI;
qInit[3] = 0.1;
qInit[4] = 0.1;
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, TEST_PREC);
REQUIRE (success);
// Some random velocity.
qd[0] = -0.2;
qd[1] = 0.1 * M_PI;
qd[2] = -0.1 * M_PI;
qd[3] = 0.;
qd[4] = 0.1 * M_PI;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
CalcConstraintsJacobian(model, q, cs, G);
errdRef = G * qd;
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DAssemblyQ", "") {
VectorNd weights(q.size());
VectorNd qInit(q.size());
Vector3d pos_p;
Vector3d pos_s;
Matrix3d rot_p;
Matrix3d rot_s;
Matrix3d rot_ps;
bool success;
weights[0] = 1.;
weights[1] = 1.;
weights[2] = 1.;
weights[3] = 1.;
weights[4] = 1.;
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
qInit[2] = -0.25 * M_PI;
qInit[3] = 0.1;
qInit[4] = 0.1;
success = CalcAssemblyQ(model, qInit, cs, q, weights, TEST_PREC);
pos_p = CalcBodyToBaseCoordinates(model, q, id_p, X_p.r);
pos_s = CalcBodyToBaseCoordinates(model, q, id_s, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, id_p).transpose() * X_p.E;
rot_s = CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
rot_ps = rot_p.transpose() * rot_s;
REQUIRE (success);
REQUIRE_THAT (pos_p, AllCloseVector(pos_s, TEST_PREC, TEST_PREC));
REQUIRE_THAT (0., IsClose(rot_ps(0,1) - rot_ps(1,0), TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DAssemblyQDot", "") {
VectorNd qWeights(q.size());
VectorNd qdWeights(q.size());
VectorNd qInit(q.size());
VectorNd qdInit(q.size());
SpatialVector vel_p;
SpatialVector vel_s;
bool success = false;
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
qInit[2] = -0.25 * M_PI;
qInit[3] = 0.1;
qInit[4] = 0.1;
qdWeights[0] = 1.;
qdWeights[1] = 0.;
qdWeights[2] = 0.;
qdWeights[3] = 0.;
qdWeights[4] = 0.;
qdInit[0] = -0.2;
qdInit[1] = 0.1 * M_PI;
qdInit[2] = -0.1 * M_PI;
qdInit[3] = 0.;
qdInit[4] = 0.1 * M_PI;
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, TEST_PREC);
REQUIRE (success);
CalcAssemblyQDot(model, q, qdInit, cs, qd, qdWeights);
vel_p = CalcPointVelocity6D(model, q, qd, id_p, X_p.r);
vel_s = CalcPointVelocity6D(model, q, qd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT(vel_p[i], IsClose(vel_s[i], TEST_PREC, TEST_PREC));
}
REQUIRE_THAT(qdInit[0], IsClose(qd[0], TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DForwardDynamics", "") {
VectorNd qWeights(q.size());
VectorNd qdWeights(q.size());
VectorNd qInit(q.size());
VectorNd qdInit(q.size());
SpatialVector acc_p;
SpatialVector acc_s;
bool success = false;
#ifndef RBDL_USE_SIMPLE_MATH
// The SimpleMath solver cannot solve the system close to a singular
// configuration.
// Test with zero q and qdot.
tau[0] = 0.12;
tau[1] = -0.3;
tau[2] = 0.05;
tau[3] = 0.7;
tau[4] = -0.1;
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT(acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsRangeSpaceSparse(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
#endif
// Compute non-zero assembly q and qdot;
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
qInit[2] = -0.25 * M_PI;
qInit[3] = 0.1;
qInit[4] = 0.1;
qdWeights[0] = 1.;
qdWeights[1] = 0.;
qdWeights[2] = 0.;
qdWeights[3] = 0.;
qdWeights[4] = 0.;
qdInit[0] = -0.2;
qdInit[1] = 0.1 * M_PI;
qdInit[2] = -0.1 * M_PI;
qdInit[3] = 0.;
qdInit[4] = 0.1 * M_PI;
qdInit.setZero();
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, TEST_PREC);
REQUIRE (success);
CalcAssemblyQDot(model, q, qdInit, cs, qd, qdWeights);
Matrix3d rot_ps
= (CalcBodyWorldOrientation(model, q, id_p).transpose()*X_p.E).transpose()
* CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
REQUIRE ((CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)
- CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)).norm() < TEST_PREC);
REQUIRE (rot_ps(0,1) - rot_ps(0,1) < TEST_PREC);
REQUIRE ((CalcPointVelocity6D(model, q, qd, id_p, X_p.r)
-CalcPointVelocity6D(model, q, qd, id_p, X_p.r)).norm() < TEST_PREC);
// Test with non-zero q and qdot.
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsRangeSpaceSparse(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
}
TEST_CASE_METHOD (SliderCrank3D, __FILE__"_TestSliderCrank3DImpulse", "") {
VectorNd qdPlusDirect(qd.size());
VectorNd qdPlusRangeSpaceSparse(qd.size());
VectorNd qdPlusNullSpace(qd.size());
VectorNd errdDirect(cs.size());
VectorNd errdSpaceSparse(cs.size());
VectorNd errdNullSpace(cs.size());
VectorNd qWeights(q.size());
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
VectorNd qInit(q.size());
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
qInit[2] = -0.25 * M_PI;
qInit[3] = 0.1;
qInit[4] = 0.1;
bool success = CalcAssemblyQ(model, qInit, cs, q, qWeights, TEST_PREC);
REQUIRE (success);
cs.v_plus[0] = 1.;
cs.v_plus[1] = 2.;
cs.v_plus[2] = 3.;
cs.v_plus[3] = 4.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errdDirect);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdDirect, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesRangeSpaceSparse(model, q, qd, cs
, qdPlusRangeSpaceSparse);
CalcConstraintsVelocityError(model, q, qdPlusRangeSpaceSparse, cs
, errdSpaceSparse);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdSpaceSparse, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesNullSpace(model, q, qd, cs, qdPlusNullSpace);
CalcConstraintsVelocityError(model, q, qdPlusNullSpace, cs, errdNullSpace);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdNullSpace, TEST_PREC, TEST_PREC));
cs.v_plus[0] = 0.;
cs.v_plus[1] = 0.;
cs.v_plus[2] = 0.;
cs.v_plus[3] = 0.;
qd[0] = 1.;
qd[1] = 2.;
qd[2] = 3.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errdDirect);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdDirect, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesRangeSpaceSparse(model, q, qd, cs
, qdPlusRangeSpaceSparse);
CalcConstraintsVelocityError(model, q, qdPlusRangeSpaceSparse, cs
, errdSpaceSparse);
REQUIRE_THAT(cs.v_plus, AllCloseVector(errdSpaceSparse, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesNullSpace(model, q, qd, cs, qdPlusNullSpace);
CalcConstraintsVelocityError(model, q, qdPlusNullSpace, cs, errdNullSpace);
REQUIRE_THAT(cs.v_plus, AllCloseVector(errdNullSpace, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageConstraintErrors", "") {
VectorNd err = VectorNd::Zero(cs.size());
Vector3d pos0;
Vector3d pos1;
Vector3d pos2;
Vector3d posErr;
Matrix3d rot_p;
double angleErr;
// Test in zero position.
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
q[5] = 0.;
q[6] = 0.;
q[7] = 0.;
CalcConstraintsPositionError(model, q, cs, err);
VectorNd target(6);
REQUIRE_THAT(target, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = 0.;
angleErr = sin(-0.5 * M_PI);
pos0 = CalcBodyToBaseCoordinates(model, q, idB0, Vector3d::Zero());
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
REQUIRE (std::fabs(posErr[1]) < TEST_PREC);
REQUIRE (std::fabs(posErr[2]) < TEST_PREC);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (Vector3d(1.,2.,3.), AllCloseVector(pos0, TEST_PREC, TEST_PREC));
REQUIRE_THAT (Vector3d(posErr[0], 0., angleErr), AllCloseVector(Vector3d(err[3], err[4], err[5]), TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = 0.;
q[4] = 0.;
q[5] = M_PI + 0.1;
q[6] = 0.;
q[7] = 0.;
angleErr = sin(-q[3] - q[4] + q[5] + q[6] + q[7]);
pos0 = CalcBodyToBaseCoordinates(model, q, idB0, Vector3d::Zero());
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (Vector3d(1.,2.,3.), AllCloseVector(pos0, TEST_PREC, TEST_PREC));
REQUIRE_THAT (Vector3d(posErr[0], posErr[1], angleErr), AllCloseVector(Vector3d(err[3], err[4], err[5]), TEST_PREC, TEST_PREC));
// Test in non-zero position.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = 0.8;
q[4] = -0.4;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = 0.;
angleErr = sin(-q[3] - q[4] + q[5] + q[6] + q[7]);
pos0 = CalcBodyToBaseCoordinates(model, q, idB0, Vector3d::Zero());
pos1 = CalcBodyToBaseCoordinates(model, q, idB2, X_p.r);
pos2 = CalcBodyToBaseCoordinates(model, q, idB5, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, idB2).transpose() * X_p.E;
posErr = rot_p.transpose() * (pos2 - pos1);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (Vector3d(1.,2.,3.), AllCloseVector(pos0, TEST_PREC, TEST_PREC));
REQUIRE_THAT (Vector3d(posErr[0], posErr[1], angleErr), AllCloseVector(Vector3d(err[3], err[4], err[5]), TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageConstraintJacobian", "") {
MatrixNd G(MatrixNd::Zero(cs.size(), q.size()));
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
// Zero Q configuration, both arms of the 4-bar laying on the x-axis
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
q[5] = 0.;
q[6] = 0.;
q[7] = 0.;
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = -1.;
qd[6] = -1.;
qd[7] = 0.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Both arms of the 4-bar laying on the y-axis
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.5 * M_PI;
q[4] = 0.;
q[5] = 0.5 * M_PI;
q[6] = 0.;
q[7] = 0.;
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = -1.;
qd[6] = -1.;
qd[7] = 0.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Arms symmetric wrt y axis.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -0.5 * M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = q[3] + q[4] - q[5] - q[6];
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = -2.;
qd[6] = +1.;
qd[7] = -1.;
REQUIRE ((CalcPointVelocity6D(model, q, qd, idB2, X_p.r)
- CalcPointVelocity6D(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageConstraintsVelocityErrors", "") {
VectorNd errd(VectorNd::Zero(cs.size()));
VectorNd errdRef(VectorNd::Zero(cs.size()));
MatrixNd G(cs.size(), model.dof_count);
// Arms symmetric wrt y axis.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -0.5 * M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = q[3] + q[4] - q[5] - q[6];
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = -2.;
qd[6] = +1.;
qd[7] = -1.;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
// Invalid velocities.
qd[0] = -1.;
qd[1] = -1.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = 0.;
qd[6] = 0.;
qd[7] = 0.;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
CalcConstraintsJacobian(model, q, cs, G);
errdRef = G * qd;
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageQAssembly", "") {
VectorNd weights(q.size());
VectorNd err(cs.size());
VectorNd errRef(VectorNd::Zero(cs.size()));
weights[0] = 0.;
weights[1] = 0.;
weights[2] = 0.;
weights[3] = 1.;
weights[4] = 0.;
weights[5] = 1.;
weights[6] = 0.;
weights[7] = 0.;
VectorNd qRef = VectorNd::Zero(q.size());
qRef[0] = 1.;
qRef[1] = 2.;
qRef[2] = 3.;
qRef[3] = M_PI * 3 / 4;
qRef[4] = -0.5 * M_PI;
qRef[5] = M_PI - qRef[3];
qRef[6] = -qRef[4];
qRef[7] = qRef[3] + qRef[4] - qRef[5] - qRef[6];
REQUIRE (qRef[3] + qRef[4] - qRef[5] - qRef[6] - qRef[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, qRef, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, qRef, idB5, X_s.r)).norm() < TEST_PREC);
bool success;
// Feasible initial guess.
VectorNd qInit = VectorNd::Zero(q.size());
qInit = qRef;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT(CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT(inRange(q[3] + q[4]), IsClose(inRange(q[5] + q[6] + q[7]), TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[3], IsClose(q[3], TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[5], IsClose(q[5], TEST_PREC, TEST_PREC));
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[3] = qRef[3];
qInit[4] = qRef[4];
qInit[5] = qRef[5];
qInit[6] = qRef[6];
qInit[7] = qRef[7] + 0.05;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE (success);
REQUIRE_THAT(CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT(inRange(q[3] + q[4]), IsClose(inRange(q[5] + q[6] + q[7]), TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[3], IsClose(q[3], TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[5], IsClose(q[5], TEST_PREC, TEST_PREC));
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[3] = qRef[3] - 0.2;
qInit[4] = qRef[4] - 0.;
qInit[5] = qRef[5] + 0.1;
qInit[6] = qRef[6] - 0.03;
qInit[7] = qRef[7] + 0.05;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE (success);
REQUIRE_THAT(CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT(inRange(q[3] + q[4]), IsClose(inRange(q[5] + q[6] + q[7]), TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[3], IsClose(q[3], TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[5], IsClose(q[5], TEST_PREC, TEST_PREC));
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Perturbed initial guess.
qInit[3] = qRef[3] + 0.01;
qInit[4] = qRef[4] + 0.02;
qInit[5] = qRef[5] - 0.03;
qInit[6] = qRef[6] - 0.02;
qInit[7] = qRef[7] + 0.01;
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1.e-12);
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE (success);
REQUIRE_THAT(CalcBodyToBaseCoordinates(model, q, idB2, X_p.r), AllCloseVector(CalcBodyToBaseCoordinates(model, q, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT(inRange(q[3] + q[4]), IsClose(inRange(q[5] + q[6] + q[7]), TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[3], IsClose(q[3], TEST_PREC, TEST_PREC));
REQUIRE_THAT(qInit[5], IsClose(q[5], TEST_PREC, TEST_PREC));
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageQDotAssembly", "") {
VectorNd weights(q.size());
weights[0] = 0.;
weights[1] = 0.;
weights[2] = 0.;
weights[3] = 1.;
weights[4] = 0.;
weights[5] = 1.;
weights[6] = 0.;
weights[7] = 0.;
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -0.5 * M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = q[3] + q[4] - q[5] - q[6];
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
VectorNd qdInit = VectorNd::Zero(q.size());
qdInit[0] = 1.;
qdInit[1] = 2.;
qdInit[2] = 3.;
qdInit[3] = 0.01;
qdInit[4] = 0.5;
qdInit[5] = -0.7;
qdInit[6] = -0.5;
qdInit[7] = 0.3;
CalcAssemblyQDot(model, q, qdInit, cs, qd, weights);
MatrixNd G(MatrixNd::Zero(cs.size(), q.size()));
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
CalcConstraintsJacobian(model, q, cs, G);
err = G * qd;
REQUIRE_THAT(CalcPointVelocity6D(model, q, qd, idB2, X_p.r), AllCloseVector(CalcPointVelocity6D(model, q, qd, idB5, X_s.r), TEST_PREC, TEST_PREC));
REQUIRE_THAT(errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
REQUIRE_THAT(qdInit[3], IsClose(qd[3], TEST_PREC, TEST_PREC));
REQUIRE_THAT(qdInit[5], IsClose(qd[5], TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageForwardDynamics", "") {
VectorNd qddDirect;
VectorNd qddNullSpace;
cs.SetSolver(LinearSolverColPivHouseholderQR);
#ifndef RBDL_USE_SIMPLE_MATH
// The SimpleMath solver cannot solve the system close to a singular
// configuration.
// Configuration 1.
q[0] = 0.;
q[1] = 0.;
q[2] = 0.;
q[3] = 0.;
q[4] = 0.;
q[5] = 0.;
q[6] = 0.;
q[7] = 0.;
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = 0.;
qd[4] = 0.;
qd[5] = 0.;
qd[6] = 0.;
qd[7] = 0.;
REQUIRE (qd[3] + qd[4] - qd[5] - qd[6] - qd[7] == 0.);
REQUIRE ((CalcPointVelocity(model, q, qd, idB2, X_p.r)
- CalcPointVelocity(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
tau[0] = 0.;
tau[1] = 0.;
tau[2] = 0.;
tau[3] = 1.;
tau[4] = -2.;
tau[5] = 3.;
tau[6] = -5.;
tau[7] = 7.;
qddDirect = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qddDirect);
REQUIRE_THAT (CalcPointAcceleration6D(model, q, qd, qddDirect, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddDirect, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
qddNullSpace = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qddNullSpace);
REQUIRE_THAT (CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
#endif
// Configuration 2.
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -0.5 * M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = q[3] + q[4] - q[5] - q[6];
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
qd[0] = 0.;
qd[1] = 0.;
qd[2] = 0.;
qd[3] = -1.;
qd[4] = -1.;
qd[5] = -2.;
qd[6] = +1.;
qd[7] = -1.;
REQUIRE (qd[3] + qd[4] - qd[5] - qd[6] - qd[7] == 0.);
REQUIRE ((CalcPointVelocity(model, q, qd, idB2, X_p.r)
- CalcPointVelocity(model, q, qd, idB5, X_s.r)).norm() < TEST_PREC);
tau[0] = 0.;
tau[1] = 0.;
tau[2] = 0.;
tau[3] = 1.;
tau[4] = -2.;
tau[5] = 3.;
tau[6] = -5.;
tau[7] = 7.;
qddDirect = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qddDirect);
REQUIRE_THAT (CalcPointAcceleration6D(model, q, qd, qddDirect, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddDirect, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
qddNullSpace = VectorNd::Zero(q.size());
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qddNullSpace);
REQUIRE_THAT (CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB2, X_p.r),
AllCloseVector(CalcPointAcceleration6D(model, q, qd, qddNullSpace, idB5, X_s.r), TEST_PREC, TEST_PREC)
);
}
TEST_CASE_METHOD (FloatingFourBarLinkage, __FILE__"_TestFloatingFourBarLinkageImpulse", "") {
VectorNd qdPlusDirect(qd.size());
VectorNd qdPlusRangeSpaceSparse(qd.size());
VectorNd qdPlusNullSpace(qd.size());
VectorNd errd(cs.size());
q[0] = 1.;
q[1] = 2.;
q[2] = 3.;
q[3] = M_PI * 3 / 4;
q[4] = -0.5 * M_PI;
q[5] = M_PI - q[3];
q[6] = -q[4];
q[7] = q[3] + q[4] - q[5] - q[6];
REQUIRE (q[3] + q[4] - q[5] - q[6] - q[7] == 0.);
REQUIRE ((CalcBodyToBaseCoordinates(model, q, idB2, X_p.r)
- CalcBodyToBaseCoordinates(model, q, idB5, X_s.r)).norm() < TEST_PREC);
cs.v_plus[0] = 1.;
cs.v_plus[1] = 2.;
cs.v_plus[2] = 3.;
cs.v_plus[3] = 4.;
cs.v_plus[4] = 5.;
cs.v_plus[5] = 6.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errd);
REQUIRE_THAT(cs.v_plus, AllCloseVector(errd, TEST_PREC, TEST_PREC));
cs.v_plus[0] = 0.;
cs.v_plus[1] = 0.;
cs.v_plus[2] = 0.;
cs.v_plus[3] = 0.;
cs.v_plus[4] = 0.;
cs.v_plus[5] = 0.;
qd[0] = 1.;
qd[2] = 2.;
qd[4] = 3.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errd);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointConstraintErrors", "") {
VectorNd err(VectorNd::Zero(cs.size()));
VectorNd errRef(VectorNd::Zero(cs.size()));
Vector3d pos_p;
Vector3d pos_s;
Matrix3d rot_p;
Matrix3d rot_s;
Matrix3d rot_ps;
Vector3d rotationVec;
// Test in zero position.
CalcConstraintsPositionError(model, q, cs, err);
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
// Test in another configuration.
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25*M_PI,0.01,0.01));
q[0] = 0.4;
q[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, q);
CalcConstraintsPositionError(model, q, cs, err);
pos_p = CalcBodyToBaseCoordinates(model, q, id_p, X_p.r);
pos_s = CalcBodyToBaseCoordinates(model, q, id_s, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, id_p).transpose() * X_p.E;
rot_s = CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
rot_ps = rot_p.transpose() * rot_s;
rotationVec = - 0.5 * Vector3d
( rot_ps(1,2) - rot_ps(2,1)
, rot_ps(2,0) - rot_ps(0,2)
, rot_ps(0,1) - rot_ps(1,0));
errRef.block<3,1>(0,0) = pos_s - pos_p;
errRef[3] = rotationVec[2];
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointConstraintJacobian", "") {
MatrixNd G(MatrixNd::Zero(cs.size(), model.dof_count));
// Test in zero position.
G.setZero();
CalcConstraintsJacobian(model, q, cs, G);
VectorNd errRef(VectorNd::Zero(cs.size()));
VectorNd err = G * qd;
REQUIRE_THAT (errRef, AllCloseVector(err, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointConstraintsVelocityErrors", "") {
VectorNd errd(VectorNd::Zero(cs.size()));
VectorNd errdRef(VectorNd::Zero(cs.size()));
MatrixNd G(cs.size(), model.dof_count);
VectorNd qWeights(model.dof_count);
VectorNd qInit(model.q_size);
bool success = false;
// Compute assembled configuration.
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25 * M_PI, 0.1,0.1));
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, qInit);
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, 1e-14, 800);
REQUIRE (success);
// Some random velocity.
qd[0] = -0.2;
qd[1] = 0.1 * M_PI;
qd[2] = -0.1 * M_PI;
qd[3] = 0.;
qd[4] = 0.1 * M_PI;
CalcConstraintsVelocityError(model, q, qd, cs, errd);
CalcConstraintsJacobian(model, q, cs, G);
errdRef = G * qd;
REQUIRE_THAT (errdRef, AllCloseVector(errd, TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointAssemblyQ", "") {
VectorNd weights(model.dof_count);
VectorNd qInit(model.q_size);
Vector3d pos_p;
Vector3d pos_s;
Matrix3d rot_p;
Matrix3d rot_s;
Matrix3d rot_ps;
bool success;
weights[0] = 1.;
weights[1] = 1.;
weights[2] = 1.;
weights[3] = 1.;
weights[4] = 1.;
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25 * M_PI, 0.1,0.1));
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, qInit);
success = CalcAssemblyQ(model, qInit, cs, q, weights, 1e-14, 800);
pos_p = CalcBodyToBaseCoordinates(model, q, id_p, X_p.r);
pos_s = CalcBodyToBaseCoordinates(model, q, id_s, X_s.r);
rot_p = CalcBodyWorldOrientation(model, q, id_p).transpose() * X_p.E;
rot_s = CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
rot_ps = rot_p.transpose() * rot_s;
REQUIRE (success);
REQUIRE_THAT (pos_p, AllCloseVector(pos_s, TEST_PREC, TEST_PREC));
REQUIRE_THAT (0., IsClose(rot_ps(0,1) - rot_ps(1,0), TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointAssemblyQDot", "") {
VectorNd qWeights(model.dof_count);
VectorNd qdWeights(model.dof_count);
VectorNd qInit(model.q_size);
VectorNd qdInit(model.dof_count);
SpatialVector vel_p;
SpatialVector vel_s;
bool success = false;
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25 * M_PI, 0.1,0.1));
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, qInit);
qdWeights[0] = 1.;
qdWeights[1] = 0.;
qdWeights[2] = 0.;
qdWeights[3] = 0.;
qdWeights[4] = 0.;
qdInit[0] = -0.2;
qdInit[1] = 0.1 * M_PI;
qdInit[2] = -0.1 * M_PI;
qdInit[3] = 0.;
qdInit[4] = 0.1 * M_PI;
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, 1e-14, 800);
REQUIRE (success);
CalcAssemblyQDot(model, q, qdInit, cs, qd, qdWeights);
vel_p = CalcPointVelocity6D(model, q, qd, id_p, X_p.r);
vel_s = CalcPointVelocity6D(model, q, qd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (vel_p[i], IsClose(vel_s[i], TEST_PREC, TEST_PREC));
}
REQUIRE_THAT (qdInit[0], IsClose(qd[0], TEST_PREC, TEST_PREC));
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointForwardDynamics", "") {
VectorNd qWeights(model.dof_count);
VectorNd qdWeights(model.dof_count);
VectorNd qInit(model.q_size);
VectorNd qdInit(model.dof_count);
SpatialVector acc_p;
SpatialVector acc_s;
bool success = false;
#ifndef RBDL_USE_SIMPLE_MATH
// The SimpleMath solver cannot solve the system close to a singular
// configuration.
// Test with zero q and qdot.
tau[0] = 0.12;
tau[1] = -0.3;
tau[2] = 0.05;
tau[3] = 0.7;
tau[4] = -0.1;
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT(acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsRangeSpaceSparse(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
#endif
// Compute non-zero assembly q and qdot;
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25 * M_PI, 0.1,0.1));
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, qInit);
qdWeights[0] = 1.;
qdWeights[1] = 0.;
qdWeights[2] = 0.;
qdWeights[3] = 0.;
qdWeights[4] = 0.;
qdInit[0] = -0.2;
qdInit[1] = 0.1 * M_PI;
qdInit[2] = -0.1 * M_PI;
qdInit[3] = 0.;
qdInit[4] = 0.1 * M_PI;
qdInit.setZero();
success = CalcAssemblyQ(model, qInit, cs, q, qWeights, 1e-14, 800);
REQUIRE (success);
CalcAssemblyQDot(model, q, qdInit, cs, qd, qdWeights);
Matrix3d rot_ps
= (CalcBodyWorldOrientation(model, q, id_p).transpose() * X_p.E).transpose()
* CalcBodyWorldOrientation(model, q, id_s).transpose() * X_s.E;
REQUIRE ((CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)
- CalcBodyToBaseCoordinates(model, q, id_p, X_p.r)).norm() < TEST_PREC);
REQUIRE (rot_ps(0,1) - rot_ps(0,1) < TEST_PREC);
REQUIRE ((CalcPointVelocity6D(model, q, qd, id_p, X_p.r)
-CalcPointVelocity6D(model, q, qd, id_p, X_p.r)).norm() < TEST_PREC);
// Test with non-zero q and qdot.
ForwardDynamicsConstraintsDirect(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT (acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsNullSpace(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT(acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
ForwardDynamicsConstraintsRangeSpaceSparse(model, q, qd, tau, cs, qdd);
acc_p = CalcPointAcceleration6D(model, q, qd, qdd, id_p, X_p.r);
acc_s = CalcPointAcceleration6D(model, q, qd, qdd, id_s, X_s.r);
for(size_t i = 2; i < 6; ++i) {
REQUIRE_THAT(acc_p[i], IsClose(acc_s[i], TEST_PREC, TEST_PREC));
}
}
TEST_CASE_METHOD (SliderCrank3DSphericalJoint, __FILE__"_TestSliderCrank3DSphericalJointImpulse", "") {
VectorNd qdPlusDirect(model.dof_count);
VectorNd qdPlusRangeSpaceSparse(model.dof_count);
VectorNd qdPlusNullSpace(model.dof_count);
VectorNd errdDirect(cs.size());
VectorNd errdSpaceSparse(cs.size());
VectorNd errdNullSpace(cs.size());
VectorNd qWeights(model.dof_count);
VectorNd qInit(model.q_size);
qWeights[0] = 1.;
qWeights[1] = 1.;
qWeights[2] = 1.;
qWeights[3] = 1.;
qWeights[4] = 1.;
Quaternion quat = Quaternion::fromZYXAngles(Vector3d(-0.25 * M_PI, 0.1,0.1));
qInit[0] = 0.4;
qInit[1] = 0.25 * M_PI;
model.SetQuaternion(id_s, quat, qInit);
bool success = CalcAssemblyQ(model, qInit, cs, q, qWeights, 1e-14, 800);
REQUIRE (success);
cs.v_plus[0] = 1.;
cs.v_plus[1] = 2.;
cs.v_plus[2] = 3.;
cs.v_plus[3] = 4.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errdDirect);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdDirect, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesRangeSpaceSparse(model, q, qd, cs
, qdPlusRangeSpaceSparse);
CalcConstraintsVelocityError(model, q, qdPlusRangeSpaceSparse, cs
, errdSpaceSparse);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdSpaceSparse, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesNullSpace(model, q, qd, cs, qdPlusNullSpace);
CalcConstraintsVelocityError(model, q, qdPlusNullSpace, cs, errdNullSpace);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdNullSpace, TEST_PREC, TEST_PREC));
cs.v_plus[0] = 0.;
cs.v_plus[1] = 0.;
cs.v_plus[2] = 0.;
cs.v_plus[3] = 0.;
qd[0] = 1.;
qd[1] = 2.;
qd[2] = 3.;
ComputeConstraintImpulsesDirect(model, q, qd, cs, qdPlusDirect);
CalcConstraintsVelocityError(model, q, qdPlusDirect, cs, errdDirect);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdDirect, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesRangeSpaceSparse(model, q, qd, cs
, qdPlusRangeSpaceSparse);
CalcConstraintsVelocityError(model, q, qdPlusRangeSpaceSparse, cs
, errdSpaceSparse);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdSpaceSparse, TEST_PREC, TEST_PREC));
ComputeConstraintImpulsesNullSpace(model, q, qd, cs, qdPlusNullSpace);
CalcConstraintsVelocityError(model, q, qdPlusNullSpace, cs, errdNullSpace);
REQUIRE_THAT (cs.v_plus, AllCloseVector(errdNullSpace, TEST_PREC, TEST_PREC));
}
TEST_CASE (__FILE__"_ConstraintCorrectnessTest", "") {
DoublePerpendicularPendulumAbsoluteCoordinates dba
= DoublePerpendicularPendulumAbsoluteCoordinates();
DoublePerpendicularPendulumJointCoordinates dbj
= DoublePerpendicularPendulumJointCoordinates();
//1. Set the pendulum modeled using joint coordinates to a specific
// state and then compute the spatial acceleration of the body.
dbj.q[0] = M_PI/3.0; //About z0
dbj.q[1] = M_PI/6.0; //About y1
dbj.qd[0] = M_PI; //About z0
dbj.qd[1] = M_PI/2.0; //About y1
dbj.tau[0]= 0.;
dbj.tau[1]= 0.;
ForwardDynamics(dbj.model,dbj.q,dbj.qd,dbj.tau,dbj.qdd);
Vector3d r010 = CalcBodyToBaseCoordinates(
dbj.model,dbj.q,dbj.idB1,
Vector3d(0.,0.,0.),true);
Vector3d r020 = CalcBodyToBaseCoordinates(
dbj.model,dbj.q,dbj.idB2,
Vector3d(0.,0.,0.),true);
// Vector3d r030 = CalcBodyToBaseCoordinates(
// dbj.model,dbj.q,dbj.idB2,
// Vector3d(dbj.l2,0.,0.),true);
SpatialVector v010 = CalcPointVelocity6D(
dbj.model,dbj.q,dbj.qd,dbj.idB1,
Vector3d(0.,0.,0.),true);
SpatialVector v020 = CalcPointVelocity6D(
dbj.model,dbj.q,dbj.qd,dbj.idB2,
Vector3d(0.,0.,0.),true);
// SpatialVector v030 = CalcPointVelocity6D(
// dbj.model,dbj.q,dbj.qd,dbj.idB2,
// Vector3d(dbj.l2,0.,0.),true);
SpatialVector a010 = CalcPointAcceleration6D(
dbj.model,dbj.q,dbj.qd,dbj.qdd,
dbj.idB1,Vector3d(0.,0.,0.),true);
SpatialVector a020 = CalcPointAcceleration6D(
dbj.model,dbj.q,dbj.qd,dbj.qdd,
dbj.idB2,Vector3d(0.,0.,0.),true);
SpatialVector a030 = CalcPointAcceleration6D(
dbj.model,dbj.q,dbj.qd,dbj.qdd,
dbj.idB2,Vector3d(dbj.l2,0.,0.),true);
//2. Set the pendulum modelled using absolute coordinates to the
// equivalent state as the pendulum modelled using joint
// coordinates. Next
/*
dba.q[0] = dbj.q[0];
dba.q[1] = r020[0];
dba.q[2] = r020[1];
dba.q[3] = r020[2];
dba.q[4] = 0;
dba.q[5] = 0;
dba.q[6] = dbj.q[0]+dbj.q[1];
dba.qd[0] = dbj.qd[0];
dba.qd[1] = v020[0];
dba.qd[2] = v020[1];
dba.qd[3] = v020[2];
dba.qd[4] = 0;
dba.qd[5] = 0;
dba.qd[6] = dbj.qd[0]+dbj.qd[1];
*/
dba.q[0] = r010[0];
dba.q[1] = r010[1];
dba.q[2] = r010[2];
dba.q[3] = dbj.q[0];
dba.q[4] = 0;
dba.q[5] = 0;
dba.q[6] = r020[0];
dba.q[7] = r020[1];
dba.q[8] = r020[2];
dba.q[9] = dbj.q[0];
dba.q[10] = dbj.q[1];
dba.q[11] = 0;
dba.qd[0] = v010[3];
dba.qd[1] = v010[4];
dba.qd[2] = v010[5];
dba.qd[3] = dbj.qd[0];
dba.qd[4] = 0;
dba.qd[5] = 0;
dba.qd[6] = v020[3];
dba.qd[7] = v020[4];
dba.qd[8] = v020[5];
dba.qd[9] = dbj.qd[0];
dba.qd[10] = dbj.qd[1];
dba.qd[11] = 0;
VectorNd err(dba.cs.size());
VectorNd errd(dba.cs.size());
CalcConstraintsPositionError(dba.model,dba.q,dba.cs,err,true);
CalcConstraintsVelocityError(dba.model,dba.q,dba.qd,dba.cs,errd,true);
//The constraint errors at the position and velocity level
//must be zero before the accelerations can be tested.
for(unsigned int i=0; i<err.rows();++i){
REQUIRE_THAT (0, IsClose(err[i], TEST_PREC, TEST_PREC));
}
for(unsigned int i=0; i<errd.rows();++i){
REQUIRE_THAT (0, IsClose(errd[i], TEST_PREC, TEST_PREC));
}
//Evaluate the accelerations of the constrained pendulum and
//compare those to the joint-coordinate pendulum
for(unsigned int i=0; i<dba.tau.rows();++i){
dba.tau[i] = 0.;
}
ForwardDynamicsConstraintsDirect(dba.model,dba.q,dba.qd,
dba.tau,dba.cs,dba.qdd);
SpatialVector a010c =
CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB1,Vector3d(0.,0.,0.),true);
SpatialVector a020c =
CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(0.,0.,0.),true);
SpatialVector a030c =
CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(dba.l2,0.,0.),true);
REQUIRE_THAT (a010, AllCloseVector(a010c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a020, AllCloseVector(a020c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a030, AllCloseVector(a030c, TEST_PREC, TEST_PREC));
/*
ForwardDynamicsConstraintsNullSpace(dba.model,dba.q,dba.qd,
dba.tau,dba.cs,dba.qdd);
a010c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB1,Vector3d(0.,0.,0.),true);
a020c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(0.,0.,0.),true);
a030c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(dba.l2,0.,0.),true);
REQUIRE_THAT (a010, AllCloseVector(a010c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a020, AllCloseVector(a020c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a030, AllCloseVector(a030c, TEST_PREC, TEST_PREC));
bool here=true;
ForwardDynamicsConstraintsRangeSpaceSparse(dba.model,dba.q,dba.qd,
dba.tau,dba.cs,dba.qdd);
a010c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB1,Vector3d(0.,0.,0.),true);
a020c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(0.,0.,0.),true);
a030c = CalcPointAcceleration6D(dba.model,dba.q,dba.qd,dba.qdd,
dba.idB2,Vector3d(dba.l2,0.,0.),true);
REQUIRE_THAT (a010, AllCloseVector(a010c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a020, AllCloseVector(a020c, TEST_PREC, TEST_PREC));
REQUIRE_THAT (a030, AllCloseVector(a030c, TEST_PREC, TEST_PREC));
*/
}