Статья приурочена к курсу «Компьютерное зрение. Advanced»
Беспилотные технологии все больше проникают во все направления нашей жизни. Сейчас вполне реальной является ситуация, когда дрон облетает промышленный реактор, отслеживая микротрещины на корпусе. При этом, камера дрожит от вибраций, объект меняет масштаб за секунду, солнце бликует на металле, а система должна принимать решения за 30 миллисекунд. И это вовсе не хайп, а реальный вызов современной робототехники.
И именно в таких реальных кейсах, когда специалистам по машинному зрению приходится выходить из своих лабораторий что называется “в поля” и начинается реальность индустриального CV. В частности, эффективность алгоритма, обеспечивающего в лабораторных условиях эффективность в 99%, падает до 30% на вибрирующем дроне над горячим цехом. В этой статье мы попробуем разобраться с тем, почему так происходит и как это исправить.
По сути, мы посмотрим, как собрать пазл из компьютерного зрения, геометрии камеры и механики подвеса в единую систему трекинга, которая работает не в тепличных условиях, а на производстве.
Промышленный трекинг — это не "просто алгоритм"
Итак, в лаборатории вы берете статичную камеру, хорошее освещение, фиксированный масштаб, медленно движущийся объект, и вменяемое время реакции. В результате любой трекер (KCF, CSRT, DeepSORT) покажет 95%+ точности. Но в индустрии все иначе. Камера в полете должна вращаться, при этом она вибрирует, получению корректного изображения мешают тень, солнце, блики от сварки и т. д. Кроме того, масштаб объекта может изменяться от 5 до 50 метров и время реакции у нас меньше, так как в полете есть риск столкновения с снимаемым объектом.

