ВведениеВо второй части — практическая часть проекта: установка и первый запуск, эксперименты по созданию идеального промпта, а также попытки добавить «память» ВведениеВо второй части — практическая часть проекта: установка и первый запуск, эксперименты по созданию идеального промпта, а также попытки добавить «память»

End-to-End беспилотник на VLM в домашних условиях. Часть 2

2026/03/01 19:53
12м. чтение

Введение

Во второй части — практическая часть проекта: установка и первый запуск, эксперименты по созданию идеального промпта, а также попытки добавить «память» о прошлом шаге. Промпт теперь не содержит жестких инструкций поведения и работает быстрее! В конце - переход от симуляции к реальному роверу: сборка, настройка и проверка работы на железе.

Первая часть тут

А весь код тут

Установка и первый запуск на хосте

Установка происходит просто, командой ./host.sh устанавливается образ и запускается контейнер с Ubuntu 22.04 и ROS2 Humble. Далее в созданном контейнере нужно собрать пакеты ./b и активировать окружение . e:

./b && . e

Проверить работу симулятора и его связь с ROS2 можно, запустив управление с помощью клавиатуры:

ros2 launch robot sim_teleop.py

За контроль как виртуального, так и реального робота отвечает универсальный класс BaseDriver. Как было сказано в первой части, управление осуществляется командами, задающими постоянную скорость. Ее величину нужно подобрать под конкретного робота и тип поверхности. Самое главное - это сбалансировать скорость движения и скорость обработки кадра, чтобы робот не успевал уехать слишком далеко пока ждёт ответа на прошлый кадр. Если не получается достаточно уменьшить скорость (робот просто перестаёт двигаться), ограничиваем максимальное число тактов в ожидании команды max_steps_without_command.

Управление с VLM

Подготовка

Теперь настало время заменить ручное управление на команды визуально-языковой модели (VLM). Сперва нужно установить ollama и скачать нужную модель:

ollama run qwen2.5vl:latest

В последующие разы скачанная модель будет запускаться автоматически при старте автопилота. Как было сказано в первой части, оптимальным выбором стал qwen2.5vl:latest (7b), он стоит в launch-файлах по умолчанию.

За отправку запросов к ollama и обработку её сообщений отвечает нода vlm_control. В файле resource/vlm_prompt.txt записана управляющая инструкция (промпт), специально подобранный для 7b модели. Он загружается при старте ноды и его можно легко редактировать. Промпт вместе с кадром отправляется в модель, из полученного от модели ответа извлекается выбранная команда управления (если их несколько, то берётся последняя), команда отправляется роботу. Весь пайплайн одной командой:

ros2 launch robot vlm_control_sim.py

Чтобы показать, почему итоговый вариант промпта именно такой, рассмотрим еще несколько его вариантов, начиная с самого простого, постепенно добавляя нужные элементы.

Управление в один токен

Как известно, в авторегрессионных VLM основное время инференса тратится на генерацию ответа: каждый следующий токен вычисляется отдельным шагом декодера. В результате именно длина ответа, а не размер кадра или промпта, становится главным фактором задержки. По этой причине первое желание - генерировать ответ как можно короче, в пределе только управляющую команду:

You control a wheel robot. Available commands to robot: <FORWARD> - slowly go forward <RIGHT> - turn right without moving <LEFT> - turn left without moving <BACK> - slowly go back <STOP> - stay Your goal: Using available commands to correct your direction get close to WHITE BALL as much as possible and touch it. What available command to choose to achieve the goal? Answer in short!

На симуляции, показанной ниже, видно, что в плане скорости удалось добиться поставленной цели - 2-3 кадра в секунду уже неплохо (см. счетчик в верхнем левом углу консоли). VLM отвечает одной лишь командой без генерации лишних токенов, как от нее и требовалось. Однако ответы модели совершенно бессвязные:

Управление одной командой
Управление одной командой

Цепочка рассуждений

А вот если позволить модели немного «подумать», можно получить более осознанное поведение. В нашем случае нужно просто убрать из промпта требование отвечать коротко:

You control a wheel robot. Available commands to robot: <FORWARD> - slowly go forward <RIGHT> - turn right without moving <LEFT> - turn left without moving <BACK> - slowly go back <STOP> - stay Your goal: Using available commands to correct your direction get close to WHITE BALL as much as possible and touch it. What available command to choose to achieve the goal?

Избавившись от навязанных ограничений, модель qwen2.5vl начинает сама создавать цепочки рассуждений в процессе генерации ответа:

Управление через генерацию рассуждений
Управление через генерацию рассуждений

Ответы стали заметно длиннее, существенно снизилась скорость реакции. Но хорошо заметно, что у робота уже что-то начинает получаться. Он «осознает» происходящее и пытается справиться с задачей…но можно лучше!

Оптимальная инстукция

Здесь будет приведен финальный вариант промпта, который очень хорошо работает для данной задачи:

You control a wheel robot. Available commands to robot: <FORWARD> - slowly go forward <RIGHT> - turn right without moving <LEFT> - turn left without moving <BACK> - slowly go back <STOP> - stay Your goal: Using available commands to correct your direction get close to WHITE BALL as much as possible and touch it. - Describe in new line started with ???: Where WHITE BALL located now relative to robot and what approximate distance to it? - If you don't see WHITE BALL - move back. - Answer in new line started with >>>: What available command to choose to achieve the goal?

Основное отличие - указание перед тем как дать ответ кратко описать сцену. Знаки вопросов при этом выполняют чисто техническую функцию, о которой будет рассказано ниже. Подсказка двигаться назад в случае если цели не видно - вспомогательный, но очень полезный функционал. Без него задача следования за целью выполняется, но при потере цели из виду робот просто останавливается. Символ «>>>» и дополнительные указания начинать с новой строки так же не являются обязательными, но, случайно или нет, с ними работает чуть стабильнее и быстрее. Почему? Это та самая необъяснимая «магия» промптинга, которая способна отбить любое желание с ним работать - каждое слово или символ могут полностью сломать поведение модели без какой либо логики и очевидных причин. Возможно, такая нестабильность конкретно в данной задаче - следствие попыток использовать очень короткие промпты, оптимальные с точки зрения скорости обработки и эффективности. Возможно я просто забыл в конце добавить слово «пожалуйста».

Но зато, посмотрите как четко и без прерываний робот справляется с задачей (видео ускорено):

Оптимальный промпт
Оптимальный промпт

Ответы компактные, но содержат всё необходимое для принятия правильного решения.

Подсказка ехать назад при потере объекта из вида также хорошо работает, не мешая основной логике:

Простой поиск объекта
Простой поиск объекта

К сожалению, провести аккуратное «ablation study» и честно оценить вклад каждой строки/формулировки промпта не получилось: модель слишком чувствительна к мелким правкам текста. Это в целом типичный эффект для глубокого обучения — небольшие изменения входа могут заметно менять выходной сигнал. В компьютерном зрении похожая чувствительность проявляется, например, когда слабый шум изображения приводит к заметным колебаниям координат боксов детектора (YOLO) или границ сегментационных масок. У языковых моделей вход дискретный (токены вместо пикселей), но принцип тот же: одна «невинная» правка в промпте способна заметно перестроить траекторию генерации и как следствие, итоговый ответ. Тем не менее, я вкратце приведу базовое описание работы VLM, которое в целом объясняет полученные результаты.

Интерпретация результатов

Проведённые эксперименты - это наглядная демонстрация сразу двух тесно связанных феноменов: обучения по контексту (in-context learning, ICL) и цепочки рассуждений (chain of thought, CoT) как частного случая ICL.

Иными словами, у универсальной большой VLM нет явно выученной функции вида «картинка → команда», как это бывает в специализированных end-to-end моделях управления. Зато в процессе обучения в её параметрах закрепились более общие и декомпозированные знания, распределённые между модальностями:

  • в визуальной части — навыки восприятия, например задачи вида «обнаружь объект на изображении», «определи его положение в кадре», «опиши сцену»;

  • в языковой части — абстрактные правила и причинно-следственные зависимости, такие как «если цель находится слева — следует повернуть налево», «если это препятствие — лучше объехать его справа».

Когда мы заставляем модель отвечать на эти вопросы последовательно, мы фактически вынуждаем её разложить исходную задачу управления на несколько более простых подзадач: сначала восприятие и интерпретация сцены, затем — выбор действия на основе описанных правил. Соответствующие знания из визуальной и языковой подсистем не используются напрямую, а извлекаются в виде сгенерированных токенов, которые помещаются в общий контекст модели.

Далее, благодаря авторегрессионной природе генерации, эти токены начинают влиять на последующие шаги вывода. Модель использует собственные промежуточные результаты как входные данные для следующих предсказаний и тем самым совместно обрабатывает визуальную информацию и логические правила. В результате сложная задача «восприятие → решение → действие» сводится к последовательному решению элементарных шагов — предсказаний следующего токена.

