Forward kinematics of a 2-DOF planar arm
Two joints, two link lengths, and high-school trig. Compute where the end-effector is, plot the reachable workspace, and you've unlocked the foundation of every arm problem.
Every serious robotics textbook spends a chapter on forward kinematics before it says anything interesting. There's a reason: if you can't answer "where is the hand?" you can't do anything with the arm. Let's do the smallest useful case in ten minutes.
The setup
A planar arm with two revolute joints — shoulder and elbow. The shoulder is at the origin. Link 1 has length L1. Link 2 has length L2. Joint angles are θ1 (shoulder) and θ2 (elbow, relative to link 1).
Your job: given (θ1, θ2, L1, L2), find the end-effector position (x, y).
Why forward first, not inverse
Inverse kinematics (given (x, y), find the angles) is the more useful direction in practice — it's what you do when you want the arm to reach a point. But FK is the right place to start because:
- FK has exactly one answer. IK often has zero or two.
- FK is the building block. You cannot write IK without knowing FK.
- FK teaches the coordinate-frame thinking that every future topic requires.
Derive it on paper
Place joint 1 at the origin. Joint 2 sits at the end of link 1:
x1 = L1 · cos(θ1)
y1 = L1 · sin(θ1)
Link 2 is oriented at angle θ1 + θ2 in the world frame (because joint 2's angle is relative to link 1, and link 1 was at angle θ1). So the end-effector is at:
x = L1 · cos(θ1) + L2 · cos(θ1 + θ2)
y = L1 · sin(θ1) + L2 · sin(θ1 + θ2)
That's it. Two lines of trig. Now let's make it real.
Python implementation
import math
def forward_kinematics(theta1, theta2, L1=1.0, L2=1.0):
"""Return (x, y) end-effector position for a 2-DOF planar arm.
Angles in radians. Joint 2 is measured relative to link 1.
"""
x = L1 * math.cos(theta1) + L2 * math.cos(theta1 + theta2)
y = L1 * math.sin(theta1) + L2 * math.sin(theta1 + theta2)
return x, y
# Quick sanity check: arms folded = origin, arm straight out = (L1+L2, 0)
print(forward_kinematics(0, math.pi)) # (0, 0) both links overlap
print(forward_kinematics(0, 0)) # (2, 0) straight out along +x
print(forward_kinematics(math.pi/2, 0)) # (0, 2) straight up
Plot the workspace
The reachable set of points is a donut — inner radius |L1 − L2|, outer radius L1 + L2. Let's verify visually:
import numpy as np
import matplotlib.pyplot as plt
L1, L2 = 1.0, 0.7
pts = []
for t1 in np.linspace(-math.pi, math.pi, 60):
for t2 in np.linspace(-math.pi, math.pi, 60):
pts.append(forward_kinematics(t1, t2, L1, L2))
xs, ys = zip(*pts)
plt.scatter(xs, ys, s=1, alpha=0.3)
plt.gca().set_aspect('equal')
plt.title(f"Reachable workspace, L1={L1}, L2={L2}")
plt.show()
You'll see an annulus. The hole in the middle exists when L1 ≠ L2 — the arm can never quite reach points inside radius |L1 − L2| because neither joint bends that much.
The matrix version (for when you need it)
The trig-based form above is clear but doesn't generalize. For any n-link arm, we use homogeneous transformation matrices. Each link contributes a 3×3 matrix (in 2D) or 4×4 (in 3D) that packages rotation and translation:
def T(theta, L):
c, s = math.cos(theta), math.sin(theta)
return np.array([
[c, -s, L*c],
[s, c, L*s],
[0, 0, 1],
])
T1 = T(theta1, L1)
T2 = T(theta2, L2)
T_total = T1 @ T2 # compose
x, y = T_total[0, 2], T_total[1, 2]
This style extends to 6-DOF arms, spatial arms, and parallel mechanisms without changing form. We'll come back to it in Lesson 4 (Product of Exponentials).
What this doesn't cover
- 3D arms — you need rotation matrices in SO(3), not just cos/sin. Lesson 2.
- Inverse kinematics — Lesson 5 (analytical) and Lesson 6 (iterative).
- Velocity and force — that's the Jacobian, Lesson 7.
- Dynamics — what torques you need to produce a motion. Lesson 8.
Exercise: verify your intuition
Before moving on, answer these without running code:
- If
L1 = L2 = 1andθ1 = θ2 = 0, where is the end-effector? - If you rotate
θ1by +π and leaveθ2at 0, where does the arm point? - What's the maximum distance the end-effector can be from the origin? The minimum?
If you got all three, you own this material. If not, re-read the derivation and try again — the rest of the track builds on this.
Next up: rigid-body transforms — the clean way to stop worrying about which angle is relative to what.
Comments
Sign in to post a comment.