Comprendre la programmation temps réel

Arrière-plan

L’informatique en temps réel est une caractéristique clé de nombreux systèmes robotiques, en particulier des applications critiques pour la sécurité et la mission telles que les véhicules autonomes, les engins spatiaux et la fabrication industrielle. Nous concevons et prototypons ROS 2 en gardant à l’esprit les contraintes de performances en temps réel, car il s’agit d’une exigence qui n’a pas été prise en compte dans les premières étapes de ROS 1 et il est désormais impossible de refactoriser ROS 1 pour qu’il soit compatible avec le temps réel.

Ce document décrit les exigences de l’informatique en temps réel et les meilleures pratiques pour les ingénieurs en logiciel. Bref:

Pour faire un système informatique en temps réel, notre boucle en temps réel doit se mettre à jour périodiquement pour respecter les délais. Nous ne pouvons tolérer qu’une petite marge d’erreur sur ces délais (notre gigue maximale autorisée). Pour ce faire, nous devons éviter les opérations non déterministes dans le chemin d’exécution, telles que : les événements de défaut de page, l’allocation/désallocation dynamique de la mémoire et les primitives de synchronisation qui bloquent indéfiniment.

Un exemple classique de problème de contrôle couramment résolu par le calcul en temps réel est l’équilibrage d’un pendule inversé. Si le contrôleur bloquait pendant une durée étonnamment longue, le pendule tomberait ou deviendrait instable. Mais si le contrôleur se met à jour de manière fiable à un rythme plus rapide que le moteur contrôlant le pendule ne peut fonctionner, le pendule s’adaptera avec succès aux données du capteur pour équilibrer le pendule.

Maintenant que vous savez tout sur l’informatique en temps réel, essayons une démo !

Installer et exécuter la démo

La démo en temps réel a été écrite avec les systèmes d’exploitation Linux à l’esprit, car de nombreux membres de la communauté ROS faisant de l’informatique en temps réel utilisent Xenomai ou RT_PREEMPT comme solutions en temps réel. Comme de nombreuses opérations sont effectuées dans la démo pour optimiser les performances ou spécifiques au système d’exploitation, la démo ne se construit et ne s’exécute que sur les systèmes Linux. Donc, si vous êtes un utilisateur OSX ou Windows, n’essayez pas cette partie !

Cela doit également être construit à partir de la source à l’aide d’une API DDS statique. Actuellement, la seule implémentation prise en charge est Connext.

Tout d’abord, suivez les instructions pour compiler ROS 2 from source en utilisant Connext DDS comme middleware.

Exécutez les tests

Avant de lancer, assurez-vous d’avoir au moins 8 Go de RAM libre. Avec le verrouillage de la mémoire, le swap ne fonctionnera plus.

Sourcez votre setup.bash ROS 2.

Exécutez le binaire de démonstration et redirigez la sortie. Vous pouvez utiliser sudo au cas où vous auriez une erreur d’autorisation :

pendulum_demo > output.txt

Que diable vient-il de se passer ?

Tout d’abord, même si vous avez redirigé stdout, vous verrez une sortie vers la console (de stderr) :

mlockall failed: Cannot allocate memory
Couldn't lock all cached virtual memory.
Pagefaults from reading pages not yet mapped into RAM will be recorded.

Après l’étape d’initialisation du programme de démonstration, il tentera de verrouiller toute la mémoire cache dans la RAM et d’empêcher les futures allocations de mémoire dynamique à l’aide de mlockall. Cela permet d’empêcher les défauts de page de charger beaucoup de nouvelle mémoire dans la RAM. (Voir l’article sur la conception en temps réel pour plus d’informations.)

La démo continuera comme d’habitude lorsque cela se produit. Au bas du fichier output.txt généré par la démo, vous verrez le nombre de défauts de page rencontrés lors de l’exécution :

rttest statistics:
  - Minor pagefaults: 20
  - Major pagefaults: 0

Si nous voulons que ces défauts de page disparaissent, nous devrons…

Ajuster les autorisations pour le verrouillage de la mémoire

Ajouter à /etc/security/limits.conf (comme sudo) :

<your username>    -   memlock   <limit in kB>

Une limite de -1 est illimitée. Si vous choisissez cette option, vous devrez peut-être l’accompagner de ulimit -l unlimited après avoir modifié le fichier.

Après avoir enregistré le fichier, déconnectez-vous et reconnectez-vous. Ensuite, relancez l’appel pendulum_demo.

Vous verrez soit zéro défaut de page dans votre fichier de sortie, soit une erreur indiquant qu’une exception bad_alloc a été interceptée. Si cela se produisait, vous n’aviez pas assez de mémoire disponible pour verrouiller la mémoire allouée au processus dans la RAM. Vous devrez installer plus de RAM sur votre ordinateur pour voir zéro défaut de page !

Aperçu des sorties

