AnyLearn
All lessons
Roboticsintermediate

Forward Kinematics and DH Parameters

Translate a list of joint angles into an end-effector pose. Learn the four Denavit-Hartenberg parameters, build per-link transforms, multiply them into T_0^n, and implement FK for a 2-link planar arm in NumPy.

Not signed in โ€” your progress and quiz score won't be saved.
Lesson progress1 / 8

The forward kinematics problem

Forward kinematics (FK) answers: given the vector of joint angles (or displacements) q=[q1,q2,โ€ฆ,qn]โŠคq = [q_1, q_2, \ldots, q_n]^\top, where is the end-effector? Formally, FK is a function

T0EE(q):Rnโ†’SE(3)T_0^{EE}(q): \mathbb{R}^n \rightarrow SE(3)

that maps joint space to Cartesian (task) space. For a 2-DOF planar arm the output collapses to (x,y,ฮธ)(x, y, \theta); a 6-DOF industrial arm produces a full 4ร—44 \times 4 transform. FK is deterministic: one qq gives exactly one pose. That makes it easy to compute โ€” the hard direction (pose โ†’\to joints) is inverse kinematics, which we tackle next lesson.

Full lesson text

All 8 steps on one page โ€” for reading, reference, and search.

Show

1. The forward kinematics problem

Forward kinematics (FK) answers: given the vector of joint angles (or displacements) q=[q1,q2,โ€ฆ,qn]โŠคq = [q_1, q_2, \ldots, q_n]^\top, where is the end-effector? Formally, FK is a function

T0EE(q):Rnโ†’SE(3)T_0^{EE}(q): \mathbb{R}^n \rightarrow SE(3)

that maps joint space to Cartesian (task) space. For a 2-DOF planar arm the output collapses to (x,y,ฮธ)(x, y, \theta); a 6-DOF industrial arm produces a full 4ร—44 \times 4 transform. FK is deterministic: one qq gives exactly one pose. That makes it easy to compute โ€” the hard direction (pose โ†’\to joints) is inverse kinematics, which we tackle next lesson.

2. The kinematic chain

A serial robot is a sequence of rigid links connected by joints. Frame {i}\{i\} is attached to link ii. The relative transform Tiโˆ’1iT_{i-1}^{i} depends on the joint variable qiq_i plus the fixed geometry of link ii. FK chains every link transform:

T0n=T01(q1)โ€…โ€ŠT12(q2)โ€…โ€Šโ‹ฏโ€…โ€ŠTnโˆ’1n(qn)T_0^n = T_0^1(q_1) \; T_1^2(q_2) \; \cdots \; T_{n-1}^n(q_n)

For a revolute joint rotating about the zz-axis of frame {iโˆ’1}\{i-1\}, only qiq_i changes; everything else is geometry. The question is: how do you parameterize that fixed geometry consistently across all links? That is exactly what Denavit-Hartenberg conventions solve.

3. The four DH parameters

Denavit and Hartenberg (1955) proved that any two consecutive joint axes can be related by exactly four parameters. For the transform from frame {iโˆ’1}\{i-1\} to frame {i}\{i\}:

ParameterSymbolTypeMeaning
Link lengthaia_ifixedDistance along xix_i between ziโˆ’1z_{i-1} and ziz_i
Link twistฮฑi\alpha_ifixedAngle between ziโˆ’1z_{i-1} and ziz_i about xix_i
Link offsetdid_ifixed*Distance along ziโˆ’1z_{i-1} between origins
Joint angleฮธi\theta_ivariableRotation about ziโˆ’1z_{i-1}

(*For a prismatic joint, did_i is variable and ฮธi\theta_i is fixed.) The convention locks down axes unambiguously, keeping DH tables transferable between teams and textbooks.

4. The DH transform matrix

The standard DH transform from frame {iโˆ’1}\{i-1\} to frame {i}\{i\} is a product of four elementary transforms:

Tiโˆ’1i=Rz(ฮธi)โ€…โ€ŠTz(di)โ€…โ€ŠTx(ai)โ€…โ€ŠRx(ฮฑi)T_{i-1}^{i} = R_z(\theta_i) \; T_z(d_i) \; T_x(a_i) \; R_x(\alpha_i)

Expanding into one matrix:

Tiโˆ’1i=[cฮธiโˆ’sฮธicฮฑisฮธisฮฑiaicฮธisฮธicฮธicฮฑiโˆ’cฮธisฮฑiaisฮธi0sฮฑicฮฑidi0001]T_{i-1}^{i} = \begin{bmatrix} c\theta_i & -s\theta_i c\alpha_i & s\theta_i s\alpha_i & a_i c\theta_i \\ s\theta_i & c\theta_i c\alpha_i & -c\theta_i s\alpha_i & a_i s\theta_i \\ 0 & s\alpha_i & c\alpha_i & d_i \\ 0 & 0 & 0 & 1 \end{bmatrix}

