6 февр. 2026 г.
RoboticsLeRobotSO-100VLAGR00THugging FaceNVIDIA

Я собрал SO-100 и попытался сделать его умным: LeRobot, π0 и GR00T N1.6-3B

Мой честный опыт сборки SO-100 и построения end-to-end пайплайна на Hugging Face LeRobot: где ломается реальность, сколько это стоит, и как я готовлюсь к GR00T N1.6-3B.

С чего все началось

Если коротко: я хотел увидеть, может ли доступный манипулятор быть чем-то большим, чем "PWM-игрушка". Меня интересовало не просто движение по заранее прошитым траекториям, а управление, которое можно:

  • научить через демонстрации;
  • оценить цифрами (точность, повторяемость, success rate);
  • улучшать итерациями, не переписывая систему каждый раз заново.

В дипломе я собрал прототип SO-100 и запустил VLA-управление на базе π0 (через LeRobot/OpenPI). Сейчас я хочу поднять планку и аккуратно подойти к GR00T N1.6-3B.

Почему именно LeRobot

Я выбрал Hugging Face LeRobot, потому что он не "про одну модель", а про процесс. В документации четко прописан путь, который повторяется для любого железа:

  • калибруешь руки;
  • записываешь датасет в LeRobotDataset;
  • тренируешь policy;
  • делаешь rollout и собираешь eval.

И это важно: когда у тебя робот реальный, хаос от "скрипт на скрипте" убивает больше времени, чем любая ошибка в архитектуре.

SO-100 в leader-follower конфигурации (временное внешнее фото из open-source репозитория, позже заменю на свои)

Модельный слой: что я беру из GR00T N1.6-3B

Мой целевой ориентир по foundation-политике сейчас: nvidia/GR00T-N1.6-3B. Если переводить модельную карточку на инженерный язык:

  • входы: изображения, текстовая инструкция, проприоцепция;
  • выходы: непрерывные действия;
  • action-head сделан через flow matching (родственник диффузии) и генерирует chunk действий.

Важно: на стороне LeRobot сейчас хорошо задокументирована интеграция GR00T N1.5 (док). N1.6-3B существует как отдельный релиз и тулчейн NVIDIA, и я отношусь к нему как к следующей ступени. Мой принцип здесь простой: сначала стабилизировать контур и датасет, потом менять policy.

Реальность SO-100: цифры из диплома

Собранный прототип дал мне такие диапазоны (и это важно, чтобы не ждать от дешевого стенда невозможного):

  • точность позиционирования: ~8–12 мм;
  • повторяемость: ~2–4 мм;
  • успешность базовых задач: 65–93%;
  • частота управления на ноутбуке (RTX 3060 Laptop): ~5–7 Гц.

Эти цифры не стыдные. Они "честные" для 3D-печатной механики и сервоприводов среднего класса. И именно они определяют, какие задачи в принципе имеют смысл.

Step-by-step: как я выстроил пайплайн (и как бы делал это заново)

Ниже я специально пишу так, чтобы можно было повторить. Где возможно, опираюсь на официальные команды из документации LeRobot.

Шаг 0. Подготовка: не пропусти это

  1. Авторизоваться в Hugging Face CLI, чтобы датасеты спокойно пушились на Hub.
  2. Зафиксировать robot.id и teleop.id и не менять их между сессиями.
huggingface-cli login --token ${HUGGINGFACE_TOKEN} --add-to-git-credential
HF_USER=$(huggingface-cli whoami | head -n 1)

Шаг 1. Установка LeRobot (и Feetech)

Для SO-100 документация просит поставить Feetech SDK:

pip install -e ".[feetech]"

Если ты нацеливаешься на GR00T, то у LeRobot есть отдельная инструкция по lerobot[groot] и flash-attn (CUDA-only). Я не копирую ее целиком, но держу ссылку: GR00T N1.5 в LeRobot.

Шаг 2. Найти порты и настроить моторы

SO-100 неприятен тем, что к мотор-коннекторам потом трудно подлезть. Поэтому сначала:

  • найти порты (команда из доков SO-100);
  • выставить ID/baudrate на каждом моторе;
  • и только потом собрать/дэйзи-чейн.
lerobot-find-port
 
lerobot-setup-motors \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem585A0076841
 
lerobot-setup-motors \
  --teleop.type=so100_leader \
  --teleop.port=/dev/tty.usbmodem575E0031751

Шаг 3. Калибровка (это главный «ритуал»)

