Вбудовуємо AI в ROS 2: від камери до керування

Клієнт прийшов із UGV на базі ROS 2 Humble: стереопара глибини, LiDAR Velodyne VLP-16 і Jetson Orin NX. Вимога — object detection з частотою 30 Hz, але PyTorch YOLOv8 в лоб давав 12 FPS. Проблема: CPU-bound через OpenCV-конвертацію та блокуючий callback executor. Розповідаємо, як ми переписали архіт

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1441
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    998
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1267
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    713
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1003

Клієнт прийшов із UGV на базі ROS 2 Humble: стереопара глибини, LiDAR Velodyne VLP-16 і Jetson Orin NX. Вимога — object detection з частотою 30 Hz, але PyTorch YOLOv8 в лоб давав 12 FPS. Проблема: CPU-bound через OpenCV-конвертацію та блокуючий callback executor. Розповідаємо, як ми переписали архітектуру ноди та вичавили 125 FPS.

Наша команда спеціалізується на інтеграції AI-моделей в ROS 2 вже понад 7 років. Ми навчилися обходити типові проблеми: latency, перевантаження шини, несумісність фреймворків. Нижче — реальні кейси та перевірені рішення.

Архітектура ROS 2 + AI: як це працює?

AI-модель в ROS 2 — це Node, підписаний на sensor topics і публікуючий результати. Ключове: не блокувати callback executor. Ми проєктуємо ноду так, що інференс виконується в окремому потоці, а публікація — через publisher.

import rclpy from rclpy.node import Node from sensor_msgs.msg import Image from vision_msgs.msg import Detection2DArray from cv_bridge import CvBridge import torch import numpy as np class ObjectDetectionNode(Node): def __init__(self): super().__init__('object_detection') # Завантаження моделі self.model = torch.hub.load( 'ultralytics/yolov8', 'yolov8n', pretrained=True ) self.model.eval() if torch.cuda.is_available(): self.model.cuda() self.bridge = CvBridge() # Підписка на камеру self.subscription = self.create_subscription( Image, '/camera/color/image_raw', self.image_callback, 10 # QoS depth ) # Публікація результатів self.detection_pub = self.create_publisher( Detection2DArray, '/detections', 10 ) self.get_logger().info('Object detection node started') def image_callback(self, msg: Image): # Конвертація ROS Image → numpy cv_image = self.bridge.imgmsg_to_cv2(msg, desired_encoding='rgb8') # Інференс with torch.no_grad(): results = self.model(cv_image) # Конвертація в ROS Detection2DArray detections = self._to_detection_array(results, msg.header) self.detection_pub.publish(detections) def main(): rclpy.init() node = ObjectDetectionNode() rclpy.spin(node) rclpy.shutdown() 

LiDAR + PointNet для 3D сприйняття: як це реалізувати?

Для 3D сцен використовуємо PointNet. LiDAR дані у форматі PointCloud2 конвертуємо в numpy, субсемплюємо до 1024 точок і прогоняємо через модель.

from sensor_msgs.msg import PointCloud2 import sensor_msgs_py.point_cloud2 as pc2 class LidarPerceptionNode(Node): def __init__(self): super().__init__('lidar_perception') self.pointnet_model = load_pointnet_model('pointnet_weights.pth') self.sub = self.create_subscription( PointCloud2, '/velodyne_points', self.lidar_callback, 10 ) def lidar_callback(self, msg: PointCloud2): # Конвертація PointCloud2 → numpy (N, 3) points = np.array(list(pc2.read_points(msg, field_names=("x","y","z")))) # Субсемплювання до 1024 точок для PointNet indices = np.random.choice(len(points), 1024, replace=False) sampled = points[indices] # Інференс tensor = torch.FloatTensor(sampled).unsqueeze(0).cuda() with torch.no_grad(): classes = self.pointnet_model(tensor) # Публікація класифікованих об'єктів 

Оптимізація latency: як досягти 30 Hz?

