Беспилотник на VLM и ROS2: гайд 2026 года | AiManual
AiManual Logo Ai / Manual.
01 Мар 2026 Гайд

Беспилотник на VLM и ROS2: пошаговый гайд от установки до запуска на реальном железе

Полный гайд по созданию автономного беспилотника на Vision-Language моделях и ROS2. Установка, настройка промптов, работа с реальным железом.

Все эти годы беспилотники летали по точкам. Координаты, телеметрия, слепое следование по GPS. Скучно. С приходом Vision-Language моделей (VLM) робот наконец-то начал понимать, что видит. Дрон не просто 'летит в точку 55.7558° N, 37.6173° E'. Он теперь видит 'разрушенный мост', 'группу людей в красном' или 'открытый проход между деревьями'. Это другая вселенная.

Здесь и сейчас мы собираем дрон, который летает по командам на естественном языке, обрабатывая видеопоток в реальном времени с помощью локальной VLM. Никакого облака. Никакого Starlink. Все вычисления на борту. В качестве мозга - ROS2 Humble (или новее, если к 2026 году вышел Iron Iridum, но принцип тот же). В качестве глаз и интеллекта - VLM типа Llama 3.2 Vision или Qwen2-VL, запущенная через Ollama. Ну что, полетели?

Куда мы прилетим: архитектура простым языком

Типичная ошибка новичка - начать паять и компилировать, не поняв, как все будет общаться. Давайте сразу расставим точки. Схема выглядит так:

  • Датчики (камера, IMU, лазерный дальномер) → ROS2-ноды. Они публикуют данные (изображения, телеметрию) в топики.
  • VLM-сервис (Ollama с загруженной моделью) → подписывается на топик с кадрами, получает изображение, обрабатывает промпт, возвращает текстовое описание или команду.
  • Мозг (наш Python-скрипт на ROS2) → берет описание от VLM, преобразует в навигационные команды ('поверни налево', 'лети к синей двери') и публикует их в топик управления.
  • Контроллер (BaseDriver или аналогичный драйвер полетного контроллера) → читает топик управления и отправляет низкоуровневые команды на моторы.

Важно: Обратите внимание, что это не end-to-end управление, где VLM напрямую выдает значения для моторов. Такой подход пока слишком опасен для реального полета. Мы используем VLM как высокоуровневый планировщик, а низкоуровневый контроль оставляем классическим ПИД-регуляторам. Если вам интересна именно end-to-end архитектура, посмотрите наш отдельный материал: End-to-End беспилотник на VLM.

Железо и софт: что покупаем и качаем

Не пытайтесь сразу закинуть 40-миллиардную модель на Raspberry Pi. Начните с симуляции, а для реальных полетов потребуется довольно мощная одноплатка. Вот минимальный набор на 2026 год:

Компонент Рекомендация Зачем
Одноплатный компьютер NVIDIA Jetson Orin Nano (16GB) или лучше Именно для VLM нужен мощный GPU с поддержкой CUDA. Raspberry Pi 5 не потянет модели >7B параметров в разумное время. Здесь подробнее про Jetson и VLM.
Камера Raspberry Pi HQ Camera или аналог с MIPI CSI Высокое разрешение (12 МП) важно для VLM, чтобы различать детали. Убедитесь в совместимости с вашей платой.
Полетный контроллер Pixhawk 6C (или любая версия, поддерживаемая PX4) Стандарт де-факто. Отлично интегрируется с ROS2 через проект px4_msgs и micro-ROS.
Рама, моторы, ESC Любой готовый китайский квадрокоптер 450-500мм Не изобретайте велосипед. Берите готовый набор, чтобы сосредоточиться на софте. Похожий подход мы применяли для наземного робота.

Софт:

  • ОС: Ubuntu 22.04 LTS или 24.04 LTS (для Jetson - JetPack 6.0, актуальный на 2026 год).
  • ROS2: Humble Hawksbill (или новее, но Humble - LTS до 2027).
  • Ollama: последняя версия (на 01.03.2026 это, допустим, 0.6.x).
  • VLM-модель: Llama 3.2 Vision 11B (баланс скорости и качества) или Qwen2-VL-7B (быстрее).

1 Установка ROS2 и окружения за 10 минут

Открываем терминал и не мудрим. Я предпочитаю устанавливать ROS2 из бинарников, а не из исходников (экономит часы жизни).

# 1. Настройка локали и ключей (стандартный скрипт от ROS)
sudo apt update && sudo apt install -y locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

