3-Link Manipulator

Playing with Jacobians — drag a target and watch 1-, 2-, and 3-link arms chase it.

Overview

This is a small planar inverse kinematics visualization. Drag anywhere and the arm works out how to put its end effector on that point. There are three configurations — 1-link, 2-link, and 3-link — and stepping up through them is the point: one link is trivial, two is a proper IK problem, and by three links there are infinitely many valid solutions and the solver just falls into one of them.

The interesting part is how it solves. There's no analytic solution in here — the arm follows its Jacobian, the matrix that says "if I wiggle each joint a little, which way does the hand move?" Invert that relationship (approximately, carefully) and you can walk the joints downhill toward any target. This used to be a GameMaker export; the version here is a rewrite.

Demo

links method
[ θ: — | err: — | manip: — ]

[ drag to move the target. green crosshair = reachable, red = dream on. the yellow ellipse is the manipulability ellipsoid. ]

Things to try: drag the target through the middle of the workspace and watch the yellow ellipse — that's the manipulability ellipsoid, the Jacobian's picture of which directions the hand can move fast. When the arm straightens out toward the edge of its reach, the ellipse collapses into a line: that's a singularity, and the flattened direction is the one the arm physically cannot move in. Watch the manip number in the readout hit zero at the same moment. Then switch between Jᵀ and DLS and drag the target straight through the base of the arm — the transpose gets hesitant near singularities, while DLS with a healthy λ plows through with a kind of damped indifference. That difference is the entire reason DLS exists.

The Jacobian

For a planar arm, the Jacobian is a 2×n matrix — one column per joint, and column i is just the vector from joint i to the end effector, rotated 90°. That's it. That's the whole matrix. Each column answers "if this joint rotates a little, which way does the hand swing?"

The problem is you want to go the other way — you have a desired hand motion and need joint motions — and a 2×3 matrix doesn't have an inverse. The two workarounds in this demo:

  • Jacobian transpose — don't invert anything, just use Jᵀ as if it were an inverse. It's not, but it always points downhill on the error, so with a sensible step size you get there. Free, stable, a little drunk near singularities.
  • Damped least squares — solve (JJᵀ + λ²I) z = e, then Δθ = Jᵀz. The λ² on the diagonal means the matrix is always invertible, even at singularities — you trade a little accuracy everywhere for not exploding anywhere. This is the one that ships in real robots.
the whole solver JavaScript
// column i of J: perpendicular of (end effector - joint i)
for (var i = 0; i < N; i++) {
	Jx[i] = -(ee.y - joints[i].y);
	Jy[i] =   ee.x - joints[i].x;
}

// damped least squares: (JJt + lambda^2 I) z = e, then dtheta = Jt z
var a = dot(Jx, Jx) + lambda * lambda;
var b = dot(Jx, Jy);
var c = dot(Jy, Jy) + lambda * lambda;
var det = a * c - b * b;          // never zero — that's the point of lambda
var zx = (c * ex - b * ey) / det;
var zy = (a * ey - b * ex) / det;
for (var i = 0; i < N; i++) {
	theta[i] += speed * (Jx[i] * zx + Jy[i] * zy);
}

Twelve lines, and it's the same math whether the arm has one link or twenty. The manipulability ellipse falls out of the same numbers: JJᵀ is a 2×2 symmetric matrix, its eigenvectors are the ellipse axes, and the square roots of its eigenvalues are the axis lengths. Everything on the screen is the Jacobian wearing a different hat.

History

The original version was GameMaker Studio 2 exported to HTML5, at a mighty 300×300. It used the same Jacobian transpose update this page does — and the GML source still contains the commented-out corpse of my first attempt, an analytic inverse full of csc θ₂ terms that divided by zero every time the elbow straightened. Swapping it for the transpose was the day the whole thing clicked: you don't need to invert the matrix, you just need to keep pointing downhill.

The WebGL rewrite draws the grid, arm, trail, target, and ellipse as one triangle batch in a single draw call, with the solve running once per frame. The GameMaker build is still up if you want the museum piece.

Development Stack
Then: GameMaker Studio 2, HTML5 export, 300x300
Now:  WebGL, one draw call, this page
Math: Jacobian transpose + damped least squares IK