Проект трекинга курьера часто начинается с простой задачи: показывать клиенту, где находится доставка. Но продакшн требует решения dead-reckoning при потере GPS, map snapping на дорожный граф и синхронизацию между тремя компонентами: приложением курьера, серверной шиной и приложением клиента. На одном из проектов с 500 курьерами мы смогли снизить задержку обновления до 2 секунд, перейдя с WebSocket на MQTT и настроив Redis с TTL 60 секунд. Такой подход экономит часы разработки и снижает эксплуатационные затраты до 30% за счёт сокращения трафика. Ошибки на этапе выбора протокола или настройки батарейного режима приводят к переписыванию кода — поэтому важно с самого начала заложить правильную архитектуру. Закажите аудит вашего текущего решения — мы подберём оптимальную конфигурацию.
Почему трекинг курьера — это не просто GPS?
Продакшн-система включает четыре ключевых компонента:
- Dead-reckoning при потере сигнала GPS — интерполяция последних координат с учётом скорости и направления.
- Map snapping к дорогам — сырые GPS-точки прыгают относительно дороги, их надо притягивать к дорожному графу.
- Логику состояний курьера: idle → assigned → picking_up → delivering → completed.
- Батарейный компромисс на стороне приложения курьера: 5 секунд против 15 секунд.
Приложение курьера: сбор и передача координат
На Android используем FusedLocationProviderClient из play-services-location в Foreground Service. Интервал обновления — компромисс: 5 секунд даёт точность, 15 секунд экономит батарею. Для пешего курьера — Priority.PRIORITY_HIGH_ACCURACY + 5 секунд. Для авто — Priority.PRIORITY_BALANCED_POWER_ACCURACY + 10 секунд с setMinUpdateDistanceMeters(20f).
На iOS — CLLocationManager с desiredAccuracy: kCLLocationAccuracyBestForNavigation в активном режиме и переключением на Significant Location Changes при фоне. activityType = .automotiveNavigation активирует фильтрацию шума GPS.
Фильтрация аномальных точек обязательна. GPS на городских улицах выдаёт скачки на 50-200 метров. Простой фильтр: отбрасываем точку, если horizontalAccuracy > 50 метров или расчётная скорость > 200 км/ч.
func shouldAcceptLocation(_ location: CLLocation) -> Bool { guard location.horizontalAccuracy > 0, location.horizontalAccuracy <= 50 else { return false } if let lastLocation = lastAcceptedLocation { let timeDelta = location.timestamp.timeIntervalSince(lastLocation.timestamp) let distance = location.distance(from: lastLocation) let impliedSpeed = distance / timeDelta if impliedSpeed > 55.6 { return false } } return true } Буферизация в локальной БД + отправка пачками при восстановлении сети — стандартная схема.
Серверная шина: WebSocket или MQTT?
| Характеристика | WebSocket (Socket.IO) | MQTT (EMQ X) |
|---|---|---|
| Трафик | Выше из-за заголовков | Ниже в 3 раза за счёт бинарных фреймов |
| Работа с мобильными сетями | Хуже — разрыв соединения требует переподключения | Лучше — QoS + сессии с persist |
| Масштабирование | Подходит до нескольких тысяч курьеров | Легко масштабируется до десятков тысяч |
| Стек | Node.js, FastAPI | Mosquitto, EMQ X, AWS IoT Core |
Для небольших нагрузок выбираем Socket.IO. Для масштаба — MQTT. [MQTT] (https://en.wikipedia.org/wiki/MQTT) определяет бинарный протокол, что даёт выигрыш в трафике до 70% на мобильной сети.
Хранение текущей позиции — Redis: SET courier:{id}:position с TTL 60 секунд. История маршрута — TimescaleDB.
Map snapping. Сырые GPS-координаты выравниваем по дорогам через Google Roads API или OSRM self-hosted (бесплатно, <10 мс).
Как обеспечить плавную анимацию маркера?
Обновления каждые 5-10 секунд. Без анимации маркер прыгает. Интерполируем движение между двумя точками.
На Android через ValueAnimator:
val animator = ValueAnimator.ofFloat(0f, 1f).apply { duration = 3000 interpolator = LinearInterpolator() addUpdateListener { animation -> val fraction = animation.animatedFraction val lat = prevLat + (newLat - prevLat) * fraction val lon = prevLon + (newLon - prevLon) * fraction marker.position = LatLng(lat, lon) } } animator.start() На iOS — CADisplayLink или UIView.animate с кастомным timing. Угол поворота маркера считаем через atan2.
В Flutter — TweenAnimationBuilder с Tween<LatLng>. Для google_maps_flutter обновляем Marker(position: interpolatedPosition) в Ticker каждые 16 мс.
Состояния курьера
| Состояние | Описание | UI клиента |
|---|---|---|
| idle | Курьер свободен | "Ищем курьера" |
| assigned | Назначен на заказ | "Курьер едет к ресторану" |
| picking_up | Забирает заказ | "Курьер у ресторана" |
| delivering | Везёт заказ | "Курьер везёт ваш заказ" |
| completed | Доставлен | "Заказ доставлен" |
Переходы управляются сервером. Закажите разработку под ваш сценарий — мы учтём все нюансы.
Что входит в работу
- Проектирование протокола (WebSocket/MQTT) и схемы данных.
- Разработка приложения курьера с фоновым трекингом.
- Реализация серверной шины с Redis и TimescaleDB.
- Разработка UI клиента с плавной анимацией и состояниями.
- Нагрузочное тестирование: 100/500/1000 курьеров.
- Документация и обучающая встреча.
Процесс работы и сроки
- Аналитика: аудит текущей инфраструктуры, определение протокола.
- Проектирование: архитектура шины, схемы состояний.
- Разработка: реализация приложений и сервера.
- Тестирование: юнит-тесты, интеграционное, нагрузочное.
- Деплой: релиз в App Store и Google Play.
Срок: от 2 до 4 недель в зависимости от числа платформ и готовности серверного API.
Почему стоит работать с нами?
Мы реализовали трекинг для 15+ логистических проектов. Наши инженеры сертифицированы по iOS и Android. Гарантируем точность трека до 5 метров и стабильность при 10 000 одновременных сессий. Получите консультацию по вашему кейсу — свяжитесь с нами.







