Sommaire

Voici un synopsis pour les décideurs pressés. J'ai réalisé un moteur de recherche d'itinéraire adapté au vélo. Le but c'est de minimiser non pas la distance, mais l'énergie fournie par le cycliste. J'utilise les données d'OpenStreetMap et SRTM. Le backend est publié sous licence BSD-2, je n'ai pas de frontend, je recherche des volontaires.

Présentation

Je suis un cycliste, et non un sportif. Pour moi la bicyclette est un moyen de déplacement idéal, mais uniquement un moyen de déplacement. Pour aller au boulot, pour aller faire des courses, ou plus généralement pour aller quelque part j'utilise mon vélo quand je peux c'est-à-dire quand la distance est raisonnable (comprise entre 5km et 15km, en dessous ça ne vaut pas le coup, au-dessus c'est trop pour moi), quand j'ai un endroit pour le déposer avec une bonne probabilité de le retrouver (ce qui exclue beaucoup de déplacements dans Paris pour mon plus grand désespoir), et quand je n'ai pas besoin ou que j'ai le moyen de me changer.

Excepté pour mon trajet domicile travail, le choix de l'itinéraire est un problème systématique. Il se trouve que je n'ai pas encore à ce jour trouvé un moyen de calculer un itinéraire qui m'est adapté.

Il est important pour moi que l'itinéraire n’en soit pas absurde, il n'est pas normal de descendre pour remonter si l'on peut facilement l'éviter. Il ne faut toutefois pas tomber dans l'excès inverse et ne pas faire un détour de 10km pour éviter une petite côte. Bref, j'ai voulu construire mon propre moteur de routage, avec un critère adapté au cycliste.

Il vient ensuite le choix du critère, habituellement on choisit la distance, ou le temps avec une vitesse dépendante de la catégorie de route. Aucun de ces deux critères n'est pour moi adapté au vélo, j'ai donc choisi d'utiliser un critère tout à fait naturel, l'énergie dépensée par le cycliste.

Ce critère est un bon compromis, si l'on peut éviter de descendre pour remonter au prix d'un petit détour cela sera énergétiquement valable, a contrario si fait un détour monstrueux on va dépenser plus d'énergie dans le détour que l'obstacle initial.

J'ai commencé à travailler là-dessus il y a deux ans, ça a beaucoup évolué, voici comment ça marche.

Données

Pour commencer, il faut des données, allons-y !

Données cartographiques

Concernant les données cartographiques, OpenStreetMap est le Graal pour ce genre de projet, il contient les routes, les points, des informations de circulation (autorisé ou non au cycliste, sens unique, contre sens cyclable…). Bref, c'est merveilleux, je suis heureux et les oiseaux chantent, sauf que…

Pour calculer mon énergie, j'ai besoin de l'altitude des points.

Données altimétriques

Après des essais avec les données de l'IGN accessibles, j'ai vite jeté l'éponge. Ces données posent deux problèmes principaux :

l'étendue spatiale limitée,

la licence qui est trop restrictive.

J'abandonne les données IGN. Toutefois, cela m'aura permis de comprendre certains trucs sur les systèmes de projection et de me familiariser avec proj.4, couteau suisse pour travailler avec des données cartographiques. Tout n'est pas perdu.

Il se trouve qu'un jour, j'entends parler des données SRTM au boulot (merci Aurore), ça avait l'air super intéressant. Passé le fait que la documentation est pour ces données ce que le discernement est pour un militant politique, j'étais très heureux.

J'interpole l'altitude des nœuds d'OSM à l'aide des données SRTM, et ça roule. Pour être plus précise, l'interpolation est faite avec une interpolation bilinéaire, des interpolations plus fines (qui ont été testés) n'apportent pas grand-chose par rapport aux problèmes qu'elles soulèvent.

Modèle de cycliste

Bon, c'est bien maintenant que j'ai des données je vais pouvoir optimiser un itinéraire… Euh, wait il manque pas un truc ? Il faut définir la façon dont un cycliste dépense son énergie.

Rien de plus simple, une fois qu'on a défini un modèle de pédaleur.

Hypothèses

Comme dans tous les modèles, nous avons des hypothèses, je vais essayer de les expliciter, j'en oublierai peut-être certaines, j'en suis désolé.

Le cycliste, même sportif, n'est pas relativiste

Cette hypothèse est très forte, et elle me choque beaucoup. Toutefois, je vais faire de la mécanique Newtonniene.

Le bipède fournit une puissance constante

Cette hypothèse peut sembler saugrenue, mais en fait elle est la clef d'une pratique efficace du vélo, c'est-à-dire l'utilisation judicieuse des différents développements pour fournir une puissance constante. Je ne compte plus le nombre de fois où je me suis fait doubler en côte par des vélocipédistes que je prenais une grande joie à enrhumer par la suite.

Si c'est le fait de pédaler en descente qui vous dérange, dites-vous bien que certains cyclistes le font, et quand niveau énergétique ça ne change pas grand-chose, le temps passé en descente est faible en général.

La route est droite est sans arrêts

Comme il n'y a aucun moyen de savoir si un virage entraîne un freinage et donc une perte d'énergie, à moins d'utiliser des heuristiques qui en fonction de l'angle prennent une décision, il n'y aura pas de perte d'énergie. Le modèle fonctionne donc comme si la route était droite. Pour information, j'ai essayé quelques heuristiques sur des trajets que je connaissais, sans résultats conformes à la réalité.

De même, on n’a aucun moyen de savoir quand le cycliste s'arrête, il ne s'arrête donc pas. Nous sommes donc en présence d'un chauffard à bicyclette ne respectant aucun feu tricolore, et ne voyant pas les virages (tout en restant sur la route qui est toujours droite par définition, c'est beau la théorie, hein ?).

