The Denavit-Hartenberg Convention

Published on 2026-11-04·v1.0

Learning Objectives

  • Name the four Denavit-Hartenberg parameters aa, α\alpha, dd, θ\theta, say which one is the joint variable for a revolute and for a prismatic joint, and read each one off a drawing.
  • Assign link frames step by step on a three-joint arm, including the cases of intersecting and parallel axes, and fill in the table.
  • Write the link transform of the classic convention and of the modified convention, and say which convention a given source uses.
  • Check forward kinematics computed from a table against a product of transforms written directly from the geometry, numerically.
  • Recognize what goes wrong when a table written for one convention is fed to the formula of the other.

Context & Motivation

forward-kinematics-of-serial-manipulators wrote the pose of the end effector as a product of link transforms, and homogeneous-transformations-for-robot-poses gave each factor its meaning: a 4×44\times4 matrix that places one frame in another. Nothing in that recipe says where each link frame should sit. With frames placed freely, each link transform needs six numbers, three for position and three for orientation.

Denavit and Hartenberg (1955) proposed rules for placing the frames so that only four numbers per link remain. Modern Robotics (Lynch and Park, Appendix C) reports that they also showed four to be the minimum. The payoff is a compact table: one row per joint, one column per parameter, one moving entry per row. Murray, Li and Sastry (1994) noted that DH parameters were available for standard industrial robots and used by most commercial simulation and programming systems, and toolkits such as the Robotics Toolbox for Python still build arm models from them.

The trouble is that two incompatible versions of the rules are in common use, and both are called "DH". A table is meaningless unless you know which one wrote it.

Core Theory

Vectors are columns, and a matrix i−1Ti{}^{i-1}T_i gives the pose of frame {i}\{i\} in frame {i−1}\{i-1\}, so p(i−1)=i−1Ti p(i)\mathbf p^{(i-1)}={}^{i-1}T_i\,\mathbf p^{(i)}. Products are read left to right as motions of the current (moving) frame. Angles are positive by the right-hand rule about the named axis. Write cx=cos⁡xc_x=\cos x and sx=sin⁡xs_x=\sin x.

The idea: only x and z moves

Both conventions put a zz axis on every joint axis. For a revolute joint, zz is the axis of rotation. For a prismatic joint, zz points in the direction of positive travel. Between two consecutive joint axes there is a common normal, the line that meets both at right angles, and the xx axis is laid along it. With those two choices, getting from one frame to the next takes only a rotation and a translation along xx and a rotation and a translation along zz. Lateral motion along yy is never needed, which is where the saving from six numbers to four comes from.

The four numbers are:

parameter name measured
aa link length along the common normal, between the two joint axes
α\alpha link twist angle between the two joint axes, about the common normal
dd link offset along a joint axis, between two consecutive common normals
θ\theta joint angle angle between two consecutive common normals, about a joint axis

For a revolute joint θ\theta is the variable and aa, α\alpha, dd are constants. For a prismatic joint dd is the variable and θ\theta is constant. The conventions differ in which joint axis gets which frame index, and so in the order of the four motions.

The classic convention

In the classic convention (also called standard or distal), the axis of joint ii is zi−1z_{i-1}, and frame {i}\{i\} sits at the far end of link ii. The rules:

  1. Put zi−1z_{i-1} along the axis of joint ii.
  2. Put xix_i along the common normal from zi−1z_{i-1} to ziz_i, with origin oio_i where that normal meets ziz_i.
  3. Then aia_i is the distance from zi−1z_{i-1} to ziz_i along xix_i, αi\alpha_i the angle from zi−1z_{i-1} to ziz_i about xix_i, did_i the distance from oi−1o_{i-1} to the foot of xix_i along zi−1z_{i-1}, and θi\theta_i the angle from xi−1x_{i-1} to xix_i about zi−1z_{i-1}.

The link transform is a rotation and a translation along zi−1z_{i-1}, then a translation and a rotation along the new xx:

Ai=Rotz(θi) Transz(di) Transx(ai) Rotx(αi)=(cθi−sθicαisθisαiaicθisθicθicαi−cθisαiaisθi0sαicαidi0001).A_i=\mathrm{Rot}_z(\theta_i)\,\mathrm{Trans}_z(d_i)\,\mathrm{Trans}_x(a_i)\,\mathrm{Rot}_x(\alpha_i)=\begin{pmatrix}c_{\theta_i}&-s_{\theta_i}c_{\alpha_i}&s_{\theta_i}s_{\alpha_i}&a_ic_{\theta_i}\\ s_{\theta_i}&c_{\theta_i}c_{\alpha_i}&-c_{\theta_i}s_{\alpha_i}&a_is_{\theta_i}\\ 0&s_{\alpha_i}&c_{\alpha_i}&d_i\\ 0&0&0&1\end{pmatrix}.