where cฮธi=cosโกฮธic\theta_i = \cos\theta_i, sฮธi=sinโกฮธis\theta_i = \sin\theta_i, etc. Every robot link gets one row in a DH table, and one call to this formula.

5. Worked example: 2-link planar arm

Consider two links of lengths l1l_1 and l2l_2 in the xx-yy plane, revolute joints rotating about zz:

Joint iiฮธi\theta_idid_iaia_iฮฑi\alpha_i
1q1q_10l1l_10
2q2q_20l2l_20

Because both ฮฑi=0\alpha_i = 0 and di=0d_i = 0, each DH matrix simplifies to:

Tiโˆ’1i=[ciโˆ’si0licisici0lisi00100001]T_{i-1}^{i} = \begin{bmatrix} c_i & -s_i & 0 & l_i c_i \\ s_i & c_i & 0 & l_i s_i \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

The FK end-effector position is:

x=l1cosโกq1+l2cosโก(q1+q2),y=l1sinโกq1+l2sinโก(q1+q2)x = l_1 \cos q_1 + l_2 \cos(q_1+q_2), \qquad y = l_1 \sin q_1 + l_2 \sin(q_1+q_2)

Verify with q1=q2=0q_1 = q_2 = 0: both cosines are 1, so x=l1+l2x = l_1 + l_2, y=0y = 0 โ€” the arm stretches along the xx-axis. Correct.

6. NumPy implementation: 2-link FK

import numpy as np

def dh_transform(theta, d, a, alpha):
    """Standard DH transform T_{i-1}^{i}."""
    ct, st = np.cos(theta), np.sin(theta)
    ca, sa = 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]
    ])

def fk_2link(q1, q2, l1=1.0, l2=0.8):
    """FK for a 2-link planar arm. Returns 4x4 T_0^EE."""
    T01 = dh_transform(theta=q1, d=0, a=l1, alpha=0)
    T12 = dh_transform(theta=q2, d=0, a=l2, alpha=0)
    return T01 @ T12

# Test: straight out along x
T = fk_2link(0, 0)
print("EE position:", T[:3, 3])  # [1.8, 0, 0]

# 90 deg + 45 deg configuration
T = fk_2link(np.pi/2, np.pi/4)
print("EE position:", np.round(T[:3, 3], 4))

The function is trivially extensible to nn links by adding rows to a DH table and multiplying in a loop.

7. DH parameter pipeline

From joint angles to end-effector pose:

flowchart LR
  Q["Joint angles q1 ... qn"]
  DH["DH table: theta d a alpha per joint"]
  TI["Per-link transform T_{i-1}^{i} via DH formula"]
  CHAIN["Chain multiply T_0^n = T01 x T12 x ... x T_{n-1}n"]
  EE["End-effector pose T_0^n in SE(3)"]
  Q --> DH
  DH --> TI
  TI --> CHAIN
  CHAIN --> EE

8. FK for larger arms and validation

For a 6-DOF industrial arm (e.g. PUMA-style), the DH table has six rows. FK is still the same chain multiply โ€” just six matrices instead of two. A few engineering tips:

  • Table first, code second. Write out the DH table for your robot, check it against the manufacturer's spec, then generate transforms. Bugs in the table are much easier to spot than bugs in matrix code.
  • Validate at zero config. With all qi=0q_i = 0, compute T0nT_0^n analytically and compare to code. Any mismatch points to a sign or axis error.
  • Check end-effector reachability. The maximum reach is โˆ‘iโˆฃaiโˆฃ+โˆฃdiโˆฃ\sum_i |a_i| + |d_i|; if your target is outside this sphere, FK will never put the EE there โ€” IK will fail.

Once FK is verified, you have the foundation for everything else: Jacobians, workspace analysis, and motion planning.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. Forward kinematics maps:
    • End-effector pose to joint angles.
    • Joint angles to end-effector pose.
    • Joint torques to joint accelerations.
    • Cartesian velocity to joint velocity.
  2. In the standard DH convention, which parameter is the joint variable for a revolute joint?
    • $a_i$ (link length)
    • $\alpha_i$ (link twist)
    • $d_i$ (link offset)
    • $\theta_i$ (joint angle)
  3. For a 2-link planar arm with $l_1 = l_2 = 1$ and $q_1 = q_2 = 0$, what is the end-effector $x$-position?
    • 0
    • 1
    • 2
    • $\sqrt{2}$
  4. Which matrix product gives the end-effector pose for a 3-link arm?
    • $T_0^3 = T_0^1 + T_1^2 + T_2^3$
    • $T_0^3 = T_2^3 \; T_1^2 \; T_0^1$
    • $T_0^3 = T_0^1 \; T_1^2 \; T_2^3$
    • $T_0^3 = (T_0^1)^3$
  5. The DH parameter $\alpha_i$ (link twist) represents:
    • The rotation of joint $i$ around its own axis.
    • The distance along $x_i$ between consecutive $z$-axes.
    • The angle between $z_{i-1}$ and $z_i$ measured about $x_i$.
    • The offset along $z_{i-1}$ between consecutive origins.

Related lessons