# 2. Добавление репозитория ROS2 (актуальная версия на 01.03.2026 может быть Iron, уточняем)
# Допустим, мы используем Humble (стабильный LTS)
sudo apt install -y software-properties-common curl
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
echo \"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main\" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null

# 3. Установка ROS2 Humble Desktop (полный пакет с тулзами)
sudo apt update
sudo apt install -y ros-humble-desktop python3-colcon-common-extensions

# 4. Настройка переменных окружения (добавляем в ~/.bashrc)
echo \"source /opt/ros/humble/setup.bash\" >> ~/.bashrc
source ~/.bashrc

# 5. Проверка: запускаем demo
ros2 run demo_nodes_cpp talker &
ros2 run demo_nodes_cpp listener &
# Если видите обмен сообщениями - все ок.
💡
Если вы готовитесь к соревнованиям или хакатонам, стандартный набор инструментов может не хватить. Посмотрите наш гайд по настройке LeRobot для таких задач: Подготовка к робототехническому хакатону AMD. Там много полезных советов по отладке.

2 Ollama и выбор VLM: качаем модель и не плавим плату

Установка Ollama проста, но выбор модели критичен. Для квадрокоптера важна скорость инференса (FPS) и понимание пространственных отношений.

# Установка Ollama (официальный скрипт)
curl -fsSL https://ollama.com/install.sh | sh

# Запуск сервиса
sudo systemctl enable ollama
sudo systemctl start ollama

# Скачиваем модель. Внимание: 11B параметров займет ~11 ГБ.
# Для Jetson Orin Nano с 16 ГБ ОЗУ это предел.
ollama pull llama3.2-vision:11b  # Актуальная на 01.03.2026 модель

# ИЛИ более легкую модель для тестов
ollama pull qwen2-vl:7b

# Проверяем работу: отправляем тестовый промпт с изображением
ollama run llama3.2-vision:11b \"Describe what you see in this image.\" -f /tmp/test.jpg

Какую модель выбрать? Вот мой субъективный рейтинг на начало 2026:

  • Llama 3.2 Vision 11B: Лучший баланс. Отлично понимает контекст сцен, пространственные отношения ('левее', 'позади дерева'). Инференс на Jetson Orin Nano: ~1-2 секунды на кадр.
  • Qwen2-VL-7B: Быстрее, но иногда 'тупит' на сложных сценах. Хороша для простых навигационных команд ('лети к двери').
  • AlpamayoR1 (если доступна для Ollama): Специально заточена под робототехнику и reasoning. Если вы хотите, чтобы дрон 'рассуждал' о своих действиях, это топовый выбор. Читали про AlpamayoR1?

3 Поднимаем железо: камера, Pixhawk и BaseDriver

Самый нервный этап. Если вы не подключили железо правильно, все остальное бессмысленно.

# 1. Подключаем камеру к CSI-порту Jetson. Проверяем драйверы.
# Установка инструментов для камеры (для Jetson)
sudo apt install -y nvidia-jetpack-multimedia

# Проверка: ищем устройство камеры
ls -la /dev/video*
# Должно появиться /dev/video0

# 2. Установка драйверов и утилит для Pixhawk (через PX4)
# Создаем workspace для ROS2
mkdir -p ~/drone_ws/src
cd ~/drone_ws/src

# Клонируем необходимые пакеты (включая micro-ROS для общения с Pixhawk)
git clone https://github.com/PX4/px4_msgs.git
# BaseDriver - наш мост между ROS2 и полетным контроллером
git clone https://github.com/your-repo/base_driver_ros2.git  # Замените на актуальный репозиторий

# 3. Сборка
cd ~/drone_ws
colcon build
source install/setup.bash

# 4. Подключаем Pixhawk по USB (или UART). Ищем устройство.
ls /dev/serial/by-id/
# Должно быть что-то вроде /dev/serial/by-id/usb-...

# 5. Запускаем BaseDriver (предварительно настроив его конфиг на ваш порт)
ros2 run base_driver base_driver_node
# Если видите топики /mavros/... и /drone/control - связь установлена.

Предупреждение: Никогда не запускайте моторы в помещении без защиты! Первые тесты проводите с отключенными пропеллерами или на столе, зафиксировав раму. BaseDriver может отправить случайный сигнал 'взлет', и дрон взлетит в потолок.

4 Пишем мозг: ROS2-нода, которая спрашивает у VLM дорогу

Создаем пакет drone_vlm_brain. Вот его основа.

#!/usr/bin/env python3
import rclpy
from rclpy.node import Node
from sensor_msgs.msg import Image
from std_msgs.msg import String
from geometry_msgs.msg import Twist
import cv2
import requests
import json
import base64
from cv_bridge import CvBridge

