MAATRIX / Блог / Курьерская служба: маршрутизация 200 адресов в день на своём сервере

Курьерская служба: маршрутизация 200 адресов в день на своём сервере

MAATRIX

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

Как маршруты считают сейчас

В большинстве курьерских служб, которые не доросли до собственной разработки, путь один и тот же в три стадии.

Стадия первая — ручная. Диспетчер (иногда сам курьер) смотрит на список адресов, группирует их «на глаз» по районам, прикидывает порядок объезда исходя из личного знания города. Это работает, пока курьеров пять, а адресов на смену — двадцать-тридцать. При росте ручная сборка маршрута начинает съедать заметную часть рабочего времени диспетчера каждое утро, и это время не масштабируется — второй диспетчер не ускоряет процесс вдвое, потому что знание города у людей разное и маршруты получаются неровные.

Стадия вторая — Google Maps или Яндекс.Карты руками, по одному адресу за раз. Диспетчер строит точку А-Б-В-Г, смотрит на пробки, меняет порядок, строит заново. Это чуть быстрее ручной прикидки, но всё ещё линейно зависит от количества точек и не решает главную задачу — это не оптимизация маршрута под ограничения (вместимость машины, временные окна доставки, несколько курьеров одновременно), а просто визуализация одного порядка объезда, который диспетчер сам придумал.

Стадия третья — облачный сервис маршрутизации. Загружаете список адресов через API или веб-интерфейс, сервис строит оптимальный (или близкий к оптимальному) маршрут с учётом ограничений, отдаёт последовательность точек и иногда — готовую ссылку для навигатора курьера. Это уже настоящая оптимизация, и на небольшом объёме это разумный вариант: разработка своей системы не окупится, а тариф воспринимается как разумная цена вопроса.

Проблема начинается, когда служба растёт. У задачи маршрутизации почти всегда есть кривая стоимости, привязанная к числу точек, запросов или активных курьеров, и она не плоская — с ростом объёма доставок счёт от сервиса маршрутизации растёт вместе с ним, причём часто быстрее, чем растёт выручка от новых заказов, потому что маржа на доставку и так тонкая.

Почему тариф за точку бьёт по марже сильнее всего именно при росте

Это стоит проговорить прямо, потому что на старте бизнеса эта проблема не видна.

Когда у курьерской службы 20 адресов в день, оплата за маршрутизацию — это условно чашка кофе. Когда адресов 200, а вы держите несколько курьеров и пересчитываете маршруты не раз в сутки, а при каждом новом заказе или отмене (чтобы не гонять курьера впустую), число запросов к сервису маршрутизации растёт не линейно, а быстрее — потому что каждая перестройка маршрута под изменившийся список точек тоже стоит денег. Заголовок этой статьи не случайно берёт число 200 — это тот порядок величины, на котором зависимость от стороннего тарифа перестаёт быть мелочью и начинает заметно давить на юнит-экономику одной доставки.

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

Третья сторона — зависимость от чужого API как единой точки отказа. Если сервис маршрутизации перестаёт отвечать (лимиты, авария на стороне провайдера, блокировка платежа), у вас на несколько часов останавливается не вспомогательная функция, а сама возможность посчитать, кому куда ехать. Для курьерской службы это операционный риск, а не абстрактная зависимость от вендора.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Альтернатива: открытые алгоритмы маршрутизации на своём сервере

Задача построения маршрута доставки — классическая математическая задача, и для неё давно существуют открытые (open source) инструменты, которые можно развернуть на собственном сервере и не платить за запрос вообще. Стоимость — это стоимость сервера, фиксированная и не зависящая от того, 50 у вас точек в день или 500.

Два инструмента, которые обычно и составляют основу такой системы:

OSRM (Open Source Routing Machine) — движок построения маршрутов по дорожной сети. Он берёт данные OpenStreetMap для нужного города или региона, строит из них граф дорог и по запросу «откуда — куда» отдаёт кратчайший или самый быстрый путь с учётом дорожной сети, разворотов, односторонних улиц. Это тот слой, который отвечает на вопрос «сколько ехать от точки А до точки Б» и строит саму геометрию маршрута для навигации курьера.

VROOM (Vehicle Routing Open-source Optimization Machine) — решатель задачи маршрутизации транспорта (VRP, vehicle routing problem). Он берёт список точек доставки, список курьеров с их вместимостью и рабочим временем, временные окна для каждого адреса (если клиент просил привезти с 14 до 16) — и отдаёт оптимальное распределение точек по курьерам и оптимальный порядок объезда для каждого. VROOM использует OSRM как источник данных о расстояниях и времени в пути между точками — они работают в связке.

