Recover from a Stop
A stop cuts the arm's motor power wherever it is. Afterwards the arm is unhomed and will not move until it recovers, and the plate may be in the jaw, at the source or at the destination. The script below stops a move on purpose, then recovers the way you would after a real stop.
You need the SDK installed and the PF400 connector running. Run this one in simulation mode first: it moves the arm and then stops it.
import asyncio
from unitelabs.sdk import AsyncApiClient
SLOW = 0 # fixed jog profile, 10% of rated speed (connector 0.4.0+)
async def read_state(arm):
# Powered, homed and idle? The stream opens with an event that
# carries no state, so read until one does
states = await arm.information_provider.subscribe_state()
async with states:
async for state in states:
if "Homed" in state:
return state
raise RuntimeError("The state stream closed without a state")
async def wait_until_homed(arm, homed):
# The state stream reports changes as they happen; wait for one
# that shows the arm homed (or not). The first event has no state
states = await arm.information_provider.subscribe_state()
async with states:
async for state in states:
if state.get("Homed") is homed:
return state
raise RuntimeError("The state stream closed without a state")
async def main():
client = AsyncApiClient()
arm = await client.get_service_by_name(name="Brooks PreciseFlex 400")
safety = arm.safety_controller
# The arm only moves once it is homed
if not (await read_state(arm))["Homed"]:
await arm.movement_controller.home()
# A 100 mm lift. Every axis needs an offset, and on an arm with a
# linear rail the rail does too and the action ends in "_with_rail"
lift = dict(column=100.0, shoulder=0.0, elbow=0.0, wrist=0.0)
suffix = ""
if await arm.information_provider.get_rail_installed():
lift["rail"], suffix = 0.0, "_with_rail"
move_by = arm.joint_movement_controller.actions["move_by" + suffix]
# Start the lift slowly, then stop it partway
move = asyncio.create_task(move_by(**lift, profile=SLOW))
await asyncio.sleep(0.5)
await safety.stop() # halts and cuts motor power
try:
await move
except Exception as error: # the move fails: it never arrived
print("Move interrupted:", error)
print("After stop:", await wait_until_homed(arm, False))
# Before recovering, look at the arm. Recovery re-homes it, and
# the jaw opens and closes again: take out any plate it holds.
input("Clear the gripper and the arm's path, then press Enter")
await safety.recover() # clears the fault and re-homes
print("After recover:", await wait_until_homed(arm, True))
asyncio.run(main())
Run it with uv run recover_from_a_stop.py. It prints the error the interrupted move raised, the
arm's state after the stop with Homed false, and the state after recovery with Homed true.
Stop or cancel
stop is for anything that should not happen: a collision about to occur, a person in reach. To
call off a move you no longer want, use cancel_motion instead. The arm slows to a halt with power
on and the gripper still holding, so there is nothing to recover. It may come to rest near a
waypoint rather than on it, so read its position back before the next move.
After a stop in a workflow
Don't retry a transfer automatically after a stop. The workflow cannot tell whether the plate is still at the source, in the jaw, or already at the destination, and running the same pick-up again can drive the jaw into a plate that is already there. Pause the workflow instead, let an operator check the arm and put the plate where it belongs, then recover and continue. Basic Human-in-the-Loop shows how to pause for an operator.
recover and home move the arm, and homing is a large move to the home sensors. Check that nothing is in its way before you call either one.Last updated