
Привет! Меня зовут Дмитрий Ивахненко из команды Автономного транспорта Яндекса. В этой статье я подробно разберу, как устроены сервисы удалённого управления автономными юнитами — роботакси, роботами‑доставщиками и грузовиками.
Реальный мир полон нестандартных ситуаций, в которых даже самые совершенные алгоритмы могут встретить сложности: внезапно появившееся препятствие, сбой датчиков, сложные погодные условия. В этих случаях важно обеспечить безопасность, бесперебойную работу и предсказуемое поведение техники. Для этого и нужны операторы и системы удалённого управления — они «подстраховывают» автономные устройства.
В статье — архитектурные решения и инструменты, которые позволили сделать весь процесс удалённой помощи масштабируемым, надёжным и эффективным.
Автономный транспорт: из чего состоит флот
Чтобы говорить про сервисы удалённого управления, сначала нужно определить, чем именно мы управляем. В Яндексе автономный транспорт — это сразу три разных класса юнитов, и у каждого свои задачи, сенсорность и ограничения.
Грузовики. Самые крупные и самые «дальнобойные». Они работают между городами: «склад — склад», длинные маршруты, высокая средняя скорость и много времени вне зоны стабильной связи. Внутри обязательно есть водитель‑испытатель — он следит, чтобы машина не нарушала ПДД и корректно отрабатывала пограничные ситуации.

На борту больше 30 сенсоров: 17 камер, 6 лидаров и дополнительная периферия. Это тяжёлый юнит, который в любой момент может находиться «где‑то там, далеко» и всё равно должен стабильно получать команды и телеметрию.
Роботакси. Чуть компактнее грузовиков, но никак не проще технологически. У такси четыре лидара, девять камер, а основной сценарий — городская пассажирская перевозка. Внутри также присутствует водитель‑испытатель: эксплуатация в городе требует мгновенной реакции и строгого соблюдения ПДД.

Эти юниты работают в насыщенной среде — плотный трафик, пробки, пешеходы, взаимные влияния поведения, сложные перекрёстки. Соответственно, требования к качеству данных, стабильности каналов и скорости принятия решений намного выше.
Роботы‑доставщики. Наши самые маленькие юниты, но технически они далеко не игрушки: один лидар, шесть камер и отдельная камера в лотке для трансляции того, как ваша коробка с «Наполеоном» аккуратно доезжает из Лавки до подъезда.