Добавление информации о прошлом состоянии

До сих пор оставалась нереализованной одна «тудушка» из первой части - грамотное подключение истории состояний робота в тело промпта. Очередной "подход к снаряду" привел к более-менее рабочему варианту.

Неожиданная трудность, с которой пришлось столкнуться состояла в том, что при наивной реализации, когда весь предыдущий ответ модели или даже несколько таких подгружались прямо в промпт, это приводило к полной потере работоспособности - константный ответ при любой картинке на входе. Модель как будто переставала понимать, что от нее просят. Даже при специальных выделениях части с историей состояний и указаний на нее в промпте.

Решение простое - брать только самую нужную информацию: прошлое описание сцены и выбранную команду. С командой всё просто - её мы и так сами выбрали перед отправкой к роботу. Будем добавлять её в начало промпта на каждом следующем шаге с подписью, чтобы модель поняла что там ей подсовывают:

if self.add_last_command: prompt = f"Last step command: {self.history[-1]['last_command']}" + "\n" + prompt

Результат примерно такой: информация только об одной принятой моделью прошлой командой особо ей не помогает в принятии решений, а в текущей реализации даже мешает - появились ненужные остановки:

Промпт с информацией о прошлой команде
Промпт с информацией о прошлой команде

Почему изначальное поведение так сильно ломается сказать затрудняюсь, были опробованы разные несложные варианты, в том числе попросить модель проговорить самой свое прошлое движение, чтобы потенциально лучше его обработать в цепочке рассуждений.

Чтобы выделить в ответе модели описание сцены на прошлом шаге простой эвристики недостаточно. К счастью можно просто попросить модель сделать это самой в рамках её же ответа. В промпте для этого есть такой спецсимвол «???» (см. метод parse_scene_description). Краткое описание предыдущей сцены так же подается в начало промпта со специальной пометкой:

if self.add_last_scene: prompt = f"Last step scene: {self.history[-1]['last_scene']}" + "\n" + prompt

Любопытно, но эффект еще хуже чем при добавлении последней команды, робот просто не может думать:

2691845633df9ea0959991d61a65375a.gif

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

Про интерпретируемость VLM

Когда речь заходит о VLM, иногда звучит тезис (который и я пару раз слышал): их ключевое преимущество над «классическими» моделями зрения — способность объяснять свои решения, а значит они уже не «чёрные ящики». Здесь важна оговорка: модель не извлекает из себя скрытую «причину» решения — она генерирует правдоподобный текст про решение. Такое объяснение часто полезно как подсказка, куда смотреть и что проверить, но оно не гарантированно верно причинно: это может быть постфактум-рационализация, подогнанная под запрос и стиль промпта. Поэтому полностью интерпретируемой модель не становится, и иногда самым честным ответом на «почему?» остаётся просто «потому», даже если поверх этого можно придумать красивую историю.

Ниже пара показательных примеров. В промте была просьба сначала подробно описать возможные последствия выбора каждой из команд и на основе этих рассуждений принять решение. Как видно из логов, подобные объяснения не сильно помогают понять причину неудачи. На первом кадре модель почему-то решила, что ехать прямо - значит удаляться от объекта:

Робот неверно понимает логику движения
Робот неверно понимает логику движения

На втором кадре странная логика поворота вправо при расположении объекта слева:

Еще одна ошибка интерпретации в целом правильно распознанной сцены
Еще одна ошибка интерпретации в целом правильно распознанной сцены

Сборка робота

Платформа

В качестве платформы был выбран Waveshare 4WD rover. Классно выглядит, неплохо ездит, батарея вроде неплохая. Возможно лучше было поискать что-то еще проще, потому что этот ровер, например, всегда держит wifi точку доступа с одним открытым адресом для управления им через браузер - бесполезный функционал, запускает по умолчанию демо-скрипт,

Камера

Широкоугольная камера имеет угол обзора в 126 град. и это то что нужно, меньше не берите. Для таких камер часто надо докупать шлейф, потому что в 5-ой версии RaspberryPi уменьшили ширину гнезда для шлейфа камеры (зато их стало два).

Допники

Радиатор лучше брать родной, он не загораживает собой отверстия для монтажа платы.

Настройка робота

OC

Настройка самого робота будет зависеть от конкретной модели. Например, для waveshare ровера сначала нужно вручную отключить запускаемый при старте python-процесс, блокирующий камеру (официальная инструкция).

