Что, если навигатор перестанет упрямо твердить «Развернитесь!», когда вы свернули с маршрута и предложит новый, более вам подходящий?

Изначально мы хотели решить этот конкретный кейс, ведь слишком прямолинейный алгоритм не допускал, что пользователь может намеренно выбрать другой путь, и всегда стоял на своём. Вот как это выглядело:

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

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

Дискриминация маршрута

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

Исходные веса: алгоритм выбирает центральный путь как самый дешёвый по сумме весов
Исходные веса: алгоритм выбирает центральный путь как самый дешёвый по сумме весов

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

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

Предпочитаемые рёбра

Дальше, казалось, дело техники — включить этот алгоритм в приложении. Сделали — и всё пошло не по плану. Мы не учли, что пользователь не всегда отклоняется от маршрута «осознанно». Значительная часть перестроений происходит случайно, особенно в начале маршрута: например, из-за погрешностей работы GPS или незначительных изменений маршрута в процессе движения по дворам. Такие случаи не должны менять маршрут. Пришлось откатить и пересмотреть подход.

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

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

В итоге у нас уже два алгоритма, но не было логики их включения.

Логика включения

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

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

В результате мы:

  • сократили время срабатывания новой логики, 

  • добавили отключающий дискриминацию триггер — любое последующее перестроение маршрута после срабатывания дискриминации.

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

  • если пользователь выбирает первый маршрут — считаем, что ему всегда важно получать самый быстрый маршрут в моменте;

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

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

Логично для системы, но не для пользователя. И так понятно, что новый маршрут будет хуже. Нет смысла предлагать «лучший» вариант вернуться на исходный маршрут — это лишний раз будет раздражать пользователя бесполезными уведомлениями. 

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

Также не забыли и про ещё один сценарий: движение по дворам в начале маршрута. Мы этого можем не замечать, но пока навигатор запущен и машина выезжает из двора на улицу, он может несколько раз перестроить маршрут. Это связано как с уточнением GPS-сигнала, так и с препятствиями во дворах, которые приходится объезжать. В результате наша логика обросла ещё одним условием: пока пользователь едет по двору, мы не дискриминируем маршрут, т.е. любое перестроение во дворе не будет влиять на дальнейший маршрут.  

Вывод

Как только мы внедрили алгоритм дискриминации, концепцию предпочитаемых рёбер и гибкую логику включения, навигация в 2ГИС стала заметно удобнее. 

Самое важное — мы избавились от ощущения, что навигатор «не понимает» пользователя. Маршруты стали предсказуемее и логичнее: система лучше понимает намерения водителя и предлагает более подходящий вариант. Особенно приятно, что ушёл негативный фидбек — теперь поведение навигатора вызывает меньше вопросов и раздражения.

Мы не останавливаемся и продолжаем развивать наши алгоритмы. Если хочешь с нами, то у на как раз открыты вакансии в команде Транспорт. Нам очень нужны C++/Qt/QML разработчик под Android, менеджер продукта, проектный менеджер и Senior QA-инженер.