Pour voir plus de sortie, nous devons exécuter le nœud pendulum_logger.

Dans un shell avec votre source install/setup.bash, appelez :

pendulum_logger

Vous devriez voir le message de sortie :

Logger node initialized.

Dans un autre shell avec setup.bash, invoquez à nouveau pendulum_demo.

Dès que cet exécutable démarre, vous devriez voir l’autre shell imprimer constamment la sortie :

Commanded motor angle: 1.570796
Actual motor angle: 1.570796
Mean latency: 210144.000000 ns
Min latency: 4805 ns
Max latency: 578137 ns
Minor pagefaults during execution: 0
Major pagefaults during execution: 0

La démo contrôle une simulation de pendule inversé très simple. La simulation du pendule calcule sa position dans son propre fil. Un nœud ROS simule un capteur d’encodeur moteur pour le pendule et publie sa position. Un autre nœud ROS agit comme un simple contrôleur PID et calcule le prochain message de commande.

Le nœud d’enregistrement imprime périodiquement l’état du pendule et les statistiques de performances d’exécution de la démo pendant sa phase d’exécution.

Une fois la pendulum_demo terminée, vous devrez CTRL-C sortir du nœud de l’enregistreur pour quitter.

Latence

Lors de l’exécution de pendulum_demo, vous verrez les statistiques finales collectées pour la démo :

rttest statistics:
  - Minor pagefaults: 0
  - Major pagefaults: 0
  Latency (time after deadline was missed):
    - Min: 3354 ns
    - Max: 2752187 ns
    - Mean: 19871.8 ns
    - Standard deviation: 1.35819e+08

PendulumMotor received 985 messages
PendulumController received 987 messages

Les champs de latence vous indiquent la latence minimale, maximale et moyenne de la boucle de mise à jour en nanosecondes. Ici, la latence signifie la durée après laquelle la mise à jour était censée se produire.

Les exigences d’un système en temps réel dépendent de l’application, mais disons que dans cette démo, nous avons une boucle de mise à jour de 1 kHz (1 milliseconde) et nous visons une latence maximale autorisée de 5 % de notre période de mise à jour.

Ainsi, notre latence moyenne était vraiment bonne dans cette exécution, mais la latence maximale était inacceptable car elle dépassait en fait notre boucle de mise à jour ! Ce qui s’est passé?

Nous souffrons peut-être d’un ordonnanceur non déterministe. Si vous utilisez un système Linux vanille et que le noyau RT_PREEMPT n’est pas installé, vous ne pourrez probablement pas atteindre l’objectif en temps réel que nous nous sommes fixé, car le planificateur Linux ne vous permettra pas de préempter les threads au niveau de l’utilisateur.

Voir l’article sur la conception en temps réel <https://design.ros2.org/articles/realtime_background.html#multithreaded-programming-and-synchronization>`__ pour plus d’informations.

La démo tente de définir le planificateur et la priorité des threads de la démo pour qu’ils soient adaptés aux performances en temps réel. Si cette opération échoue, un message d’erreur s’affichera : « Impossible de définir la priorité et la stratégie de planification : opération non autorisée ». Vous pouvez obtenir des performances légèrement meilleures en suivant les instructions de la section suivante :

Définition des autorisations pour le planificateur

Ajouter à /etc/security/limits.conf (comme sudo) :

<your username>    -   rtprio   98

La plage du champ rtprio (priorité en temps réel) est de 0 à 99. Cependant, ne fixez PAS la limite à 99, car vos processus pourraient alors interférer avec des processus système importants qui s’exécutent avec la priorité la plus élevée (par exemple, le chien de garde). Cette démo tentera d’exécuter la boucle de contrôle à la priorité 98.

Tracé des résultats

Vous pouvez tracer les statistiques de latence et de défaut de page collectées dans cette démo après son exécution.

Étant donné que le code a été instrumenté avec rttest, des arguments de ligne de commande utiles sont disponibles :

Commande

Description

Valeur par défaut

-je

Spécifiez le nombre d’itérations pour exécuter la boucle en temps réel

1000

-dans

Spécifiez la période de mise à jour avec l’unité par défaut étant les microsecondes.

Utilisez le suffixe « s » pour les secondes, « ms » pour les millisecondes,

« us » pour les microsecondes et « ns » pour les nanosecondes.

1 milliseconde

-F

Indiquez le nom du fichier d’écriture des données collectées.

Exécutez à nouveau la démo avec un nom de fichier pour enregistrer les résultats :

pendulum_demo -f pendulum_demo_results

Exécutez ensuite le script rtest_plot sur le fichier résultant :

rttest_plot pendulum_demo_results

Ce script produira trois fichiers :

pendulum_demo_results_plot_latency.svg
pendulum_demo_results_plot_majflts.svg
pendulum_demo_results_plot_minflts.svg

Vous pouvez visualiser ces tracés dans une visionneuse d’images de votre choix.