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.

Updated · AI-authored, review-gated · how lessons are made

Not signed in: your progress and quiz score won't be saved.
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):RnSE(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):RnSE(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 Ti1iT_{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)    Tn1n(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 {i1}\{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 {i1}\{i-1\} to frame {i}\{i\}:

ParameterSymbolTypeMeaning
Link lengthaia_ifixedDistance along xix_i between zi1z_{i-1} and ziz_i
Link twistαi\alpha_ifixedAngle between zi1z_{i-1} and ziz_i about xix_i
Link offsetdid_ifixed*Distance along zi1z_{i-1} between origins
Joint angleθi\theta_ivariableRotation about zi1z_{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 {i1}\{i-1\} to frame {i}\{i\} is a product of four elementary transforms:

Ti1i=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:

Ti1i=[cθisθicαisθisαiaicθisθicθicαicθ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:

Ti1i=[cisi0licisici0lisi00100001]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=l1cosq1+l2cos(q1+q2),y=l1sinq1+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 iai+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