Настройка мини-ПК RaspberryPi (так же см. репозиторий) начинается с установки на нее Raspberry Pi OS. Я ставил bookworm версию, на всякий случай сохранил образ ТУТ.

Контейнер ROS2

После этого в домашнюю директорию нужно склонировать репозиторий и установить докер-контейнер с ROS2:

bash robot.sh

Первый запуск будет долгим, потому что скрипт пойдет настраивать драйвера для pi-камеры. Они легко ставятся в нативную Raspberry Pi OS, а вот в контейнер с обычной Ubuntu сделать это оказалось непросто. Пришлось собирать на месте из исходников. После завершения установки останется собрать внутри контейнера пакет robot и активировать среду двумя быстрыми командами:

./b && . e

Чтобы проверить работу камеры можно воспользоваться скриптом, который либо выведет окно, либо сохранит кадры в png-файлы:

python3 tools/picamera_test.py

Замечние: по умолчанию скрипт robot.sh создает контейнер без проброса X. Если RaspberryPi подключен к монитору, то можно поменять последний флаг на True в robot.sh:

./build_and_run.sh ros:humble-ros-base camerabot camerabot false true

Далее пересоздать контейнер.

Базовые проверки

Теперь можно проверить связь между роботом и хостом:

  • на хосте: ros2 run robot listener

  • на роботе: ros2 run robot talker

Если в консоли робота видны сообщения с хоста, то всё хорошо, можно поуправлять роботом с камерой с помощью кнопок.

  • на хосте: ros2 launch robot robot_teleop.py

  • на роботе: ros2 launch robot sensing_control.py

Запуск робота с VLM

Если все описанные выше шаги были успешно пройдены, то ровер поедет и с VLM:

  • на хосте: ros2 launch robot vlm_control_ugv.py

  • на роботе: ros2 launch robot sensing_control.py

Запуск в реальности
Запуск в реальности

Заключение

В заключение — несколько оговорок. Выводы, сделанные на основе проведённых опытов, не претендуют на универсальность: другие модели могут вести себя существенно иначе. Кроме того, при запуске даже одной модели на разных видеокартах уже наблюдалась заметная разница в поведении. Также замечу, что несмотря на продолжительный опыт работы с беспилотниками, за VLM берусь впервые, так что надеюсь на любую конструктивную обратную связь).

Источник

Отказ от ответственности: Статьи, размещенные на этом веб-сайте, взяты из общедоступных источников и предоставляются исключительно в информационных целях. Они не обязательно отражают точку зрения MEXC. Все права принадлежат первоисточникам. Если вы считаете, что какой-либо контент нарушает права третьих лиц, пожалуйста, обратитесь по адресу crypto.news@mexc.com для его удаления. MEXC не дает никаких гарантий в отношении точности, полноты или своевременности контента и не несет ответственности за любые действия, предпринятые на основе предоставленной информации. Контент не является финансовой, юридической или иной профессиональной консультацией и не должен рассматриваться как рекомендация или одобрение со стороны MEXC.

Вам также может быть интересно

Соглашение OpenAI с Пентагоном раскрывает важнейшие меры защиты от автономного оружия и слежки

Соглашение OpenAI с Пентагоном раскрывает важнейшие меры защиты от автономного оружия и слежки

BitcoinWorld Соглашение OpenAI с Пентагоном раскрывает важные меры защиты от автономного оружия и слежки В значительном событии для искусственного интеллекта
Поделиться
bitcoinworld2026/03/02 00:55
Garlinghouse даёт зелёный свет партнёрству банков с XRP

Garlinghouse даёт зелёный свет партнёрству банков с XRP

Брэд Гарлингхаус заявляет, что банки могут заключать сделки с XRP, пока продолжаются переговоры по закону о прозрачности, в то время как Coinbase оспаривает положения о стейблкоинах. Генеральный директор Ripple Брэд Гарлингхаус
Поделиться
LiveBitcoinNews2026/03/02 01:30
Потеря Bitcoin триллионов в стоимости не остановила интерес традиционных гигантов к сектору цифровых активов

Потеря Bitcoin триллионов в стоимости не остановила интерес традиционных гигантов к сектору цифровых активов

 
  Финансы
 
 
  Поделиться 
  
   Поделиться этой статьей
   
    Копировать ссылкуX (Twitter)LinkedInFacebookEmail
   
  
 


 
  Потеря Bitcoin триллионов в стоимости не оста
Поделиться
Coindesk2026/03/02 01:07