AngoraTek EN
← Retour au blog
Notes de terrain 7 min de lecture

La carte est la partie facile du suivi de flotte

Dix mille véhicules qui transmettent leur position toutes les cinq secondes, cela fait 2 000 mises à jour de position par seconde. Au changement d'équipe, quand l'appareil de chaque chauffeur se reconnecte en même temps, comptez trois à cinq fois ce débit pendant les premières minutes de la matinée. Chacun de ces rapports doit être validé, persisté, et affiché à l'écran avant qu'un répartiteur n'appelle un chauffeur.

La carte, elle, est la partie facile. N'importe quel fournisseur sait dessiner un camion qui bouge sur une tuile. Ce qui rend le suivi de flotte difficile, et ce qui décide de la confiance des répartiteurs, c'est le pipeline qui tourne en dessous. Des millions de mises à jour par heure le traversent, chacune validée, persistée, servie assez vite pour que l'écran ne contredise jamais la réalité. Un répartiteur attrape une carte qui ment exactement une fois. Après, il appelle directement les chauffeurs, et l'outil de suivi devient un tableau de bord très cher que personne n'ouvre.

Ce qui se casse quand le pipeline est bâti à moitié

Voici ce qui arrive quand la construction s'arrête à mi-chemin. Le rapport d'un véhicule arrive à la seconde 61 au lieu de la seconde 60, patiente dans une file, puis s'écrit sur un pool de connexions déjà saturé. À l'écran, le camion affiche 300 mètres de retard sur sa position réelle, et le répartiteur envoie un chauffeur à une adresse que ce camion a quittée dix minutes plus tôt. Rien n'a planté. Le système est simplement en retard de façon constante, ce qui est pire, parce que le retard ressemble à de la vérité.

Deux bases de données et un serveur de tuiles

L'architecture de ce problème est ennuyeuse, et l'ennui est justement le point. Les véhicules publient leurs positions par MQTT vers un service d'ingestion. Le service écrit les positions courantes dans Redis et persiste chaque position dans PostgreSQL. Un serveur de tuiles lit dans Redis et alimente le frontend.

Redis garde la dernière position connue de chaque véhicule avec un TTL de 5 minutes, si bien que la vue temps réel ne touche jamais une base de données. PostgreSQL conserve l'historique complet des trajets, et c'est ainsi qu'on répond aux questions sur mardi dernier, pas seulement aux questions sur l'instant.

Quatre décisions qui valent la dispute

Tout le reste de cette stack est interchangeable. Pas ces quatre décisions.

  1. MQTT, pas HTTP. Connexions persistantes, surcharge minuscule par message, garanties de livraison intégrées. La plupart des boîtiers télématiques de flotte parlent déjà MQTT nativement. L'UDP brut est plus rapide sur le papier, mais vous renoncez aux garanties de livraison qui comptent énormément dès que 10 000 appareils partagent un même service. Une carte aux trous inexpliqués se débogue plus mal qu'une carte à la latence légèrement supérieure.
  1. Regroupez vos écritures Redis. Accumulez 50 à 100 positions et écrivez-les en un seul aller-retour. Une écriture par position sature le pool de connexions en charge, et elle le fait pendant le rush matinal, le pire moment possible. Faites cette erreur une fois en environnement de test, pour savoir reconnaître l'odeur à 7 h 02 du matin.
  1. Prévoyez la contre-pression dès le premier jour. Quand le débit de Redis prend du retard sur le rythme d'ingestion, jetez les positions périmées plutôt que de laisser le retard croître sans limite. Une position vieille de 90 secondes est pire que pas de position du tout, parce que quelqu'un peut s'en servir pour piloter.
  1. Stockez chaque horodatage en UTC. Un véhicule qui passe du CET au EET rapporte l'heure différemment de part et d'autre de la frontière. L'UTC lève l'ambiguïté avant qu'elle ne se transforme en incident de 2 heures du matin.
// Batch ingestion in Rust
async fn ingest_positions(batch: Vec<PositionReport>, redis: &RedisClient) {
    let mut pipe = redis.pipeline();

    for report in batch {
        let key = format!("vehicle:{}", report.vehicle_id);
        pipe.set_ex(key, serialize(&report), 300); // 5 min TTL
    }

    pipe.exec_async().await?;
}

