Skip to content

Collision stop and speed limits

Between what a controller asks for and what reaches the drives sit two safeguards: the speed limits, which every controller applies, and the collision stop, which the controllers you hand one to apply. Both judge the targets a controller sends, not the arm the drives measure. Most of what they do not catch follows from that.

Speed limits

Each controller moves each arm's chain targets no faster than the arm's tuning for that controller allows: a top speed per joint and one for the tool frame. They come from the arm's description, and no joint's target ever moves faster than the top speed the description states for the joint itself. A running controller is retuned through ArmTuning.speed and its configure.

Every tick, a step of the targets that would outrun a limit is cut short, every joint's share of it by the same factor, so the arm keeps the path it was asked along and only takes longer over it. A velocity target that would outrun a limit is slowed the same way. The step is measured from the target last sent, never from where the arm measures, so an arm lagging behind its target does not earn a bigger step to catch up.

The limits bound the targets, not the arm. The drives chase each target with the gains they are given, so an arm pushed by hand, falling under gains too soft to hold it, or catching up with a target it lagged behind moves at whatever speed that gives. The gripper is not a chain joint, and these limits do not pace it.

The collision stop

A CollisionStop guards one robot. It is built from the workcell and handed to every controller of the robot; a controller built without one sends its targets wherever they go. It guards pairs: two of the robot's links, except the pairs an arm's description excludes because their meshes always overlap, and each link against each KeepOutBox of the workcell. Links are measured by the convex hulls of their collision pieces, a box as a box square to the world frame.

Measuring takes longer than a tick, so the stop measures on a thread of its own, around the targets the controllers send, and hands each tick every pair standing within its margin and a few centimeters past it, with how fast each joint opens or closes it. Each tick, of the step a controller is about to send, the stop takes out only what closes on one of those gaps too fast: a pair may close by a share of what is left of its gap past the margin, so it slows as it nears the margin and stops there, within about 150 milliseconds. The rest of the step goes through, so an arm driven sideways into something slides along it, and an arm driven away from it leaves at once, with no hold to wait out. A pair already inside its margin, as the arms start up or as a box goes up around them, may only open: an arm may leave a box, never go deeper in.

The step is cut joint by joint: it keeps as much of each joint's motion as it can. A hand driven by pose, as the end-effector pose controller does, slides along a surface toward where the hand is asked to be. An arm driven by joint targets that run on through what it would hit, a joint stream or a leader, comes to rest at the point nearest its target joint by joint, which may be not far past where it met the surface.

What a cut step does to a run depends on the controller. A trajectory ends where the stop first slowed it, and play raises StoppedShortError. The end-effector pose controller walks on from where the stop sent the arm, at the pace of the hand driving it. A follower the stop leaves more than a few degrees from its leader engages again, at the engage speed, rather than snapping to its leader once it is let go; one left nearer goes on mirroring. The joint controller goes on to its command. CollisionStop.last_stop says which links were closing on what.

What neither catches

  • The arm itself. Both judge targets. An arm too soft to hold its own weight, under a low gain override, sags past where the stop sends it, and an arm knocked or pushed goes where it is pushed.
  • What is not modeled. The stop knows the robot's links and the keep-out boxes you declared, nothing else: not a person, not an object on the table unless a box stands around it, not what the gripper holds.
  • What a straight line misses. The stop steps along each gap as a straight line from where it last measured it, so a fast arm, or one turning a link's corner toward something, can come inside the margin by what that misses. The margin is the knob for that: the faster the arms move, the wider the margin they need. A stop holds every pair at DEFAULT_MARGIN_M unless within_arm_margin_m, between_arms_margin_m or keep_out_margins_m says otherwise.
  • Everything else. Neither is a safety-rated stop. To stop the arms where they are, call Runtime.freeze; to take the torque off, cut the drives' power.

In the sim, World.contacts says what touched during a whole run, which is how to find out whether a margin was wide enough before trying it on the arms.