ENME480 · Wiki
Kinematics Reference
DH parameters, transformation matrices, forward/inverse kinematics for robotics
Overview
This reference guide contains essential kinematics formulas, DH parameters, and mathematical tools you'll need for ENME480. Keep this handy during labs and homework assignments.
Mathematical Fundamentals
Rotation Matrices
import numpy as np
# Rotation around X-axis (roll)
def Rx(theta):
return np.array([
[1, 0, 0],
[0, np.cos(theta), -np.sin(theta)],
[0, np.sin(theta), np.cos(theta)]
])
# Rotation around Y-axis (pitch)
def Ry(theta):
return np.array([
[np.cos(theta), 0, np.sin(theta)],
[0, 1, 0],
[-np.sin(theta), 0, np.cos(theta)]
])
# Rotation around Z-axis (yaw)
def Rz(theta):
return np.array([
[np.cos(theta), -np.sin(theta), 0],
[np.sin(theta), np.cos(theta), 0],
[0, 0, 1]
])
# Combined rotation (ZYX Euler angles)
def Rzyx(roll, pitch, yaw):
return Rz(yaw) @ Ry(pitch) @ Rx(roll)
Homogeneous Transformations
def homogeneous_transform(R, p):
"""Create 4x4 homogeneous transformation matrix."""
T = np.eye(4)
T[:3, :3] = R
T[:3, 3] = p
return T
def translation_matrix(x, y, z):
"""Create translation matrix."""
return np.array([
[1, 0, 0, x],
[0, 1, 0, y],
[0, 0, 1, z],
[0, 0, 0, 1]
])
def rotation_matrix(R):
"""Create rotation matrix."""
T = np.eye(4)
T[:3, :3] = R
return T
DH Parameters
DH Parameter Table
| Joint | θ | d | a | α |
|---|---|---|---|---|
| Base | θ₁ | d₁ | a₁ | α₁ |
| Shoulder | θ₂ | d₂ | a₂ | α₂ |
| Elbow | θ₃ | d₃ | a₃ | α₃ |
| Wrist 1 | θ₄ | d₄ | a₄ | α₄ |
| Wrist 2 | θ₅ | d₅ | a₅ | α₅ |
| Wrist 3 | θ₆ | d₆ | a₆ | α₆ |
DH Transformation Matrix
def dh_transform(theta, d, a, alpha):
"""Compute DH transformation matrix."""
ct = np.cos(theta)
st = np.sin(theta)
ca = np.cos(alpha)
sa = np.sin(alpha)
T = np.array([
[ct, -st*ca, st*sa, a*ct],
[st, ct*ca, -ct*sa, a*st],
[0, sa, ca, d],
[0, 0, 0, 1]
])
return T
UR3e DH Parameters
Standard DH Parameters
# UR3e DH parameters (in meters and radians)
ur3e_dh = [
# [theta, d, a, alpha]
[0, 0.1519, 0, -np.pi/2], # Base
[0, 0, -0.24365, 0], # Shoulder
[0, 0, -0.21325, 0], # Elbow
[0, 0.11235, 0, -np.pi/2], # Wrist 1
[0, 0.08535, 0, np.pi/2], # Wrist 2
[0, 0.0819, 0, 0] # Wrist 3
]
# Joint limits (in radians)
ur3e_limits = [
[-2*np.pi, 2*np.pi], # Base
[-2*np.pi, 2*np.pi], # Shoulder
[-np.pi, 0], # Elbow
[-2*np.pi, 2*np.pi], # Wrist 1
[-2*np.pi, 2*np.pi], # Wrist 2
[-2*np.pi, 2*np.pi] # Wrist 3
]
Modified DH Parameters
# Modified DH parameters (alternative convention)
ur3e_mdh = [
# [theta, d, a, alpha]
[0, 0.1519, 0, 0], # Base
[0, 0, 0.24365, -np.pi/2], # Shoulder
[0, 0, 0.21325, 0], # Elbow
[0, 0.11235, 0, -np.pi/2], # Wrist 1
[0, 0.08535, 0, np.pi/2], # Wrist 2
[0, 0.0819, 0, 0] # Wrist 3
]
Forward Kinematics
Basic Forward Kinematics
def forward_kinematics(joint_angles, dh_params):
"""Compute forward kinematics using DH parameters."""
T = np.eye(4)
for i, (theta, d, a, alpha) in enumerate(dh_params):
# Add joint angle to theta
current_theta = theta + joint_angles[i]
# Compute transformation for this joint
Ti = dh_transform(current_theta, d, a, alpha)
# Multiply transformations
T = T @ Ti
return T
# Example usage
joint_angles = [0, -np.pi/2, 0, 0, 0, 0] # Home position
T_end_effector = forward_kinematics(joint_angles, ur3e_dh)
# Extract position and orientation
position = T_end_effector[:3, 3]
orientation = T_end_effector[:3, :3]
Position and Orientation Extraction
def extract_pose(T):
"""Extract position and orientation from transformation matrix."""
position = T[:3, 3]
orientation = T[:3, :3]
# Convert to Euler angles (ZYX convention)
yaw = np.arctan2(orientation[1, 0], orientation[0, 0])
pitch = np.arctan2(-orientation[2, 0],
np.sqrt(orientation[2, 1]**2 + orientation[2, 2]**2))
roll = np.arctan2(orientation[2, 1], orientation[2, 2])
return position, np.array([roll, pitch, yaw])
def extract_quaternion(T):
"""Extract quaternion from transformation matrix."""
from scipy.spatial.transform import Rotation as R
orientation = T[:3, :3]
r = R.from_matrix(orientation)
return r.as_quat() # [x, y, z, w]
Inverse Kinematics
Analytical IK for UR3e
def ur3e_inverse_kinematics(T_desired, elbow_config=1):
"""
Analytical inverse kinematics for UR3e.
Args:
T_desired: Desired end-effector pose (4x4 matrix)
elbow_config: 1 for elbow up, -1 for elbow down
Returns:
List of joint angles (6x1)
"""
# Extract position and orientation
p_desired = T_desired[:3, 3]
R_desired = T_desired[:3, :3]
# DH parameters
d1, d4, d5, d6 = 0.1519, 0.11235, 0.08535, 0.0819
a2, a3 = 0.24365, 0.21325
# Wrist center position
p_wrist = p_desired - d6 * R_desired[:, 2]
# Joint 1 (base rotation)
theta1 = np.arctan2(p_wrist[1], p_wrist[0])
# Joint 2 and 3 (shoulder and elbow)
r = np.sqrt(p_wrist[0]**2 + p_wrist[1]**2)
s = p_wrist[2] - d1
# Cosine law
D = (r**2 + s**2 - a2**2 - a3**2) / (2 * a2 * a3)
if abs(D) > 1:
raise ValueError("Target position not reachable")
theta3 = elbow_config * np.arccos(D)
theta2 = np.arctan2(s, r) - np.arctan2(a3 * np.sin(theta3),
a2 + a3 * np.cos(theta3))
# Joints 4, 5, 6 (wrist)
R03 = forward_kinematics([theta1, theta2, theta3, 0, 0, 0], ur3e_dh)[:3, :3]
R36 = R03.T @ R_desired
# Extract Euler angles from R36
theta4 = np.arctan2(R36[1, 2], R36[0, 2])
theta5 = np.arctan2(np.sqrt(R36[0, 2]**2 + R36[1, 2]**2), R36[2, 2])
theta6 = np.arctan2(-R36[2, 1], R36[2, 0])
return [theta1, theta2, theta3, theta4, theta5, theta6]
# Example usage
T_desired = np.eye(4)
T_desired[:3, 3] = [0.3, 0.0, 0.5] # Desired position
try:
joint_angles = ur3e_inverse_kinematics(T_desired, elbow_config=1)
print(f"Joint angles: {[f'{x:.3f}' for x in joint_angles]}")
except ValueError as e:
print(f"Error: {e}")
Numerical IK (Jacobian Method)
def numerical_inverse_kinematics(T_desired, initial_guess, max_iter=100, tol=1e-6):
"""Numerical inverse kinematics using Jacobian method."""
from scipy.optimize import minimize
def objective_function(joint_angles):
T_current = forward_kinematics(joint_angles, ur3e_dh)
# Position error
pos_error = np.linalg.norm(T_current[:3, 3] - T_desired[:3, 3])
# Orientation error (simplified)
ori_error = np.linalg.norm(T_current[:3, :3] - T_desired[:3, :3])
return pos_error + 0.1 * ori_error
# Minimize objective function
result = minimize(objective_function, initial_guess,
method='L-BFGS-B',
bounds=ur3e_limits,
options={'maxiter': max_iter})
if result.success:
return result.x
else:
raise ValueError("IK optimization failed")
# Example usage
initial_guess = [0, -np.pi/2, 0, 0, 0, 0]
try:
joint_angles = numerical_inverse_kinematics(T_desired, initial_guess)
print(f"Numerical IK result: {[f'{x:.3f}' for x in joint_angles]}")
except ValueError as e:
print(f"Error: {e}")
Jacobian Matrix
Analytical Jacobian
def compute_jacobian(joint_angles, dh_params):
"""Compute analytical Jacobian matrix."""
n_joints = len(dh_params)
J = np.zeros((6, n_joints))
# Current end-effector pose
T_current = forward_kinematics(joint_angles, dh_params)
p_current = T_current[:3, 3]
# Compute Jacobian columns
for i in range(n_joints):
# Perturb joint i
joint_angles_perturbed = joint_angles.copy()
joint_angles_perturbed[i] += 1e-6
# Compute perturbed pose
T_perturbed = forward_kinematics(joint_angles_perturbed, dh_params)
p_perturbed = T_perturbed[:3, 3]
# Linear velocity component
J[:3, i] = (p_perturbed - p_current) / 1e-6
# Angular velocity component (simplified)
# For full implementation, compute orientation differences
return J
# Example usage
joint_angles = [0, -np.pi/2, 0, 0, 0, 0]
J = compute_jacobian(joint_angles, ur3e_dh)
print(f"Jacobian shape: {J.shape}")
Jacobian-Based Control
def jacobian_control(joint_angles, desired_velocity, dh_params, dt=0.01):
"""Simple Jacobian-based velocity control."""
J = compute_jacobian(joint_angles, dh_params)
# Pseudo-inverse of Jacobian
J_inv = np.linalg.pinv(J)
# Joint velocities
joint_velocities = J_inv @ desired_velocity
# Update joint angles
new_joint_angles = joint_angles + joint_velocities * dt
return new_joint_angles
# Example usage
desired_velocity = np.array([0.1, 0, 0, 0, 0, 0]) # Move in X direction
new_angles = jacobian_control(joint_angles, desired_velocity, ur3e_dh)
Trajectory Generation
Linear Trajectory
def linear_trajectory(start_pos, end_pos, num_points):
"""Generate linear trajectory between two points."""
t = np.linspace(0, 1, num_points)
trajectory = []
for ti in t:
pos = start_pos + ti * (end_pos - start_pos)
trajectory.append(pos)
return np.array(trajectory)
# Example usage
start_pos = np.array([0.3, 0.0, 0.5])
end_pos = np.array([0.5, 0.2, 0.3])
trajectory = linear_trajectory(start_pos, end_pos, 50)
Joint Space Trajectory
def joint_trajectory(start_angles, end_angles, num_points):
"""Generate trajectory in joint space."""
t = np.linspace(0, 1, num_points)
trajectory = []
for ti in t:
angles = start_angles + ti * (np.array(end_angles) - np.array(start_angles))
trajectory.append(angles)
return np.array(trajectory)
# Example usage
start_angles = [0, -np.pi/2, 0, 0, 0, 0]
end_angles = [np.pi/4, -np.pi/3, -np.pi/6, 0, 0, 0]
joint_traj = joint_trajectory(start_angles, end_angles, 100)
Utility Functions
Distance and Angle Calculations
def euclidean_distance(p1, p2):
"""Calculate Euclidean distance between two points."""
return np.linalg.norm(np.array(p1) - np.array(p2))
def angle_between_vectors(v1, v2):
"""Calculate angle between two vectors."""
cos_angle = np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))
cos_angle = np.clip(cos_angle, -1, 1) # Avoid numerical errors
return np.arccos(cos_angle)
def normalize_angle(angle):
"""Normalize angle to [-π, π]."""
return np.arctan2(np.sin(angle), np.cos(angle))
Matrix Operations
def skew_symmetric(v):
"""Create skew-symmetric matrix from vector."""
return np.array([
[0, -v[2], v[1]],
[v[2], 0, -v[0]],
[-v[1], v[0], 0]
])
def rotation_matrix_to_axis_angle(R):
"""Convert rotation matrix to axis-angle representation."""
theta = np.arccos((np.trace(R) - 1) / 2)
if abs(theta) < 1e-6:
return np.array([0, 0, 1]), 0
axis = np.array([
R[2, 1] - R[1, 2],
R[0, 2] - R[2, 0],
R[1, 0] - R[0, 1]
])
axis = axis / (2 * np.sin(theta))
return axis, theta
Common Formulas
Trigonometric Identities
# Sum and difference formulas
def sin_sum(a, b):
return np.sin(a) * np.cos(b) + np.cos(a) * np.sin(b)
def cos_sum(a, b):
return np.cos(a) * np.cos(b) - np.sin(a) * np.sin(b)
# Double angle formulas
def sin_double(a):
return 2 * np.sin(a) * np.cos(a)
def cos_double(a):
return np.cos(a)**2 - np.sin(a)**2
Vector Operations
def cross_product(v1, v2):
"""Cross product of two 3D vectors."""
return np.array([
v1[1] * v2[2] - v1[2] * v2[1],
v1[2] * v2[0] - v1[0] * v2[2],
v1[0] * v2[1] - v1[1] * v2[0]
])
def dot_product(v1, v2):
"""Dot product of two vectors."""
return np.sum(v1 * v2)
Verification Examples
Test Forward Kinematics
def test_forward_kinematics():
"""Test forward kinematics with known values."""
# Test home position
home_angles = [0, -np.pi/2, 0, 0, 0, 0]
T_home = forward_kinematics(home_angles, ur3e_dh)
print("Home position:")
print(f"Position: {T_home[:3, 3]}")
print(f"Orientation:\n{T_home[:3, :3]}")
# Test with different angles
test_angles = [np.pi/4, -np.pi/3, -np.pi/6, 0, 0, 0]
T_test = forward_kinematics(test_angles, ur3e_dh)
print("\nTest position:")
print(f"Position: {T_test[:3, 3]}")
print(f"Orientation:\n{T_test[:3, :3]}")
# Run test
test_forward_kinematics()
Getting Help
Mathematical Resources
- Linear Algebra: Khan Academy
- Robotics Math: Modern Robotics
- Python Math: SciPy Documentation
Course Support
- Piazza: Ask questions on course forum
- Office Hours: Get help from TA or instructor
- Lab Sessions: Hands-on help during labs
Next Steps
After mastering kinematics:
- Practice with Python examples above
- Implement FK/IK for different robot configurations
- Start Week 6 lab: See Week 6 Lab
- Work on homework problems using these formulas