Maker ยท Technical Deep-Dive

SO-101 Rig Migration: Mac โ†’ Raspberry Pi 5

Fixing a root cause instead of routing around it โ€” plus a debugging arc that found the opposite fix from the one assumed going in

Result: โœ… Migration validated โ€” first real recording session completed on the Pi 5, both cameras on dedicated USB 3.0 ports, arm controller on the hub. The camera-crash problem that motivated this move is gone, and gone for a real, root-cause reason, not a workaround.

The problem that started this

Symptom: cameras worked fine standalone, but crashed during an actual rollout with "camera too slow."

Root cause, found after ~2 hours: the USB hub wasn't communicating reliably under real load. Switching ports helped but remained unstable.

Underlying reason: the Mac has no native USB-A ports โ€” both cameras and the arm's serial controller all funneled through external hubs, competing for shared bandwidth. Not a camera problem. A Mac-connectivity problem.

The decision: migrate to a Pi 5 (4GB)

Why this genuinely fixes the root cause, not just works around it: the Pi 5 has 4 native USB ports (2ร— USB 3.0, 2ร— USB 2.0) โ€” enough for main camera, gripper camera, and the arm's serial controller to each get a direct, dedicated port, no hub aggregation at all. This removes the actual bottleneck rather than routing around it.

Bonus, not yet acted on: the Pi 5 also has the AI Camera (IMX500) โ€” a CSI camera, entirely separate from the USB bus. Headroom for a future third camera angle with zero impact on the USB situation.

Pre-migration state

Migration day: the real debugging arc

1. Motor 5 intermittently missing from the bus scan

Traced to a marginal physical connector, confirmed by its inconsistent appear/disappear pattern โ€” not a fixed hardware fault.

2. The servo controller needed the hub, not a direct port โ€” the opposite of the camera fix

Moving the servo controller from a direct Pi USB port to the USB hub stabilized the connection completely. Direct Pi USB ports proved unstable for this specific low-bandwidth serial connection โ€” the opposite arrangement from what the camera situation would suggest.

Hubs can help stabilize/condition a low-bandwidth signal even while being exactly wrong for high-bandwidth video. This wasn't predicted going in โ€” it was found through today's actual debugging, and it's a genuinely useful distinction for any future USB troubleshooting on this rig: "hub vs. direct" isn't a universal rule, it depends on what's actually being carried.

3. "Overload error" on shutdown โ€” benign, not a fault

Cycled across different motors (4, then 5, then 2, then 1) on different runs. Diagnosed as the expected, safe protective response: whichever joint happens to be under gravitational load at the exact moment torque disables triggers the error. Confirmed benign by the pattern itself (a different motor each time, not one recurring failure point) and by three consecutive fully clean, error-free active teleoperation runs (18s, 31s, 35s, all at a rock-solid 59.73Hz).

4. Recalibrated both arms as a precaution

Genuinely healthy ranges on every joint โ€” no signs of mechanical damage from the overload scares.

5. The real scare: the follower arm went completely dead

After all the software fixes above, the follower arm showed zero response and zero torque โ€” didn't move, didn't even resist gravity.

Root cause: the follower's separate power supply (an old laptop adapter, chosen for its higher current) had been physically unplugged โ€” left disconnected during the day's cable reorganization while moving the rig to the Pi 5. The data/USB connection was fine the entire time, which is exactly why software reported zero errors โ€” the arm simply had no power to move anything.

Fix: reconnect the power cable. Confirmed with a full clean 52-second teleoperation test โ€” completely stable, zero issues.

General lesson: if an arm connects cleanly, times cleanly, and reports no errors, but simply doesn't move โ€” check power before touching any code. A healthy communication bus and healthy motor power are two completely separate things, and a bus can look perfectly fine while the motors have nothing to actually work with.

Audio side quest: episode-boundary cues

spd-say initially failed with No such file or directory. Root cause, worked through in order:

  1. speech-dispatcher wasn't installed at all โ†’ installed via apt
  2. Default audio backend is PulseAudio, which wasn't installed โ†’ espeak-ng "test" (bypassing speech-dispatcher entirely) also produced no sound, isolating the problem to ALSA output routing rather than speech-dispatcher itself
  3. The Pi 5 has two independent HDMI audio devices (card 0: vc4hdmi0, card 1: vc4hdmi1) โ€” one per micro-HDMI port. Only the port with a display actually attached outputs audio.
  4. speaker-test -D plughw:0,0 produced audible sound โ†’ card 0 is the correct, connected device
  5. speaker-test -D plughw:1,0 failed with error -524 โ€” expected, since nothing is plugged into that port
sudo apt install -y speech-dispatcher espeak-ng

Set card 0 as the ALSA default in ~/.asoundrc:

defaults.pcm.card 0
defaults.ctl.card 0

Confirmed working:

espeak-ng "test"
spd-say "test"
Lesson for any Pi 5 audio issue going forward: if sound hangs (not errors outright), suspect the ALSA/PulseAudio backend before the application layer. And on Pi 5 specifically, always confirm which HDMI card is actually connected via speaker-test on each one โ€” the two ports are independent devices, not a single fallback pair.

Working configuration

Follower arm: /dev/ttyUSB1 (via USB hub)
Leader arm:   /dev/ttyUSB0 (via USB hub)
Main camera (overhead):  /dev/video2, direct USB 3.0 port
Gripper camera (wrist):  /dev/video0, direct USB 3.0 port

Port numbers can shift on reboot/reconnect โ€” always reconfirm with lerobot-find-port and v4l2-ctl --list-devices rather than assuming these persist.

Result: migration validated

Successfully completed a real recording session (episode 0, 12.8s, clean 29.93Hz) using both cameras on dedicated USB 3.0 ports and the arm controller via the hub. The original goal โ€” proving the Pi 5's native ports would fix the Mac's USB-hub camera instability โ€” is confirmed.

What's next

A full validation dataset was recorded on this rig and transferred to the ML Desktop for training โ€” full details, including a real gotcha in how lerobot-record names its output folder, are in ML Desktop: LeRobot Setup & First Pi 5 Dataset.