У робота свои сложности: малая высота (и, как следствие, — огромные слепые зоны), плотные пешеходные зоны, хаотичные сценарии взаимодействия с людьми. Его зона ответственности — последняя миля.
Юнитов у нас уже много: сотни роботов‑доставщиков, десятки роботакси и растущий парк грузовиков. И их количество будет только увеличиваться.
С таким флотом никак не обойтись без инфраструктуры для управления, телеметрии, диагностики, удалённых подсказок и автоматических реакций. Именно поэтому внутри направления автономного транспорта мы развиваем экосистему сервисов, которая помогает всей этой технике жить в реальной среде, безопасно работать и оставаться управляемой — как вручную через операторов, так и автоматически через внутренние API.
Экосистема
Начнём с того, чем обеспечен автономный транспорт с инфраструктурной стороны. Во‑первых, это сервисы поставки карт. Они помогают роботам понимать, где они находятся, куда движутся, что их окружает и как меняется среда вокруг. Это фундамент, без которого ни одно решение на борту работать не будет.
Дальше идут сервисы удалённого управления — как раз о них я буду подробно рассказывать. Это инструменты, которые позволяют нам следить за флотом, помогать юнитам в нештатных ситуациях и решать десятки сопутствующих задач, возникающих у техники, которая работает в насыщенной и непредсказуемой городской среде.
Есть и сервисы эксплуатации. Они знают буквально всё о каждом юните — от «рождения» на заводе до конца жизненного цикла. Это информация о комплектации робота‑доставщика, о том, какой водитель‑испытатель был закреплён за грузовиком, какие детали менялись и как вёл себя юнит в разные периоды эксплуатации.
Отдельный слой — сервисы интеграции с партнёрами. Они обеспечивают роботов работой, маршрутами, логистикой. Роботы, конечно, питаются не едой, но «пропитанием» в виде задач и заказов мы их обеспечиваем постоянно.
Дальше — главная тема статьи: сервисы удалённого управления, которые страхуют юниты в сложных ситуациях.
Сервисы удалённого управления
Первый вопрос: кто ими пользуется? Очевидный ответ — операторы удалённого управления. У них три основные группы задач:
-
Realtime‑взаимодействие: если робот застрял, не может проехать или, скажем, на него кто‑то попытался залезть сверху, оператор помогает юниту справиться с проблемой.
-
Управление флотом целиком: мы изредка управляем роботами, когда кто‑то из них призвал оператора, если на карте возникло неизвестное препятствие или в других нештатных ситуациях.
-
Работа с прошедшими событиями: разбор случаев, которые уже произошли, разметка, принятие решений, которые помогут улучшить будущее поведение робота.
Но операторы — далеко не единственный источник нагрузки на эти сервисы. Основной трафик создают именно другие внутренние сервисы. Им нужно знать всё о местоположении и состоянии юнитов, о том, кто уехал на смену, кто закончил, кто едет за заказом. Партнёрские системы, например, выводят роботов‑доставщиков на линию и отправляют им команды: «поезжай в Лавку», «забери заказ», «вези его получателю».
Этот поток команд и телеметрии идёт непрерывно и в обе стороны. В какой‑то момент его объём стал настолько большим, что ситуация превратилась в технический вызов.
Какие инструменты делаем
Теперь — что именно мы создаём, чтобы этот флот мог работать в реальной среде. Начнём с простых вещей.
Снапшоты
Снапшот — это агрегированное событие. Если, например, несколько юнитов пожаловались на одну и ту же точку на повороте, мы объединяем их сигналы в одно событие по конкретной локации. Оператор видит это не как тысячу отдельных жалоб, а как один осмысленный кейс.

Внутри снапшота находится слепок всего, что юниты «видели» в этом месте: записи камер и лидаров, телеметрия и данные всех сенсоров. На основе этого оператор может понять, что там происходило, и решить, что делать дальше. Иногда это доразметка для ML, чтобы система в будущем аккуратнее работала в подобных точках. Иногда — мгновенное действие.
Динамические полигоны
Когда нужно отреагировать быстро, оператор может прямо по данным снапшота нарисовать динамический полигон — запретную зону. Допустим, в этом месте идёт ремонт, мешает проезду, и в результате на него жалуются все подряд. Оператор отмечает область как временно закрытую, и система начинает объезжать её автоматически, уменьшая количество повторных обращений.

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