Комментарии (15)


  1. saege5b
    24.06.2025 09:33

    Вот ещё бы как-то решили бы проблему спуфинга жпс...


    1. Diminho Автор
      24.06.2025 09:33

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


  1. Nick0las
    24.06.2025 09:33

    Тема конечно интересная. А вы не думали добавить в навигатор возможность личной настройки правил дискриминации? А то ваш навигатор упорно не желает вести по грунтовым дорогам, даже если это намного быстрее (и комфортнее для некоторых водителей), зато часто предлагает неприятные маневры типа U-образного разворота на многополосном шоссе, хотя в принципе можно проехать немного дольше, но без таких маневров.


    1. Diminho Автор
      24.06.2025 09:33

      Мы действительно рассматривали возможность ручной настройки, но скорее склоняемся к тому, чтобы алгоритм сам предлагал более «человечные» маршруты — без необходимости лезть в параметры. На практике ими пользуется довольно небольшая часть пользователей, а настройки такого рода скорее запутают, чем помогут.

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


      1. Nick0las
        24.06.2025 09:33

        Для разных водителях разные маршруты оказываются более человечными. Кто-то не хочет лезть на грунт, кто-то не любит напрягающие маневры типа разворота на 180 на трассе. Кто-то согласен ехать немного дольше но зато поменьше перекрестков и светофоров. А то приходится дополнительные точки ставить чтобы навигатор повел тебя так как хочешь, а не так как ездит абстрактный усредненный водитель.


  1. wataru
    24.06.2025 09:33

    А что у вас за алгоритм который вот так вот просто перебирает ребра по приоритету? Или это просто упрощение для картинки в статье и у вас там какой-нибудь A* на самом деле?


    1. spovst
      24.06.2025 09:33

      На гифках для простоты обычный алгоритм Дейкстры, у нас A*, да, частным случаем которого Дейкстра и является.


      1. wataru
        24.06.2025 09:33

        Ну нет, в обычной дейкстре выбирается не ребро минимальной стоимости, а вершина с минимальной оценкой. Вы же там в тексте именно про ребро пишите. Хотя по гифке непонятно, может там и оценка минимальная береться.


        1. spovst
          24.06.2025 09:33

          На гифках дейкстра, как я и сказал. Анимации максимально упрощены, чтобы деталями не грузить, но там, естественно, не сам вес ребра учитывается при его извлечении из очереди, а накопленная оценка пути.


  1. mit5x
    24.06.2025 09:33

    Почему навигаторы, до сих по не имеют такой настройки:

    1) избегать выезда на перекрёсток с поворотом налево без светофора?

    2) строить маршрут без разворота.

    3) строить маршрут без поворотов налево.

    ?


    1. Diminho Автор
      24.06.2025 09:33

      На самом деле такие настройки уже встроены в алгоритм — он старается избегать разворотов, неудобных поворотов налево и других не самых комфортных манёвров.

      Полностью исключить такие манёвры не всегда возможно, потому что алгоритм старается найти баланс между комфортом и оптимальностью маршрута по времени и расстоянию.

      Если где-то получилось иначе — интересно разобраться, почему так вышло. Будем благодарны, если поделитесь примерами.


  1. aMster1
    24.06.2025 09:33

    Еще один момент бесит в навигаторах при перестроении маршрута - он "забывает" выбор пользователя.

    Например при первоначальном выборе маршрута большинство навигаторов предлагает пару-тройку вариантов ( побыстрее но подлинее, коротко но через центр, повихлять но вроде без пробок), пользователь выбирает нужный ему вариант и все. Дальше его уже никто не спрашивает, и если вдруг сложились звезды что навигатор решил перестроить маршрут - он его перестраивает по своему разумению. Никакой памяти чего там этот кожаный мешок хотел у навигатора нет. А может он на заправку хотел заскочить, или друга подкинуть? И не просто так выбрал маршрут...


    1. Panzerschrek
      24.06.2025 09:33

      Если пользователь знает лучше, как ему ехать, зачем ему тогда навигатор?


      1. V2D1M
        24.06.2025 09:33

        Пользователь знает, что не надо ехать через центр. Лучше выбрать альтернативный , чуть более длинный маршрут. Но если мы пропустили съезд, или на маршруте возникла пробка, то пользователь хочет, чтобы маршрут был переложен не снова через центр, а примерно как он выбрал изначально.


      1. aMster1
        24.06.2025 09:33

        Я навигатор использую когда по городу передвигаюсь который не очень хорошо знаю, например к сестре приезжаю и беру у нее машину. Я в целом и общем знаю город, но чуть поподробнее - нет. Там то улицу перекопают, то односторонку сделают. То развязку построят. Но некоторых улиц я стараюсь избегать, поскольку там можно попасть в пробку за 20 секунд.

        В общем тут вопрос в тактике и стратегии.