All projects

2D SCARA Kinematics

Forward-kinematics demonstration for the planar two-link SCARA arm.
Forward kinematics

Joint-angle and base-position controls update the arm pose and all three local coordinate frames.

Kinematics derivation

The mechanism is a planar serial chain with two revolute joints. Let the base origin be o0=[x0,y0]T\underset{\sim}{o}_0=[x_0,y_0]^T, and let each link extend along the local ii-axis of its joint frame. The first link is oriented by θ1\theta_1 relative to the base frame. Because θ2\theta_2 is measured relative to link 1, the absolute orientation of link 2 is θ1+θ2\theta_1+\theta_2.

Planar two-link SCARA geometry showing link lengths l1 and l2, joint angles theta1 and theta2, coordinate frames, and the end-effector projections x e and y e.
Figure 1. Forward-kinematics geometry.

Forward kinematics

Solving the direct (forward) kinematics of the 2D SCARA is fairly straightforward: given the joint angles, we can determine the location of the gripper in base coordinates.

o2=ai0+bj0\underset{\sim}{o}_2 = a\vect{i}_0 + b\vect{j}_0

Now, just looking at the above figure, we can see two things fairly clearly:

a=l1cosθ1+l2cos(θ1+θ2),b=l1sinθ1+l2sin(θ1+θ2).\begin{aligned} a &= l_1\cos\theta_1+l_2\cos(\theta_1+\theta_2), \\ b &= l_1\sin\theta_1+l_2\sin(\theta_1+\theta_2). \end{aligned}

And so,

o2=o0+[ab]=o0+[l1cosθ1+l2cos(θ1+θ2)l1sinθ1+l2sin(θ1+θ2)]\underset{\sim}{o}_2 =\underset{\sim}{o}_0+ \begin{bmatrix}a\\b\end{bmatrix} =\underset{\sim}{o}_0+ \begin{bmatrix} l_1\cos\theta_1+l_2\cos(\theta_1+\theta_2) \\ l_1\sin\theta_1+l_2\sin(\theta_1+\theta_2) \end{bmatrix}

The key thing here is that link 2 (θ2\theta_2) is measured relative to link 1 (θ1\theta_1). To get link 2’s absolute orientation, and therefore its xx and yy components, you need to add θ1\theta_1 and θ2\theta_2 together.

Now, let’s solve the velocity kinematics.

Velocity kinematics

All we really need to do here is take the derivative of the point o2\underset{\sim}{o}_2 with respect to time. Using basic differentiation rules and trig derivatives, we take the derivative of the equation above:

ddt[ab]=[l1sinθ1θ˙1l2sin(θ1+θ2)(θ˙1+θ˙2)l1cosθ1θ˙1+l2cos(θ1+θ2)(θ˙1+θ˙2)]\frac{d}{dt} \begin{bmatrix}a\\b\end{bmatrix} = \begin{bmatrix} -l_1\sin\theta_1\,\dot{\theta}_1-l_2\sin(\theta_1+\theta_2)(\dot{\theta}_1+\dot{\theta}_2) \\ l_1\cos\theta_1\,\dot{\theta}_1+l_2\cos(\theta_1+\theta_2)(\dot{\theta}_1+\dot{\theta}_2) \end{bmatrix}

Now, rearranging a bit:

[a˙b˙]=[l1sinθ1l2sin(θ1+θ2)l1cosθ1+l2cos(θ1+θ2)]J1θ˙1+[l2sin(θ1+θ2)l2cos(θ1+θ2)]J2θ˙2\begin{bmatrix}\dot{a}\\\dot{b}\end{bmatrix} = \underbrace{ \begin{bmatrix} -l_1\sin\theta_1-l_2\sin(\theta_1+\theta_2) \\ l_1\cos\theta_1+l_2\cos(\theta_1+\theta_2) \end{bmatrix}}_{\raisebox{-0.3ex}{$\vect{J}_1$}} \dot{\theta}_1 + \underbrace{ \begin{bmatrix} -l_2\sin(\theta_1+\theta_2) \\ l_2\cos(\theta_1+\theta_2) \end{bmatrix}}_{\raisebox{-0.3ex}{$\vect{J}_2$}} \dot{\theta}_2 v˙=J1θ˙1+J2θ˙2=[J1J2][θ˙1θ˙2]=J(q)q˙\vect{\dot{v}} =\vect{J}_1\dot{\theta}_1+\vect{J}_2\dot{\theta}_2 =\begin{bmatrix}\vect{J}_1&\vect{J}_2\end{bmatrix} \begin{bmatrix}\dot{\theta}_1\\\dot{\theta}_2\end{bmatrix} =J(\vect{q})\vect{\dot{q}}

This is the Jacobian matrix, which, in this case, is a 2×22\times2 matrix that maps joint velocities to end-effector velocities in the base frame.

