23 May 2026

Jacobians in Robotic Arms

How the geometric Jacobian maps joint velocities to end-effector twist, why it matters for control and singularity analysis, and how to compute and use it for a planar 2-DOF arm.

What Is the Jacobian?

The Jacobian is the matrix that relates joint-space velocities q˙\dot{\mathbf{q}} to the end-effector's Cartesian velocity (its twist) v\mathbf{v} [1]:

v=J(q)q˙\mathbf{v} = J(\mathbf{q})\,\dot{\mathbf{q}}

For an nn-DOF arm with a 6-D task space, JR6×nJ \in \mathbb{R}^{6 \times n}. For planar arms, the twist reduces to [x˙,,y˙,,ϕ˙][\dot{x},, \dot{y},, \dot{\phi}]^\top and JR3×nJ \in \mathbb{R}^{3 \times n}. Because JJ depends on q\mathbf{q}, the mapping changes as the robot moves — which is what makes Jacobian-based control non-trivial.

A 2-DOF Planar Arm

Consider the simplest non-trivial case: two revolute joints in the plane. The kinematic chain below shows the two links l1l_1, l2l_2 and the joint angles θ1\theta_1, θ2\theta_2.

l₁ l₂ θ₁ θ₂ p
2-DOF planar arm — joint angles θ₁, θ₂ measured from the previous link

DH Parameters

The Denavit–Hartenberg convention [2] parameterises each joint with four numbers. For a planar arm all link offsets and twist angles are zero:

Jointθ (variable)da (link length)α
1θ₁0l₁0
2θ₂0l₂0
Denavit–Hartenberg parameters for the 2-DOF planar arm

Forward Kinematics

Composing the two joint transforms gives the end-effector position:

px=l1cosθ1+l2cos(θ1+θ2)py=l1sinθ1+l2sin(θ1+θ2)p_x = l_1 \cos\theta_1 + l_2 \cos(\theta_1+\theta_2) \qquad p_y = l_1 \sin\theta_1 + l_2 \sin(\theta_1+\theta_2)

At θ1=0.4,rad\theta_1 = 0.4,\text{rad}, θ2=0.2,rad\theta_2 = 0.2,\text{rad} with l1=l2=1,ml_1 = l_2 = 1,\text{m}, the homogeneous transform T02T_0^2 is:

T₀²


0.8253-0.564601.7464
0.56460.825300.9541
0010
0001


End-effector frame at θ₁ = 0.4 rad, θ₂ = 0.2 rad

Deriving the Jacobian

Differentiating pxp_x and pyp_y with respect to θ1\theta_1 and θ2\theta_2, and stacking the orientation row ϕ˙=θ˙1+θ˙2\dot{\phi} = \dot{\theta}_1 + \dot{\theta}_2, yields the 3×23 \times 2 geometric Jacobian:

J(q)=[l1s1l2s12l2s12l1c1+l2c12l2c1211]J(\mathbf{q}) = \begin{bmatrix} -l_1 s_1 - l_2 s_{12} & -l_2 s_{12} \\ l_1 c_1 + l_2 c_{12} & l_2 c_{12} \\ 1 & 1 \end{bmatrix}

where s1=sinθ1s_1 = \sin\theta_1, c1=cosθ1c_1 = \cos\theta_1, s12=sin(θ1+θ2)s_{12} = \sin(\theta_1 + \theta_2), c12=cos(θ1+θ2)c_{12} = \cos(\theta_1 + \theta_2).

Singularities

The arm loses a degree of freedom when det(JJ)=0\det(J J^\top) = 0. For the position rows (dropping orientation), detJ2×2=l1l2sinθ2\det J_{2\times2} = l_1 l_2 \sin\theta_2. This is zero when θ2=0\theta_2 = 0 or π\pi — the arm is fully extended or fully folded. Near these configurations the inverse Jacobian blows up and joint velocities become unbounded for any Cartesian motion.

Velocity Mapping

The plot below shows simulated joint velocities θ˙1\dot{\theta}_1, θ˙2\dot{\theta}_2 fed through J(q)J(\mathbf{q}) to produce end-effector velocities x˙\dot{x}, y˙\dot{y} over time. Note how the Cartesian speed oscillates with the arm configuration even when joint speeds are smooth.

Jacobian Transpose Control

The Jacobian transpose JJ^\top lets you convert a desired end-effector force f\mathbf{f} into joint torques τ\boldsymbol{\tau} without inverting JJ. This is used in impedance control and gravity compensation:

τ=J(q)f\boldsymbol{\tau} = J^\top(\mathbf{q})\,\mathbf{f}

For resolved-rate motion control — driving the arm to a goal in Cartesian space — the damped least-squares pseudo-inverse avoids singularity blow-up:

J=J(JJ+λ2I)1J^\dagger = J^\top(J J^\top + \lambda^2 I)^{-1}
  1. 1Input: goal pose p_goal, current config q, damping λ
  2. 2repeat
  3. 3p ← forward_kinematics(q)
  4. 4e ← p_goal − p // task-space error
  5. 5J ← jacobian(q)
  6. 6J† ← Jᵀ · inv(J·Jᵀ + λ²·I) // damped pseudo-inverse
  7. 7q̇ ← J† · Kp · e // resolved-rate law
  8. 8q ← q + q̇ · Δt
  9. 9until ‖e‖ < ε

Implementation

import numpy as np

def jacobian(q: np.ndarray, l: np.ndarray) -> np.ndarray:
    """Geometric Jacobian for an n-DOF planar revolute arm."""
    n = len(q)
    J = np.zeros((3, n))
    cum = np.cumsum(q)
    for i in range(n):
        suffix_cos = sum(l[j] * np.cos(cum[j]) for j in range(i, n))
        suffix_sin = sum(l[j] * np.sin(cum[j]) for j in range(i, n))
        J[0, i] = -suffix_sin
        J[1, i] = suffix_cos
        J[2, i] = 1.0
    return J

def damped_pinv(J: np.ndarray, lam: float = 0.01) -> np.ndarray:
    JJT = J @ J.T
    return J.T @ np.linalg.inv(JJT + lam**2 * np.eye(JJT.shape[0]))

Null Space and Redundancy

When n>6n > 6 the arm is redundant: there is a whole family of joint configurations that achieve the same end-effector pose. The null space of JJ spans the self-motions that leave the end-effector stationary. These extra degrees of freedom can be used to avoid joint limits or obstacles:

q˙=Jv+(IJJ)z\dot{\mathbf{q}} = J^\dagger\,\mathbf{v} + (I - J^\dagger J)\,\mathbf{z}

where (IJJ)(I - J^\dagger J) projects any vector z\mathbf{z} onto the null space of JJ. Choosing z=H(q)\mathbf{z} = -\nabla H(\mathbf{q}) for some penalty function HH (joint-limit proximity, manipulability measure) exploits redundancy without disturbing the primary task.

References

  1. [1]Spong, M. W., Hutchinson, S., & Vidyasagar, M. Robot Modeling and Control. 2006.
  2. [2]Siciliano, B. et al. Robotics: Modelling, Planning and Control. 2009.