stefanopineda.com / robot_setup
Minimal feasible host for a remote SO-ARM101 (BusLinker / Waveshare bus-servo adapter + STS3215) running Hugging Face LeRobot. The host computer is the brain; the servo board is only a USB power + serial bridge.
For SO-ARM101 + LeRobot you are not programming an Arduino-class MCU. You need a host PC next to the arm that runs Python/PyTorch, talks USB-serial to the BusLinker (or Waveshare) board, and streams camera frames into a policy.
Earlier generic “MCU + ROS” advice assumed a programmable real-time controller on the arm. SO-ARM101 does not work that way. Official LeRobot hardware docs treat the arm as Feetech bus servos behind a USB MotorBus adapter, with all algorithms on the host.
| Topic | Generic MCU robot | SO-ARM101 + LeRobot |
|---|---|---|
| Brain | Split: PC high-level, MCU low-level | Host PC is the brain (Python / PyTorch / LeRobot) |
| Board on the arm | Arduino / STM32 / etc. running firmware you write | BusLinker V3 / Waveshare bus servo adapter — USB bridge + power |
| Motors | Often pulse/dir or CAN drivers | STS3215 daisy-chained bus servos (IDs + baudrate in EEPROM) |
| Software stack | ROS 2 + custom serial often default | Hugging Face LeRobot + Feetech extra (pip install -e ".[feetech]") |
| Typical tasks | Scripted pick + classical CV / YOLO | Leader teleop → dataset → imitation learning policy → replay on follower |
| USB ports | 1 MCU + cameras | Often 2 adapters (leader + follower) + 1–2 USB cameras |
| Fail-safe | MCU watchdog can stop motors if PC dies | Weaker by default — host process + servo timeouts; plan power kill + supervision |
For remote SO-ARM101 work (teleop, data collection, then learned policies), the host loop is roughly:
/dev/ttyACM* or /dev/ttyUSB*)Size the host for the heaviest job you will run at the robot site. Teleop recording and policy inference have very different GPU needs.
| Tier | Workload | Typical hardware | Good for |
|---|---|---|---|
| T0 — Teleop / record only | Leader→follower streaming, USB cameras, write LeRobot episodes | Laptop, N100/N305 mini-PC, Pi 5 8 GB — GPU optional | Collecting demos; no onboard neural policy yet |
| T1 — Policy inference (recommended min) | ACT / diffusion-style policies, 1–2 cams @ ~30 Hz class loops | RTX 3050 6–8 GB or Jetson Orin Nano 8 GB, 16–32 GB RAM | Autonomous tabletop tasks after training elsewhere or lightly on-box |
| T2 — Larger VLAs / multi-cam | SmolVLA-class, heavier vision backbones, dual HD streams | RTX 4060–4070 8–12 GB or Orin NX 16 GB, 32 GB RAM | Richer policies without cloud round-trips |
| T3 — Training / foundation finetune | Long policy training, GR00T-class finetunes | RTX 4090 / A6000 / cloud (often 16–24 GB+ VRAM; some recipes ~25 GB) | Train off-site; copy checkpoints to the robot host |
Two equal “minimum viable” hosts for SO-ARM101 autonomy. Teleop-only can go cheaper (Path C).
≈ $500–850 used · $750–1,100 new
≈ $250–600 depending on kit + storage
≈ $0 (existing laptop) – $220 — not the long-term autonomy host
| Item | Spec | Notes |
|---|---|---|
| Host PC | Path A or Path B above | Lives next to the arm (USB tether) |
| GPU (Path A) | RTX 3050 6/8 GB (or 4060) | NVIDIA Linux drivers + CUDA for PyTorch |
| RAM / SSD | 32 GB / 1 TB NVMe | Datasets are video-heavy |
| Bus servo adapter(s) | BusLinker V3 or Waveshare Bus Servo Adapter | Usually included with kit; one per arm bus |
| Arm kit | SO-ARM101 follower (± leader) | STS3215 bus servos, kit PSU(s) |
| Cameras | 1–2× USB3 720p/1080p UVC | Scene + optional wrist view |
| USB cables | Short, data-rated | Avoid unpowered multi-hop hubs when possible |
| Network | Gigabit Ethernet | Tailscale remote access |
| Power kill | Switched strip / E-stop on servo PSU | Independent of host OS health |
Assume Ubuntu 24.04 LTS is already installed on the host next to the arm
(Desktop or Server). Run as a user with sudo. Commands are copy-pasteable.
# Update OS
sudo apt update && sudo apt -y full-upgrade
sudo apt -y install curl wget git build-essential \
ca-certificates gnupg lsb-release software-properties-common \
htop tmux vim net-tools usbutils v4l-utils \
python3-pip python3-venv python3-dev \
openssh-server
# Optional: reboot after kernel upgrade
sudo reboot
# Set a clear hostname (shows up in Tailscale / mDNS)
sudo hostnamectl set-hostname robot-pc
echo "10.0.0.1 robot-pc" | sudo tee -a /etc/hosts # adjust if needed
# Time sync (critical for logs + certs)
timedatectl
sudo timedatectl set-ntp true
# Security updates
sudo apt -y install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# Recommended: Ubuntu’s proprietary driver meta-package
ubuntu-drivers devices
sudo ubuntu-drivers autoinstall
# or pin a known-good series, e.g.:
# sudo apt -y install nvidia-driver-550
sudo reboot
# Verify
nvidia-smi
# Expect GPU name, driver version, CUDA version listed
nvidia-smi works, install CUDA toolkit only if you compile custom ops.
Many vision stacks ship wheels that bundle CUDA runtime — prefer those first.
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
# log out/in for group membership
# NVIDIA Container Toolkit (GPU in containers)
distribution=$(. /etc/os-release;echo $ID$VERSION_ID)
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | \
sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt -y install nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
# Smoke test
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
sudo apt -y install v4l-utils python3-opencv
v4l2-ctl --list-devices
python3 - <<'PY'
import cv2
cap = cv2.VideoCapture(0)
ok, frame = cap.read()
print("camera_ok=", ok, "shape=", None if frame is None else frame.shape)
cap.release()
PY
# Serial access for Feetech MotorBus adapters
sudo usermod -aG dialout "$USER"
sudo usermod -aG video "$USER"
sudo usermod -aG plugdev "$USER"
# CRITICAL on many Ubuntu installs: brltty claims CH340/CH341 USB-serial IDs
# used by some bus servo boards and blocks /dev/ttyUSB* or ttyACM*
sudo apt -y remove brltty
# reboot or replug adapters after removal
# Temporary access (until groups apply / udev rules land)
# sudo chmod 666 /dev/ttyACM0 /dev/ttyACM1
# Stable symlinks — replace vendor/product from `lsusb` after plugging adapters
sudo tee /etc/udev/rules.d/99-so101-motorbus.rules <<'EOF'
# Example: adjust idVendor/idProduct per `lsusb` for each adapter
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="so101_follower", MODE="0666"
# Second adapter example — different serial or physical port mapping as needed
# SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", ATTRS{serial}=="XXXX", SYMLINK+="so101_leader", MODE="0666"
EOF
sudo udevadm control --reload-rules
sudo udevadm trigger
# Re-login, then: ls -l /dev/ttyACM* /dev/ttyUSB* /dev/video*
You are not on site. The host owns USB to the arm — treat it as a hardened jump host. Remember: there is no independent MCU application brain to “fail safe” for you.
sudo systemctl enable --now ssh
# On your laptop: ssh-copy-id user@robot-pc-local-ip
# Then on robot PC, prefer keys only:
sudo sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
sudo sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sudo systemctl restart ssh
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --ssh
# authenticate via the printed URL from a browser on any device
tailscale status
tailscale ip -4
tailscale ssh can replace opening port 22 publicly. WireGuard self-host is fine if you prefer.
sudo apt -y install ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
# If Tailscale handles SSH, you can skip public 22 entirely.
# Allow SSH only if needed on LAN:
sudo ufw allow from 192.168.0.0/16 to any port 22 proto tcp
sudo ufw allow in on tailscale0
sudo ufw enable
sudo ufw status verbose
# Simple low-latency path for debugging (LAN/VPN only):
# MediaMTX / go2rtc / WebRTC stacks are preferable to raw open RTSP on the internet.
# Example placeholder: use your chosen streamer bound to Tailscale IP only.
ip -4 addr show tailscale0
Follow the official guides for exact flags as versions move: LeRobot install, SO-101 assembly / motors / calibrate. Below is a practical host-side sequence after Ubuntu/JetPack basics.
# Prefer a fresh conda/mamba or venv per LeRobot docs for your platform
cd ~
git clone https://github.com/huggingface/lerobot.git
cd lerobot
# Install package + Feetech motors support (required for SO-ARM101)
pip install -e ".[feetech]"
# On x86_64 Ubuntu with NVIDIA GPU, ensure torch sees CUDA:
python -c "import torch; print(torch.__version__, torch.cuda.is_available())"
torch.cuda.is_available() is false, fix drivers/torch before debugging the arm.
# Power the adapter(s) with the kit PSU, then:
lerobot-find-port
# On Linux you will often see /dev/ttyACM0, /dev/ttyACM1
# Use chmod or dialout group if permission denied:
# sudo chmod 666 /dev/ttyACM0
# One motor at a time per official SO-101 guide — example shapes:
lerobot-setup-motors \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0
lerobot-calibrate \
--robot.type=so101_follower \
--robot.port=/dev/ttyACM0 \
--robot.id=my_follower_arm
# Leader (if present) uses --teleop.type=so101_leader and its own port/id
# Then follow the imitation-learning real-robot tutorial for teleop + record + train + eval
Classic YOLO pick-and-place is not the primary SO-ARM101 path. LeRobot policies usually consume raw camera frames + joint state end-to-end. You can still add OpenCV/YOLO for debugging or hybrid stacks, but size the GPU for the policy first.
lsusb, dmesg -w, lerobot-find-port. Remove brltty if the device never appears.
lerobot-setup-motors one motor at a time (official sequence). Optional Windows Feetech GUI for debug only.
lerobot-calibrate for follower and leader so joint zeros match.
| Check | Command / action | Pass criteria |
|---|---|---|
| GPU (if Path A/B autonomy) | nvidia-smi / torch.cuda.is_available() |
True / GPU listed |
| Cameras | v4l2-ctl --list-devices + OpenCV grab |
Stable frames |
| MotorBus | lerobot-find-port |
Port found; survives replug |
| Motors | setup-motors + calibrate | IDs unique; calibration saved |
| Teleop | Leader moves follower (if dual arm) | Smooth tracking, no USB drops |
| Remote SSH | tailscale ssh user@robot-pc |
Login without public port 22 |
| Power kill | Cut servo PSU while host stays up | Arm loses actuation immediately |
| Reboot persistence | sudo reboot |
Drivers, dialout, Tailscale, ports return |
# Prefer manual start for motion-capable processes.
# If you must daemonize inference, gate on a
# hardware enable or explicit env flag.
[Service]
Type=simple
User=YOUR_USER
WorkingDirectory=/home/YOUR_USER/lerobot
# ExecStart=... only after dry-run validation
Restart=no
~/.cache/huggingface/lerobot/calibration/SO-ARM101 makes the host PC the entire brain and the BusLinker a USB/power bridge to STS3215 servos under Hugging Face LeRobot. For remote object manipulation, place an RTX 3050-class or Jetson Orin Nano next to the arm, install Ubuntu/JetPack, Feetech-enabled LeRobot, cameras, and Tailscale. Use a light machine only for teleop/data collection. Train big models off-site. Compensate for the missing independent MCU with a physical servo power kill and local supervision on first remote runs.