Here, it is easy to see how it represents the end-effector velocities, but it will definitely get messy for higher DOFs!

Inverse kinematics

Now for the inverse kinematics, which I find to be a bit more fun. Here, we are given the end-effector position and want to find the corresponding joint angles, θ1\theta_1 and θ2\theta_2. This is a bit trickier, but we can use the law of cosines to solve for the angles.

Looking back at the figure, the important part is to draw the hypotenuse formed by aa and bb. We can then use cosine and tangent relationships to solve for the various angles. As shown in the figure, we have also labeled α\alpha and β\beta, which are the angles of the drawn triangles. We can solve for these and then use them to determine the joint angles.

First, let’s solve for θ2\theta_2, since that one is a bit easier. There are technically two ways to do this (well, 1.5, the second way uses the first, but with a few additional steps): a simple method, and a more involved method using atan2 for better accuracy at small angles, as well as the ability to handle quadrants.

The main trick is to solve for this interior angle and recognize that πAngle=θ2\pi-\text{Angle}=\theta_2.

Two-link planar arm with the interior triangle angle at the elbow labeled pi minus theta two for the cosine-law inverse-kinematics derivation.
Figure 2. Interior angle used in the cosine-law solution.

So, using the law of cosines, we can solve for θ2\theta_2:

l2=l12+l222l1l2cos(πθ2)l^2 = l_1^2 + l_2^2 - 2l_1l_2\cos(\pi-\theta_2)

The equation above works, but we can also use the trig identity cos(πx)=cos(x)\cos(\pi-x)=-\cos(x) to simplify a bit:

l2=l12+l22+2l1l2cos(θ2)l^2 = l_1^2 + l_2^2 + 2l_1l_2\cos(\theta_2)

And so:

θ2=cos1 ⁣(l2l12l222l1l2)\theta_2=\cos^{-1}\!\left(\frac{l^2-l_1^2-l_2^2}{2l_1l_2}\right)

Now, that’s technically all we need. That’s method 1. But to get a little fancy (and improve the accuracy), we can instead use arctangent, or specifically, the quadrant-aware atan2, to retrieve the angle.

c2=cosθ2=l2l12l222l1l2c_2=\cos\theta_2=\frac{l^2-l_1^2-l_2^2}{2l_1l_2}

Since cosine alone does not distinguish between the positive and negative angle branches, we grab the sine:

s2=sinθ2=±1c22s_2=\sin\theta_2=\pm\sqrt{1-c_2^2}

Finally, to retrieve the angle while preserving its sign and quadrant, we use:

θ2=atan2(s2,c2)\theta_2=\operatorname{atan2}(s_2,c_2)

Moving on to finding θ1\theta_1.

We can find α\alpha simply by using the target-origin coordinates:

α=atan2(b,a)\alpha=\operatorname{atan2}(b,a)

For β\beta, we can also use the law of cosines. For example:

l22=l12+l22l1lcosβl_2^2=l_1^2+l^2-2l_1l\cos\beta

But again, we don’t really like the law of cosines, so let’s use atan2 instead. There is actually another right angle hidden in the figure:

Two-link planar arm with link two resolved into components parallel and perpendicular to link one, forming the right triangle used to calculate beta.
Figure 3. Right triangle used for β.

And so:

β=atan2 ⁣(l2sinθ2,  l1+l2cosθ2)\begin{aligned} \beta &=\operatorname{atan2}\!\left(l_2\sin\theta_2,\;l_1+l_2\cos\theta_2\right) \end{aligned}

Here, θ2\theta_2 is already known.

And finally:

θ1=αβ\theta_1=\alpha-\beta

There we have it: the inverse kinematics for a 2D SCARA arm. Note that the s2s_2 derivation gives us two possible solutions for θ2\theta_2, elbow up and elbow down, which also give us two corresponding solutions for θ1\theta_1.

Repository structure

The derivation above describes the underlying mathematics. In the repository, the calculation layer lives in kinematics.py. It provides 2D rotation matrices along with forward-, inverse-, and velocity-kinematics functions. Three small applications sit above that shared layer:

  1. forward_kinematics.py controls the base position and two joint angles while plotting the link geometry and local coordinate frames.
  2. inverse_kinematics.py moves a Cartesian target, compares two analytical formulations, and switches between the positive and negative solution branches.
  3. velocity_kinematics.py controls joint position and joint rate, displays the Jacobian, and animates the resulting motion.

Keeping the numerical model separate from the interface makes the equations easier to inspect and reuse while each demonstration stays focused on one kinematics question.

Run locally

Create a Python environment with NumPy and Matplotlib, then launch any demonstration independently:

python forward_kinematics.py
python inverse_kinematics.py
python velocity_kinematics.py