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

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

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

В итоге у нас уже два алгоритма, но не было логики их включения.
Логика включения
Первая идея была максимально простой: если нас раздражает, что навигатор, после того как мы сошли с предложенного маршрута, упрямо пытается вернуть нас обратно и каждый раз предлагает развернуться — значит, можно использовать пару проигнорированных разворотов как триггер для включения дискриминации маршрута.
Сделали первую сборку — и поняли, что это так не работает. На практике ключевое — тестировать алгоритмы в реальной поездке, сидя за рулём на привычных маршрутах. Оказалось, что ждать двух разворотов слишком долго — хочется сразу новый маршрут. А если продолжать сходить с маршрута, алгоритм начинает ухудшать маршрут до бесконечности.
В результате мы:
сократили время срабатывания новой логики,
добавили отключающий дискриминацию триггер — любое последующее перестроение маршрута после срабатывания дискриминации.
Параллельно подумали и про предпочитаемые рёбра. Сохранять выбранный маршрут — это хорошо, но чаще всего нужен просто лучший маршрут в данный момент. Мы реализовали несколько эвристик, чтобы лучше определять, когда стоит сохранить маршрут, а когда пересчитать его заново для оптимального результата:
если пользователь выбирает первый маршрут — считаем, что ему всегда важно получать самый быстрый маршрут в моменте;
если выбран альтернативный — считаем, что он пользователю приглянулся по каким-то причинам больше других, и его важно сохранить.
Казалось, что это всё. Но во время следующей итерации тестирования в реальных условиях обнаружилась ещё одна раздражающая проблема: стоит отклониться от хорошего маршрута и получить дискриминированный вариант — навигатор сразу подсказывает, что найден маршрут лучше.
Логично для системы, но не для пользователя. И так понятно, что новый маршрут будет хуже. Нет смысла предлагать «лучший» вариант вернуться на исходный маршрут — это лишний раз будет раздражать пользователя бесполезными уведомлениями.
Поэтому мы дополнили логику новым условием: поиск лучшего маршрута должен начинаться не сразу, а через некоторое время после срабатывания алгоритма дискриминации.
Также не забыли и про ещё один сценарий: движение по дворам в начале маршрута. Мы этого можем не замечать, но пока навигатор запущен и машина выезжает из двора на улицу, он может несколько раз перестроить маршрут. Это связано как с уточнением GPS-сигнала, так и с препятствиями во дворах, которые приходится объезжать. В результате наша логика обросла ещё одним условием: пока пользователь едет по двору, мы не дискриминируем маршрут, т.е. любое перестроение во дворе не будет влиять на дальнейший маршрут.
Вывод
Как только мы внедрили алгоритм дискриминации, концепцию предпочитаемых рёбер и гибкую логику включения, навигация в 2ГИС стала заметно удобнее.
Самое важное — мы избавились от ощущения, что навигатор «не понимает» пользователя. Маршруты стали предсказуемее и логичнее: система лучше понимает намерения водителя и предлагает более подходящий вариант. Особенно приятно, что ушёл негативный фидбек — теперь поведение навигатора вызывает меньше вопросов и раздражения.
Мы не останавливаемся и продолжаем развивать наши алгоритмы. Если хочешь с нами, то у на как раз открыты вакансии в команде Транспорт. Нам очень нужны C++/Qt/QML разработчик под Android, менеджер продукта, проектный менеджер и Senior QA-инженер.
Комментарии (15)
Nick0las
24.06.2025 09:33Тема конечно интересная. А вы не думали добавить в навигатор возможность личной настройки правил дискриминации? А то ваш навигатор упорно не желает вести по грунтовым дорогам, даже если это намного быстрее (и комфортнее для некоторых водителей), зато часто предлагает неприятные маневры типа U-образного разворота на многополосном шоссе, хотя в принципе можно проехать немного дольше, но без таких маневров.
Diminho Автор
24.06.2025 09:33Мы действительно рассматривали возможность ручной настройки, но скорее склоняемся к тому, чтобы алгоритм сам предлагал более «человечные» маршруты — без необходимости лезть в параметры. На практике ими пользуется довольно небольшая часть пользователей, а настройки такого рода скорее запутают, чем помогут.
В любом случае на текущих правилах не останавливаемся — будем постепенно добавлять новые, чтобы лучше учитывать реальные предпочтения. Спасибо за предложения, обязательно подумаем. Если поделитесь конкретными примерами — будем очень признательны. Всегда интересно увидеть, где у нас есть потенциал для улучшений.
Nick0las
24.06.2025 09:33Для разных водителях разные маршруты оказываются более человечными. Кто-то не хочет лезть на грунт, кто-то не любит напрягающие маневры типа разворота на 180 на трассе. Кто-то согласен ехать немного дольше но зато поменьше перекрестков и светофоров. А то приходится дополнительные точки ставить чтобы навигатор повел тебя так как хочешь, а не так как ездит абстрактный усредненный водитель.
wataru
24.06.2025 09:33А что у вас за алгоритм который вот так вот просто перебирает ребра по приоритету? Или это просто упрощение для картинки в статье и у вас там какой-нибудь A* на самом деле?
spovst
24.06.2025 09:33На гифках для простоты обычный алгоритм Дейкстры, у нас A*, да, частным случаем которого Дейкстра и является.
wataru
24.06.2025 09:33Ну нет, в обычной дейкстре выбирается не ребро минимальной стоимости, а вершина с минимальной оценкой. Вы же там в тексте именно про ребро пишите. Хотя по гифке непонятно, может там и оценка минимальная береться.
spovst
24.06.2025 09:33На гифках дейкстра, как я и сказал. Анимации максимально упрощены, чтобы деталями не грузить, но там, естественно, не сам вес ребра учитывается при его извлечении из очереди, а накопленная оценка пути.
mit5x
24.06.2025 09:33Почему навигаторы, до сих по не имеют такой настройки:
1) избегать выезда на перекрёсток с поворотом налево без светофора?
2) строить маршрут без разворота.
3) строить маршрут без поворотов налево.
?
Diminho Автор
24.06.2025 09:33На самом деле такие настройки уже встроены в алгоритм — он старается избегать разворотов, неудобных поворотов налево и других не самых комфортных манёвров.
Полностью исключить такие манёвры не всегда возможно, потому что алгоритм старается найти баланс между комфортом и оптимальностью маршрута по времени и расстоянию.
Если где-то получилось иначе — интересно разобраться, почему так вышло. Будем благодарны, если поделитесь примерами.
aMster1
24.06.2025 09:33Еще один момент бесит в навигаторах при перестроении маршрута - он "забывает" выбор пользователя.
Например при первоначальном выборе маршрута большинство навигаторов предлагает пару-тройку вариантов ( побыстрее но подлинее, коротко но через центр, повихлять но вроде без пробок), пользователь выбирает нужный ему вариант и все. Дальше его уже никто не спрашивает, и если вдруг сложились звезды что навигатор решил перестроить маршрут - он его перестраивает по своему разумению. Никакой памяти чего там этот кожаный мешок хотел у навигатора нет. А может он на заправку хотел заскочить, или друга подкинуть? И не просто так выбрал маршрут...
Panzerschrek
24.06.2025 09:33Если пользователь знает лучше, как ему ехать, зачем ему тогда навигатор?
V2D1M
24.06.2025 09:33Пользователь знает, что не надо ехать через центр. Лучше выбрать альтернативный , чуть более длинный маршрут. Но если мы пропустили съезд, или на маршруте возникла пробка, то пользователь хочет, чтобы маршрут был переложен не снова через центр, а примерно как он выбрал изначально.
aMster1
24.06.2025 09:33Я навигатор использую когда по городу передвигаюсь который не очень хорошо знаю, например к сестре приезжаю и беру у нее машину. Я в целом и общем знаю город, но чуть поподробнее - нет. Там то улицу перекопают, то односторонку сделают. То развязку построят. Но некоторых улиц я стараюсь избегать, поскольку там можно попасть в пробку за 20 секунд.
В общем тут вопрос в тактике и стратегии.
saege5b
Вот ещё бы как-то решили бы проблему спуфинга жпс...
Diminho Автор
Мы тоже хотим решить эту проблему — и, честно говоря, давно и серьёзно над этим работаем. Задача, мягко говоря, непростая. Стараемся двигаться шаг за шагом, улучшая работу приложения в тех сценариях, где это возможно.