Le sportif et son instrument de torture constituent un unique point matériel

L'hypothèse est simple, on considère qu'il n'y a aucune déformation de roues ou pneu et qu'il n'y a pas de mouvement de rotation interne. En pratique, on veut surtout dire qu'il n'y a pas d'énergie stockée sous ces formes.

L'attirail est soumis aux forces suivantes

Les forces considérées sont seulement les forces résultantes dans l'axe longitudinal. Les forces dans l'axe perpendiculaire à la route et dans l'axe latéral sont perpendiculaires au mouvement, donc de travail nul.

Outre la force provenant du mouvement de la bête perchée sur sa monture, les forces présence sont les suivantes :

Le poids (en cas de pente négative, cette résultante aura tendance à accélérer le vélo, en cas de pente positive elle aura tendance à le ralentir), c'est la force qu'on doit considérer.

La résistance au roulement. C'est la force qu'exerce la route sur le vélo, mais aussi la qualité des axes, \textit{etc}. Cette force est toujours néfaste au cycliste.

Les frottements de l'air. Sauf dans le cas où l'on a le vent dans le dos à une vitesse supérieure à laquelle le vélocipède se déplace, elle est néfaste au mouvement.

Modélisation des forces

Donc je vais ici modéliser une à une les forces intervenantes sur l'ensemble du chevalier et de sa monture.

Force exercée par le vélocipédiste

La puissance fournie par la force exercée par l'animal est donc de Pᶜ = Fᶜv donc pour avoir une puissance constante, la force considérée sera Fᶜ = Pᶜ/v.

Le poids

La masse considérée pour le calcul du poids, et la masse totale de la bête, du vélo et des bagages. Seule la résultante par rapport à l'axe longitudinal est considérée.

Donc Fᵖ = -m⋅g⋅sin(θ) où θ est l'angle de la pente. Si θ est positif la force est motrice (en descente).

La résistance au roulement

La résistance au roulement est toujours une force s'opposant au mouvement, elle est modélisée de la façon suivante : Fʳ = -m⋅g⋅Cᵣ où Cᵣ est un coefficient caractéristique de la route et des pneus.

Une valeur courante est Cᵣ = .01, dans le cas de route en mauvais état ou de pneus larges (comme VTT) on pourra avoir tendance à l'augmenter, dans le cas d'un vélo de course à le diminuer.

Les frottements de l'air

La force de résistance du vent est modélisée comme suit : Fᵃ = ½ ρₐ⋅S⋅Cₓ⋅vₐ²

où :

ρₐ est la masse volumique de l'air, elle change en fonction de la température, mais ce changement est assez faible dans les plages classiques de la pratique du cyclisme,

S est la surface exposée par le cycliste,

Cₓ est le coefficient de pénétration dans l'air (profilage de casque, etc.). En général on travaille directement avec le produit SCₓ.

vₐ est la vitesse de l'air par rapport au cycliste. S'il n'y a pas de vent, alors la vitesse de l'air par rapport au sportif est l'opposée de la vitesse du vélocipédiste par rapport à l'air, sinon il faut tenir compte du vent et de sa direction.

Résumé du modèle

Pour résumer :

l'animal est soumis à des forces

on peut appliquer un principe fondamental de la dynamique

