Inverse Kinematics
Back to Home

INVERSE KINEMATICS

September 04, 2021

 View on Github

The most important tool of the theoretical physicist is his wastebasket.

Albert Einstein

The original paper on the technique described here was written by Andreas Aristidou and Joan Lasenby and can be accessed here.


BACKGROUND

Kinematics solvers are divided into two main categories: forward and inverse. Forward kinematics attempts to find a target point from a set of conditions, which in this example are joint angles for an arm. This is relatively simple to implement using basic trigonometry. Inverse kinematics, as its name suggests, does the opposite. Given a target point, what set of conditions (i.e., joint angles) will lead to that target point?

Inverse kinematics (IK) is an important problem in fields like robotics, where joint angles must be calculated to move the end of an arm to a known desired location.

Implementing inverse kinematics is not as easy as forward kinematics. Some exact methods use computationally expensive operations that may not guarantee a solution in all cases. However, an algorithm published in 2011 provides a numerical solution that is both fast and easy to implement called FABRIK, which we’ll use in this project. FABRIK stands for Forward And Backward Reaching Inverse Kinematics and works by repeatedly iterating forward and backward along the joint-arm system, finding differences in the positions of points and their target points and updating those positions accordingly.

ALGORITHM

For an in-depth explanation of FABRIK and how it works, I recommend this video by EgoMoose.

FABRIK simplifies the problem of IK by dividing it into multiple smaller problems of points on a line. Each line segment has two points on either end, PAP_A and PBP_B. It iteratively goes through each point, comparing it to a target point, and updates the position of the previous point to be in the direction of the target point while keeping the same segment length. The first cycles “forwards”, reaching out toward the green point from the red point. It then repeats “backwards”, reaching from the green point to the red. This is repeated until the difference between the target point and the end point is smaller than some set value.

The basic process for the forward reaching pass is outlined in figure 1.

diagram of steps of FABRIK Fig. 1 - FABRIK Forward Reaching Pass Outline

The steps, where PTP_T is the target point:

  1. Find the displacement vector, D\overrightarrow{D}, from PTP_T to PAP_A.
  2. Normalize vector D\overrightarrow{D}, so it has a magnitude of one, denoted by D^\hat{D}.
  3. Multiply D^\hat{D} by the scalar length of segment ABAB. Moving along this vector from point PTP_T is where the new location of PAP_A will be, denoted by PAP'_A. Move PBP_B to PTP_T to get PBP'_B. Segment ABA'B' has the same length as ABAB.
  4. Repeat, setting the new PTP_T to PAP'_A, the new PBP_B to the original PAP_A, and PAP_A to the next point down the arm.

The process is almost the same for the backward reaching pass. At the end of the forward pass, the first point on the left will have moved from the original P0P_0, according to the algorithm. If you want the end to move, this is fine, but for a stationary point like in our simulation, another pass is required, this time with PTP_T set to the original position of P0P_0 and iterating over the points from left to right, or whatever the opposite direction of the forward pass happens to be. It is therefore necessary to keep track of the original P0P_0, so I made a separate variable to hold its coordinates.

IMPLEMENTATION NOTES

In this implementation, the points and segments are stored in C++ vectors. I created a segment class that contains two pointers, one for either end of the segment.

Before we can actually call the forward and backward pass functions, we have to make sure the target point is actually reachable, simply by checking if

i=0nLseg(i)(xTx0)2+(yTy0)2\sum_{i=0}^{n}{L_{seg}(i)} \geq \sqrt{(x_T-x_0)^2+(y_T-y_0)^2}

where (x0,y0)(x_0, y_0) and (xT,yT)(x_T, y_T) are the coordinates for the end and target points, respectively, and Lseg(i)L_{seg}(i) is the length of the ii‘th segment. This relates the total length of the arm to the distance it has to reach.

If the point is reachable, then we can begin the cycle of forward and backward passes. These run in a while loop until the distance between the target end point and end of the arm is smaller than some tolerance, which seemed to work well when I set it to one.

If the point is not reachable, then we can still make it reach in the direction of the target point. I did this by finding the displacement vector between the first and target points, normalizing it, then multiplying by the total length of the arm. This results in a vector with its direction pointing to the target and its length that of the arm. Adding this vector’s components to the coordinates of the first point will result in the farthest reachable point in the direction of the original target point. We can now use this new point and run the algorithm with it as the target point.

CONCLUSION

A full implementation can add features such as functionality in 3 dimensions, contraints on joint angles, or multiple end effectors instead of a single arm. These are all important in real-world applications, including robotics and animation. You could also increase the number of points on the arm, which would result in a more noodle-like structure as opposed to the rigidity of long arm segments. The FABRIK algorithm is simple and runs with fewer iterations and is more computationally efficient than many others, and was a great project to implement.