Forward Kinematics & Denavit–Hartenberg Parameters
Where is the gripper for given joint angles? Learn the forward kinematics problem, link and joint parameters, the Denavit–Hartenberg (DH) convention (θ, d, a, α) and the modified DH variant, assigning frames step by step, building the DH table, multiplying link transforms, planar arm closed-form kinematics, typical 6-axis arm structures (spherical wrist), the product-of-exponentials alternative, and kinematic calibration — with NumPy code computing the pose of a 3-link arm with DH matrices and checking it against the closed form.
Forward kinematics (FK) maps joint values to the pose of the end effector. The controller computes it thousands of times per second for display, safety zones, and as the basis of inverse kinematics and Jacobians.
The Denavit–Hartenberg convention describes any serial chain with just four parameters per joint, turning FK into a product of standard matrices.
An analogy
Forward kinematics is like following a chain of instructions: 'turn the shoulder 30°, then along the upper arm 40 cm, turn the elbow 45°, forearm 30 cm…' — you end up at a unique place. Each instruction is one DH transform.
The FK problem
Given joint variables q = (q₁ … qₙ), find the end-effector pose (position and orientation). For serial robots FK always has a unique answer and is straightforward to compute — unlike inverse kinematics. For planar arms, simple trigonometry sums the link vectors.
Assigning DH frames
Standard DH procedure:
- zᵢ₋₁ along the axis of joint i
- xᵢ along the common normal between zᵢ₋₁ and zᵢ
- θᵢ: angle about zᵢ₋₁ from xᵢ₋₁ to xᵢ; dᵢ: distance along zᵢ₋₁
- aᵢ: distance along xᵢ between the z axes; αᵢ: twist about xᵢ from zᵢ₋₁ to zᵢ
- Build the table, then multiply Aᵢ matrices (modified DH attaches frames differently — don't mix!)
Industrial arm structure
Most 6-axis arms have three 'arm' joints (waist, shoulder, elbow) that position the wrist centre and a spherical wrist (three axes intersecting at a point) that orients the tool. This decoupling makes inverse kinematics solvable in closed form (Pieper's solution). The flange-to-TCP transform (tool frame) is added after the DH chain.
Alternatives and accuracy
Product of exponentials (screw axes in the base frame) avoids DH's frame-assignment ambiguities and handles parallel axes gracefully. URDF (ROS) describes robots with joint origins and axes directly. Real robots deviate from nominal geometry (manufacturing tolerances, gear backlash, compliance, thermal growth); kinematic calibration identifies parameter errors with laser trackers to improve absolute accuracy from ~1 mm to ~0.1–0.3 mm.
Formulas
- DH link transform:
Aᵢ = Rot_z(θᵢ)·Trans_z(dᵢ)·Trans_x(aᵢ)·Rot_x(αᵢ) - Chain:
⁰Tₙ = A₁·A₂·…·Aₙ - Planar 3R position:
x = Σ Lᵢ cos(θ₁ + … + θᵢ); y = Σ Lᵢ sin(θ₁ + … + θᵢ) - Planar orientation:
φ = θ₁ + θ₂ + θ₃ - Product of exponentials:
T(θ) = e^[S₁]θ₁ … e^[Sₙ]θₙ·M
Worked example: Planar 2R FK
Given: L₁ 0.4, L₂ 0.3, q₁ 0°, q₂ 90°
Find: (x, y).
- x = 0.4 + 0.3 cos 90°
- y = 0 + 0.3 sin 90°
Answer: (0.40, 0.30) m
Worked example: Orientation
Given: q = (30°, 45°, −30°)
Find: φ.
- Sum of angles
Answer: 45°
Worked example: Prismatic joint
Given: SCARA z-axis d₃ = −0.12 m
Find: Effect.
- d is the variable along z
Answer: Tool lowers 120 mm
DH forward kinematics of a 3-link arm
import numpy as np
# Forward kinematics with Denavit-Hartenberg parameters (a 3-link planar arm, then generic DH)
def dh(theta, d, a, alpha):
ct, st, ca, sa = np.cos(theta), np.sin(theta), np.cos(alpha), np.sin(alpha)
return np.array([[ct, -st * ca, st * sa, a * ct],
[st, ct * ca, -ct * sa, a * st],
[0, sa, ca, d],
[0, 0, 0, 1]])
links = [0.40, 0.30, 0.10]
q = np.radians([30, 45, -30])
T = np.eye(4)
for qi, a in zip(q, links):
T = T @ dh(qi, 0.0, a, 0.0)
x, y = T[0, 3], T[1, 3]
phi = np.degrees(np.arctan2(T[1, 0], T[0, 0]))
print(f"end effector x = {x:.4f} m, y = {y:.4f} m, orientation {phi:.1f} deg")
# Check with the closed-form planar sum
c = np.cumsum(q)
print(f"closed form: x = {sum(l*np.cos(t) for l, t in zip(links, c)):.4f}, y = {sum(l*np.sin(t) for l, t in zip(links, c)):.4f}")
# end effector x = 0.4948 m, y = 0.5605 m, orientation 45.0 deg
# closed form: x = 0.4948, y = 0.5605The general DH function reproduces the planar closed-form answer exactly, and the orientation equals the sum of joint angles (30 + 45 − 30 = 45°). The same dh() function with a 6-row table (including nonzero d and α) computes the pose of any industrial arm — only the table changes between robot models.
Common mistakes
- Wrong: Mixing standard and modified DH tables. — Right: Use one convention consistently with its matching matrix.
- Wrong: Forgetting the tool transform. — Right: FK gives the flange; append the TCP offset.
- Wrong: Degrees vs radians. — Right: Trig functions need radians.
Applications
- Robot controllers: Real-time TCP display and zones.
- Simulation: URDF/DH models in Gazebo and RoboDK.
- Calibration: Identifying DH errors with laser trackers.
- Exoskeletons: Mapping joint sensors to limb pose.
Key takeaways
- FK: joints → pose, unique.
- Aᵢ = Rot_z(θ)Trans_z(d)Trans_x(a)Rot_x(α).
- ⁰Tₙ = A₁A₂…Aₙ, then add the tool.
- Calibration improves absolute accuracy.
Going further
- Modified DH (Craig): Frame at the proximal end of links.
- URDF & xacro: Robot description in ROS.
- Kinematic calibration: Least-squares parameter identification.