Φferromotion · textbook · chapter 10 rust → wasm · on-device

Do everything at once

A robot rarely has just one job. It has to reach the target and keep its whole body clear of things and move smoothly — all at the same time, and reacting to a world that moves. This chapter fuses those into one motion, geometrically, with the same Rust code the native tools use.

01 — the problemMany jobs, one motion

Give an arm a goal for its hand and it is easy to servo toward it. Add an obstacle and it gets awkward: now the elbow, the forearm, the whole body has to stay clear too, and the importance of that flips from irrelevant when the obstacle is far to overriding when it is close. Stitching such behaviors together with hand-tuned weights is brittle, and re-planning every time the world moves is slow. What you want is for the behaviors to combine themselves, correctly, moment to moment.

02 — the policiesEach behavior brings its own metric

RMPflow makes each behavior a Riemannian Motion Policy: a desired acceleration paired with a metric — a state-dependent weight that says how much this behavior should matter right now. Reach-the-goal has a modest, steady metric. Avoid-the-obstacle has a metric that blows up as the arm nears the surface. The behaviors are fused by a metric-weighted combination pulled back onto the joints, so near the obstacle its policy dominates and far away it simply disappears — no mode switch, no planner.

Below, a two-link arm reaches for the goal. Drag the obstacle into its path and watch the whole arm bend around it; drag the goal anywhere in reach.

avoidance
clearance
hand → goal

the dashed ring is the obstacle's influence; push it onto the arm and the whole body flows around while the hand keeps reaching — clearance never goes negative

03 — the metric decidesClose means loud, far means silent

The obstacle's dashed ring is where its metric starts to bite. Drag the obstacle outside everything and the arm reaches in a clean line — the avoidance policy is present but weighted to nothing, so it contributes nothing. Slide it onto the path and, as the nearest arm point crosses the ring, that policy's weight climbs steeply and the arm yields exactly as much as it must, no more.

This is the whole idea, and why it composes. You never wrote a rule for "if near obstacle, stop reaching." Each behavior states what it wants and how much it cares, in its own coordinates; the geometry does the arbitration. Add a third behavior — a joint limit, a second obstacle, a preferred posture — and it drops in the same way, its metric deciding when it speaks. The response is graded, not a switch: a closer obstacle simply forces a tighter, later pass.

04 — the checkReaches, and never touches

On load, this page put the obstacle squarely on the straight path between the arm's start and its goal, and ran the policy to rest:

smallest clearance to the obstacle over the run
final hand-to-goal error
verdict

The clearance stays positive the whole way — the metric's blow-up makes contact impossible — and the hand still lands on the goal. Both jobs, done at once, with nothing planned ahead.

05 — the pointComposition without a planner

State what you want, and how much you mean it.

Every behavior carries its own metric, and the geometry fuses them into one reactive motion — reaching where reaching matters, yielding where safety matters — so new behaviors compose in without re-planning and without a tangle of hand-tuned weights.

This is why reactive geometric methods took hold for whole-body control: a real robot faces a moving, cluttered world where a plan is stale the moment it is made, and RMPflow answers it by making arbitration a property of the geometry rather than a script. It draws on the same kinematics — the real Jacobians of the arm — as the model-based controllers, but spends them on reacting rather than planning. It is the composition member of this series: where other chapters found the one quantity that governs a system, this one lets many behaviors each name their own, and trusts the metric to referee.

What you just drove: RmpArm from ferromotion-control on a two-link arm built from URDF through the real ferromotion-core kinematics, compiled to WebAssembly — the same code the native tools link against. Every frame pulls back the attractor and per-control-point obstacle policies to a joint acceleration and integrates it; the Jacobians are the arm's real ones. Nothing precomputed — the motion is solved live as you drag.

Verified in the library: the arm reaches the goal while every control point clears an obstacle on the direct path · a far obstacle does not perturb the reach at all · the pass tightens continuously as the obstacle nears the path · clearance never goes negative. Each is a test in cargo test, not a claim in prose. See also ch.1 · ch.2 · ch.3 · ch.4 · ch.5 · ch.6 · ch.7 · ch.8 · ch.9.

Institute for Physical AI · the Rust library · crates.io