The modified convention

In the modified convention (also called proximal), the axis of joint ii is ziz_i, and frame {i}\{i\} sits at the near end of link ii, on its own joint. The parameters keep their meanings but the twist and length that lead into joint ii carry the index i−1i-1:

i−1Ti=Rotx(αi−1) Transx(ai−1) Rotz(θi) Transz(di)=(cθi−sθi0ai−1sθicαi−1cθicαi−1−sαi−1−disαi−1sθisαi−1cθisαi−1cαi−1dicαi−10001).{}^{i-1}T_i=\mathrm{Rot}_x(\alpha_{i-1})\,\mathrm{Trans}_x(a_{i-1})\,\mathrm{Rot}_z(\theta_i)\,\mathrm{Trans}_z(d_i)=\begin{pmatrix}c_{\theta_i}&-s_{\theta_i}&0&a_{i-1}\\ s_{\theta_i}c_{\alpha_{i-1}}&c_{\theta_i}c_{\alpha_{i-1}}&-s_{\alpha_{i-1}}&-d_is_{\alpha_{i-1}}\\ s_{\theta_i}s_{\alpha_{i-1}}&c_{\theta_i}s_{\alpha_{i-1}}&c_{\alpha_{i-1}}&d_ic_{\alpha_{i-1}}\\0&0&0&1\end{pmatrix}.

A rotation about xx commutes with a translation along xx, and the same holds for zz, so some sources write the factors as Transx Rotx Rotz Transz\mathrm{Trans}_x\,\mathrm{Rot}_x\,\mathrm{Rot}_z\,\mathrm{Trans}_z. That is the same matrix.

Who uses which

  • Modern Robotics (Lynch and Park), Appendix C: the modified form, with parameters αi−1\alpha_{i-1}, ai−1a_{i-1}, did_i, ϕi\phi_i and exactly the matrix above. The book does not use the word "modified". Its main text uses the product of exponentials instead of DH.
  • Murray, Li and Sastry (A Mathematical Introduction to Robotic Manipulation), Chapter 3, Section 2.3: the classic matrix, with the joint angle written ϕi\phi_i.
  • Robotics Toolbox for Python documentation: classes RevoluteDH and RevoluteMDH, and a flag that is true "for modified Denavit-Hartenberg notation (eg. Craig's textbook)" and false "for standard Denavit-Hartenberg notation (eg. Siciliano, Spong, Paul)".
  • Wikipedia, "Denavit-Hartenberg parameters": names Craig's Introduction to Robotics (3rd edition) as a book that uses the modified parameters.

Modern Robotics also warns that some authors put the joint axis on xx instead of zz, and concludes that "it is essential to include a concise description of the link frames together with the parameter values."

Special cases

  • Intersecting axes. There is no common normal, so a=0a=0 and xx is taken perpendicular to the plane of the two axes. Either of its two directions works, and the choice flips the sign of α\alpha.
  • Parallel axes. There are infinitely many common normals. Pick the one that zeroes the most parameters, usually d=0d=0.
  • Base and tool. The rules leave the first and last frames partly free. Choose them to zero parameters. A frame displaced sideways along yy cannot be reached with xx and zz motions (Modern Robotics gives a pure translation along yy as the example), so a tool frame is often added as one extra constant transform after the table.

Worked Examples

Example 1: frames on an articulated arm, step by step

The arm has a vertical base joint, a horizontal shoulder joint at height d1=0.4d_1=0.4 m, and a horizontal elbow joint parallel to the shoulder. The upper arm is a2=0.5a_2=0.5 m and the forearm, measured to the tool tip, is a3=0.4a_3=0.4 m. The lengths are assumed.

Step 1, axes. Joint 1 axis: vertical through the base. Joint 2 axis: horizontal, perpendicular to the plane of the arm. Joint 3 axis: parallel to joint 2, through the elbow.

Step 2, classic zz axes. z0z_0 on joint 1, pointing up. z1z_1 on joint 2 and z2z_2 on joint 3, both pointing the same way, chosen so that a positive θ2\theta_2 lifts the arm. With x0x_0 pointing along the arm at θ1=0\theta_1=0, this makes z1=−y0z_1=-y_0 at θ1=0\theta_1=0.