Ключевым отличием лаборатории от индустрии является то, что в подвижной системе камера и объект движутся одновременно. Вы не можете вычесть фон — фон сам движется. Алгоритм должен отличать собственное движение дрона от движения цели.
Движение камеры — главный убийца трекинга
Когда дрон висит на ветру, его камера совершает колебания с амплитудой до 50 пикселей на кадре (при 4K-разрешении). Стандартный трекер на основе корреляции (KCF) теряет объект за 2-3 кадра. Причиной этому является то, что большинство трекеров предполагают, что объект перемещается плавно (модель постоянной скорости). Но при дрожи дрона движение становится разрывным — объект "скачет" на 10-20 пикселей между кадрами. Здесь наилучшим решением является встраивание компенсации движения камеры перед трекингом.
Также, при быстром пролете дрона (10-15 м/с) выдержка 1/500 секунды даёт смаз в 30-50 пикселей. Для сравнения: лицо человека занимает 100-150 пикселей на 4K. Смаз размывает текстуры — трекеры на основе гистограмм (CAMShift) или шаблонов ломаются.
Дрон приближается к объекту — размер на кадре меняется в 5 раз за 3 секунды. Трекеры с фиксированным окном (KCF, MOSSE) не умеют менять масштаб динамически. DeepSORT умеет, но требует детектора на каждом кадре — а это 200 мс на GPU Jetson.
Механика тоже может вносить свои коррективы. Так, задержки от сервопривода тоже могут приводить “отставанию” от реальности.
Рассмотрим полный цикл: захват кадра (33 мс для 30 fps), далее обработка CV (20-100 мс), затем расчёт управления (5 мс) и собственно движение сервоприводов (20-50 мс). Итого: 80-200 мс задержки. За это время дрон пролетает 1-2 метра. В результате классический "pipeline" отстаёт от реальности.
Трёхуровневая архитектура: CV + Геометрия + Механика
Рабочая система трекинга на подвижной платформе строится не как монолит, а как три связанных контура:
[Глобальный контур] → [Локальный контур] → [Контур подвеса]
В таблице ниже представлены ошибки для каждого из этих контуров.
GPS/IMU |
CV трекер |
Gimbal control |
Грубая позиция |
Точная 2D-позици |
Компенсация дрожи |
(ошибка 1-5 м) |
(ошибка 2-5 пикс) |
(ошибка <1 пикс) |
Давайте рассмотрим каждый из этих контуров подробнее.
Уровень 1: Геометрия камеры и компенсация движения
Прежде чем искать объект, нужно понять, как двигалась сама камера. Для этого используется гомография (проективное преобразование между кадрами).
На дроне это можно реализовать следующим образом:
Берём два последовательных кадра I_t и I_{t-1}
Находим ключевые точки (ORB, AKAZE — быстрые, не SIFT)
Сопоставляем их методом RANSAC
Вычисляем матрицу гомографии H (3x3), описывающую поворот+масштаб+сдвиг камеры
Применяем H к предыдущей позиции объекта — получаем предсказание, где он должен быть сейчас, если бы не двигался сам
Вот псевдокод компенсации движения камеры
Point2d predictPositionWithCameraMotion(Point2d prevPos, Mat H) { // Переводим в однородные координаты Mat prevHomog = (Mat_<double>(3,1) << prevPos.x, prevPos.y, 1.0); // Применяем гомографию Mat currHomog = H * prevHomog; // Возвращаемся в пиксельные return Point2d(currHomog.at<double>(0) / currHomog.at<double>(2), currHomog.at<double>(1) / currHomog.at<double>(2)); }
В результате даже при сильной вибрации дрона предсказанная позиция отклоняется от реальной всего на 5-10 пикселей вместо 50.
Уровень 2: Алгоритмы трекинга для подвижной камеры
Важно понимать, что ни один трекер не идеален. Но для обеспечения эффективной работы промышленные системы используют гибридный подход, который заключается в следующем.
Для ближнего трекинга (<20 метров) используется DIS (Dense Inverse Search), при этом скорость: 300-500 fps на GPU. Такой трекинг устойчив к смазу и частичным перекрытиям. Минусом такого подхода является то, что он не умеет менять масштаб и сбрасывается при резких поворотах
Для дальнего трекинга (>20 метров) применяется KCF с адаптивным масштабом. При этом, классический KCF немного модифицируется. Так, мы добавляем в него поиск по масштабу в 3-х вариантах (0.95x, 1.0x, 1.05x) на каждом 5-м кадре. Это даёт устойчивость к приближению дрона.
Для высокоточной фиксации можно использовать TreS (Tracking by Segmentation). Когда объект нужно держать с субпиксельной точностью (например, центр сварного шва), используем сегментацию. U-Net на выделенном GPU выдаёт маску объекта за 15 мс — и затем ищем центр масс маски, а не bounding box.

Уровень 3: Механика подвеса (Gimbal) как часть CV-системы
Gimbal это устройство, которое стабилизирует движение камеры во время съёмки. Подвес — это поворотная опора, позволяющая вращать объект вокруг одной оси. По сути, gimbal это самый важный и недооценённый компонент, так как он не просто "держатель камеры", а активный исполнительный механизм, который должен работать в связке с алгоритмами.

Разберем классическую ошибку: подача ошибки трекинга напрямую на PID-регулятор подвеса. В результате у нас возникает следующая проблема: CV выдал смещение на 20 пикселей → Gimbal резко дёрнулся → новый кадр смазан → CV теряет объект.
Здесь более правильным решением будет использование каскадного управления и в качестве примера посмотрим код на Python:
# Внутренний контур: компенсация высокочастотной дрожи (100-200 Гц) # Работает на гироскопе IMU, без участия CV gimbal.imu_compensation_enabled = True # Внешний контур: слежение за объектом (30-60 Гц) # Использует CV + фильтр Калмана if cv_tracker.is_tracking(): error_px = target_position - current_position # Преобразуем пиксельную ошибку в угловую скорость # Для камеры с фокусным расстоянием f: угол = arctan(error_px / f) angular_error = atan2(error_px.x, focal_length_x) # Фильтруем низкие частоты (плавное движение) filtered_error = low_pass_filter(angular_error, cutoff=5.0) # 5 Гц # Отправляем на подвес gimbal.set_target_velocity(filtered_error * kp)
Для лучшей работы можно также добавить секретный ингредиент, а именно использовать предсказание от инерциальной системы. Если IMU говорит "дрон резко поворачивает налево", gimbal должен начать поворот до того, как CV увидит смещение. Это снижает латентность с 100 мс до 20 мс.
Реальный кейс: Инспекция нефтепровода с дрона
Давайте разберем реальный кейс, в котором дрон летит вдоль трубы диаметром 1 м на расстоянии 5 м. Нужно отслеживать сварной шов с точностью ±2 мм для автоматической фотосъёмки дефектов.

Здесь мы сталкиваемся со следующими проблемами: труба однотонная, текстуры почти нет, в свою очередь дрон мотает ветром (амплитуда 0.5 м), а солнце создаёт блики на металле.
Для того, чтобы справиться с этими проблемами мы можем во-первых воспользоваться искусственной текстурой. В таком случае, подсветка лазерным проектором создаёт на трубе паттерн из точек и CV ищет не шов, а искажения паттерна.
Каскад трекинга можно разделить на три уровня точности:
Грубый уровень (GPS + IMU): знаем, где труба в глобальных координатах
Средний уровень (гомография + оптический поток): держим шов в центре кадра
Точный уровень (корреляция шаблона): определяем фазу сварного шва с точностью до 1 мм
Наконец, наш незаменимый элемент – gimbal. Устанавливаем камеру на 3-осевой подвес с энкодерами 0.01°. CV вычисляет ошибку, но gimbal сглаживает движение, чтобы кадры не смазывались.
В результате мы получаем точность 1.5 мм, скорость обработки 25 мс на кадр (Jetson Xavier NX), дрон летит 8 м/с без потери цели.
Практические рекомендации
В завершении статьи давайте рассмотрим несколько практических рекомендаций. Начнем с выбора оборудования. Для лёгких дронов весом менее двух килограмм лучше всего использовать Intel RealSense T265 (VIO) + IMU и алгоритмы трекинга на встроенном DSP.
Для тяжёлых промышленных дронов наиболее подходящим решением будет NVIDIA Jetson Orin + FLIR Blackfly S и написание собственного трекера на CUDA.
Для тех, кому необходима максимальная точность можно предложить стереокамеру + LiDAR (Ouster OS0). Дорого, но оно того стоит, ведь в замен вы получите метрическую глубину.
Для быстрого старта можно воспользоваться следующим кодом на Python + OpenCV:
import cv2 import numpy as np class IndustrialTracker: def init(self): self.tracker = cv2.TrackerKCF_create() self.prev_gray = None self.lk_params = dict(winSize=(15, 15), maxLevel=2, criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)) def compensate_camera_motion(self, curr_gray): if self.prev_gray is None: self.prev_gray = curr_gray return np.eye(3) # Находим поток для компенсации flow = cv2.calcOpticalFlowFarneback(self.prev_gray, curr_gray, None, 0.5, 3, 15, 3, 5, 1.2, 0) # Упрощённо: усредняем движение mean_motion = np.mean(flow.reshape(-1, 2), axis=0) self.prev_gray = curr_gray return mean_motion def update(self, frame, bbox=None): gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) motion = self.compensate_camera_motion(gray) if bbox is not None: # Инициализация трекера self.tracker.init(frame, bbox) return bbox # Компенсируем движение камеры в предсказании success, bbox = self.tracker.update(frame) if success: # Корректируем bbox с учётом движения камеры bbox = (bbox[0] - motion[0], bbox[1] - motion[1], bbox[2], bbox[3]) return bbox if success else None
Заключение
В этой статье мы поговорили о том, как можно улучшить трекинг объектов. В завершении этой темы хотелось бы отметить, что никогда нельзя верить трекеру на 100%, то есть всегда добавляйте детектор перезахвата (каждые N кадров проверяйте, там ли объект).
Для улучшения стабильности надо всегда использовать фильтр Калмана обязателен, так как даже простой линейный фильтр даёт +20% к стабильности. Подвес всегда дешевле, чем улучшение алгоритма компьютерного зрения — лучше потратиться на качественный gimbal вместо месяца оптимизации алгоритма. И тестируйте на вибростенде — лабораторные тесты без вибрации бесполезны.
Важно понимать, что трекинг на подвижной камере — это не алгоритмическая задача, а системная. Вы не решите её, написав идеальный трекер. Вы решите её, построив мосты между CV, геометрией камеры и механикой подвеса. И да, это возможно за один спринт, если знаете, где лежат грабли.
? Хотите проверить, готовы ли вы к курсу? Пройдите вступительное тестирование — тут. |

А чтобы разобраться глубже, приходите на открытые уроки курса:
➊ «Трекинг с подвижной камеры: алгоритмы и механика компьютерного зрения на роботах для индустриальных задач» — 29 апреля, 20:00 → [Записаться]
➋ «Видеоаналитика и распознавание действий: от 3D‑свёрток до визуально языковых моделей» — 21 мая, 20:00 → [Записаться]
Danusha0000000
а теперь давайте развивать тему на нужны в России дроны перехватчики дронов и недорого.