hero-image
HOME
hero-image

Robotic Arm Inverse Kinematics Simulation (Python)

hero-image
Arjun

Project Timeline

Aug 2025 - Aug-2025

OVERVIEW

Implemented an interactive 2-link robotic arm simulation in Python that calculates and visualizes joint angles in real time using inverse kinematics. The system allows the user to move the mouse within the workspace, and the arm automatically adjusts to reach the target position while respecting joint angle and reach constraints. The goal was to use this simulation and adjust these parameters to implement them in testing a real world robotic arm.

HighlightS

  • Modeled a 2-link planar robotic arm with configurable link lengths and servo angle limits (±160°).
  • Implemented an inverse kinematics solver to compute feasible joint angles using geometric relationships and validation (reachability + joint limits + elbow configuration).
  • Used forward kinematics to update joint positions and rendered the arm in real time using Matplotlib.
  • Added live mouse interaction so the arm tracks the cursor within its workspace and displays the corresponding joint angles.

SKILLS

PythonNumPyMatplotlibInverse Kinematics2-DOF Planar Manipulator

Additional Details

Simulation Overview

Pasted image

Code for Simulation

import numpy as np
import matplotlib.pyplot as plt

# Arm segment lengths
L1 = 180  # 18 cm
L2 = 150  # 15 cm

# Servo angle limit: ±160° in radians
ANGLE_LIMIT = 8 * np.pi / 9  # ~2.79 radians

# Create the figure
fig, ax = plt.subplots()
ax.set_xlim(-L1 - L2, L1 + L2)  # normal axis range
ax.set_ylim(-L1 - L2, L1 + L2)
ax.set_aspect('equal')
ax.grid(True)
plt.title("Robotic Arm Simulation")

# Initialize plot elements
arm_line, = ax.plot([], [], 'ro-', linewidth=4)
target_dot, = ax.plot([], [], 'bx', markersize=10)

# Place the angle box neatly in upper-right area (inside plot, out of the way)
angle_text = ax.text(
    L1 + L2 - 100, L1 + L2 - 50, '', fontsize=12, color='black',
    bbox=dict(facecolor='white', edgecolor='gray', boxstyle='round,pad=0.5')
)


def inverse_kinematics(x, y):
    D = np.hypot(x, y)
    if D > (L1 + L2):
        x *= (L1 + L2) / D
        y *= (L1 + L2) / D
        D = L1 + L2

    cos_theta2 = (x**2 + y**2 - L1**2 - L2**2) / (2 * L1 * L2)
    cos_theta2 = np.clip(cos_theta2, -1.0, 1.0)
    theta2_options = [np.arccos(cos_theta2), -np.arccos(cos_theta2)]

    for theta2 in theta2_options:
        k1 = L1 + L2 * np.cos(theta2)
        k2 = L2 * np.sin(theta2)
        theta1 = np.arctan2(y, x) - np.arctan2(k2, k1)
        y1 = L1 * np.sin(theta1)

        if y1 >= 0 and abs(theta1) <= ANGLE_LIMIT and abs(theta2) <= ANGLE_LIMIT:
            return theta1, theta2

    return None, None


def forward_kinematics(theta1, theta2):
    x0, y0 = 0, 0
    x1 = L1 * np.cos(theta1)
    y1 = L1 * np.sin(theta1)
    x2 = x1 + L2 * np.cos(theta1 + theta2)
    y2 = y1 + L2 * np.sin(theta1 + theta2)
    return [(x0, y0), (x1, y1), (x2, y2)]


def on_mouse_move(event):
    if event.xdata is None or event.ydata is None:
        return

    x, y = event.xdata, event.ydata
    theta1, theta2 = inverse_kinematics(x, y)
    if theta1 is None:
        return

    joints = forward_kinematics(theta1, theta2)
    xs, ys = zip(*joints)
    arm_line.set_data(xs, ys)
    target_dot.set_data([x], [y])

    # Update servo angle display
    angle_text.set_text(
        f"Servo Angles:\nθ1 = {np.degrees(theta1):.1f}°\nθ2 = {np.degrees(theta2):.1f}°"
    )

    fig.canvas.draw_idle()


fig.canvas.mpl_connect('motion_notify_event', on_mouse_move)
plt.show()
lowinertia
Portfolio Builder for Engineers
Created by Aram Lee
© 2025 Low Inertia. All rights reserved.