Калибровка критична, потому что без нее датасет становится "почти" правильным. А "почти" в обучении роботов означает "потратить неделю и не понять почему".

Команды из документации SO-100:

lerobot-calibrate \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem58760431551 \
  --robot.id=my_follower
 
lerobot-calibrate \
  --teleop.type=so100_leader \
  --teleop.port=/dev/tty.usbmodem58760431551 \
  --teleop.id=my_leader

Моя практика: я делаю калибровку одинаково каждый раз, и фиксирую "позы" как ритуал. Это единственный способ потом сравнивать эксперименты честно.

Шаг 4. Телеоперация и запись датасета

В tutorial LeRobot показан пример на SO-101, но сама схема одинаковая: follower + leader + камеры + --dataset.single_task.

python -m lerobot.record \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem585A0076841 \
  --robot.id=my_follower \
  --robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 1280, height: 720, fps: 30}}" \
  --teleop.type=so100_leader \
  --teleop.port=/dev/tty.usbmodem58760431551 \
  --teleop.id=my_leader \
  --display_data=true \
  --dataset.repo_id=${HF_USER}/so100_pickplace_v1 \
  --dataset.num_episodes=50 \
  --dataset.single_task="Pick the block and place it into the bin"

Здесь мой важный опыт из диплома: лучше 50 стабильных эпизодов в одинаковых условиях, чем 10 красивых, но разных "сюжетов". Разнообразие добавляется позже, когда базовый контур работает.

Шаг 5. Replay как sanity-check

Перед training я всегда делаю replay одного эпизода. Это дешевле любого обучения.

python -m lerobot.replay \
  --robot.type=so100_follower \
  --robot.port=/dev/tty.usbmodem585A0076841 \
  --robot.id=my_follower \
  --dataset.repo_id=${HF_USER}/so100_pickplace_v1 \
  --dataset.episode=0

Если replay выглядит плохо: дело почти всегда в калибровке, механике или данных. Не в модели.

Шаг 6. Обучение policy

В базовом tutorial LeRobot показан policy.type=act как старт. Я люблю начинать именно так, потому что это быстрый baseline и сразу показывает, жизнеспособен ли датасет.

python lerobot/scripts/train.py \
  --dataset.repo_id=${HF_USER}/so100_pickplace_v1 \
  --policy.type=act \
  --output_dir=outputs/train/act_so100_pickplace_v1 \
  --job_name=act_so100_pickplace_v1 \
  --policy.device=cuda \
  --wandb.enable=true

Дальше уже имеет смысл переходить к VLA-политикам, если baseline не разваливается.

Шаг 7. Где здесь GR00T N1.6-3B

Мой текущий план выглядит так:

  1. LeRobot pipeline стабилен.
  2. Датасет повторяемый.
  3. Baseline policy дает предсказуемый результат.
  4. Только после этого я меняю policy слой на GR00T.

В LeRobot для GR00T N1.5 это делается через policy.type=groot (пример есть в доке). Я использую это как "контрольную точку", а N1.6-3B держу как следующий этап.

Если хочешь сухой чеклист именно по запуску, без истории: SO-100 + GR00T N1.6: мой пошаговый playbook.

Три вещи, которые я бы сделал раньше

  1. Ввел бы жесткий pre-flight checklist и не делал бы "еще один быстрый запуск" без него.
  2. Логировал бы температуру/нагрузку сервоприводов в каждой сессии.
  3. Разделил бы режимы: record, replay, eval и не мешал бы их в один день.

Почему это вообще имеет смысл

Смысл этого проекта для меня был не в "еще одном видосе с роборукой", а в том, чтобы собрать воспроизводимый цикл: данные, обучение, rollout, итерации. Когда цикл становится предсказуемым, ты перестаешь гадать и начинаешь улучшать систему.

Мой рабочий playbook (placeholder)

Положил в /public как быстрый артефакт. Позже заменю на PDF с реальными фото, схемами и конфигами.

TXT~0.5 KB

Финальный вывод

LeRobot оказался не "еще одной библиотекой", а рамкой, которая заставляет тебя работать как инженер: фиксировать IDs, калибровать, валидировать данные и только потом звать большие модели.

А SO-100 оказался отличным стендом именно потому, что он безжалостно показывает слабые места: люфты, jitter, перегрев, latency. Эти проблемы не исчезают от смены policy. Их надо решать процессом и железом. И только потом - GR00T.

Визуализация rollout в LeRobot (временное внешнее изображение из репозитория)

Стек

PythonPyTorchLeRobotFeetech STS3215SO-100VLA