MyModelRobotBack to viewer
Learn the algorithm

Inverse kinematics in MyModelRobot: axis-constrained CCD

MyModelRobot uses axis-constrained cyclic coordinate descent (CCD) to move a selected segment towards a target. It adjusts one joint value at a time, measuring the result before continuing. This is a particular method for solving inverse kinematics (IK): you specify a desired position and the program searches for joint values that achieve it. Select a segment and move its target to see this search in action.

One sweep of CCD

Explore the same per-joint update in a simplified 2D arm. The handle is the centre of the final link. The full viewer also transforms spatial URDF axes and tries alternate starting offsets when progress stalls.

xy q₁ q₂p(q)t
● Actual centre p(q)○ Target t

Click or drag in the diagram to move the target. With the diagram focused, use arrow keys; Shift makes larger steps. Dashed rays from the active joint are the two directions compared by CCD.

Joint updates: 0

Position error (diagram units):

The exercise shows degrees and radians; the viewer stores joint angles in radians. This illustration has one positional target and no orientation target.

Try it: change a joint slider: that is forward kinematics. Move the target and predict which joint turns first. Press Step twice. Why must the second update use the centre’s new position? Then try Beyond reach, compare the residual gap, and solve from another starting bend.

Forward and inverse: two directions of the same problem

Forward kinematics starts with joint values. An angular joint rotates; a sliding joint translates. MyModelRobot combines those motions with the joint origins defined in URDF, following the parent–child hierarchy to calculate each link’s position and orientation.

Write the selected point’s position as p(q), where q is the list of joint values. Forward kinematics evaluates p(q). Inverse kinematics asks for q that brings this point close to a target t:

min e = ‖t − p(q)‖

The error e is the straight-line distance to the target. Different joint configurations may produce the same position, and some targets have no solution. MyModelRobot searches iteratively from the current pose.

What are you moving?

Double-click a segment, or enable the pose tool and select one. A ray from the camera through the pointer identifies the visible mesh and its owning robot link.

The point being positioned is the centre of the selected link’s visual bounding box, calculated in that link’s coordinates. This geometric handle ties placement to the segment’s shape rather than an arbitrary surface detail; it is not a centre of mass. Your click may be elsewhere on the surface, so the tool retains a fixed world-space offset between your click and this centre. The segment follows without suddenly snapping its centre under the pointer.

A screen pointer supplies two coordinates; the target needs three. Initially, pointer rays intersect a plane through the click, facing the camera. The intersection plus the retained offset gives the centre target; moving the pointer moves this target in a parallel plane. The wheel or depth buttons move the plane nearer or farther. The camera pauses during placement so this mapping stays stable.

Which joints take part?

The solver walks from the selected link towards its ancestors. It treats links connected by fixed joints as one rigid assembly, preventing rigid decorations from creating false branch boundaries. It includes movable joints until it reaches a branching assembly, then stops before moving that shared anchor.

Thus, selecting one arm generally adjusts that arm’s chain while its branching anchor stays steady. Joints farther down the tree than the selected link keep their values: their links follow the segment with the existing bends. Choosing a forearm and choosing a hand can therefore produce different motions.

One joint at a time: axis-constrained CCD

MyModelRobot uses cyclic coordinate descent (CCD). Each sweep visits the chosen joints from the selected segment towards the anchor. For each joint, it recalculates the current point and the joint’s axis in world coordinates, using the parent’s current transform and the URDF joint origin.

For a revolute or continuous joint, the allowed motion is rotation around that axis. Imagine looking straight down it. The solver projects the vectors from the joint origin to the selected point and to the target onto the plane perpendicular to the axis. Call those projected vectors u and v, and the unit axis a. The signed correction is:

Δq = atan2(a · (u × v), u · v)

This turns the point’s projected direction towards the target’s. Ordinary sweeps cap each correction at 0.25 radians. If the point lies on the rotation axis, rotation cannot change its position, so that joint is skipped.

A prismatic joint instead slides along its axis:

Δq = (t − p) · a

This extracts the part of the position error that sliding can correct. Each ordinary update is capped at 4% of the robot’s overall scale. After every joint adjustment, forward kinematics updates the link transforms before the next joint is considered.

Repeating, respecting limits, and learning from the result

Revolute and prismatic values stay within their joint limits. Continuous angles wrap to an equivalent value between −π and π. Angles use radians; translations use metres.

Short batches across animation frames keep the browser responsive, normally allowing up to 20 sweeps and about 4 milliseconds per call. It keeps the best pose found. Its reach tolerance is 0.1% of the robot’s bounding-box diagonal at selection time, with a minimum of one micrometre. When progress stops, deterministic trial offsets can help a straight or awkward configuration begin moving; unsuccessful trials are discarded.

Try selecting a forearm, moving the target sideways, and then changing its depth. Watch which sliders respond and which remain steady. Place the pose to keep it, or cancel to restore the starting values.

This is a local position solver: orientation is free, collision warnings are advisory, and “Best pose” means the best configuration found by this search. A remaining gap may reflect reach, limits, or a local stall. Try another starting bend or select a different segment to explore why.