Оба проекта живут на GitHub, распространяются под открытыми лицензиями, не требуют лицензионных отчислений и разворачиваются в Docker-контейнерах — это буквально несколько команд на сервере, а не отдельный проект разработки с нуля.

Есть и альтернативные open source решатели VRP — например, Google OR-Tools как библиотека для написания собственного решателя под нестандартные ограничения (если стандартной модели VROOM вам не хватает). Но для типовой курьерской задачи «развезти N адресов силами M курьеров с учётом временных окон» связки OSRM + VROOM обычно достаточно без написания своего кода поверх.

Разворачиваем маршрутизацию: карта, движок, API

Практический план разворачивания OSRM для вашего города или региона выглядит так.

Сначала скачиваете актуальный экстракт OpenStreetMap для нужного региона (сервисы вроде Geofabrik публикуют регулярные экспорты в формате .osm.pbf по странам и регионам бесплатно):

wget https://download.geofabrik.de/russia/moscow-latest.osm.pbf

Дальше — три этапа обработки данных под профиль передвижения (для курьерской доставки обычно нужен автомобильный профиль car.lua, который идёт в комплекте с OSRM):

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-extract -p /opt/car.lua /data/moscow-latest.osm.pbf
docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-partition /data/moscow-latest.osrm
docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-customize /data/moscow-latest.osrm

После обработки поднимаете сам сервер маршрутизации, который слушает HTTP и отвечает на запросы:

docker run -t -i -p 5000:5000 -v "${PWD}:/data" osrm/osrm-backend osrm-routed --algorithm mld /data/moscow-latest.osrm

На этом этапе у вас уже есть рабочий движок построения маршрутов между двумя точками — можно проверить простым запросом:

curl "http://localhost:5000/route/v1/driving/37.6173,55.7558;37.6011,55.7387?overview=false"

Это уже полноценная замена ручному построению маршрута точка-в-точку. Но курьерской службе нужна не пара точек, а оптимальный объезд десятков и сотен адресов несколькими курьерами — здесь в дело вступает VROOM.

Решаем задачу маршрутизации: курьеры, временные окна, вместимость

VROOM тоже разворачивается в Docker и по умолчанию обращается к локальному OSRM за данными о расстояниях:

docker run -d -p 3000:3000 --network host vroomvrp/vroom-docker:v1.14.0

Дальше вся работа сводится к формированию JSON-запроса, где вы описываете реальность вашей курьерской службы: список заказов (jobs) с координатами и, если нужно, временным окном доставки, и список курьеров (vehicles) с точкой старта, вместимостью машины или сумки и рабочим временем смены.

Упрощённый пример запроса на 3 точки и одного курьера:

{
  "vehicles": [
    {
      "id": 1,
      "start": [37.6156, 55.7522],
      "end": [37.6156, 55.7522],
      "time_window": [28800, 61200],
      "capacity": [20]
    }
  ],
  "jobs": [
    {"id": 1, "location": [37.6011, 55.7387], "delivery": [1], "time_windows": [[32400, 39600]]},
    {"id": 2, "location": [37.6298, 55.7601], "delivery": [1]},
    {"id": 3, "location": [37.5921, 55.7204], "delivery": [1], "time_windows": [[36000, 43200]]}
  ]
}

time_window и time_windows заданы в секундах от полуночи — так 28800 это 8:00, 61200 это 17:00. capacity и delivery — условные единицы вместимости, могут быть числом коробок, весом или объёмом. VROOM возвращает оптимальный порядок объезда для каждого курьера, время прибытия к каждой точке с учётом дорожной сети (через OSRM) и сигнализирует, если заказ технически невозможно вписать в маршрут при заданных ограничениях — диспетчер сразу видит проблемные заказы, а не узнаёт о них от опоздавшего курьера.

На реальных 200 адресах в день с несколькими курьерами именно учёт временных окон и вместимости при одновременном распределении по нескольким машинам — та работа, которую руками или через простую карту-визуализатор не сделать, а платный облачный API маршрутизации делает за деньги на каждый запрос. VROOM делает то же самое на вашем сервере бесплатно после разворачивания.

Дальше это оборачивается в простой скрипт или небольшой backend-сервис: список заказов на смену выгружается из вашей CRM или системы приёма заказов, автоматически собирается в JSON нужного формата, отправляется в VROOM, а результат — последовательность точек по каждому курьеру — возвращается диспетчеру или сразу в приложение курьера. Если у вас уже есть собственный трекинг курьеров, эта часть логично встраивается рядом — подробнее о развёртывании такого трекинга на своём сервере есть отдельный разбор: трекинг курьеров на своём сервере.

Экономика: фиксированная стоимость сервера против растущей платы за облако

