DH Parameter Examples and Common Configurations
Practical examples of DH parameter configurations for common robot manipulators with complete Python implementations.
2-Link Planar Arm
Simplest manipulator for learning DH parameters.
Interactive 2-Link Arm
DH Table
| Joint | $\theta$ | $d$ | $a$ | $\alpha$ |
|---|---|---|---|---|
| 1 | $\theta_1$ | 0 | $L_1$ | 0 |
| 2 | $\theta_2$ | 0 | $L_2$ | 0 |
Implementation
1import numpy as np
2
3def dh_matrix(theta, d, a, alpha):
4 """DH transformation matrix"""
5 ct, st = np.cos(theta), np.sin(theta)
6 ca, sa = np.cos(alpha), np.sin(alpha)
7
8 return np.array([
9 [ct, -st*ca, st*sa, a*ct],
10 [st, ct*ca, -ct*sa, a*st],
11 [0, sa, ca, d ],
12 [0, 0, 0, 1 ]
13 ])
14
15class PlanarArm2DOF:
16 def __init__(self, L1=1.0, L2=0.8):
17 self.L1 = L1
18 self.L2 = L2
19
20 def forward_kinematics(self, theta1, theta2):
21 """Compute end-effector pose"""
22 T1 = dh_matrix(theta1, 0, self.L1, 0)
23 T2 = dh_matrix(theta2, 0, self.L2, 0)
24
25 T = T1 @ T2
26
27 x = T[0, 3]
28 y = T[1, 3]
29 phi = np.arctan2(T[1, 0], T[0, 0])
30
31 return x, y, phi
32
33 def plot_arm(self, theta1, theta2):
34 """Visualize arm configuration"""
35 import matplotlib.pyplot as plt
36
37 # Joint positions
38 p0 = np.array([0, 0])
39 p1 = np.array([self.L1 * np.cos(theta1),
40 self.L1 * np.sin(theta1)])
41 p2 = np.array([p1[0] + self.L2 * np.cos(theta1 + theta2),
42 p1[1] + self.L2 * np.sin(theta1 + theta2)])
43
44 plt.figure(figsize=(8, 8))
45 plt.plot([p0[0], p1[0], p2[0]], [p0[1], p1[1], p2[2]], 'o-', linewidth=2)
46 plt.plot(p0[0], p0[1], 'ro', markersize=10, label='Base')
47 plt.plot(p2[0], p2[1], 'go', markersize=10, label='End-effector')
48 plt.grid(True)
49 plt.axis('equal')
50 plt.legend()
51 plt.title(f'θ₁={np.degrees(theta1):.1f}°, θ₂={np.degrees(theta2):.1f}°')
52 plt.show()
53
54# Example
55arm = PlanarArm2DOF(L1=1.0, L2=0.8)
56x, y, phi = arm.forward_kinematics(np.pi/4, np.pi/3)
57print(f"End-effector: x={x:.3f}, y={y:.3f}, φ={np.degrees(phi):.1f}°")
3-DOF RRR Manipulator
Spatial manipulator with 3 revolute joints.
DH Table
| Joint | $\theta$ | $d$ | $a$ | $\alpha$ |
|---|---|---|---|---|
| 1 | $\theta_1$ | $L_1$ | 0 | $\pi/2$ |
| 2 | $\theta_2$ | 0 | $L_2$ | 0 |
| 3 | $\theta_3$ | 0 | $L_3$ | 0 |
Implementation
1class RRRManipulator:
2 def __init__(self, L1=0.5, L2=0.4, L3=0.3):
3 self.L1 = L1
4 self.L2 = L2
5 self.L3 = L3
6
7 def forward_kinematics(self, theta1, theta2, theta3):
8 """Compute end-effector pose"""
9 T1 = dh_matrix(theta1, self.L1, 0, np.pi/2)
10 T2 = dh_matrix(theta2, 0, self.L2, 0)
11 T3 = dh_matrix(theta3, 0, self.L3, 0)
12
13 T = T1 @ T2 @ T3
14 return T
15
16 def get_joint_positions(self, theta1, theta2, theta3):
17 """Get position of each joint"""
18 T0 = np.eye(4)
19 T1 = dh_matrix(theta1, self.L1, 0, np.pi/2)
20 T2 = dh_matrix(theta2, 0, self.L2, 0)
21 T3 = dh_matrix(theta3, 0, self.L3, 0)
22
23 positions = [
24 T0[:3, 3],
25 T1[:3, 3],
26 (T1 @ T2)[:3, 3],
27 (T1 @ T2 @ T3)[:3, 3]
28 ]
29
30 return positions
31
32# Example
33robot = RRRManipulator()
34T = robot.forward_kinematics(0, np.pi/4, np.pi/6)
35print("End-effector position:", T[:3, 3])
SCARA Robot
Selective Compliance Assembly Robot Arm - 2 revolute + 1 prismatic + 1 revolute.
DH Table
| Joint | $\theta$ | $d$ | $a$ | $\alpha$ |
|---|---|---|---|---|
| 1 | $\theta_1$ | 0 | $L_1$ | 0 |
| 2 | $\theta_2$ | 0 | $L_2$ | $\pi$ |
| 3 | 0 | $d_3$ | 0 | 0 |
| 4 | $\theta_4$ | 0 | 0 | 0 |
Implementation
1class SCARArobot:
2 def __init__(self, L1=0.3, L2=0.25, d3_min=0.0, d3_max=0.2):
3 self.L1 = L1
4 self.L2 = L2
5 self.d3_min = d3_min
6 self.d3_max = d3_max
7
8 def forward_kinematics(self, theta1, theta2, d3, theta4):
9 """Compute end-effector pose"""
10 # Clamp prismatic joint
11 d3 = np.clip(d3, self.d3_min, self.d3_max)
12
13 T1 = dh_matrix(theta1, 0, self.L1, 0)
14 T2 = dh_matrix(theta2, 0, self.L2, np.pi)
15 T3 = dh_matrix(0, d3, 0, 0)
16 T4 = dh_matrix(theta4, 0, 0, 0)
17
18 T = T1 @ T2 @ T3 @ T4
19 return T
20
21 def workspace_limits(self):
22 """Calculate workspace dimensions"""
23 r_max = self.L1 + self.L2
24 r_min = abs(self.L1 - self.L2)
25 z_min = self.d3_min
26 z_max = self.d3_max
27
28 return {
29 'r_min': r_min,
30 'r_max': r_max,
31 'z_min': z_min,
32 'z_max': z_max
33 }
34
35# Example
36scara = SCARArobot()
37T = scara.forward_kinematics(np.pi/6, np.pi/4, 0.1, 0)
38print(f"SCARA end-effector: [{T[0,3]:.3f}, {T[1,3]:.3f}, {T[2,3]:.3f}]")
39print("Workspace:", scara.workspace_limits())
Stanford Arm
5-DOF manipulator: 2 revolute + 1 prismatic + 2 revolute.
DH Table
| Joint | $\theta$ | $d$ | $a$ | $\alpha$ |
|---|---|---|---|---|
| 1 | $\theta_1$ | 0 | 0 | $-\pi/2$ |
| 2 | $\theta_2$ | $d_2$ | 0 | $\pi/2$ |
| 3 | 0 | $d_3$ | 0 | 0 |
| 4 | $\theta_4$ | 0 | 0 | $-\pi/2$ |
| 5 | $\theta_5$ | 0 | 0 | 0 |
Implementation
1class StanfordArm:
2 def __init__(self, d2=0.154, d3_min=0.0, d3_max=0.5):
3 self.d2 = d2
4 self.d3_min = d3_min
5 self.d3_max = d3_max
6
7 def forward_kinematics(self, theta1, theta2, d3, theta4, theta5):
8 """Compute end-effector pose"""
9 d3 = np.clip(d3, self.d3_min, self.d3_max)
10
11 T1 = dh_matrix(theta1, 0, 0, -np.pi/2)
12 T2 = dh_matrix(theta2, self.d2, 0, np.pi/2)
13 T3 = dh_matrix(0, d3, 0, 0)
14 T4 = dh_matrix(theta4, 0, 0, -np.pi/2)
15 T5 = dh_matrix(theta5, 0, 0, 0)
16
17 T = T1 @ T2 @ T3 @ T4 @ T5
18 return T
19
20# Example
21stanford = StanfordArm()
22T = stanford.forward_kinematics(0, np.pi/4, 0.3, 0, 0)
23print("Stanford arm end-effector:", T[:3, 3])
PUMA 560
Classic 6-DOF industrial robot.
DH Table
| Joint | $\theta$ | $d$ | $a$ | $\alpha$ |
|---|---|---|---|---|
| 1 | $\theta_1$ | 0 | 0 | $\pi/2$ |
| 2 | $\theta_2$ | 0 | $a_2$ | 0 |
| 3 | $\theta_3$ | $d_3$ | $a_3$ | $-\pi/2$ |
| 4 | $\theta_4$ | $d_4$ | 0 | $\pi/2$ |
| 5 | $\theta_5$ | 0 | 0 | $-\pi/2$ |
| 6 | $\theta_6$ | 0 | 0 | 0 |
Implementation
1class PUMA560:
2 def __init__(self):
3 # PUMA 560 dimensions (meters)
4 self.a2 = 0.4318
5 self.a3 = 0.0203
6 self.d3 = 0.15005
7 self.d4 = 0.4318
8
9 def forward_kinematics(self, joints):
10 """
11 Compute forward kinematics
12
13 Args:
14 joints: List of 6 joint angles [θ1, θ2, θ3, θ4, θ5, θ6]
15 """
16 theta1, theta2, theta3, theta4, theta5, theta6 = joints
17
18 T1 = dh_matrix(theta1, 0, 0, np.pi/2)
19 T2 = dh_matrix(theta2, 0, self.a2, 0)
20 T3 = dh_matrix(theta3, self.d3, self.a3, -np.pi/2)
21 T4 = dh_matrix(theta4, self.d4, 0, np.pi/2)
22 T5 = dh_matrix(theta5, 0, 0, -np.pi/2)
23 T6 = dh_matrix(theta6, 0, 0, 0)
24
25 T = T1 @ T2 @ T3 @ T4 @ T5 @ T6
26 return T
27
28 def get_all_transforms(self, joints):
29 """Get transformation to each joint frame"""
30 theta1, theta2, theta3, theta4, theta5, theta6 = joints
31
32 T1 = dh_matrix(theta1, 0, 0, np.pi/2)
33 T2 = dh_matrix(theta2, 0, self.a2, 0)
34 T3 = dh_matrix(theta3, self.d3, self.a3, -np.pi/2)
35 T4 = dh_matrix(theta4, self.d4, 0, np.pi/2)
36 T5 = dh_matrix(theta5, 0, 0, -np.pi/2)
37 T6 = dh_matrix(theta6, 0, 0, 0)
38
39 transforms = [
40 np.eye(4),
41 T1,
42 T1 @ T2,
43 T1 @ T2 @ T3,
44 T1 @ T2 @ T3 @ T4,
45 T1 @ T2 @ T3 @ T4 @ T5,
46 T1 @ T2 @ T3 @ T4 @ T5 @ T6
47 ]
48
49 return transforms
50
51# Example
52puma = PUMA560()
53joints = [0, np.pi/4, 0, 0, 0, 0]
54T = puma.forward_kinematics(joints)
55print("PUMA 560 end-effector:")
56print(T)
Generic N-DOF Manipulator
Flexible class for any serial manipulator.
1class GenericManipulator:
2 def __init__(self, dh_table):
3 """
4 Args:
5 dh_table: List of dicts with keys:
6 - 'a': link length
7 - 'alpha': link twist
8 - 'd_const': constant offset (for revolute)
9 - 'theta_const': constant angle (for prismatic)
10 - 'joint_type': 'revolute' or 'prismatic'
11 """
12 self.dh_table = dh_table
13 self.n_joints = len(dh_table)
14
15 def forward_kinematics(self, joint_values):
16 """
17 Compute forward kinematics
18
19 Args:
20 joint_values: List of joint values (angles or distances)
21 """
22 T = np.eye(4)
23
24 for i, (value, dh) in enumerate(zip(joint_values, self.dh_table)):
25 if dh['joint_type'] == 'prismatic':
26 d = value
27 theta = dh.get('theta_const', 0)
28 else: # revolute
29 d = dh.get('d_const', 0)
30 theta = value
31
32 Ti = dh_matrix(theta, d, dh['a'], dh['alpha'])
33 T = T @ Ti
34
35 return T
36
37 def check_joint_limits(self, joint_values):
38 """Check if joints are within limits"""
39 for i, (value, dh) in enumerate(zip(joint_values, self.dh_table)):
40 limits = dh.get('limits', {'min': -np.inf, 'max': np.inf})
41 if not (limits['min'] <= value <= limits['max']):
42 return False, f"Joint {i+1} out of range"
43 return True, "OK"
44
45# Example: Custom 4-DOF robot
46custom_dh = [
47 {'a': 0, 'alpha': np.pi/2, 'd_const': 0.5, 'joint_type': 'revolute',
48 'limits': {'min': -np.pi, 'max': np.pi}},
49 {'a': 0.4, 'alpha': 0, 'd_const': 0, 'joint_type': 'revolute',
50 'limits': {'min': -np.pi/2, 'max': np.pi/2}},
51 {'a': 0, 'alpha': 0, 'theta_const': 0, 'joint_type': 'prismatic',
52 'limits': {'min': 0, 'max': 0.3}},
53 {'a': 0, 'alpha': 0, 'd_const': 0.2, 'joint_type': 'revolute',
54 'limits': {'min': -np.pi, 'max': np.pi}}
55]
56
57robot = GenericManipulator(custom_dh)
58joints = [np.pi/6, np.pi/4, 0.15, 0]
59valid, msg = robot.check_joint_limits(joints)
60if valid:
61 T = robot.forward_kinematics(joints)
62 print("End-effector:", T[:3, 3])
Workspace Visualization
1def plot_workspace_3d(robot, n_samples=5000):
2 """Plot 3D workspace by sampling joint space"""
3 import matplotlib.pyplot as plt
4 from mpl_toolkits.mplot3d import Axes3D
5
6 points = []
7
8 for _ in range(n_samples):
9 # Random valid joint configuration
10 joints = []
11 for dh in robot.dh_table:
12 limits = dh.get('limits', {'min': -np.pi, 'max': np.pi})
13 value = np.random.uniform(limits['min'], limits['max'])
14 joints.append(value)
15
16 # Forward kinematics
17 T = robot.forward_kinematics(joints)
18 points.append(T[:3, 3])
19
20 points = np.array(points)
21
22 # Plot
23 fig = plt.figure(figsize=(10, 8))
24 ax = fig.add_subplot(111, projection='3d')
25 ax.scatter(points[:, 0], points[:, 1], points[:, 2],
26 alpha=0.1, s=1, c=points[:, 2], cmap='viridis')
27 ax.set_xlabel('X (m)')
28 ax.set_ylabel('Y (m)')
29 ax.set_zlabel('Z (m)')
30 ax.set_title('Robot Workspace')
31 plt.colorbar(ax.scatter(points[:, 0], points[:, 1], points[:, 2],
32 c=points[:, 2], cmap='viridis'), ax=ax)
33 plt.show()
34
35# Example
36plot_workspace_3d(robot, n_samples=3000)
Joint Limit Handling
1class RobotWithLimits:
2 def __init__(self, dh_table, joint_limits):
3 self.dh_table = dh_table
4 self.joint_limits = joint_limits
5
6 def clamp_joints(self, joints):
7 """Clamp joints to valid range"""
8 clamped = []
9 for joint, limits in zip(joints, self.joint_limits):
10 clamped.append(np.clip(joint, limits['min'], limits['max']))
11 return np.array(clamped)
12
13 def safe_forward_kinematics(self, joints):
14 """FK with automatic joint clamping"""
15 joints_safe = self.clamp_joints(joints)
16
17 # Warn if clamping occurred
18 if not np.allclose(joints, joints_safe):
19 print("Warning: Joints clamped to limits")
20 for i, (orig, safe) in enumerate(zip(joints, joints_safe)):
21 if not np.isclose(orig, safe):
22 print(f" Joint {i+1}: {orig:.3f} → {safe:.3f}")
23
24 # Compute FK
25 T = np.eye(4)
26 for value, dh in zip(joints_safe, self.dh_table):
27 if dh['joint_type'] == 'prismatic':
28 Ti = dh_matrix(dh.get('theta_const', 0), value,
29 dh['a'], dh['alpha'])
30 else:
31 Ti = dh_matrix(value, dh.get('d_const', 0),
32 dh['a'], dh['alpha'])
33 T = T @ Ti
34
35 return T, joints_safe
Further Reading
Related Snippets
- Denavit-Hartenberg (DH) Parameters
Denavit-Hartenberg convention for systematically describing robot manipulator … - Four-Wheel Independent Drive Kinematics
Forward and inverse kinematics for four-wheel independent drive robots with … - Front-Wheel Drive (FWD) Kinematics
Forward and inverse kinematics for front-wheel drive robots with Ackermann … - Jacobian Matrices for Forward and Inverse Kinematics
Jacobian matrices relate joint velocities to end-effector velocities, essential … - Mecanum Wheel Kinematics
Forward and inverse kinematics for mecanum wheel robots, enabling … - Rear-Wheel Drive (RWD) Kinematics
Forward and inverse kinematics for rear-wheel drive robots with front-wheel … - Two-Wheel Balancing Robot Kinematics and Control
Kinematics and control for two-wheel self-balancing robots (inverted pendulum on … - Two-Wheel Differential Drive Robot Kinematics
Forward and inverse kinematics for two-wheel differential drive robots, the most …