1
\$\begingroup\$

I'm having problems solving IK with Jacobian Pseudoinverse method.

What I do is:

  1. At each local joint frame q_i of the chain q calculate the cross product between rotation axis (taken from local transform) and joint-to-end-effector vector (end-effector is transformed to q_i coordinates):

    // for each joint q_i -> to parent iteration // Get Local Joint Position and Rotation Axis lTransform = ... // local transform of i-th joint if (lTransform.Decompose(s, q, t)) { q.AxisAngle(lRotationAxis, angle); // quaternion, XMQuaternionToAxisAngle } // Transform End-Effector To Local Joint Coordinates lEndPosition = Vector3::Transform(lEndPosition, lTransform); // each iteration is updated to-parent lJointToEnd = lEndPosition - lTransform.Translation(); lResultAxis = lRotationAxis.Cross(lJointToEnd); jacobian(0, i) = lResultAxis.x; jacobian(1, i) = lResultAxis.y; jacobian(2, i) = lResultAxis.z; jacobian(3, i) = lRotationAxis.x; jacobian(4, i) = lRotationAxis.y; jacobian(5, i) = lRotationAxis.z; 
  2. Set up desired change in position dX = globalObjective - globalEndPosition (fill the rest with zeros [orientations]).

  3. Calculate configuration dQ with jacobian pseudoinverse:

    jT = jacobian.transpose(); inv = (jacobian * jT).inverse(); jpinv = jT * inv; dQ = jpinv * dX; 
  4. So what exactly is dQ? Each value should represents a local change in i-th joint.

    • Is it the angle of rotation around rotation axis in local joint frame?
    • Or maybe translation value along cross product?

I tried applying both variations above updating local joint transform with simple matrix multiplication and none of this works. The end-effector position seems to be moving very randomly - at each iteration it's very different.

\$\endgroup\$
1
  • \$\begingroup\$ I need to calculate this locally, because the end-effector does not always represent a leaf of the skeleton. \$\endgroup\$ Commented Jan 18, 2017 at 13:49

1 Answer 1

0
\$\begingroup\$

The answer is this:

  1. Jacobian needs to be calculated globally. For each joint in global coordinates rotation axis needs to be calculated as a cross product between joint-to-end and joint-to-target. The vector that goes into the Jacobian position field is a cross product between joint-to-end and rotation axis - it is a vector along which movement needs to be made (wikipedia under "angular velocity" names it: cross-radial component).

    // For each joint (except end-effector) JointPosition = ToWorld(jointChainID[j]).Translation(); JointToEnd = EndPosition - JointPosition; JointToTarget = TargetPosition - JointPosition; // Calculate Rotation Axis RotationAxis = JointToEnd.Cross(JointToTarget); RotationAxis.Normalize(); // Cross-Radial Vector Result = JointToEnd.Cross(RotationAxis); // Jacobian column entry jacobian(0, j) = Result.x; jacobian(1, j) = Result.y; jacobian(2, j) = Result.z; jacobian(3, j) = RotationAxis.x; jacobian(4, j) = RotationAxis.y; jacobian(5, j) = RotationAxis.z; 
  2. The result dQ contains rotation angles for each joint along RotationAxis (in global coordinates). Then I apply changes locally (this is weird, as I apply global changes to local joint coordinates, but somehow this works):

    // Apply changes to local skinning matrices // For each joint (except end-effector) jointID = JointChainID[j]; gTransform = Matrix::CreateFromAxisAngle(cache.RotationAxes[j], ToRadians(dQ(j))); SkinningLocal[jointID] = gTransform * SkinningLocal[jointID]; 
\$\endgroup\$

You must log in to answer this question.