Step 3, frame 1. z0z_0 and z1z_1 intersect at the shoulder, so a1=0a_1=0 and o1o_1 is the shoulder. x1x_1 must be perpendicular to both, so it is horizontal, in the plane of the arm. The angle from z0z_0 (up) to z1z_1 about x1x_1 is +90∘+90^\circ, since Rotx(90∘)\mathrm{Rot}_x(90^\circ) sends (0,0,1)(0,0,1) to (0,−1,0)(0,-1,0). The shoulder is 0.40.4 m up z0z_0, so d1=0.4d_1=0.4.

Step 4, frame 2. z1∥z2z_1\parallel z_2, so the common normal is chosen along the upper arm: x2x_2 points from shoulder to elbow, o2o_2 is the elbow, a2=0.5a_2=0.5, α2=0\alpha_2=0, d2=0d_2=0.

Step 5, tool frame. o3o_3 at the tip, x3x_3 along the forearm, z3∥z2z_3\parallel z_2: a3=0.4a_3=0.4, α3=0\alpha_3=0, d3=0d_3=0.

ii aia_i (m) αi\alpha_i did_i (m) θi\theta_i
1 00 90∘90^\circ 0.40.4 θ1\theta_1
2 0.50.5 00 00 θ2\theta_2
3 0.40.4 00 00 θ3\theta_3

At θ=(0,0,0)\theta=(0,0,0) the product gives a tip at (0.9,0,0.4)(0.9,0,0.4): arm horizontal, stretched along x0x_0, as the frames were drawn. At θ=(30∘,45∘,−60∘)\theta=(30^\circ,45^\circ,-60^\circ):

0T3=A1A2A3=(0.83650.22410.50.64080.48300.1294−0.86600.3700−0.25880.965900.65000001).{}^0T_3=A_1A_2A_3=\begin{pmatrix}0.8365&0.2241&0.5&0.6408\\0.4830&0.1294&-0.8660&0.3700\\-0.2588&0.9659&0&0.6500\\0&0&0&1\end{pmatrix}.

Example 2: checking the table against a direct product

The check must not reuse the table. Describe the same arm by its physical motions: turn about the vertical by θ1\theta_1, rise d1d_1, pitch up by θ2\theta_2 (a rotation about −y-y), move a2a_2 along the arm, pitch by θ3\theta_3, move a3a_3:

Tdir=Rotz(θ1) Transz(d1) Roty(−θ2) Transx(a2) Roty(−θ3) Transx(a3).T_{\text{dir}}=\mathrm{Rot}_z(\theta_1)\,\mathrm{Trans}_z(d_1)\,\mathrm{Rot}_y(-\theta_2)\,\mathrm{Trans}_x(a_2)\,\mathrm{Rot}_y(-\theta_3)\,\mathrm{Trans}_x(a_3).

At the same angles its last column is (0.6408,0.3700,0.6500)(0.6408,0.3700,0.6500), which also matches the plane-geometry formula (c1r, s1r, d1+a2s2+a3s23)\big(c_1r,\ s_1r,\ d_1+a_2s_2+a_3s_{23}\big) with r=a2c2+a3c23=0.7399r=a_2c_2+a_3c_{23}=0.7399. The rotation parts differ, and Tdir−1 0T3=Rotx(90∘)T_{\text{dir}}^{-1}\,{}^0T_3=\mathrm{Rot}_x(90^\circ): the direct frame keeps its yy axis on the joint axis, the DH frame puts zz there. That offset is a choice of tool frame, so it must be the same at every configuration. With rng = numpy.random.default_rng(5) and 1000 configurations drawn from rng.uniform(-pi, pi, (1000, 3)), the largest entry of ∣0T3−TdirRotx(90∘)∣\lvert{}^0T_3-T_{\text{dir}}\mathrm{Rot}_x(90^\circ)\rvert was 2.2×10−162.2\times10^{-16}. With the sign of α1\alpha_1 flipped to −90∘-90^\circ, the same test fails at once: positive θ2\theta_2 now lowers the arm, and at the example angles the tip lands at height 0.150.15 m instead of 0.650.65 m.

Example 3: the same arm in the modified convention

Now ziz_i sits on joint ii. Frame {1}\{1\} has z1z_1 vertical (joint 1) with its origin at shoulder height. Frame {2}\{2\} has z2z_2 on the shoulder axis, at the shoulder. Frame {3}\{3\} has z3z_3 on the elbow axis, at the elbow. The tip needs one more constant row, since no joint lives there.

ii αi−1\alpha_{i-1} ai−1a_{i-1} (m) did_i (m) θi\theta_i
1 00 00 0.40.4 θ1\theta_1
2 90∘90^\circ 00 00 θ2\theta_2
3 00 0.50.5 00 θ3\theta_3
tool 00 0.40.4 00 00