Мы собираем данные от всех юнитов: уровень загрязнения сенсоров, интенсивность деградации лидара, общее состояние флота. Агрегируем эту информацию и передаём старшему смены. Он уже принимает решение — например, временно выводить флот из этой зоны или перестраивать маршруты.
Это относительно простые кейсы — с предсказуемыми решениями. Но иногда задача другая: нужно не просто проанализировать ситуацию, а подключиться к юниту в реальном времени и напрямую помочь ему выбраться или принять решение за него. В этот момент начинаются настоящие технические сложности.
Realtime‑управление
Для прямого вмешательства в работу юнита нам нужны два ключевых компонента. Первый — видеостриминг. Оператор должен видеть реальную обстановку вокруг: что происходит спереди, с боков, что за препятствие перед роботом и почему он решил остановиться. Без живой картинки никакое осмысленное решение принять нельзя.
Второй компонент — передача управляющих команд. Формально команды мы умели отправлять давно — те же динамические полигоны разлетаются по всему флоту именно так. Но есть принципиальная разница между флотскими командами и командами ручного управления.
Если полигон прилетит с задержкой в несколько секунд — ничего страшного. А вот если оператор пытается вывести робота из узкого кармана или объехать препятствие, задержка между действием и эффектом должна стремиться к нулю. Команды должны доходить быстро, надёжно и предсказуемо.
Поэтому в realtime‑режиме мы должны обеспечить каналы связи, которые выдерживают такие требования, и архитектуру, в которой управляющее сообщение не попадёт в общую очередь, не затеряется и не превратится в «команду для прошлого».
Видеостриминг
Сразу хочется прояснить: это не видеозвонок, когда ваш собеседник сидит дома с более‑менее стабильным интернетом (возможно, даже в штанах), и вам достаточно просто видеть, что он улыбается и машет рукой.
У нас же всё иначе. Робот движется в пространстве. Препятствия тоже движутся. И оператор должен понимать не художественные детали происходящего, а динамику сцены: куда едет машина, откуда появляется объект, насколько быстро он приближается. Нам нужно доставить картинку хоть как‑то, чтобы можно было принять решение.

Да, чем выше качество, тем лучше. Но детальность граффити на стене нам не нужна — нам важно различать движение и корректно оценивать обстановку.
Чем больше камер — тем шире обзор. На грузовике их 17, и с ними можно действительно «увидеть всё». На роботе‑доставщике камер меньше, но там свои ограничения, о которых мы ещё поговорим.
Но главная проблема видеостриминга — связь.
Грузовики могут оказаться далеко от базовых станций — связь становится слабее, и часть пакетов начинает теряться. В городе ситуация не лучше: вышки могут быть перегружены из‑за плотного трафика. А у робота‑доставщика своя разновидность проблемы — он низкий, и его легко «закрывают» крупные объекты вроде машин или киосков, из‑за чего сигнал до станции просто не проходит. Это реальность, и с этим мы живём постоянно.
Вторая часть уравнения — это «железо». Например, у робота‑доставщика батарея не бесконечная, а значит, мы можем использовать для видеостриминга только аппаратные кодеки. Софтварные кодеки дают лучшую гибкость, но съедают батарею быстрее, а значит, робот будет меньше работать и чаще уходить на зарядку. То же самое с камерами: они дают такой обзор, какой дают.
Если нам не хватает количества камер, мы используем fisheye. И вот здесь возникает весёлая математика: движения объектов на fisheye — нелинейные. Кодекам это не нравится — поток становится тяжелее, хуже сжимается, хуже доставляется, больше нагружает сеть… А значит, сильнее сажает батарею.

Чтобы с этим справиться, мы делим кадр и трансформируем пространство так, чтобы уменьшить бесполезные области вроде тёмных и сильно искажённых краёв fisheye и привести движение объектов к линейному виду, с которым кодек справляется сильно лучше. В итоге поток становится легче, требует меньше трафика и надёжнее доставляется через нестабильные каналы.
Когда мы только начинали делать видеостриминг, ещё «до ковидных времён», использовали простой RTSP. Это работало, но никак не масштабировалось. RTP‑поток был нешифрованным, поэтому приходилось гонять его через VPN. А VPN — это дополнительные точки отказа, лишние прокси‑хопы, увеличение RTT и добавочная стоимость эксплуатации. Для системы масштабов автономного флота такая очевидная и простая вещь становится объектом оптимизации и переосмысления.

К тому же RTCP плохо управляется. Нам обязательно нужно уметь менять битрейт и FPS на лету, если связь деградирует. RTCP такой гибкости практически не давал.
Поэтому мы перешли на другую технологию — WHIP при поддержке наших коллег из Yandex Infrastructure.
Что изменилось принципиально:
-
Отказались от pull‑схемы. Раньше нам приходилось «забирать» поток с робота. Теперь робот сам устанавливает PTLS‑соединение и пушит поток нам.
-
Убрали VPN, прокси и весь лишний «зоопарк». Стало меньше движущихся частей, меньше нестабильных нод, выше надёжность.
-
Получили возможность управлять битрейтом и FPS. В WHIP это предусмотрено из коробки, и теперь мы можем действительно адаптироваться под качество канала.

