autoplay
theme
2026 · active · Robotics · Tool · Generative

Houdini Robot Toolkit

A Houdini digital asset that animates a 6-axis arm along a drawn curve, measures what the move would do to the hardware, and blocks export until it is safe.

Draw a curve, get a robot move. A drawn curve can easily ask for a move no arm can follow.

The solve

The asset takes a rest skeleton and a goal curve and solves the arm along it, colouring the result by whatever you are currently worried about. Here that is joint velocity, blue at 0 and red at the profile's 180 deg/s limit.

Houdini viewport showing a robot arm with its goal curve, achieved path coloured by joint velocity, and magenta markers on the frames that fail validation
The analysis overlay, with the flagged frames clustered around the wrist.

Three inputs are usually wired:

  • Goal curve — the path the tool follows. Drawn curves arrive messy, so they are fused, decimated and fit to a spline before they become a goal.
  • Goal point — a single transform to reach instead of a curve; when wired it takes over from the built-in target.
  • Tool geometry — not decoration. The TCP is read out of this mesh, from a point or group named tcp if you declare one, and guessed from the extent along +Y if you do not.

A rest-skeleton override and a reserved collision input are also exposed.

Houdini network showing CURVE_IN, POINT_IN and TOOL_IN nulls feeding the robot_arm asset
The three inputs: a curve, a goal point, and the tool.

The gate

A pre-flight check runs the whole clip and reports on joint limits, angle continuity, unwrapping, joint velocity, wrist branch flips, frame range, tracking residual, and whether the analysis cache still matches the live solve.

With Gate Export On Pre-Flight on, a clip that fails cannot be written out.

The Run Pre-Flight Check button with Gate Export On Pre-Flight ticked
Run Pre-Flight Check, with the export gate on.
READY - 0 warningsOK    Joint limits         all samples within profile limitsOK    Angle continuity     no step exceeds 180 degOK    Unwrap               continuous anglesOK    Joint velocity       peak 166.7 deg/s of 180 allowedOK    Wrist branch         resolved at 131 frames (tool pose unchanged)OK    Frame range          1-162, matches playbarOK    Tracking residual    peak 248.33 mmOK    Solve cache          matches the live solve

What clears the gate is a joint CSV: frame, time_s, j1_deg through j6_deg, and speed_pct. TD Robot Control's Go worker plays it directly and Motion-Core imports it; on the FR20, the toolkit's own player does.

Four ways to pose the arm

The spine of the asset is one menu: FK, IK, imported CSV, or a baked solve. Everything downstream is identical whichever you pick: same analysis, same gate, same export.

That split matters because IK is how you author and FK is what a controller takes. The FK angles are stated in the robot frame, clamped to the profile limits, so they are the numbers the controller will actually receive. Bake IK to FK freezes a solve into joint keys, and the hard half is doing it flip-free: a six-axis arm can reach the same tool pose with the wrist rolled either way, and a re-solve is free to pick the other branch between cooks. Left alone, the tool goes where you asked and the elbow snaps through 180° getting there.

The bake resolves those, and reports how many it found. Because a flip is a different joint configuration reaching the same TCP, resolving it changes the joint path and nothing the tool does; the pre-flight reports it as "tool pose unchanged".

Baked 354 frames to ik_baked_fk (285 wrist flips resolved)

Once baked, the clip stops moving under you and a single joint can be hand-corrected without re-solving.

Import CSV closes the loop the other way: a clip that already exists, off the robot or out of Blender, comes back in and is measured by the same validator. Nothing gets a pass for having come from somewhere trusted.

Retiming instead of redrawing

Most first drafts fail on one thing: they are too fast. A hand-drawn curve has no idea what a joint can do.

The fix is not to redraw the curve. It is to redistribute time along it. Retime samples the solve, finds where the joints are over budget, and slows only those stretches.

Before and after retiming. The rejected frames, in magenta, are gone.

Measured on a second clip end to end, at the default safety factor of 0.8:

as authoredafter retime
verdictBLOCKED — 1 failureREADY — 0 warnings
peak joint velocity820.5 deg/s (J4, frame 98)166.7 deg/s of 180
frames150162
frames flagged in the viewport230

Retiming only the stretches that were over budget cost 12 extra frames. The retime is still an estimate, because it samples the solve rather than proving it, so the gate runs again afterwards.

On the real arm

The asset was built against a UFactory 850 (UF850). It now also builds a Fairino FR20 from its URDF, and for that arm the IK is a closed-form solve instead of the iterative one, so every frame can take the branch nearest the frame before.

The same clip on the FR20 and in the asset.

On the FR20, Retime plans against acceleration as well as velocity. A clip inside the velocity limits can still ask a joint to speed up harder than the controller allows, and the player then slows the whole clip down. An earlier export of the same curve would have played 8.54× slower than drawn.

first clipthe clip above
retimed at150 deg/s²the measured 300 / 600 deg/s²
length323 frames, 13.4s336 frames, 14.0s
playedits designed length; 3 runs, 0 controller errors0 controller errors
tracking0.80° worst, 0.29° RMS0.85° worst, 0.23° RMS

Both streamed by ServoJ at 125 Hz. Tracking is measured after removing the controller's lag of about 0.1s. The second column comes from a recorded run of the clip in the video, not from the take filmed.

Limits

Retime cannot move the curve, so a clip that fails on joint limits or tracking residual, a curve that goes where the arm cannot reach, has to be redrawn. Two of the three test curves fail exactly that way and stay BLOCKED no matter how they are retimed.

On the FR20, Pre-Flight also checks the clip against the room: the arm as capsules fitted to its meshes, the room measured by touching the arm's tool to the floor and walls. A clip that comes too close fails the gate.

A white Fairino FR20 arm on a plywood base in a small lab, with a red cart holding a laptop beside it
The FR20 in the lab, with the control cart.
Role
Creator and sole author; operated the robot
Built with
Houdini · VEX · Python · KineFX
Asset
wenyi::robot_arm::1.0, six tabs in workflow order
Robot
UF850 and Fairino FR20; other arms from a URDF and a profile JSON
Hardware
The FR20 and the lab, Volvox Labs
Feeds
TD Robot Control plays the exported joint CSV directly and Motion-Core imports it; on the FR20, the toolkit's own ServoJ player
Sibling
xArm Blender Animation, the keyframed route to the same file
Source
github.com/wzhang1998/houdini-robot-toolkit
Tools
Houdini · VEX · Python · KineFX · UF850 · Fairino FR20