The final pose equals the classic 0T3{}^0T_3 to 2.2×10−162.2\times10^{-16} on the same 1000 configurations. The intermediate frames do not agree, and that is the visible difference between the conventions:

text
frame classic origin classic z modified origin modified z {1} (0, 0, 0.4) shoulder shoulder axis (0, 0, 0.4) vertical (joint 1) {2} (0.306, 0.177, 0.754) elbow axis (0, 0, 0.4) shoulder shoulder axis {3} (0.641, 0.370, 0.650) tip elbow axis (0.306, 0.177, 0.754) elbow axis

Classic frame {i}\{i\} is on joint i+1i+1, modified frame {i}\{i\} is on joint ii. Anything indexed by frame, such as a center of mass or an inertia written "in frame 2", changes meaning between the two.

Example 4: a prismatic joint, and mixing conventions

A SCARA-type arm has two vertical revolute joints and a vertical prismatic joint that moves the tool down. Assume a shoulder height 0.50.5 m and link lengths 0.40.4 m and 0.30.3 m. In the classic convention z2z_2 points down along the travel, so α2=180∘\alpha_2=180^\circ, and the variable sits in the dd column:

ii aia_i (m) αi\alpha_i did_i (m) θi\theta_i
1 0.40.4 00 0.50.5 θ1\theta_1
2 0.30.3 180∘180^\circ 00 θ2\theta_2
3 00 00 d3d_3 00

At (30∘,45∘,0.1 m)(30^\circ,45^\circ,0.1\text{ m}) the tip is (0.4241,0.4898,0.4000)(0.4241,0.4898,0.4000), which is (0.4c1+0.3c12, 0.4s1+0.3s12, 0.5−d3)\big(0.4c_1+0.3c_{12},\ 0.4s_1+0.3s_{12},\ 0.5-d_3\big). The modified table, rows (0,0,0.5,θ1)(0,0,0.5,\theta_1), (0,0.4,0,θ2)(0,0.4,0,\theta_2), (180∘,0.3,d3,0)(180^\circ,0.3,d_3,0), gives the same pose to 1.1×10−161.1\times10^{-16} over 1000 random configurations from the same generator.

Now the common mistake. Feed the classic table of Example 1 into the modified formula at (30∘,45∘,−60∘)(30^\circ,45^\circ,-60^\circ): the tip comes out at (0.5365,−0.4000,0.6364)(0.5365,-0.4000,0.6364), 0.780.78 m from the true (0.6408,0.3700,0.6500)(0.6408,0.3700,0.6500). The modified table fed to the classic formula is off by 1.161.16 m. Neither error raises an exception, since both results are valid rigid transforms. Only a check against an independent computation, as in Example 2, catches it.

Common Misconceptions & Pitfalls

  • "A DH table describes the robot." It describes the robot plus a convention plus a set of frame choices. Without the convention, the same numbers placed the tip 0.780.78 m away in Example 4.
  • "aia_i is the length of link ii." It is the distance between two joint axes along their common normal. Modern Robotics points out that it "does not necessarily correspond to the actual length of the physical link".
  • "The table is unique." At intersecting and parallel axes, and at the base and tool, the rules leave choices. Two correct tables for the same arm can differ in signs and zeros.
  • "Classic and modified differ by a shift of index." The index shifts, the order of the factors changes, and the frames move to different joints (Example 3). The final pose agrees only if the tool row is handled.
  • "Any frame can be a DH frame." A sideways offset along yy cannot be expressed, so tools are often attached with one extra fixed transform.

Summary

The Denavit-Hartenberg rules put a zz axis on every joint and an xx axis on every common normal, which reduces each link transform to four parameters: link length aa, twist α\alpha, offset dd and joint angle θ\theta, with θ\theta variable for a revolute joint and dd variable for a prismatic one. The classic convention puts joint ii on zi−1z_{i-1} and uses RotzTranszTransxRotx\mathrm{Rot}_z\mathrm{Trans}_z\mathrm{Trans}_x\mathrm{Rot}_x. The modified convention puts it on ziz_i and uses RotxTransxRotzTransz\mathrm{Rot}_x\mathrm{Trans}_x\mathrm{Rot}_z\mathrm{Trans}_z. Modern Robotics and Craig use the modified form, Murray, Li and Sastry, Spong, Siciliano and Paul the classic one, according to the sources above. On a three-joint articulated arm both tables reproduced a direct product of transforms to 2.2×10−162.2\times10^{-16}, while a table read with the wrong formula missed by 0.780.78 m without any warning.

Documentation Links