Laissez HERE faire son vrai travail

Les services de routage, de trafic et de géocodage de HERE sont vraiment bons dans leur métier. Ils ne sont pas conçus pour recevoir des positions à haute fréquence. Les tordre dans ce sens échoue toujours de la même manière coûteuse : les points d'accès au routage supposent que vous interrogez une poignée d'itinéraires, pas que vous écrivez des milliers de rapports par seconde.

Utilisez-les pour ce qu'ils font bien. Le calcul d'itinéraire fournit les chemins optimaux de l'origine à la destination, avec le trafic en direct. Le géocodage transforme « le dépôt de la rue des Lilas » en coordonnée. Le géocodage inverse redonne l'adresse à partir de la coordonnée, pour confirmer une livraison. Pour l'ingestion de positions et la gestion d'état, vos propres couches Redis et PostgreSQL portent la charge. Un outil marche mieux quand il fait son vrai travail.

Dessinez à la vitesse de l'œil, pas à celle des mises à jour

Le frontend a un seul travail : ne jamais laisser 2 000 mises à jour par seconde atteindre la boucle de rendu. Temporisez les mises à jour de position et rendez au maximum à 60 images par seconde. Ne relancez jamais le rendu à chaque message WebSocket, aussi tentant que ce soit.

Entre deux corrections réelles, l'interpolation linéaire fait glisser les véhicules au lieu de les faire saccader. Les répartiteurs lisent cette fluidité comme une qualité de donnée, et d'une certaine façon, c'en est une. L'exception, le camion qui a cessé de bouger, est traitée plus bas.

Puis retirez de la vue ce qui sort du viewport. Ne dessinez que les véhicules à l'intérieur des limites actuelles de la carte. Personne ne peut donner un sens à 10 000 véhicules à la fois, et le GPU non plus.

function interpolatePosition(
  prev: Position,
  next: Position,
  progress: number
): Position {
  return {
    lat: prev.lat + (next.lat - prev.lat) * progress,
    lng: prev.lng + (next.lng - prev.lng) * progress,
    heading: prev.heading + (next.heading - prev.heading) * progress,
  };
}

Ce que cette conception ne sait toujours pas faire

Un véhicule dans un tunnel, un parking souterrain ou une vallée de montagne ne transmet rien pendant des minutes. Ensuite le TTL de 5 minutes expire, le marqueur disparaît de la carte, et le répartiteur ne peut pas distinguer un tunnel d'une panne. La correction tient en deux volets. Mettez les positions en tampon sur l'appareil et rejouez-les à la reconnexion, sinon les trajets perdent discrètement leur milieu. Côté frontend, affichez « vu il y a 42 s » comme un état à part entière, pour que l'âge soit visible plutôt qu'implicite.

L'interpolation ne ment que dans un seul cas : celui du camion arrêté. Si le prochain relevé ne vient jamais, le marqueur continue de glisser loin de la vérité. Coupez l'interpolation quand l'âge de la position dépasse un intervalle de transmission, ou quand la dernière vitesse transmise descend sous l'allure d'un piéton.

La plus petite limite reste la dérive GPS. Un « déplacement » à moins de 5 mètres de la dernière position connue, c'est en général du bruit, pas un camion qui se serait téléporté. Un algorithme de calage sur la route ou un seuil de déplacement minimal suffit, avec un seul paramètre.

À chaque composant sa place

CouchePourÀ éviter
API HERERoutage, trafic, géocodageIngestion de positions en temps réel
RedisDernières positions connues, état chaudStockage long terme, analytique
PostgreSQLHistorique des trajets, stockage persistantServir des requêtes temps réel
Mapbox GLCartes interactives, styles personnalisésTraitement de données côté serveur
Si vos répartiteurs appellent les chauffeurs pour vérifier la carte, vous payez pour une deuxième carte qui existe déjà. Réglez d'abord le pipeline d'ingestion.

Le pipeline bien réglé, la carte s'occupe du reste.

AngoraTek construit des systèmes de suivi de flotte. Si vous faites grandir des opérations sensibles à la localisation, [nous aimerions vraiment savoir sur quoi vous travaillez](/fr/#contact).

Merci d'avoir lu jusqu'ici. Si cela ressemble au problème sur lequel vous butez, parlez-nous-en. Un fondateur lit chaque message.

Engager la conversation