l'énergie dépensée par le cycliste s'extrait simplement

Par contre, l'équation différentielle obtenue est non linéaire, et après l'avoir trituré (à plusieurs) dans tous les sens, elle ne semble pas analytiquement soluble. Et une simulation numérique avec un pas temporel est exclue pour des questions de performance. Une approximation sera faite, j'y reviendrai plus tard.

Implémentation

Faute d'avoir trouvé un nom intéressant, le moteur de routage se nomme rv, comme routage vélo, le nom du premier dossier que j'ai créé. Au final, je me suis habitué à ce nom, et un nom comme Hervé pour une interface me semblerait approprié.

Historique

J'ai commencé à travailler dessus ce projet il y a plus de deux ans, et j'ai abouti à des premiers résultats il y a environ un an. Les grandes phases ont été :

en C++ avec un format de stockage ad-hoc et les données altimétriques de l'IGN, en C++ avec un format de stockage ad-hoc et les données altimétriques de SRTM, en C++ avec un stockage sqlite et les données altimétriques de SRTM, en C++ avec un stockage postgres non synchronisées et les données altimétriques de SRTM, en C++ avec un stockage postgres synchronisées et les données altimétriques de SRTM.

Il y a un an je suis arrivé au point 3. et à l'aide d'un serveur loué chez OVH, j'ai pu passer à l'échelle de la France. En août 2012 je suis parti 15 jours avec ~ma bite et mon couteau~ mon vélo et ma tente, et j'ai utilisé mon moteur de routage pour me guider en Normandie de camping à camping. J'en ai tiré les améliorations suivantes :

ajout du vent dans le modèle qui auparavant en était dépourvu (il y avait les frottements de l'air, mais je considérai toujours que la vitesse du vent était nulle), car la normandie et le vent, ben…

ajout de la résistance au roulement, je me suis rendu compte qu'il avait tendance à faire de trop gros détour pour éviter de petites côtes, comme si le fait de rouler pas vite (donc frottements de l'air faibles) l'autorisait à faire beaucoup de distance, mon hypothèse initiale qui consistait à considérer la résistance au roulement comme négligeable était violemment fausse, mes muscles en ont témoigné.

Après avoir effectué ces modifications (à la lueur d'un écran de netbook, sous une tente, relié au serveur en ssh au moyen d'une connexion 3G), le routage proposé m'a paru sympathique, en effet j'allai de camping sur la côte à camping sur la côte, et la côte normande n'est pas connue pour son coté plat…

Revenu en terrain civilisé, j'ai fait quelques essais en descente départ arrêté, sans pédaler, avec un traceur GPS, pour voir si la courbe de vitesse prévue correspond à la courbe de vitesse observée, et les résultats sont très bons, meilleur que les essais précédents. La résistance au roulement était un point crucial à prendre en compte.

Puis je me suis rapproché d'OpenStreetMap France, via IRC, et je me suis vu proposé une VM sur une de leurs machines fournies par la Fondation Free, un VM avec une quantité de RAM largement acceptable (et inespérée) pour continuer mes expérimentations.

Depuis je suis passé à postgres. J'ai déporté petit à petit une bonne partie de la complexité vers des procédures sur la base.

Algo de recherche

Alors, il y a rien de bien nouveau, je cherche un itinéraire minimisant une fonction de coût, ici l'énergie fournie par l'animal, j'utilise une version étendue de l'algorithme de Dijkstra, connue sous le nom d'algorithme A*. De plus, le coût de chaque arête dépend du chemin parcouru précédemment (exemple le coût d'une côte est faible si l'on a accumulé de la vitesse grâce à une descente).

J'utilise comme heuristique une heuristique dite admissible, c'est-à-dire une minoration de l'énergie dépensée correspondant à l'énergie dépensée sur une ligne droite de pente constante. Un raisonnement physique permet de dire que c'est une minimisation. Cette heuristique étant admissible, l'algoritme A* trouvera le chemin minimisant le coût.

Connexité