Есть и приятные бонусы. Например, раньше RTCP при переключении сим‑карт терял часть кадров — поток «обрывался». WHIP же умеет транслировать одновременно через все доступные симки, и картинка становится заметно устойчивее.
Видео мы используем не только ради внешней среды. Есть ещё одна важная задача: следить за состоянием водителя‑испытателя. Работа у него сложная: формально он просто сидит и наблюдает, но фактически несёт полную ответственность за безопасность. Это монотонная нагрузка, которая вызывает усталость.
Поэтому у нас работает ML‑модель, которая анализирует видео с водительской камеры и понимает, всё ли в порядке: не засыпает ли человек, не выглядит ли он уставшим. Это дополнительный уровень безопасности, который помогает поддерживать высокое качество эксплуатации.
Видеостриминг мы получили — отлично. Дальше — управление юнитами.
Передача управляющих команд
Самый простой способ управления — стрелочки. Робот‑доставщик прекрасно управляется именно так: как в компьютерной игре. Подключились, нажали стрелку — робот поехал. Интуитивно, понятно, быстрая обратная связь: нажали — движение случилось.
Но вот управлять стрелочками машиной или тем более грузовиком уже куда менее приятно. Там есть прицеп, масса, инерция — и вся эта история начинает выглядеть слишком опасной.
Проблема стрелочек очевидна: они чувствительны к задержкам. Если у вас плохой интернет, результат будет как в онлайн‑игре — картинка начинает лагать, вы «погибаете», видите это слишком поздно, всё начинает «ехать». Для автономного транспорта это, мягко говоря, неприемлемо.

Поэтому для машин и грузовиков мы используем другой подход — траекторное управление. Оператор рисует в интерфейсе траекторию — зелёную линию, по которой юнит должен проехать. Эта траектория уходит на машину, дальше её обрабатывают ML‑модель и алгоритмы, а затем — safety‑контур. Он проверяет, что траектория не нарушает ПДД, не ведёт к коллизиям и вообще безопасна.

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

Но есть и важный плюс. Оператор может что‑то не заметить — например, машину, стоящую за грузовиком. А вот сам юнит видит всё своими камерами и сенсорами. Он учтёт то, что оператор не увидел, и корректно оценит риски передвижения рядом с препятствием.
Теперь поговорим о последней теме — о том, как наши команды доставляются на флот. Мы можем придумать, как именно хотим реализовать управление, но этого мало: команды ещё должны корректно доходить до юнитов, и у этого процесса есть свои требования.
Как команды попадают на флот
Данные идут в обе стороны — и от роботов, и от нас. Роботов много: сейчас их сотни, дальше — тысячи. Это, конечно, не масштаб поискового сервиса, но и не маленькая система. И важно другое: каждый робот больше трёх раз в секунду отправляет нам свой статус, телеметрию, команды, а также призывы операторам.
Любой призыв может оказаться началом инцидента. Поэтому нужно доставлять их как можно быстрее — и терять по пути как можно меньше. Чем раньше мы поймём, что что‑то пошло не так, тем быстрее сможем отреагировать.

