40 min

Deploying to the arm

The first autonomous run is a supervised experiment with a short duration, a motion clamp, and a hand near the power plug - and cutting the power does not stop the arm, it drops it.

Where you are. You have a dataset you recorded, a policy trained on it, and several checkpoints saved. This lesson puts one of them on the arm and finds out what you actually built.

The moment before you press enter

Clear the desk. Not tidy it: clear it. The mug goes on another table, the keyboard slides back, the phone comes off the bench.

Then look at where the arm can reach. Sweep your own hand through the volume it will move in and notice everything that is inside it: the edge of the desk, a cable, the second webcam’s tripod leg. Everything in that volume is something the arm may hit, because it has no idea any of it exists.

Now put the power supply’s plug where your left hand can reach it without looking, sit down, and put on safety glasses.

You have a command in the terminal and your finger over the return key. Until now every motion this arm has made came from your hand on the leader. This one will not.

The idea in one paragraph

Deploying is running the trained policy in the loop instead of your hand: the cameras feed it observations, it emits joint commands, and the arm executes them at thirty times a second with nobody checking them. Three things make the difference between an experiment and an incident. The command changed recently, so most tutorials you find will tell you to do it the wrong way. The safety defaults are permissive, and the one you most want is off unless you turn it on. And killing the process does not freeze the arm, it releases it, so a stop is also a drop. Once those are handled, the run stops being a demo and becomes what it should be: twenty logged trials that produce one number.

The command

lerobot-rollout \
    --strategy.type=base \
    --policy.path=${HF_USER}/act_brick_v1 \
    --robot.type=so101_follower \
    --robot.port=/dev/ttyACM0 \
    --robot.id=my_follower \
    --robot.cameras="{ front: {type: opencv, index_or_path: /dev/video0, width: 640, height: 480, fps: 30}, wrist: {type: opencv, index_or_path: /dev/video2, width: 640, height: 480, fps: 30} }" \
    --robot.max_relative_target=5 \
    --task="put the red brick in the bowl" \
    --duration=15
--strategy.typeWhat it doesWhen
baserun autonomously, record nothingyour first runs, and quick checks
episodicepisode-oriented recording with reset phasesstructured evaluation batches
highlightring-buffer recording, saved on a keystrokecatching a rare failure you cannot reproduce
sentrycontinuous recording, uploads every few episodeslong unattended sessions
daggerhuman in the loop, correcting as it goescollecting exactly the states the policy gets wrong

Start with base and a short --duration, in seconds. Omit --duration and the rollout runs until you stop it, which is not what you want on the first attempt. All strategies accept --inference.type=rtc, real-time chunking, which smooths execution when the policy is slow enough to stall the control loop; ACT does not need it, and the large vision-language-action models in Module 5 will.

Safety, which upstream does not cover

Everything in this section is the course’s own assessment. The LeRobot documentation contains no safety section, and the SO-ARM101 repository carries no safety notes beyond a warning about power supply voltage. That is a gap rather than a signal that none is needed, so this is written from the arm’s published torque figures and LeRobot’s confirmed configuration defaults rather than from an upstream safety document.

Clear the desk nothing fragile inside the sweep Match cameras same names, same view as the reference photo Clamp motion max_relative_target is off by default Keep it short --duration=15 on the very first run Hand on plug glasses on, and be ready to catch it all five, before every autonomous run, not only the first one the arm has no collision detection, no force sensing, and no external stop button
The five gates before every autonomous run: a clear desk, cameras matched to the reference photo, the motion clamp set, a short duration, and a hand on the plug

Wider than the screen; scroll it sideways.

What this arm can actually damage, in order of likelihood. Itself, by a wide margin: stripped plastic horns, cracked printed parts, a burnt-out gripper servo. Your desk, mainly by knocking liquid onto an unenclosed control board, which is the most probable expensive outcome and the reason the desk gets cleared. You, to the extent of a pinch or a bruise; the published stall-torque figures for these servos, which the repository and the vendor pages do not agree on, work out to something under a kilogram-force at the gripper at full extension, so this is not an industrial arm and does not need a light curtain. Your eyes are the real injury risk, because a joint can snap through its range quickly and a held object can be flung. Safety glasses during autonomous runs is the one recommendation here that is not negotiable.