Un des problèmes majeurs qui est apparu, et que je me suis attelé à résoudre ces derniers mois, est la connexité. En effet, il existe des petites composantes connexes (que les gens sur le channel IRC d'OpenStreetMap France nomment ilôts isolés), ces petites composantes connexes peuvent être de trois ordres :

réelles cartographiquement, par exemple une île, et dans ce cas il est légitime qu'une demande d'itinéraire entre l'île et le continent échoue,

une erreur de cartographie d'OSM, par exemple une route non reliée,

une erreur d'étiquetage, une route autorisée au vélo reliée au reste du monde par une route interdite au vélo.

Excepté dans le premier cas, ces composantes connexes sont de taille minuscule, et il faut projeter un des points sur la grosse composante connexe. Le problème c'est qu'il faut connaître ces composantes connexes.

C'est là où ça se corse, il faut précalculer les informations de connexités, et il faut remettre à jour les informations de connexité à la mise à jour de la base. Tout est fait sous forme de procédure postgres, lors de l'import initial (relativement long) et lors des mises à jour de la base.

Code

Actuellement le code est divisé en trois parties :

le moteur de routage en C++

les procédures postgres permettant l'import initial et la mise à jour. L'avantage d'avoir cela sous forme de procédure c'est que toute la mise à jour de la base et le précalcul nécessaire est fait au sein d'une seule transaction garantissant ainsi la cohérence des données,

un binding python pour pouvoir lancer la procédure et récupérer les résultats de manière simple.

La façon d'utiliser le moteur de routage, de réaliser les imports dans la database est disponible au sein du même répertoire. Il y a aussi les infos sur comment la mettre à jour automatiquement quotidiennement via un cron.

C'est par ici que vous trouverez le code, et c'est sous licence BSD-2.

Interface de test

Mais vous pouvez avoir envie de tester le bousin sans importer la database, et je vous comprends ! En effet, l'importer à l'échelle de la France, en ordre de grandeur c'est une semaine de calcul. Après on peut la mettre à jour quotidiennement via un cron, c'est ce que je fais.

Mais je vous propose de tester sur la machine mise gracieusement à ma disposition par OpenStreetMap France. Il suffit pour cela d'utiliser le module rv tel que défini dans rvpy/rv.py . La configuration par défaut d'une instance de la classe Route utilise osm104.openstreetmap.fr sur le port 29302 pour faire le calcul.

En téléchargeant le fichier rvpy/rv.py dans le répo git vous pouvez directement faire des essais, et vous amuser avec. Le fichier rvpy/example contient un exemple d'utilisation.

Attention : Afin de ne pas écrouler la machine, une limitation de 8 calculs de routage en même temps a été imposée sur cette interface de test.

Frontend

C'est bien beau tout ça me diriez vous, mais c'est pas très utilisable par un vélocipédiste standard, et vous auriez raison. Nicolas Dandrimont a réalisé un premier frontend, disponible ici, mais n'a, a mon plus grand regret, pas le temps de développer le frontend des mes rêves.

Je n'ai pas de vrai frontend, et j'aimerai bien trouver quelqu'un qui veuille développer un frontend, pour moi le frontend idéal :

permet de choisir en cliquouillant le point de départ, d'arrivée et les éventuels waypoints.

affiche un ensemble de paramètres minimaliste (masse, vitesse de base sur le plat…)

permet via un menu/mode/onglet avancé de choisir tous les paramètres (pour ceux qui veulent jouer)

affiche l'énergie totale fournie sur le trajet, la vitesse moyenne, le dénivelé positif

trace l'itinéraire une couleur variable indiquant la pente

permet en cliquant sur un point de l'itinéraire d'avoir des infos relatives au point

affiche un profil d'altitude

Toutes ces infos sont fournies par le backend.

Conclusion

Je tiens tout d'abord à remercier Nicolas Dandrimont que j'ai régulièrement embêté depuis deux ans avec mon projet et qui m'a fourni des infos précieuses sur l'utilisation des données OSM. J'ai aussi sollicité ceux qui traînent sur le chan d'OpenStreetMap France qui ont répondu à mes questions même si elles étaient débiles, et m'ont fourni des infos sur la façon de travailler avec des bases de données OSM.

Je remercie tout particulièrement l'association OpenStreetMap France qui m'a fourni l'infrastructure nécessaire au développement de mon projet.

J'ai comme perspectives de faire un autre backend de traitement de trace GPS pour interpréter a posteriori un parcours réalisé par un cycliste en terme de dénivelé total, pente, énergie, puissance maximale. Si quelqu'un est volontaire pour faire un frontend, ça fait des idées pour continuer dans la même veine.

Et enfin, si quelqu'un a envie de faire un frontend, ça serait avec un très grand plaisir.

P.S. : Rédiger son journal dans un éditeur de texte c'est bien, devoir le reformater pour éviter que les retours à la ligne ne soient significatifs contrairement à l'habitude en MarkDown, c'est moins bien. N'hésitez pas à appuyer mon rapport de bug.

P.S. bis : en plus de la licence indiquée, ce contenu est placé sous la licence CC-BY