Клієнт прийшов із 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 2diagnostic_msgs. -
Prometheus + Grafana: експортуємо custom metrics через
prometheus_clientPython. Дашборд показує перцентилі p50/p95/p99 latency за ковзне вікно 5 хвилин. - Rosbag записи: критичні інциденти (latency > 2× від baseline) автоматично записуються в rosbag для аналізу.
- Data drift: вхідні розподіли пікселів/точок порівнюються з навчальним датасетом через KS-тест. Алерт при p-value < 0.05 — сигнал про деградацію якості детекції.
Така observability дозволяє виявити деградацію моделі до того, як вона вплине на місію робота.
Процес роботи: покроково
- Аналітика: розбір latency-бюджету, вибір моделі, профілювання.
- Проєктування: архітектура ноди, схема топиків, QoS.
- Реалізація: написання C++/Python коду, інтеграція з TensorRT/ONNX.
- Тестування: симуляція (Gazebo + гонки пікселів) та реальне залізо.
- Деплой: контейнеризація 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 тижнів, вартість розраховується індивідуально.
Зв'яжіться з нами, щоб обговорити вашу задачу — допоможемо підібрати оптимальну конфігурацію.