class VLMNavNode(Node):
    def __init__(self):
        super().__init__('vlm_nav_node')
        
        # Подписка на кадры с камеры
        self.subscription = self.create_subscription(
            Image,
            '/camera/image_raw',
            self.image_callback,
            10  # QoS
        )
        
        # Издатель для навигационных команд (высокоуровневых)
        self.cmd_pub = self.create_publisher(String, '/vlm/command', 10)
        # Издатель для низкоуровневого управления (в топик, который читает BaseDriver)
        self.control_pub = self.create_publisher(Twist, '/drone/control', 10)
        
        self.bridge = CvBridge()
        self.ollama_url = "http://localhost:11434/api/generate"
        self.model = "llama3.2-vision:11b"
        
        # Промпт-шаблон. Это ключевой момент!
        self.prompt_template = """
        You are the navigation system of a drone. Analyze the image from the drone's front camera.
        Your goal is to reach the red door. Describe the immediate action in one short phrase.
        Possible actions: "fly straight", "turn left", "turn right", "ascend", "descend", "stop", "hover".
        If the red door is clearly visible and centered, say "target_reached".
        Output ONLY the action phrase, nothing else.
        Image description:
        """
        
        self.get_logger().info('VLM Navigation Node запущен. Ожидание изображений...')
    
    def image_callback(self, msg):
        # Конвертируем ROS Image в OpenCV
        cv_image = self.bridge.imgmsg_to_cv2(msg, "bgr8")
        
        # Кодируем изображение в base64 для отправки в Ollama
        _, buffer = cv2.imencode('.jpg', cv_image)
        jpg_as_text = base64.b64encode(buffer).decode('utf-8')
        
        # Формируем промпт
        full_prompt = self.prompt_template + "\n[Image attached]"
        
        # Подготовка запроса к Ollama API
        payload = {
            "model": self.model,
            "prompt": full_prompt,
            "images": [jpg_as_text],
            "stream": False
        }
        
        try:
            response = requests.post(self.ollama_url, json=payload, timeout=10.0)
            if response.status_code == 200:
                result = response.json()
                vlm_response = result['response'].strip().lower()
                self.get_logger().info(f'VLM сказал: {vlm_response}')
                
                # Публикуем высокоуровневую команду
                cmd_msg = String()
                cmd_msg.data = vlm_response
                self.cmd_pub.publish(cmd_msg)
                
                # Преобразуем в низкоуровневую команду (Twist)
                control_cmd = Twist()
                if "turn left" in vlm_response:
                    control_cmd.angular.z = 0.5  # Поворот влево
                elif "turn right" in vlm_response:
                    control_cmd.angular.z = -0.5
                elif "fly straight" in vlm_response:
                    control_cmd.linear.x = 0.3   # Движение вперед
                elif "ascend" in vlm_response:
                    control_cmd.linear.z = 0.3   # Вверх
                elif "descend" in vlm_response:
                    control_cmd.linear.z = -0.3  # Вниз
                elif "hover" in vlm_response or "stop" in vlm_response:
                    # Нулевые скорости - зависание
                    pass
                elif "target_reached" in vlm_response:
                    self.get_logger().info('Цель достигнута!')
                    # Здесь можно инициировать посадку
                else:
                    self.get_logger().warn(f'Неизвестная команда от VLM: {vlm_response}')
                    
                self.control_pub.publish(control_cmd)
                
            else:
                self.get_logger().error(f'Ошибка Ollama API: {response.status_code}')
        except Exception as e:
            self.get_logger().error(f'Ошибка связи с VLM: {e}')

def main(args=None):
    rclpy.init(args=args)
    node = VLMNavNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == '__main__':
    main()

Этот код - сердце системы. Он делает три вещи: берет кадр с камеры, отправляет его в VLM с промптом, парсит ответ и конвертирует в команды для полетного контроллера. Самое важное здесь - промпт. Его качество определяет, будет ли дрон адекватно реагировать.

5 Искусство промптов для навигации: как разговаривать с VLM

VLM не понимает мир как человек. Ей нужно четко задать правила игры. Плохой промпт: "Куда лететь?". Хороший промпт дает контекст, список допустимых действий и жесткий формат вывода.

Вот несколько промпт-шаблонов для разных задач:

