Maker ยท Active Project

Session Log: LeRobot Setup on Raspberry Pi 4 (1GB)

Migrating the already-calibrated SO-101 leader/follower arm setup from Mac to the Pi 4

Goal: validate that the Pi can drive the arm + a USB webcam together โ€” the key open question from the project roadmap's Phase 2/3 transition.

Result: โœ… Success. Arm control and camera capture both confirmed working simultaneously on the 1GB Pi 4, at a stable 60Hz control loop.

1. System prep

sudo apt update && sudo apt upgrade -y
sudo apt install -y python3-venv python3-pip git git-lfs

Installed git-lfs upfront this time โ€” a lesson learned from the Mac setup, where skipping it caused an incomplete clone. Confirmed Python 3.13.5, comfortably above LeRobot's 3.10+ requirement.

2. Clone + install LeRobot

git clone https://github.com/huggingface/lerobot.git
cd lerobot
git lfs pull
python3 -m venv robot
source robot/bin/activate
pip install -e ".[feetech]"

Issue encountered: [Errno 28] No space left on device during the torch download, despite 48GB free on the main disk.

Root cause: pip's default temp directory (/tmp) is tmpfs (RAM-backed) on Raspberry Pi OS, capped at ~453MB โ€” too small for the ~420MB torch wheel plus build overhead.

Fix:

mkdir -p ~/pip-tmp
TMPDIR=~/pip-tmp pip install -e ".[feetech]"
rm -rf ~/pip-tmp

Install succeeded after redirecting pip's temp files to the main disk instead of RAM-backed /tmp.

Side note: the generic aarch64 torch wheel pulled in a full set of unused NVIDIA CUDA libraries (cublas, cudnn, nccl, triton, etc.) โ€” several hundred MB of dead weight on a Pi with no NVIDIA GPU. Left in place since disk space wasn't tight (41GB free afterward), but worth knowing if space becomes a concern later โ€” a CPU-only torch build would avoid this.

3. USB serial permissions

sudo usermod -a -G dialout $USER
sudo reboot

Required so the Pi user can access the arm's USB-serial controller boards without sudo. Confirmed via groups after reboot.

4. Transferring calibration (no motor re-init needed)

Key insight confirmed: motor IDs live on the servos themselves (EEPROM), not on the host computer โ€” so no re-running lerobot-setup-motors was needed when switching machines.

Calibration files, however, are host-local JSON, so these were copied from Mac to Pi via scp:

# from the Mac:
scp ~/.cache/huggingface/lerobot/calibration/robots/so_follower/my_follower_arm.json brain@lerobot-vision:~/
scp ~/.cache/huggingface/lerobot/calibration/teleoperators/so_leader/my_leader_arm.json brain@lerobot-vision:~/

# on the Pi:
mkdir -p ~/.cache/huggingface/lerobot/calibration/robots/so_follower/
mkdir -p ~/.cache/huggingface/lerobot/calibration/teleoperators/so_leader/
mv ~/my_follower_arm.json ~/.cache/huggingface/lerobot/calibration/robots/so_follower/
mv ~/my_leader_arm.json ~/.cache/huggingface/lerobot/calibration/teleoperators/so_leader/

5. Port identification

lerobot-find-port

(Different naming convention from macOS's /dev/tty.usbserial-XXXX, as expected on Linux.)

6. First teleoperation test on the Pi โ€” โœ… success

lerobot-teleoperate \
    --robot.type=so101_follower \
    --robot.port=/dev/ttyUSB0 \
    --robot.id=my_follower_arm \
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyUSB1 \
    --teleop.id=my_leader_arm

Steady 60Hz loop, calibration loaded correctly from the transferred JSON files, follower mirrored leader movement with no issues (aside from an expected small "snap to position" jump at the very start, caused by the arm being repositioned since the last calibration โ€” not an error).

7. Camera discovery โ€” initial confusion, resolved

lerobot-find-cameras opencv

Reported 6 "cameras," and all 6 failed to deliver a frame. Investigated with:

v4l2-ctl --list-devices
lsusb

Findings:

Verified the real webcam works independently of LeRobot:

ffplay /dev/video0

โœ… Connected successfully, streamed 640ร—480 @ 30fps live video.

8. Combining arm + camera in LeRobot

First attempt with --display_data=true failed with two separate issues:

  1. ImportError: 'rerun-sdk' is required โ†’ installed via pip install 'lerobot[viz]'
  2. After installing, hit a headless-session error: winit EventLoopError ... neither WAYLAND_DISPLAY nor DISPLAY is set โ€” expected, since this was run over SSH with no graphical display attached.

Dropped --display_data=true and tested camera + arm together directly:

lerobot-teleoperate \
    --robot.type=so101_follower \
    --robot.port=/dev/ttyUSB0 \
    --robot.id=my_follower_arm \
    --robot.cameras='{"main": {"type": "opencv", "index_or_path": "/dev/video0", "width": 640, "height": 480, "fps": 30}}' \
    --teleop.type=so101_leader \
    --teleop.port=/dev/ttyUSB1 \
    --teleop.id=my_leader_arm

First run: camera connection timed out (TimeoutError after 1000ms) โ€” despite ffplay proving the camera works. Likely a first-connection initialization quirk (a benign imread_ warning appeared, suggesting OpenCV briefly mishandled the device path on first touch).

Second run, identical command, no changes: โœ… succeeded โ€”

OpenCVCamera(/dev/video0) connected.
my_follower_arm SOFollower connected.
Teleop loop time: 16.76ms (60 Hz)

Ran cleanly for ~30 seconds until manually stopped with Ctrl+C. Lesson for next time: if the camera fails to connect on the first attempt, simply retry the same command โ€” this appears to be a one-off USB/driver settling quirk, not a fragile or unreliable setup.

Final validated state

ComponentStatus
LeRobot installed on Pi 4 (1GB)โœ…
Motor IDs (both arms)โœ… (unchanged from Mac setup โ€” stored on servos)
Calibration (both arms)โœ… (transferred as JSON, no recalibration needed)
Follower port/dev/ttyUSB0
Leader port/dev/ttyUSB1
WebcamTrust Webcam @ /dev/video0, 640ร—480 @ 30fps
Teleoperation (arm only)โœ… 60Hz, stable
Teleoperation + camera togetherโœ… 60Hz, stable (after one retry)
Visualization (rerun display)Not usable over headless SSH session โ€” needs a local display/desktop session to test

Roadmap status update

Notes / open items for next time

Continues on from the Pi 4 "Brain" Setup (Modern Stack) session. See also the full project roadmap.