SettingShips asWhat that means
max_relative_targetNoneno per-step motion clamp, so a wild action becomes a full-speed jump
disable_torque_on_disconnectTruea clean shutdown turns the motors off
gripper Max_Torque_Limit500 of 1000already capped at half, to stop the gripper burning out

The first row is the one to change. With no clamp, a single bad action from the policy is executed at full speed. Setting --robot.max_relative_target=5 limits how far any joint may move in one step; the flag accepts a scalar for all motors or a dictionary that names every motor - a partial dictionary raises rather than falling back - and the documentation notes it may make teleoperation feel less smooth, which is a fine trade for an autonomous run. The clamp is applied to the same units the action carries, which on this arm means degrees for the five body joints and points of a 0-to-100 gripper scale, so one scalar is doing two jobs. Treat the value 5 as a starting guess to tune on your own hardware rather than a tested number. Turn it off with --robot.max_relative_target=null once the policy has earned your trust. The register-level torque limits behind all of this are in torque limits and safety.

Torque on holds its pose and its payload Ctrl+C, or the plug out torque is disabled on disconnect disable_torque_on_disconnect = True Gravity it falls, and drops what it held a stop is not a hold: plan for the drop before you plan for the stop
Torque on and holding its pose, then Ctrl+C or the plug out disabling torque, then gravity taking the arm and whatever it was carrying

Wider than the screen; scroll it sideways.

Now measure it, honestly

The first rollout is not an evaluation. Run three supervised trials, watch what the arm does, and fix anything physical before you start counting.

Then the real thing: twenty trials minimum, the object placed in the cells in a fixed rotation, nothing adjusted between trials, every trial logged whether or not it worked. This is the protocol from Module 3’s evaluation harness, arriving on hardware where each trial costs a reset instead of a simulator call.

python code/eval_log.py trial --result failure --stage grasp --checkpoint 40k --cell A2
python code/eval_log.py report --checkpoint 40k

Two things the log gives you that a tally in your head does not. It records where each failure happened, which is the routing information for the next lesson: failures concentrated at one stage want ten or twenty more episodes targeting that stage, while failures scattered everywhere want a look at demonstration consistency or camera framing. And it prints a Wilson confidence interval, which is the honest version of your success rate. Eight successes in twenty trials is 40%, and the interval around it runs from roughly 22% to 61%. Twenty trials is the floor, not a precise instrument, and two checkpoints whose intervals overlap have not been told apart.

Run 20 trials uncut, logged Tag the stage where it went wrong Choose one fix exactly one Record more 10 to 20 targeted Retrain same recipe one change per turn of the loop, or the next number tells you nothing the success rate is the only ground truth in this diagram
The loop: run trials, tag the stage, change one thing, record, retrain, run again

Wider than the screen; scroll it sideways.

That is where this lesson hands off. What you have now is a number and a distribution of failure stages, which is the input to the data-quality loop and, when the symptom is stranger than a failure stage, to debugging by symptom.

Without hardware

  • Instead of the arm, roll the checkpoint out in the Module 2 scene. There is no motion clamp, no short first duration and no hand near the plug, because there is nothing to damage.
  • Measure this: 200 trials rather than 20, initial conditions dealt from a seed you never trained on, the same success criterion written in advance, and a Wilson interval. Trials cost milliseconds here, so a wide interval is a choice rather than a constraint.
  • What you lose: the entire safety half of this lesson, unexercised. Read it anyway. The first time you meet a powered arm should not also be the first time you read that cutting power does not stop it, it drops it.

Check yourself

1. You find a tutorial that deploys a policy with lerobot-record --policy.path=.... What has happened, and what do you run instead?

The tutorial predates LeRobot v0.6.0, which split deployment out of recording. On v0.6.x the real-robot deployment path is lerobot-rollout, with --strategy.type choosing whether the run also records. The confusion is genuinely upstream: at the time of writing LeRobot’s own agent guide still shows the old command while the cheat sheet shows the new one. Pin your version and follow the cheat sheet.