При этом мы всё равно остаёмся толерантны к потерям. Их невозможно полностью убрать: грузовик движется по трассе, а не стоит в дата‑центре; машина едет в городе, где сигнал бывает нестабильным. Поэтому часть команд неизбежно будет теряться.
Отсюда хорошо видно разницу с видеостримингом. В видеостриминге мы стараемся запихнуть в канал как можно больше данных, надеемся, что значительная их часть дойдёт, а дальше кодеки и избыточность восстанавливают картинку настолько, чтобы оператор хотя бы увидел движение. Нам не так важно качество — важно различить динамику сцены.
С командами ситуация сложнее: у нас есть команды, которые мы можем потерять, и команды, которые должны быть доставлены в период своей актуальности (до истечения своего TTL относительно времени на роботе).
С первым типом всё достаточно просто: его мы шлём по стратегии best effort без какой‑либо буферизации, персистентности, outbox‑стратегий и прочих подходов к гарантированной доставке. Главная задача — найти адресата в дискавери‑системе (напомню, что в нашей схеме робот сам приходит в бэкенд и поддерживает постоянную связь для получения новых команд в режиме реального времени) и сообщить ему команду. Если адресат не готов её принять (обрыв связи, или ровер просто выключился), то мы признаём команду утерянной и забываем про неё.
Со вторым типом команд сложность, конечно, возрастает, но не кратно: команда ставится через персистентную базу с использованием outbox‑подхода с последующим поиском адресата через дискавери (каждый робот при подключении ждёт доставки в своей распределённой очереди).
Но, как говорится, есть нюанс: команда может залежаться в буфере и прийти в неактуальных для неё условиях. Буфер может переполниться и при подключении робота начать конфликтовать с новыми командами из других источников. Ну и, кроме всего прочего, команда может потеряться на самом роботе: автоматика на устройстве получит команду и не успеет никак ответить.
Для борьбы со всеми такими кондициями и поддержанием нервов операторов и автоматики процессов в добром здравии нам приходится жонглировать системой из тайм‑аутов и ревизий для линеаризации событий и отслеживания их актуальности в пределах робота. Делаем это крайне консервативными стратегиями обновления статусов событий на бэкенде и отслеживанием распределённых локов, где источник правды — движущийся автономный объект.
Изначально наше решение строилось вокруг VPN. Был робот, а между ним и нашей сетью существовал настоящий «зоопарк» протоколов. Мы использовали UDP, если хотели как можно быстрее «заддосить» ровер потоками команд, и TCP, если требовалась более надёжная доставка. Были и pull‑схемы, и push‑схемы — всё зависело от того, какую задачу мы пытались решить. Так исторически сложилось.

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