# Шаблон 1: Следование за объектом (например, за человеком в красной футболке)
You are a drone's vision system. Your only task is to keep a specific person in the center of the frame.
Describe the person: wearing a bright red t-shirt and blue jeans.
If the person is centered, output: "center".
If the person is slightly to the left, output: "adjust_left".
If the person is slightly to the right, output: "adjust_right".
If the person is too small (far), output: "approach".
If the person is too large (close), output: "hold_distance".
If the person is not visible, output: "search_rotate".
Output ONLY one of the above keywords, no other text.
# Шаблон 2: Поиск выхода из лабиринта (по визуальным меткам)
Analyze the scene. Identify openings or passages large enough for the drone to fly through.
Prioritize openings that are well-lit.
If you see a clear, safe passage straight ahead, output: "forward".
If the best passage is to the left, output: "turn_left".
If to the right, output: "turn_right".
If the passage is above (e.g., over a low obstacle), output: "ascend".
If the passage is below, output: "descend".
If no safe passage is visible, output: "halt".
Output only the action word.

Экспериментируйте с промптами. Иногда добавление фразы "Think step by step" (стандартный прием chain-of-thought) заставляет модель рассуждать точнее, но увеличивает время ответа. Для дрона каждая миллисекунда на счету. Если интересна эволюция VLM в робототехнике, почитайте наше сравнение VLA vs VLM 2025.

6 Запуск, тестирование и команды ./host.sh, ./b, . e

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

Файл host.sh (запускает все ноды на хосте - Jetson):

#!/bin/bash
# host.sh

# 1. Запускаем сервис Ollama (если еще не запущен)
sudo systemctl start ollama
sleep 2

# 2. Запускаем драйвер камеры (пример для Jetson CSI)
ros2 run v4l2_camera v4l2_camera_node --ros-args -p video_device:="/dev/video0" -p output_encoding:="rgb8" &

# 3. Запускаем BaseDriver для связи с Pixhawk
ros2 run base_driver base_driver_node &

# 4. Запускаем наш мозг - VLM навигационную ноду
ros2 run drone_vlm_brain vlm_nav_node &

echo "Все системы запущены. Для просмотра топиков: ros2 topic list"

Файл b (build) - для быстрой сборки проекта:

#!/bin/bash
# b
cd ~/drone_ws
colcon build --symlink-install
source install/setup.bash

Команда . e (environment) - просто алиас для source setup.bash в удобной директории. Добавьте в ~/.bashrc:

alias .e="source ~/drone_ws/install/setup.bash"

Теперь рабочий процесс выглядит так:

  1. Вносите изменения в код.
  2. Запускаете ./b для сборки.
  3. Запускаете . e (или переоткрываете терминал) для обновления окружения.
  4. Запускаете ./host.sh для запуска всей системы.
  5. Наблюдаете за топиками: ros2 topic echo /vlm/command.

Нюансы, которые вылезут в самый неподходящий момент

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

Проблема: VLM отвечает слишком медленно ( >3 секунды). Дрон за это время уже улетит в стену.
Решение: Уменьшите разрешение кадра перед отправкой в Ollama. 640x480 достаточно. Добавьте в код сжатие. И подумайте о более легкой модели (Qwen2-VL-7B).

Проблема: VLM возвращает не только действие, но и пояснительный текст ("I think you should turn left because..."), который ваш код не может распарсить.
Решение: Жестче ограничивайте вывод в промпте. Добавьте фразу: "Output must be exactly one word from the list: [fly straight, turn left, turn right, ascend, descend, stop]. No explanations."

Проблема: При запуске всех нод система зависает, Jetson перегревается.
Решение: Ограничьте частоту вызова image_callback. Не нужно обрабатывать каждый кадр с 30 FPS. Используйте ROS2 таймер, чтобы опрашивать VLM, скажем, 2 раза в секунду. И купите активное охлаждение для Jetson.

Проблема: Pixhawk не отвечает на команды от BaseDriver.
Решение: Проверьте, что на Pixhawk залита последняя версия прошивки PX4 с поддержку MAVLink 2. Убедитесь, что в QGroundControl (или аналогичной утилите) задан правильный режим полета (например, Offboard).

И последнее. Не ждите, что ваш дрон сразу полетит как в кино. Первые успехи будут скромными: зависнуть в центре комнаты, повернуться к окну. Но момент, когда он впервые по вашей голосовой команде ('найди красный мяч') полетит в нужном направлении, компенсирует все часы отладки. Это уже не игрушка. Это прообраз будущего, где роботы видят и понимают.

Что дальше? Добавьте камеру глубины (Intel RealSense) для точного облета препятствий. Научите VLM читать текстовые указатели ('Exit', 'Room 101'). Или подключите все это к умному дому, чтобы дрон мог, например, лететь и проверять, закрыто ли окно на втором этаже. Технологии для этого уже есть. Осталось только собрать.

Подписаться на канал