Logs for my internship @ LAAS-CNRS

fautes

gwen.works 88e3fb33 433a03be

verified
+1 -1
biblio.typ
··· 14 14 15 15 === Une base de code partiellement open-source 16 16 17 - Une partie du code source de ce SDK n'est pas disponible, et n'est que distribué sous forme de binaires @sdk2-in-source-binaries. J'ai donc chercher à comprendre cette partie du code par ingénierie inverse, ce qui ne s'est pas avéré nécéssaire. 17 + Une partie du code source de ce SDK n'est pas disponible, et n'est que distribué sous forme de binaires @sdk2-in-source-binaries. J'ai donc chercher à comprendre cette partie du code par ingénierie inverse, ce qui ne s'est pas avéré nécessaire. 18 18 19 19 Au final, en explorant le code source du plugin pour un autre logiciel de simulation, Mujoco @mujoco @unitree-mujoco, j'ai pu comprendre comment interfacer le SDK avec Gazebo. 20 20
+21 -21
rapport/context.typ
··· 30 30 - Un _environnement_, que les actions viennent modifier 31 31 - Un _score_ (_coût_ s'il doit être minimisé, _récompense_ inversement) qui dépend de l'état pré- et post-action de l'environnement ainsi que de l'action qui a été effectuée 32 32 33 - La phase d'apprentissage consiste à trouver, par des cycles d'essai/erreur, quelles sont les meilleures actions à prendre en fonction de l'environnement actuel, avec "meilleur" définit comme "qui minimise le coût" (ou maximise la récompense): 33 + La phase d'apprentissage consiste à trouver, par des cycles d'essai/erreur, quelles sont les meilleures actions à prendre en fonction de l'environnement actuel, avec "meilleur" défini comme "qui minimise le coût" (ou maximise la récompense): 34 34 35 35 #diagram({ 36 36 node((0, 0))[Agent] ··· 41 41 edge((2, 0), (0, 0), "->", bend: 45deg)[Mise à jour] 42 42 }) 43 43 44 - Cette technique est particulièrement adaptée au problèmes qui se prêtent à une modélisation type "jeu vidéo", dans le sens où l'agent représente le personnage-joueur, et le coût un certain score, qui est condition de victoire ou défaite. 44 + Cette technique est particulièrement adaptée aux problèmes qui se prêtent à une modélisation type "jeu vidéo", dans le sens où l'agent représente le personnage-joueur, et le coût un certain score, qui est condition de victoire ou défaite. 45 45 46 46 En robotique, une approche similaire explore l'espace d'action (en général un courant à envoyer aux moteurs) de façon à optimiser le coût. 47 47 48 48 En robotique, on a des correspondances claires pour ces quatres notions: 49 49 50 50 / Agent: Robot pour lequel on développe le programme de contrôle (appelé _politique_) 51 - / Actions: Envoi d'ordres aux moteurs, souvent le courant électrique à appliquer // #footnote[il y a techniquement deux principales manières de contrôler un robot: l'envoi de commandes de courant, ou contrôle par puissance, et l'envoi de vitesses cibles, qui laisse la détermination du courant nécéssaire au microcontrolleurs sur le robot même] 51 + / Actions: Envoi d'ordres aux moteurs, souvent le courant électrique à appliquer // #footnote[il y a techniquement deux principales manières de contrôler un robot: l'envoi de commandes de courant, ou contrôle par puissance, et l'envoi de vitesses cibles, qui laisse la détermination du courant nécessaire au microcontrolleurs sur le robot même] 52 52 / Environnement: Le monde réel. C'est de loin la partie la plus difficile à simuler informatiquement. On utilise des moteurs de simulation physique, dont la pluralité des implémentations est importante, voir @why_multiple_simulators 53 53 / Coût: Ensemble de contraintes ("ne pas endommager le robot") et d'évaluations spécifiques à la tâche à effectuer ("s'est déplacé de 5m en avant selon l'axe $x$"). 54 54 ··· 98 98 L: E -> S 99 99 $ 100 100 101 - avec $E$ l'ensemble des états possibles de l'environnement, et $S$ un ensemble muni d'un ordre total (on utilise souvent $[0, 1]$). Ces fonctions coût, qui ne dépendent que de l'état actuel de l'environnement, représente un domaine du RL#footnote[Reinforcement Learning] appelé _Q-Learning_ @qlearning 101 + avec $E$ l'ensemble des états possibles de l'environnement, et $S$ un ensemble muni d'un ordre total (on utilise souvent $[0, 1]$). Ces fonctions coût, qui ne dépendent que de l'état actuel de l'environnement, représentent un domaine du RL#footnote[Reinforcement Learning] appelé _Q-Learning_ @qlearning 102 102 103 103 On remplit la colonne "Action à effectuer" avec l'action au coût le plus bas: 104 104 ··· 144 144 145 145 ==== Tendances à la "tricherie" des agents 146 146 147 - Expérimentalement, on sait que des tendances "tricheuses" émergent facilement pendant l'entraînement: l'agent découvre des séries d'actions qui causent un bug avantageux vis à vis du coût associé, soit parce qu'il y a un bug dans le calcul de l'état de l'environnement post-action, soit parce que la fonction coût ne prend pas suffisemment bien en compte toutes les possibilités de l'environnement (autrement dit, il manque de contraintes). 147 + Expérimentalement, on sait que des tendances "tricheuses" émergent facilement pendant l'entraînement: l'agent découvre des séries d'actions qui causent un bug avantageux vis à vis du coût associé, soit parce qu'il y a un bug dans le calcul de l'état de l'environnement post-action, soit parce que la fonction coût ne prend pas suffisamment bien en compte toutes les possibilités de l'environnement (autrement dit, il manque de contraintes). 148 148 149 149 Dans le cas de la robotique, cela arrive particulièrement souvent, et il faut donc un simulateur qui soit suffisamment réaliste. 150 150 151 151 ==== Sous-spécification de la fonction coût 152 152 153 - Un exemple populaire est l'expérience de pensée du Maximiseur de trombones @trombones: On imagine un agent avec pour environnement le monde réel, pour actions "prendre des décisions"; "envoyer des emails"; etc. et pour fonction récompense "le nombre de trombones existant sur Terre". Il finirait possiblement par réduire en escalavage tout être vivant capable de produire des trombones: la fonction coût est sous-spécifiée 153 + Un exemple populaire est l'expérience de pensée du Maximiseur de trombones @trombones: on imagine un agent avec pour environnement le monde réel, pour actions "prendre des décisions"; "envoyer des emails"; etc. et pour fonction récompense "le nombre de trombones existant sur Terre". Il finirait possiblement par réduire en escalavage tout être vivant capable de produire des trombones: la fonction coût est sous-spécifiée. 154 154 155 155 ==== La validation comme méthode de mitigation <why_multiple_simulators> 156 156 157 - Comme ces bugs sont des comportements non voulus, il est très probables qu'ils ne soient pas exactement les mêmes en changeant d'implémentation. 157 + Comme ces bugs sont des comportements non voulus, il est très probable qu'ils ne soient pas exactement les mêmes en changeant d'implémentation. 158 158 159 159 Il convient donc de se servir de _plusieurs_ implémentations: une sert à la phase d'entraînement, pendant laquelle l'agent développe des "tendances à la tricherie", puis une autre sert à la phase de _validation_. 160 160 161 - Cette phase consiste en le lancement de l'agent dans une autre implémentation, avec les mêmes actions mais qui, crucialement, ne comporte pas les mêmes bugs que l'environnement ayant servi à la phase d'apprentissage. 161 + Cette phase consiste à lancer l'agent dans une autre implémentation, avec les mêmes actions mais qui, crucialement, ne comporte pas les mêmes bugs que l'environnement ayant servi à la phase d'apprentissage. 162 162 163 - Les "techniques de triche" ainsi apprises deviennent inefficace, et si le score devient bien pire que celui de l'apprentissage, on peut détecter les cas de triche. 163 + Les "techniques de triche" ainsi apprises deviennent inefficaces, et si le score devient bien inférieur à celui de l'apprentissage, on peut détecter les cas de triche. 164 164 165 165 On peut même aller plus loin, et multiplier les phases de validation avec des implémentations supplémentaires, ce qui réduit encore la probabilité qu'une technique de triche se glisse dans l'agent final. 166 166 ··· 209 209 edge((2, 0), (2, .75), (0, .75), (0, 0), "-->", label-side: left)[itération], 210 210 ) 211 211 212 - Quand on "déroule" $Pi$ en en partant d'un certain état initial $s_0$, on obtient une suite d'états et d'actions: 212 + Quand on "déroule" $Pi$ en partant d'un certain état initial $s_0$, on obtient une suite d'états et d'actions: 213 213 214 214 #diagram( 215 215 $ ··· 243 243 ) 244 244 $ 245 245 246 - l'ensemble des chemins possibles avec la politique $pi$. C'est tout simplement l'ensemble de tout les "déroulements" de la politique $pi$ en partant des états possibles de l'environnement. 246 + l'ensemble des chemins possibles avec la politique $pi$. C'est tout simplement l'ensemble de tous les "déroulements" de la politique $pi$ en partant des états possibles de l'environnement. 247 247 248 248 249 - On définit également l'ensemble de _tout_ les chemins d'états possibles, peut importe la politique, $cal(C)$ : 249 + On définit également l'ensemble de _tous_ les chemins d'états possibles, peut importe la politique, $cal(C)$ : 250 250 251 251 #let definitions_paths_set = $ 252 252 cal(C) & := ··· 364 364 node(name: <bottom>, (4.5, +1.5))[$sum_(i=t+1)^oo gamma^t r(s'_i)$ ] 365 365 node((5.75, +1.5), align( 366 366 left, 367 - )[si $Pi$ avait choisit $a'_t$ \ au lieu de $a_t$]) 367 + )[si $Pi$ avait choisi $a'_t$ \ au lieu de $a_t$]) 368 368 edge(<break>, <bottom>, "->", bend: -25deg)[$a'_t$] 369 369 370 370 // top-branch path 371 371 node(name: <top>, (4.5, -1.5))[$sum_(i=t+1)^oo gamma^t r(s''_i)$] 372 372 node((5.75, -1.5), align( 373 373 left, 374 - )[si $Pi$ avait choisit $a''_t$ \ au lieu de $a_t$]) 374 + )[si $Pi$ avait choisi $a''_t$ \ au lieu de $a_t$]) 375 375 edge(<break>, <top>, "->", bend: 25deg)[$a''_t$] 376 376 377 377 // Expectation bar V(s) ··· 397 397 398 398 On considère tout les chemins à partir de l'état $s_t$, et l'on regarde l'espérance... 399 399 400 - / pour $V(s_t)$: de tout les chemins 400 + / pour $V(s_t)$: de tous les chemins 401 401 / pour $Q(s_t, a_t)$: du chemin où l'on a choisi $a_t$ 402 402 403 403 En suite, il suffit de faire la différence, pour savoir l'_avantage_ que l'on a à choisir $a_t$ par rapport au reste. ··· 487 487 488 488 489 489 490 - Pour évaluer cette distance, on regarde la plus grande des distances entre des paires de distributions de probabilité de politiques $Q_Pi$ et $Q_Pi'$, pour tout $s in S$ @trpo 490 + Pour évaluer cette distance, on regarde la plus grande des distances entre des paires de distribution de probabilité de politique $Q_Pi$ et $Q_Pi'$, pour tout $s in S$ @trpo 491 491 492 492 $ 493 493 max_(s in S) D_"KL" (Q_Pi' (s, dot) || Q_Pi (s, dot)) < delta ··· 521 521 ) 522 522 $ 523 523 524 - On a $D_"KL" (Q, Q') = 0$ (cf @dkl-zero), alors qu'il y a eu une modification très importante des probabilités de choix de l'action 1 et 2 dans tout les états possibles : si on imagine $Q(s, 1) = Q(s, 2) = 1 slash 4$, on a après modification $Q'(s, 1) = 1 slash 2$ et $Q'(s, 2) = 1 slash 8$. 524 + On a $D_"KL" (Q, Q') = 0$ (cf @dkl-zero), alors qu'il y a eu une modification très importante des probabilités de choix de l'action 1 et 2 dans tous les états possibles : si on imagine $Q(s, 1) = Q(s, 2) = 1 slash 4$, on a après modification $Q'(s, 1) = 1 slash 2$ et $Q'(s, 2) = 1 slash 8$. 525 525 526 526 ==== Région de confiance 527 527 528 - Cette contrainte définit un ensemble réduit de $Pi'$ acceptables comme nouvelle politique, aussi appelé une _trust region_ (région de confiance), d'où la méthode d'optimisation tire son nom @trpo. 528 + Cette contrainte définit un ensemble réduit de $Pi'$ acceptables comme nouvelle politique, aussi appelée une _trust region_ (région de confiance), d'où la méthode d'optimisation tire son nom @trpo. 529 529 530 530 #let ddot = [ #sym.dot #h(-1em / 16) #sym.dot ] 531 531 ··· 545 545 546 546 La _PPO_ repose sur le même principe de stabilisation de l'entraînement par limitation de l'ampleur des changements de politique à chaque pas. 547 547 548 - Cependant, les méthodes _PPO_ préfèrent changer la quantité à optimiser, pour limiter intrinsèquement l'ampleur des modifications, en résolvant un problème d'optimisation sans contraintes @ppo 548 + Cependant, les méthodes _PPO_ préfèrent changer la quantité à optimiser, pour limiter intrinsèquement l'ampleur des modifications, en résolvant un problème d'optimisation sans contrainte @ppo 549 549 550 550 551 551 $ ··· 681 681 682 682 Dans le contexte de la robotique, le calcul de l'état post-action de l'environnement est le travail du _moteur de physique_. 683 683 684 - Bien évidemment, ce sont des programmes complexes avec des résolutions souvent numériques d'équation physiques; il est presque inévitable que des bugs se glissent dans ces programmes. 684 + Bien évidemment, ce sont des programmes complexes avec des résolutions souvent numériques d'équations physiques; il est presque inévitable que des bugs se glissent dans ces programmes. 685 685 686 686 687 687 ··· 726 726 scale(7%, reflow: true, diagraph.render(read("./isaac-deptree.dot"))), 727 727 ) 728 728 729 - Bien que toutes ces dépendances puissent être spécifiées avec des contraintes de version strictes @lockfiles pour éviter des changements imprévus de comportement du code venant des bibliothèques, beaucoup celles-ci ont besoin de compiler du code C++ _à l'installation_#footnote[Pour des raisons de performance @cpp-python, certaines bibliothèques implémentent leurs fonctions critiques en C++. C'est par exemple le cas de NumPy @numpy]: fixer la version de la bibliothèque ne suffit pas donc à guarantir la reproductibilité de la compilation de l'arbre des dépendances. 729 + Bien que toutes ces dépendances puissent être spécifiées avec des contraintes de version strictes @lockfiles pour éviter des changements imprévus de comportement du code venant des bibliothèques, beaucoup ont besoin de compiler du code C++ _à l'installation_#footnote[Pour des raisons de performance @cpp-python, certaines bibliothèques implémentent leurs fonctions critiques en C++. C'est par exemple le cas de NumPy @numpy]: fixer la version de la bibliothèque ne suffit pas donc à garantir la reproductibilité de la compilation de l'arbre des dépendances.
+14 -14
rapport/gz-unitree.typ
··· 22 22 23 23 On change d'approche, en préférant plutôt utiliser les abstractions fournies par le SDK de Unitree (cf @receive-lowcmd et @send-lowstate) 24 24 25 - Enfin, si un pare-feu est actif, il faut autoriser le traffic UDP l'intervalle d'addresses IP `224.0.0.0/4`. Par exemple, avec _ufw_ @ufw 25 + Enfin, si un pare-feu est actif, il faut autoriser le trafic UDP l'intervalle d'adresses IP `224.0.0.0/4`. Par exemple, avec _ufw_ @ufw 26 26 27 27 ```bash 28 28 sudo ufw allow in proto udp from 224.0.0.0/4 ··· 34 34 gutter: 2em, 35 35 [ 36 36 37 - Pour arriver à ces solutions, _Wireshark_ @wireshark s'est avéré utile, étant capable d'inspecter du traffic RTPS#footnote[Le protocole sur lequel est construit DDS @dds], 37 + Pour arriver à ces solutions, _Wireshark_ @wireshark s'est avéré utile, étant capable d'inspecter du trafic RTPS#footnote[Le protocole sur lequel est construit DDS @dds], 38 38 39 39 40 40 C'est notamment grâce à ce traçage des paquets que le problème de domaine DDS a été découvert: notre _subscriber_ DDS était réglé sur le domaine anonyme (ID aléatoire représenté par un 0 lors de la configuration) alors que le SDK d'Unitree communique sur le domaine d'ID 1. ··· 376 376 $ ddt(Delta q) = ddt(q_"new" - q_"old") = ddt(q_"new") - ddt(q_"old") = Delta ddt(q) = Delta dot(q) $ 377 377 378 378 ] 379 - / $K_p$: prépondérance de la partie proportionelle 379 + / $K_p$: prépondérance de la partie proportionnelle 380 380 / $K_p$: prépondérance de la partie dérivée 381 381 382 382 Cette équation met à jour $tau$ pour rapprocher l'état actuel du moteur de la nouvelle consigne, en prenant en compte 383 383 384 - - L'erreur sur l'angle $Delta q$ (partie proportionelle). 384 + - L'erreur sur l'angle $Delta q$ (partie proportionnelle). 385 385 - L'erreur sur la vitesse de changement de $Delta q$ (partie dérivative). Cette prise en compte de la vitesse permet de lisser les changements appliqués aux moteurs. 386 - - Un couple dit de _feed-forward_, $tau_"ff"$, qui permet le maintient du robot à un état stable. On pourrait le déterminer en lançant une première simulation, avec pour objectif le maintient debout. Une fois la stabilité atteinte, on relève les couples des moteurs. Intuitivement, on peut voir $tau_"ff"$ comme un manière de s'affranchir de la partie "maintient debout" dans l'expression de la commande, similairement à la mise à zéro ("tarer") d'une balance. 386 + - Un couple dit de _feed-forward_, $tau_"ff"$, qui permet le maintien du robot à un état stable. On pourrait le déterminer en lançant une première simulation, avec pour objectif le maintien debout. Une fois la stabilité atteinte, on relève les couples des moteurs. Intuitivement, on peut voir $tau_"ff"$ comme un manière de s'affranchir de la partie "maintien debout" dans l'expression de la commande, similairement à la mise à zéro ("tarer") d'une balance. 387 387 388 388 On contrôle la prépondérance des deux erreurs dans le calcul de la nouvelle consigne grâce à deux coefficients, $K_p$ et $K_d$. 389 389 ··· 391 391 == `rt/lowcmd` <receive-lowcmd> 392 392 393 393 394 - On trouve dans les messages `rt/lowcmd` les champs nécéssaires à au calcul de $tau$ @h1-rt-lowcmd comme décrit précédemment: 394 + On trouve dans les messages `rt/lowcmd` les champs nécessaires à au calcul de $tau$ @h1-rt-lowcmd comme décrit précédemment: 395 395 396 396 #let greyedout = content => text(fill: luma(120), emph(content)) 397 397 #let undocumented = greyedout[Non documenté] ··· 860 860 861 861 == Vérification sur des politiques réelles 862 862 863 - Après avoir testé le bridge sur les politiques d'examples fournies par Unitree, il a été testé sur une politique en cours de développement au sein de l'équipe de robotique du LAAS, Gepetto. 863 + Après avoir testé le bridge sur les politiques d'exemples fournies par Unitree, il a été testé sur une politique en cours de développement au sein de l'équipe de robotique du LAAS, Gepetto. 864 864 865 865 L'analyse de la vidéo (cf @video) montre que le bridge fonctionne: le comportement du robot est similaire à celui sur Isaac. 866 866 ··· 920 920 921 921 #image("./profiler-two-ticks.png") 922 922 923 - Quelques mesures ont été tentées pour réduire le temps nécéssaire à l'envoi d'un message DDS: 923 + Quelques mesures ont été tentées pour réduire le temps nécessaire à l'envoi d'un message DDS: 924 924 925 - / Restreindre DDS à `localhost`: Il est possible que DDS envoie les messages en mode "broadcast", c'est-à-dire à tout addresse IP accessible dans un certain intervalle. En restreignant à `localhost`, on s'assure que le message n'a pas à être copié plusieurs fois. 925 + / Restreindre DDS à `localhost`: Il est possible que DDS envoie les messages en mode "broadcast", c'est-à-dire à tout adresse IP accessible dans un certain intervalle. En restreignant à `localhost`, on s'assure que le message n'a pas à être copié plusieurs fois. 926 926 / Déplacer dans un autre thread: C'est ce qui a motivé la désynchronisation du thread "LowStateWriter" (cf @send-lowstate) 927 - / Ajuster la fréquence d'envoi: Une fois `LowStateWriter` déplacé dans un thread indépendant, on peut ajuster la fréquence d'envoi, le thread étant récurrant#footnote[Créé avec `CreateRecurrentThreadEx`] 927 + / Ajuster la fréquence d'envoi: Une fois `LowStateWriter` déplacé dans un thread indépendant, on peut ajuster la fréquence d'envoi, le thread étant récurrent#footnote[Créé avec `CreateRecurrentThreadEx`] 928 928 929 929 Ainsi que d'autres optimisations, qui ne sont pas en rapport avec cette phase d'un cycle: 930 930 ··· 932 932 / Utilisation d'une implémentation de CRC32 plus rapide: tentative avec _CRC++_ @crcpp non achevée, à cause d'un _stack smashing_ pendant l'exécution 933 933 934 934 935 - Après optimisations, on arrive à atteindre un RTF aux alentours des 30%. Des recherches supplémentaires sont nécéssaires pour atteindre un RTF raisonnable. 935 + Après optimisations, on arrive à atteindre un RTF aux alentours des 30%. Des recherches supplémentaires sont nécessaires pour atteindre un RTF raisonnable. 936 936 937 937 == Enregistrement automatique de vidéos <video> 938 938 ··· 984 984 985 985 === Une image de base avec Docker 986 986 987 - L'environnement d'exécution des workflows ne comporte pas d'installation de Gazebo. Étant donné le temps de compilation élevé, on peut "factoriser" cette étape dans une _image de base_, de laquelle on démarre pour chaque exécution du workflow, dans laquelle tout les programmes nécéssaires sont déjà installés. 987 + L'environnement d'exécution des workflows ne comporte pas d'installation de Gazebo. Étant donné le temps de compilation élevé, on peut "factoriser" cette étape dans une _image de base_, de laquelle on démarre pour chaque exécution du workflow, dans laquelle tous les programmes nécessaires sont déjà installés. 988 988 989 - Pour cela, on part d'une image Ubuntu, dans lequelle on installe le nécéssaire: Just (pour lancer des commandes, un sorte de Makefile mais plus moderne @just), FFMpeg (pour l'encodage H.264 servant à la création du fichier vidéo), XVFB (pour émuler un serveur X, cf @simulate-x), Python (pour lancer la politique RL), Gazebo et gz-unitree. 989 + Pour cela, on part d'une image Ubuntu, dans lequelle on installe le nécessaire: Just (pour lancer des commandes, un sorte de Makefile mais plus moderne @just), FFMpeg (pour l'encodage H.264 servant à la création du fichier vidéo), XVFB (pour émuler un serveur X, cf @simulate-x), Python (pour lancer la politique RL), Gazebo et gz-unitree. 990 990 991 991 ```dockerfile 992 992 FROM ubuntu:24.04 ··· 1052 1052 [ 1053 1053 1054 1054 ==== Un environnement de développement contraignant 1055 - Développer et débugger une définition de workflow peut s'avérer complexe et particulièrement chronophage: n'ayant pas d'accès interactif au serveur exécutant celui-ci, il faut envoyer ses changements au dépôt git, attendre que le workflow s'exécute entièrement, et regarde si quelque chose s'est mal passé. 1055 + Développer et débugger une définition de workflow peut s'avérer complexe et particulièrement chronophage: n'ayant pas d'accès interactif au serveur exécutant celui-ci, il faut envoyer ses changements au dépôt git, attendre que le workflow s'exécute entièrement, et regarder si quelque chose s'est mal passé. 1056 1056 1057 1057 Par exemple, si jamais des fichiers sont manquants, ou ne sont pas au chemin attendu, il faut modifier le workflow pour y rajouter des instruction listant le contenu d'un répertoire (en utilisant `ls` ou `tree`, par exemple), lancer le workflow à nouveau et regarder les logs. 1058 1058
+4 -1
rapport/main.typ
··· 123 123 124 124 #pagebreak() 125 125 126 + #outline(depth: 3) 127 + 128 + #pagebreak() 129 + 126 130 = Remerciements 127 131 128 132 #include "thanks.typ" 129 133 130 - #outline(depth: 3) 131 134 132 135 = Contexte 133 136
+7 -8
rapport/nix.typ
··· 2 2 3 3 === État dans le domaine de la programmation 4 4 5 - La différence entre une fonction au sens mathématique et une fonction au sens programmatique consiste en le fait que, par des raisons de practicité, on permet aux `function`s des langages de programmation d'avoir des _effets de bords_. Ces effets affectent, modifient ou font dépendre la fonction d'un environnement global qui n'est pas explicitement déclaré comme une entrée (argument) de la fonction en question @purefunctions. 5 + La différence entre une fonction au sens mathématique et une fonction au sens programmatique consiste dans le fait que, pour des raisons de practicité, on permet aux `function`s des langages de programmation d'avoir des _effets de bords_. Ces effets affectent, modifient ou font dépendre la fonction d'un environnement global qui n'est pas explicitement déclaré comme une entrée (argument) de la fonction en question @purefunctions. 6 6 7 - Cette liberté permet, par exemple, d'avoir accès à la date et à l'heure courante, interagir avec un système de fichier d'un ordinateur, générer une surface pseudo aléatoire par bruit de Perlin, etc. 7 + Cette liberté permet, par exemple, d'avoir accès à la date et à l'heure courante, d'interagir avec un système de fichier d'un ordinateur, de générer une surface pseudo aléatoire par bruit de Perlin, etc. 8 8 9 - Mais, en contrepartie, on perd une équation qui est fondamentale en mathématiques: 9 + Mais, en contrepartie, on perd une équation qui est fondamentale en mathématique: 10 10 11 11 $ 12 12 forall E, F, forall f: E->F, forall (e_1, e_2) in E^2, e_1 = e_2 => f(e_1) = f(e_2) ··· 29 29 30 30 En dehors du besoin de vérifiabilité du monde de la recherche, la reproductibilité est une qualité recherchée en programmation @reproducibility. 31 31 32 - Il existe donc des langages de programmation dits _fonctionnels_, qui, de manière plus ou moins stricte, limitent les effets de bords. Certains langages font également la distinction entre une fonction _pure_ (sans effets de bord) et une fonction classique @fortran-pure. Certaines fonctions, plutôt appelées _procédures_, sont uniquement composées d'effet de bord et ne renvoie pas de valeur @ibm-function-procedure-routine. 32 + Il existe donc des langages de programmation dits _fonctionnels_, qui, de manière plus ou moins stricte, limitent les effets de bords. Certains langages font également la distinction entre une fonction _pure_ (sans effets de bord) et une fonction classique @fortran-pure. Certaines fonctions, plutôt appelées _procédures_, sont uniquement composées d'effet de bord et ne renvoient pas de valeur @ibm-function-procedure-routine. 33 33 34 34 35 35 === État dans le domaine de la robotique 36 36 37 - En robotique, pour donner des ordres au matériel, on intéragit beaucoup avec le monde extérieur (ordres et lecture d'état de servo-moteurs, flux vidéo d'une caméra, etc), souvent dans un langage plutôt bas-niveau, pour des questions de performance et de proximité abstractionnelle au matériel. 37 + En robotique, pour donner des ordres au matériel, on interagit beaucoup avec le monde extérieur (ordres et lecture d'état de servo-moteurs, flux vidéo d'une caméra, etc.), souvent dans un langage plutôt bas-niveau, pour des questions de performance et de proximité abstractionnelle au matériel. 38 38 39 39 De fait, les langages employés sont communément C, C++ ou Python#footnote[Il arrive assez communément d'utiliser Python, un langage haut-niveau, mais c'est dans ce cas à but de prototypage. Le code contrôlant les moteurs est écrit dans un langage bas niveau, mais appelé par Python via FFI.] @programming-languages-robotics, des langages bien plus impératifs que fonctionnels @imperative-languages. 40 40 ··· 148 148 149 149 === Un ecosystème de dépendances 150 150 151 - Afin de conserver la reproductibilité même lorsque l'on dépend de libraries tierces, ces dépendances doivent également avoir une compilation reproductible: on déclare donc des dépendances à des paquets Nix, disponibles sur un registre centralisé, _Nixpkgs_ @nixpkgs. 151 + Afin de conserver la reproductibilité même lorsque l'on dépend de bibliothèques tierces, ces dépendances doivent également avoir une compilation reproductible: on déclare donc des dépendances à des paquets Nix, disponibles sur un registre centralisé, _Nixpkgs_ @nixpkgs. 152 152 153 153 Ainsi, écrire un paquet Nix pour son logiciel demande parfois d'écrire des paquets Nix pour les dépendances de notre projet, si celles-ci n'existent pas encore, et cela récursivement. On peut ensuite soumettre ces autres paquets à Nixpkgs @nixpkgs-contributing afin que d'autres puissent en dépendre sans les réécrire. 154 154 ··· 234 234 - Un outil de génération de paquets Nix à partir de paquets ROS ou Gazebo, développé par Guilhem Saurel au sein de l'équipe Gepetto, _gazebros2nix_ @gazebros2nix 235 235 236 236 Au début du développement de _gz-unitree_, des essais d'utilisation des paquets Nix pour le développement et la compilation ont été réalisés, mais des erreurs subsistaient, en particulier avec Gazebo. 237 - Des efforts supplémentaires sont nécéssaires pour empaqueter _gz-unitree_. 238 - 237 + Des efforts supplémentaires sont nécessaires pour empaqueter _gz-unitree_.
+7 -7
rapport/sdk2-study.typ
··· 57 57 58 58 CycloneDDS est capable d'un débit d'environ #qty("1", "GB/s"), pour des messages d'environ #qty("1", "kB") chacun @dds-benchmark. On remarque, en pratique, des tailles de message entre #qty("0.9", "kB") et #qty("1.3", "kB") dans le cas des échanges commandes/état avec le robot. 59 59 60 - Et enfin, les topics peuvent être isolés d'autres topics via des _domain_#[s], identifiés par un numéro. Deux topics portant le même nom reste isolés si ils sont sur deux domaines différents. 60 + Et enfin, les topics peuvent être isolés d'autres topics via des _domain_#[s], identifiés par un numéro. Deux topics portant le même nom reste isolés s'ils sont sur deux domaines différents. 61 61 62 62 63 63 == Une base de code partiellement open-source ··· 90 90 ```, 91 91 ) 92 92 93 - Compiler le SDK nécéssite l'existance de ces fichiers binaires: 93 + Compiler le SDK nécessite l'existance de ces fichiers binaires: 94 94 95 95 #import "@preview/zebraw:0.6.0" 96 96 #let zebraw = (..args) => zebraw.zebraw( ··· 119 119 120 120 Ici est défini, via `set_target_properties(... IMPORTED_LOCATION)`, le chemin d'une bibliothèque à lier avec la bibliothèque finale @cmake-imported-location. Ici, c'est un des fichiers pré-compilés que l'on lie. 121 121 122 - On confirme cette nécéssite en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` : 122 + On confirme cette nécessite en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` : 123 123 124 124 #{ 125 125 show regex(".*CMake Error.*"): set text(fill: red) ··· 173 173 ... 174 174 ``` 175 175 176 - Ces particularités laissent planner quelques doutes sur la nature open-source du code: ces binaires requis sont-ils seulement présent pour améliorer l'expérience développeur en accélererant la compilation, ou "cachent"-ils du code non public? 176 + Ces particularités laissent planer quelques doutes sur la nature open-source du code: ces binaires requis sont-ils seulement présent pour améliorer l'expérience développeur en accélererant la compilation, ou "cachent"-ils du code non public? 177 177 178 178 Ces constats ont motivé une première tentative de décompilation de ces `libunitree_sdk2.a` pour comprendre le fonctionnement du SDK, via _Ghidra_ @ghidra. 179 179 180 - Cependant, la découverte de l'existance d'un bridge officiel SDK $arrows.lr$ Mujoco @unitree_mujoco a rendu l'exploration de cette piste non nécéssaire. 180 + Cependant, la découverte de l'existance d'un bridge officiel SDK $arrows.lr$ Mujoco @unitree_mujoco a rendu l'exploration de cette piste non nécessaire. 181 181 182 182 == Un autre bridge existant: `unitree_mujoco` 183 183 ··· 200 200 } 201 201 })) 202 202 203 - Un bridge se substitue au robot physique, interceptant les ordres du SDK et les traduisants en des appels de fonctions provenant de l'API du simulateur, et symmétriquement pour les envois d'états au SDK. On peut apparenter le fonctionnement d'un bridge à celui d'une attaque informatique de type "Man in the Middle" (MitM). 203 + Un bridge se substitue au robot physique, interceptant les ordres du SDK et les traduisant en des appels de fonctions provenant de l'API du simulateur, et symétriquement pour les envois d'états au SDK. On peut apparenter le fonctionnement d'un bridge à celui d'une attaque informatique de type "Man in the Middle" (MitM). 204 204 205 205 206 206 #figure(caption: [Fonctionnement via _unitree\_mujoco_ du SDK], diagram({ ··· 264 264 )[*API de Gazebo*]) 265 265 })) 266 266 267 - Le bridge de Mujoco fonctionne en interceptant les messages sur le canal `rt/lowcmd` et en en envoyant dans le canal `rt/lowstate`, qui correspondent respectivement aux commandes envoyées au robot et à l'état (angles des joints, moteurs, valeurs des capteurs, etc) reçu depuis le robot. 267 + Le bridge de Mujoco fonctionne en interceptant les messages sur le canal `rt/lowcmd` et en envoyant dans le canal `rt/lowstate`, qui correspondent respectivement aux commandes envoyées au robot et à l'état (angles des joints, moteurs, valeurs des capteurs, etc) reçu depuis le robot. 268 268 269 269 Le `low` indique que ce sont des messages bas-niveau. Par exemple, `rt/lowcmd` correspond directement à des ordres en valeurs de couple pour les moteurs, au lieu d'envoyer des ordres plus évolués, tels que "se déplacer de $x$ mètres en avant" @h1-motion-services 270 270
+2 -2
rapport/thanks.typ
··· 1 1 Je tiens à remercier Olivier Stasse et Guilhem Saurel, qui m'ont suivie pendant toute la durée du stage et répondu à mes questionnements, sans qui je n'aurais pu mener ces recherches. Merci aussi à Côme Perrot, grâce à qui j'ai pu éclaircir certaines zones d'ombre sur ma compréhension de la théorie du reinforcement learning appliquée à la robotique. 2 2 3 - Merci à Sylvie Chambon, Géraldine Morin et Ronan Guivarch, professeurs à l'ENSEEIHT, qui m'ont soutenue à travers des périodes parfois difficiles. 3 + Merci à Sylvie Chambon, Géraldine Morin et Ronan Guivarch, professeurs à l'ENSEEIHT, qui m'ont soutenue pendant des périodes parfois difficiles. 4 4 5 - Merci aussi à Laurenz Mädje et Marin Haug pour avoir créé Typst, une alternative moderne à LaTeX qui a rendu l'écriture de ce rapport bien plus agréable. Merci à Joseph Wilson (paquet Typst "Fletcher") et Johannes Wolf (paquet Typst "CeTZ"), qui ont rendu la création de diagrammes très ergonomique. 5 + Merci aussi à Laurenz Mädje et Marin Haug pour avoir créé Typst, une alternative moderne à LaTeX qui a rendu l'écriture de ce rapport bien plus agréable. Merci à Joseph Wilson (paquet Typst "Fletcher") et Johannes Wolf (paquet Typst "CeTZ"), qui ont permis la création de diagrammes très ergonomique.
+2 -4
rapport/utils.typ
··· 1 1 #let comment = content => text(fill: gray)[(Note: #content)] 2 2 #let todo = content => text(fill: red)[(TODO: #content)] 3 - #let refneeded = text(fill: luma(100), [[Réf. nécéssaire]]) 3 + #let refneeded = text(fill: luma(100), [[Réf. nécessaire]]) 4 4 #let dontbreak = content => block(breakable: false, content) 5 5 6 6 // https://github.com/typst/typst/issues/3147#issuecomment-2457554155 ··· 98 98 lines_from_start 99 99 .slice( 100 100 0, 101 - lines_from_start.position(predicate(ends)) 102 - + if keep_delimiting { 1 } else { 0 }, 101 + lines_from_start.position(predicate(ends)) + if keep_delimiting { 1 } else { 0 }, 103 102 ) 104 103 .join("\n") 105 104 } ··· 179 178 raw(lang: lang, dedent(transform(contents))) 180 179 } 181 180 } 182 -