Дальше мы восстановили стрим через TLS‑балансер — теперь весь трафик идёт поверх mTLS и полностью проверяется. А использование gRPC‑стрима дало keepalive: если соединение с ровером пропадает, он пытается восстановить его как можно быстрее. А пока соединение живёт, робот сообщает свой текущий контекст выполнения команд и отправляет телеметрии.
Таким образом, у нас получается замкнутая realtime‑система (ну, насколько это вообще возможно над сетью в распределённой среде). В итоге получаем минимальные задержки и максимальную скорость восстановления связи — всё начинает работать так, как нужно.
Дальше — что происходит на стороне оператора.
Формат поставки команд
Во‑первых, оператор может отправлять важные управляющие команды. Иногда — сразу несколько подряд: например, нажимая кнопку «вперёд» несколько раз. Кроме того, вместе с ним команды могут отправлять и другие системы, и у них может быть своё внутреннее время. Команды могут не доходить — мы к этому готовы, это норма для реальной среды.
Важно помнить, что робот, который находится «по ту сторону» от наших бэкендов, — не клиент. Он такой же участник распределённой системы, как и любой другой сервис. Более того, именно робот — единственный, кто точно знает, какие команды на нём реально исполнились. Мы можем этого не знать: часть команд или статусов потерялась по пути, а робот уже что‑то сделал.
Отсюда следует ключевой принцип: единственный источник правды о выполнении команд — сам робот.
Поэтому все команды, которые ему передаются, должны быть идемпотентными. Во‑первых, чтобы сам робот мог проверить, что он уже такую команду исполнил. Во‑вторых, потому что единственное корректное время в системе находится именно на роботе: только он знает фактический порядок исполнения.
Из этой логики вытекает важное ограничение: мы не можем использовать буферизацию. Команды нельзя где‑то сохранить и допушить позже. Если команда отправилась, но не дошла — помянем её. Если доставить её позже, уже в другом контексте, робот исполнит команду, которая ему сейчас не подходит, — и в лучшем случае просто расстроится, а в худшем уедет в кювет. Нам это, очевидно, не нужно.
Мы разобрались с тем, что происходит на стороне rover‑proxy (который мы заменили на TLS‑стрим) и что происходит на стороне operator‑proxy, куда оператор отправляет идемпотентные команды. Остаётся вопрос: что находилось между ними?
Что было посередине
Посередине тоже был настоящий «зоопарк». Мы использовали PostgreSQL для гарантий доставки, но outbox‑стратегия реализовывалась через pull‑механизм с самого робота. Частота пулов была достаточно высокой, селектов было очень много, и они были очень гранулярными. Роботы могли синхронизироваться и приходить в базу за задачами волнами — всё это превращалось в штормы запросов и местами сильно перегружало базу. Как и многое в старом решении, это работало на этапе R&D, но это не масштабировалось на этапе построения реальных процессов.
Если нужно было доставлять команды очень быстро — например, команды непосредственного управления — никакого аудита уже не было. Просто отправляли всё напрямую. Очевидно, решение так себе.
У обеих схем были проблемы:
-
Когда всё шло напрямую, было тяжело дебажить происходящее.
-
Когда команды шли через PostgreSQL, система начинала захлёбываться. Достаточно было партнёрскому сервису в один момент решить вывести всех роботов на смену, PostgreSQL перегружался, отвечал дольше, остальные бэкенды начинали тайм‑аутиться, и выход на смену шёл крайне невесело.

Так жить было нельзя. Да, теоретически можно было шардировать PostgreSQL, но по факту транзакционность здесь не нужна. Мы и так толерантны к потерям, буферизация запрещена, а главная цель — доставлять команды как можно быстрее.
Поэтому мы перешли с гранулярного pull‑outbox, инициируемого ровером, на общий pull‑outbox. Мы берём много задач за раз и раскладываем их по отдельным очередям на каждого робота. Это даёт минимальную задержку и исключает накопление команд.
Задача аудита при этом никуда не исчезает. Redis её не решает — и это нормально. Аудит мы делаем асинхронно: команды, которые проходят через rover‑proxy или operator‑proxy, отправляются в Kafka, а уже оттуда асинхронно записываются в PostgreSQL. Так можно разбирать инциденты и смотреть историю команд, не влияя на скорость их доставки.

В итоге теперь мы умеем доставлять команды с необходимым уровнем гарантий, быстро, надёжно и своевременно. При это мы сохраняем прозрачность и управляемость как для оператора, так и для СИБ при аудите событий.
Эпилог
Развитие сервисов удалённого управления и операторской поддержки — это не просто вспомогательная часть экосистемы автономного транспорта, а необходимое условие его надёжной работы в реальных условиях. Даже самые продвинутые ML‑алгоритмы и автоматические системы не могут предсказать или решить все возможные нестандартные ситуации — здесь требуется вмешательство человека и гибкая инфраструктура, способная оперативно реагировать и обеспечивать безопасность. Благодаря этому каждый робот‑доставщик, роботакси или грузовик может выполнять свою задачу даже в самых сложных или неожиданно изменяющихся условиях.
Создание такой инфраструктуры — сложная инженерная задача, требующая постоянного развития, экспериментов и оптимизации. Мы стремимся к тому, чтобы наши сервисы были максимально надёжными, быстрыми и прозрачными. Сочетание автоматизации на базе искусственного интеллекта и человеческого контроля позволяет флоту Яндекса работать с максимальным уровнем эффективности и безопасности.
Спасибо, что прочитали. Если остались вопросы — жду вас в комментариях.
Автор: dmitIvh


