Я собрал SO-100 и попытался сделать его умным: LeRobot, π0 и GR00T N1.6-3B
Мой честный опыт сборки SO-100 и построения end-to-end пайплайна на Hugging Face LeRobot: где ломается реальность, сколько это стоит, и как я готовлюсь к GR00T N1.6-3B.
Я хотел "дешевого робота с мозгами". В итоге понял: мозги начинаются не с модели, а с дисциплины пайплайна. LeRobot дал мне каркас end-to-end (calibrate → record → train → rollout), SO-100 дал дешевое железо, а дипломные эксперименты дали трезвую статистику: ~8–12 мм точности, ~2–4 мм повторяемости и ~5–7 Гц частоты управления на ноутбучном GPU.
С чего все началось
Если коротко: я хотел увидеть, может ли доступный манипулятор быть чем-то большим, чем "PWM-игрушка". Меня интересовало не просто движение по заранее прошитым траекториям, а управление, которое можно:
- научить через демонстрации;
- оценить цифрами (точность, повторяемость, success rate);
- улучшать итерациями, не переписывая систему каждый раз заново.
В дипломе я собрал прототип SO-100 и запустил VLA-управление на базе π0 (через LeRobot/OpenPI).
Сейчас я хочу поднять планку и аккуратно подойти к GR00T N1.6-3B.
Почему именно LeRobot
Я выбрал Hugging Face LeRobot, потому что он не "про одну модель", а про процесс. В документации четко прописан путь, который повторяется для любого железа:
- калибруешь руки;
- записываешь датасет в
LeRobotDataset; - тренируешь policy;
- делаешь rollout и собираешь eval.
И это важно: когда у тебя робот реальный, хаос от "скрипт на скрипте" убивает больше времени, чем любая ошибка в архитектуре.
Модельный слой: что я беру из 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. Подготовка: не пропусти это
- Авторизоваться в Hugging Face CLI, чтобы датасеты спокойно пушились на Hub.
- Зафиксировать
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
Мой текущий план выглядит так:
- LeRobot pipeline стабилен.
- Датасет повторяемый.
- Baseline policy дает предсказуемый результат.
- Только после этого я меняю policy слой на GR00T.
В LeRobot для GR00T N1.5 это делается через policy.type=groot (пример есть в доке). Я использую это как "контрольную точку", а N1.6-3B держу как следующий этап.
Если хочешь сухой чеклист именно по запуску, без истории: SO-100 + GR00T N1.6: мой пошаговый playbook.
Три вещи, которые я бы сделал раньше
- Ввел бы жесткий pre-flight checklist и не делал бы "еще один быстрый запуск" без него.
- Логировал бы температуру/нагрузку сервоприводов в каждой сессии.
- Разделил бы режимы:
record,replay,evalи не мешал бы их в один день.
Почему это вообще имеет смысл
Смысл этого проекта для меня был не в "еще одном видосе с роборукой", а в том, чтобы собрать воспроизводимый цикл: данные, обучение, rollout, итерации. Когда цикл становится предсказуемым, ты перестаешь гадать и начинаешь улучшать систему.
Мой рабочий playbook (placeholder)
Положил в /public как быстрый артефакт. Позже заменю на PDF с реальными фото, схемами и конфигами.
TXT • ~0.5 KB
Финальный вывод
LeRobot оказался не "еще одной библиотекой", а рамкой, которая заставляет тебя работать как инженер: фиксировать IDs, калибровать, валидировать данные и только потом звать большие модели.
А SO-100 оказался отличным стендом именно потому, что он безжалостно показывает слабые места: люфты, jitter, перегрев, latency. Эти проблемы не исчезают от смены policy. Их надо решать процессом и железом. И только потом - GR00T.
Стек