Free joint representation and Jacobian #1826
Replies: 3 comments
-
The main relevant files of course being: And functions:
|
Beta Was this translation helpful? Give feedback.
-
Thank you for your detailed question. I may need time to refresh the current implementation details and mathematical rationale. I'll aim to provide a more informed response by this weekend. |
Beta Was this translation helpful? Give feedback.
-
I've spent way too long writing out maths equations and scratching my head, so I decided to just test this directly with some simple code on a single rigid body: #include <dart/dart.hpp>
namespace dd = dart::dynamics;
namespace ds = dart::simulation;
int main() {
double dt = 0.01;
auto world = ds::World::create();
auto skel = dd::Skeleton::create();
world->setTimeStep(dt);
// Testing without any acceleration effects
world->setGravity(Eigen::Vector3d::Zero());
auto body_and_jnt = skel->createJointAndBodyNodePair<dd::FreeJoint>(nullptr);
auto jnt = body_and_jnt.first;
auto body = body_and_jnt.second;
body->setMomentOfInertia(1., 1., 1.);
body->setMass(1.);
world->addSkeleton(skel);
double yaw = M_PI_4;
double x_pos = 1000.;
double x_vel = 1.;
Eigen::Vector6d X_0, V_0;
X_0 << 0., 0., yaw, x_pos, 0., 0.;
V_0 << 0., 0., 0., x_vel, 0., 0.;
jnt->setPositions(X_0);
jnt->setVelocities(V_0);
for (size_t i = 0; i < 100; i++) {
world->step(false);
// True if X is stored in the world frame
assert(dd::FreeJoint::convertToTransform(jnt->getPositions()).isApprox(body->getWorldTransform()));
// True if V is stored in local coordinates
assert(jnt->getVelocities().isApprox(body->getSpatialVelocity(dd::Frame::World(), body)));
// True if V is stored in world coordinates
// Fails
//assert(jnt->getVelocities().isApprox(body->getSpatialVelocity(dd::Frame::World(), dd::Frame::World())));
}
std::cerr << "Initial X: " << X_0.transpose() << std::endl;
std::cerr << " Final X: " << jnt->getPositions().transpose() << std::endl;
std::cerr << "Initial V: " << V_0.transpose() << std::endl;
std::cerr << " Final V: " << jnt->getVelocities().transpose() << std::endl;
std::cerr << "DART VERSION " << DART_VERSION << std::endl;
} Since we start with a yaw of 90 degrees, a 1 unit translation along X in the child frame would be a 1 unit translation along Y in the parent frame, which is aligned with the world. Equally, a 1 unit translation along X in the parent frame, would be a 1 unit translation along -Y in the child frame. Since the final output is:
We can see that DART must store the (linear) position in the parent coordinate frame, and the (linear) velocity in the child coordinate frame. Which can be seen more easily when a yaw velocity is added. In my notation the top left superscript is the coordinate frame that the variable is expressed in, so If we store the position of the child with respect to the parent in the coordinates of the parent frame, this is equivalent to a rotation about the origin, and then a translation. Then Which is what DART calculates in the Reading through Featherstone's one more time, straight after the example I quoted above:
So DART is using the same system as Featherstone as expected. One thing I noticed, is that manually deriving the transform between the two units gives a different result to what DART seems to use: While DART seems to do: I'm guessing this is because we're actually storing the velocity of the body relative to the instantaneous axis-aligned origin coincident with the rigid body / child frame rather than the actual (maybe very far away) parent frame, so therefore |
Beta Was this translation helpful? Give feedback.
-
Hi,
Sorry this isn't so much an issue as a general question about implementation details and mathematical background. I am not aware of a better place to ask this question however (wouldn't it be great if there was a DART discord server haha), so I am asking it here.
I've been looking into the implementation details for various joints, as I'm working very closely with Featherstone's articulated rigid body algorithms in my research. I'm currently able to get very close to, but not quite matching DART's results with my own custom, linearised version of the algorithm when simulating my robot, but the errors (compared to DART's implementation) increase significantly as the transform from the origin to the base of my robot increases (particularly rotations), which is represented as a 6-dof free joint.
Obviously there are other ways of representing this, e.g. a pure SE3 matrix logarithm, but DART's implementation seems to have the free joint generalised position vector as the tangent space to the rotation (using the SO3 matrix logarithmic map and exponential map to convert between them) and the translation with respect to the parent body frame (with no conversion necessary). This corresponds simply to a rotation about the parent frame, followed by a translation, again with respect to the parent frame. Is this correct? DART then uses a fixed joint jacobian matrix equal to the adjoint of the transform of the child from the joint connection point, with the derivative equal to zero.
In Featherstone's book, Rigid Body Dynamics Algorithms, page 81, example 4.5:
That is, in order for the joint jacobian derivative to be zero, the joint parameterisation of the free joint should include the translation of the parent to the child, but in the coordinates of the child frame rather than the coordinates of the parent frame.
This leads to my question: Have I made a mistake in my understanding of the DART implementation, or the mathematics behind it? Or is the effect of the derivative of the joint jacobian minimal enough in stable simulation cases that a slight error here doesn't matter? Or can the jacobian derivative still be zero (and jacobian fixed) if the translation component is still given in the coordinates of the parent frame? The latter does not seem to be true later on in Featherstone's book, but using the child-frame translations instead makes calculating velocities from them that much harder.
Beta Was this translation helpful? Give feedback.
All reactions