2. Your policy grasps the brick, lifts it over the bowl, and then the run hangs. You hit Ctrl+C. What happens next, and what should you have done first?

The process shuts down cleanly, which disables torque, which removes all holding force, so the arm collapses under gravity and the brick drops from wherever it was. Before pressing anything you want a hand under the arm, the workspace clear beneath it, and nothing fragile below the payload. A stop is a release, not a freeze; the only way to hold a pose is to keep the motors powered.

3. Why is max_relative_target worth setting for a first rollout when it was never needed during teleoperation?

During teleoperation the commands came from your hand, which cannot produce a physically absurd target. A freshly trained policy can, especially in a state its dataset does not cover. With the clamp at its None default, that command is executed as a full-speed jump to an arbitrary pose. The clamp limits how far a joint may move in a single step, so a bad action becomes a small wrong motion instead of a fast one. It costs smoothness, which does not matter yet.

4. You recorded with cameras named front and wrist and deploy with them named top and side, at the same resolution and pointing the same way. Why does this fail?

The names are part of the observation structure the policy was trained against, not free-form labels. The rollout has to hand the model the same observation keys it saw during training, so a rename either errors or, worse, silently feeds the wrong image into the wrong input. The rule is simple and worth over-applying: the camera configuration at rollout matches the one used at record time, exactly.

5. Twenty trials give you eight successes. A colleague’s run gives eleven out of twenty. Is theirs better?

Not on this evidence. 8 of 20 is 40% with a 95% interval of roughly 22% to 61%; 11 of 20 is 55% with an interval of roughly 34% to 74%. Those overlap heavily, so the difference is well inside what chance produces at this sample size. Twenty trials is enough to tell a broken policy from a working one and nowhere near enough to rank two similar ones. To separate them you need more trials, or a difference large enough that twenty can see it.

6. Your first policy scores zero. What is the least useful next action, and what is the most useful?

The least useful is training a bigger model or training longer on the same data; a zero usually means the policy never had the information it needed, and more capacity does not create information. The most useful is looking at where the twenty trials died and at what the cameras actually saw. A policy that ignores the object points at camera framing or lighting; one that fails at a single stage points at coverage of that stage; one that flaps and oscillates points at inconsistent demonstrations. Each of those is a data or setup fix.

Do this

About an hour, most of it trials.

1. Pre-flight, out loud. Desk clear. Live camera views compared to the reference photos. --robot.max_relative_target=5 in the command. --duration=15. Glasses on, plug in your left hand.

2. Three supervised trials. --strategy.type=base, short duration. You are checking that the arm moves plausibly and that nothing physical is wrong, not measuring anything. Stop the moment something looks unsafe.

3. Twenty logged trials on one checkpoint. Rotate the object through your cells in a fixed order. Log every trial:

python code/eval_log.py trial --result success --checkpoint 40k --cell A1
python code/eval_log.py trial --result failure --stage approach --checkpoint 40k --cell B3

Change nothing between trials. If you find yourself nudging the bowl “back to where it should be”, that is a trial that already counts as a failure of your setup discipline.

4. Repeat on two more checkpoints. Then python code/eval_log.py report and read the per-checkpoint intervals. Notice how much they overlap; that overlap is the honest statement of what twenty trials bought you.

5. Record it. Film the trials uncut, including the failures. Write the number, the interval, the checkpoint, the LeRobot version and the dataset revision next to your task specification. That block is what makes the next iteration comparable to this one.

What you can now do

You can deploy a trained policy to a real arm with the current command rather than the one every older tutorial shows, with the motion clamp set, the duration short and the workspace cleared. You can explain why cutting power drops the arm rather than stopping it, and you plan for that instead of being surprised by it. And you can turn a rollout into a measurement: twenty logged trials, a success rate with an interval around it, and a distribution of failure stages that tells you what to change next.

What you can now do

You can run a trained policy on real hardware safely, measure it over twenty honest trials, and read the resulting number without flattering yourself.