Смысл переезда на собственную маршрутизацию — не в том, что открытые алгоритмы точнее коммерческих (это не так, у крупных облачных сервисов маршрутизации годы инженерной работы и учёт факторов вроде реального трафика в реальном времени, которого у вас на своём сервере не будет без отдельной интеграции). Смысл — в структуре расходов.

Облачный сервис маршрутизацииСвой сервер с OSRM + VROOM
Модель оплатыза запрос / точку / активного курьерафиксированная аренда сервера в месяц
Рост при увеличении объёма доставоксчёт растёт вместе с числом адресов и пересчётовстоимость не меняется до упора в ресурсы сервера
Учёт трафика в реальном времениобычно да, из коробкинет — нужна отдельная интеграция или без неё
Точка отказазависит от доступности стороннего APIзависит от вашего сервера — под вашим контролем
Данные о маршрутах и клиентахуходят на сторону сервисаостаются у вас

Сервер под связку OSRM + VROOM для одного крупного города — это не тяжёлая нагрузка: обработанные данные дорожной сети для мегаполиса занимают несколько гигабайт на диске, а сами запросы маршрутизации считаются быстро даже на паре ядер и нескольких гигабайтах оперативной памяти, потому что вся тяжёлая предобработка графа дорог (osrm-extract, osrm-partition, osrm-customize) сделана заранее, а не при каждом запросе. Для областного центра или региона хватает сервера среднего уровня; если планируете покрыть несколько городов одновременно, разумно считать отдельно объём под каждый регион — граф дорог хранится на диск для каждого экстракта отдельно.

Точную экономию в рублях мы здесь не берёмся считать — она зависит от вашего текущего тарифа и числа запросов в день. Но логика простая: у собственного сервера стоимость не зависит от числа адресов, а у оплаты за запрос — зависит напрямую, и при росте бизнеса эти две кривые расходятся всё сильнее. Если сравнивать эту логику шире — не только для маршрутизации, а вообще для того, что выгоднее держать на своей инфраструктуре, а что арендовать по подписке, есть отдельный разбор: что выгодно держать на своём сервере.

Отдельно стоит учесть надёжность самой доставки как процесса — если у вас уже случалась ситуация, когда повторный вызов курьера или пересчёт заказа приводил к задвоенному списанию с клиента, это отдельная категория граблей, не про маршрутизацию, но про ту же область: повторная доставка списала деньги дважды. А если курьерская служба обслуживает свой интернет-магазин или площадку заказов, вопрос комиссии агрегатора логично рассматривать в связке с этой темой — про отказ от процента с оборота в пользу своей витрины заказов есть материал: свой сайт заказов против комиссии.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Насколько сложно поддерживать OSRM и VROOM самостоятельно, если в команде нет выделенного разработчика?

Разворачивание — это набор команд в Docker, описанных выше, и его можно выполнить один раз по инструкции. Дальше система работает без постоянного вмешательства: обновлять карту дорожной сети имеет смысл раз в несколько месяцев (перекачать новый .osm.pbf и заново прогнать osrm-extract/partition/customize), а VROOM вообще не требует обновления данных, потому что берёт список заказов на лету через API. Постоянной ручной работы с самими движками не требуется.

Что если у меня не один город, а несколько регионов доставки?

Каждый регион — это отдельный обработанный .osrm-файл на диске, и один сервер OSRM может обслуживать несколько таких графов одновременно на разных портах или через libosrm в одном процессе. Практически проще держать несколько лёгких контейнеров osrm-routed, по одному на регион, и адресовать запросы VROOM в нужный порт в зависимости от того, для какого города строится маршрут.

Учитывает ли своя маршрутизация пробки в реальном времени?

Нет, не из коробки — базовая связка OSRM + VROOM считает маршрут по статичной дорожной сети и типичным скоростям на разных типах дорог, без живых данных о заторах. Для курьерской доставки внутри города это чаще всего приемлемо, потому что маршрут всё равно оптимизируется по порядку объезда точек, а не только по скорости одного перегона. Если учёт трафика в реальном времени критичен, это отдельная и более тяжёлая интеграция (собственные данные телеметрии или сторонний источник трафика), и её стоит оценивать отдельно от базовой задачи оптимизации маршрута.

Можно ли постепенно перейти, не отключая старый сервис маршрутизации сразу?

Да, и это разумный путь. Разверните OSRM и VROOM на тестовом сервере, прогоните через них те же списки адресов, что уходят сейчас в облачный сервис, и сравните маршруты и время построения на реальных данных вашей службы за одну-две недели. Только убедившись, что результат устраивает диспетчеров и курьеров, переключайте продакшн-поток заказов на свою систему.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →