Step 5
Optimisation: gradient descent
Turn reaching into a number to minimise, then walk downhill. Numerical IK in a dozen lines.
Reaching as a minimisation
arm.ik is switched off. Instead of solving for the joint angles with trigonometry, turn "put the gripper on the target " into one number to make as small as possible, the cost:
is the four joint angles, is where they put the gripper (given below), and is the squared distance to the target, in m². It's zero exactly when the gripper is on the target.
Walk downhill
The gradient lists how fast grows as each joint turns (one partial derivative per joint); it points uphill. Estimate it one joint at a time by nudging that joint a tiny angle each way (a central difference):
is 1 for joint and 0 for the others. Gradient descent takes a step against the gradient, again and again:
The step size matters. Too small and it crawls; too big and it overshoots, bouncing from one side of the valley to the other. works here: try 5 and watch the distance plot. Stop when m² (0.1 mm away) or after 1000 steps.
Valleys, walls and the unreachable
- Constraints. Joints have limits (
arm.limits). After each step, clip back inside them withnp.clip(q, LO, HI). - Different starts, different answers. The heatmap shows over shoulder (across) and elbow (down), with the base and wrist held where descent finished; angles past a limit are shaded. It has two dark valleys, elbow up and elbow down, and descent slides into whichever is downhill from where it starts.
- Out of reach, the lowest cost is above zero. Descent stops with the arm stretched towards the target, as close as it can get.
Jacobians, step 4 swaps the fixed step for Newton's method and converges in a handful of steps.
Your task
- Write
cost(q, target),gradient(q, target)(central differences withH) anddescend(q0, target).descendreturns(q, path): the final joint angles and the list of everyqit visited, start included. - The starter descends from the arm's current pose to each of
world.targets(), plots the distance, draws the heatmap and moves the arm there witharm.move_joints.