Latency — головний ворог real-time. Стандартний PyTorch на Jetson Orin видає ~45ms (22 Hz). Наш досвід: TensorRT (див. офіційну документацію NVIDIA) дає 3-5x прискорення — до 8ms (125 Hz). Додатково:

  • Паралельний інференс в окремому потоці (не блокує executor)
  • CUDA streams для обробки кількох кадрів
  • INT8 quantization з калібруванням на репрезентативному датасеті
Метод Latency (Jetson Orin) FPS
PyTorch (FP32) ~45 ms 22
ONNX Runtime (FP32) ~25 ms 40
TensorRT (FP16) ~12 ms 83
TensorRT (INT8) ~8 ms 125

Порівняння підходів: точність vs швидкість

TensorRT в INT8 дає прискорення в 5,6 раза при втраті точності всього 2% — ідеально для real-time.

Модель mAP@50 Latency (Jetson Orin) FPS
YOLOv8n (FP32) 0.59 45 ms 22
YOLOv8n (TensorRT INT8) 0.57 8 ms 125
YOLOv8m (TensorRT INT8) 0.62 15 ms 67

Навігація з RL policy: що входить?

from geometry_msgs.msg import Twist from nav_msgs.msg import Odometry class RLNavigationNode(Node): def __init__(self): super().__init__('rl_navigation') self.policy = load_stable_baselines3_model('navigation_policy.zip') self.cmd_vel_pub = self.create_publisher(Twist, '/cmd_vel', 10) self.odom_sub = self.create_subscription(Odometry, '/odom', self.step, 10) def step(self, odom_msg): state = self._odom_to_state(odom_msg) action, _ = self.policy.predict(state, deterministic=True) cmd = Twist() cmd.linear.x = float(action[0]) cmd.angular.z = float(action[1]) self.cmd_vel_pub.publish(cmd) 

Політика навчена в симуляторі (Isaac Gym) і перенесена на реального робота. Наш досвід: fine-tuning на реальних даних скорочує розрив sim-to-real на 40%.

Моніторинг та спостережуваність AI-нод

Продуктивність AI-ноди в ROS 2 необхідно відстежувати в реальному часі. Ми вбудовуємо наступні інструменти:

  • Діагностичні топики: /diagnostics публікує latency інференсу, FPS та завантаження GPU кожні 1 секунду. Стандарт ROS 2 diagnostic_msgs.
  • Prometheus + Grafana: експортуємо custom metrics через prometheus_client Python. Дашборд показує перцентилі p50/p95/p99 latency за ковзне вікно 5 хвилин.
  • Rosbag записи: критичні інциденти (latency > 2× від baseline) автоматично записуються в rosbag для аналізу.
  • Data drift: вхідні розподіли пікселів/точок порівнюються з навчальним датасетом через KS-тест. Алерт при p-value < 0.05 — сигнал про деградацію якості детекції.

Така observability дозволяє виявити деградацію моделі до того, як вона вплине на місію робота.

Процес роботи: покроково

  1. Аналітика: розбір latency-бюджету, вибір моделі, профілювання.
  2. Проєктування: архітектура ноди, схема топиків, QoS.
  3. Реалізація: написання C++/Python коду, інтеграція з TensorRT/ONNX.
  4. Тестування: симуляція (Gazebo + гонки пікселів) та реальне залізо.
  5. Деплой: контейнеризація Docker, ROS 2 Launch файли, моніторинг.

Типові помилки при інтеграції

  • Забути налаштувати QoS depth: при 30 FPS достатньо 10, інакше переповнення черги.
  • Використовувати один потік для інференсу та callbacks — веде до просідань.
  • Не профілювати конвертацію зображень: cv_bridge може займати 30% часу.
  • Ігнорувати calibration dataset для INT8 — падає точність на 10+%.

Чому варто працювати з нами?

Ми — команда AI/ML інженерів із 7+ роками досвіду в робототехніці. Понад 30 успішних інтеграцій AI в ROS (від безпілотних дронів до промислових маніпуляторів). Гарантуємо стабільну роботу perception pipeline на цільовій платформі. Оцініть свій проєкт — отримайте консультацію. Інтеграція займає від 2 до 4 тижнів, вартість розраховується індивідуально.

Зв'яжіться з нами, щоб обговорити вашу задачу — допоможемо підібрати оптимальну конфігурацію.