···14141515=== Une base de code partiellement open-source
16161717-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.
1717+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.
18181919Au 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.
2020
+21-21
rapport/context.typ
···3030- Un _environnement_, que les actions viennent modifier
3131- 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
32323333-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):
3333+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):
34343535#diagram({
3636 node((0, 0))[Agent]
···4141 edge((2, 0), (0, 0), "->", bend: 45deg)[Mise à jour]
4242})
43434444-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.
4444+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.
45454646En 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.
47474848En robotique, on a des correspondances claires pour ces quatres notions:
49495050/ Agent: Robot pour lequel on développe le programme de contrôle (appelé _politique_)
5151-/ 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]
5151+/ 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]
5252/ 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
5353/ 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$").
5454···9898 L: E -> S
9999$
100100101101-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
101101+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
102102103103On remplit la colonne "Action à effectuer" avec l'action au coût le plus bas:
104104···144144145145==== Tendances à la "tricherie" des agents
146146147147-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).
147147+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).
148148149149Dans le cas de la robotique, cela arrive particulièrement souvent, et il faut donc un simulateur qui soit suffisamment réaliste.
150150151151==== Sous-spécification de la fonction coût
152152153153-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
153153+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.
154154155155==== La validation comme méthode de mitigation <why_multiple_simulators>
156156157157-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.
157157+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.
158158159159Il 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_.
160160161161-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.
161161+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.
162162163163-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.
163163+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.
164164165165On 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.
166166···209209 edge((2, 0), (2, .75), (0, .75), (0, 0), "-->", label-side: left)[itération],
210210)
211211212212-Quand on "déroule" $Pi$ en en partant d'un certain état initial $s_0$, on obtient une suite d'états et d'actions:
212212+Quand on "déroule" $Pi$ en partant d'un certain état initial $s_0$, on obtient une suite d'états et d'actions:
213213214214#diagram(
215215 $
···243243 )
244244$
245245246246-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.
246246+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.
247247248248249249-On définit également l'ensemble de _tout_ les chemins d'états possibles, peut importe la politique, $cal(C)$ :
249249+On définit également l'ensemble de _tous_ les chemins d'états possibles, peut importe la politique, $cal(C)$ :
250250251251#let definitions_paths_set = $
252252 cal(C) & :=
···364364 node(name: <bottom>, (4.5, +1.5))[$sum_(i=t+1)^oo gamma^t r(s'_i)$ ]
365365 node((5.75, +1.5), align(
366366 left,
367367- )[si $Pi$ avait choisit $a'_t$ \ au lieu de $a_t$])
367367+ )[si $Pi$ avait choisi $a'_t$ \ au lieu de $a_t$])
368368 edge(<break>, <bottom>, "->", bend: -25deg)[$a'_t$]
369369370370 // top-branch path
371371 node(name: <top>, (4.5, -1.5))[$sum_(i=t+1)^oo gamma^t r(s''_i)$]
372372 node((5.75, -1.5), align(
373373 left,
374374- )[si $Pi$ avait choisit $a''_t$ \ au lieu de $a_t$])
374374+ )[si $Pi$ avait choisi $a''_t$ \ au lieu de $a_t$])
375375 edge(<break>, <top>, "->", bend: 25deg)[$a''_t$]
376376377377 // Expectation bar V(s)
···397397398398On considère tout les chemins à partir de l'état $s_t$, et l'on regarde l'espérance...
399399400400-/ pour $V(s_t)$: de tout les chemins
400400+/ pour $V(s_t)$: de tous les chemins
401401/ pour $Q(s_t, a_t)$: du chemin où l'on a choisi $a_t$
402402403403En suite, il suffit de faire la différence, pour savoir l'_avantage_ que l'on a à choisir $a_t$ par rapport au reste.
···487487488488489489490490-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
490490+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
491491492492$
493493 max_(s in S) D_"KL" (Q_Pi' (s, dot) || Q_Pi (s, dot)) < delta
···521521 )
522522$
523523524524-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$.
524524+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$.
525525526526==== Région de confiance
527527528528-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.
528528+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.
529529530530#let ddot = [ #sym.dot #h(-1em / 16) #sym.dot ]
531531···545545546546La _PPO_ repose sur le même principe de stabilisation de l'entraînement par limitation de l'ampleur des changements de politique à chaque pas.
547547548548-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
548548+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
549549550550551551$
···681681682682Dans le contexte de la robotique, le calcul de l'état post-action de l'environnement est le travail du _moteur de physique_.
683683684684-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.
684684+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.
685685686686687687···726726 scale(7%, reflow: true, diagraph.render(read("./isaac-deptree.dot"))),
727727)
728728729729-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.
729729+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
···22222323On change d'approche, en préférant plutôt utiliser les abstractions fournies par le SDK de Unitree (cf @receive-lowcmd et @send-lowstate)
24242525-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
2525+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
26262727```bash
2828sudo ufw allow in proto udp from 224.0.0.0/4
···3434 gutter: 2em,
3535 [
36363737- 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],
3737+ 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],
383839394040 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.
···376376 $ ddt(Delta q) = ddt(q_"new" - q_"old") = ddt(q_"new") - ddt(q_"old") = Delta ddt(q) = Delta dot(q) $
377377378378 ]
379379-/ $K_p$: prépondérance de la partie proportionelle
379379+/ $K_p$: prépondérance de la partie proportionnelle
380380/ $K_p$: prépondérance de la partie dérivée
381381382382Cette équation met à jour $tau$ pour rapprocher l'état actuel du moteur de la nouvelle consigne, en prenant en compte
383383384384-- L'erreur sur l'angle $Delta q$ (partie proportionelle).
384384+- L'erreur sur l'angle $Delta q$ (partie proportionnelle).
385385- 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.
386386-- 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.
386386+- 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.
387387388388On 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$.
389389···391391== `rt/lowcmd` <receive-lowcmd>
392392393393394394-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:
394394+On trouve dans les messages `rt/lowcmd` les champs nécessaires à au calcul de $tau$ @h1-rt-lowcmd comme décrit précédemment:
395395396396#let greyedout = content => text(fill: luma(120), emph(content))
397397#let undocumented = greyedout[Non documenté]
···860860861861== Vérification sur des politiques réelles
862862863863-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.
863863+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.
864864865865L'analyse de la vidéo (cf @video) montre que le bridge fonctionne: le comportement du robot est similaire à celui sur Isaac.
866866···920920921921#image("./profiler-two-ticks.png")
922922923923-Quelques mesures ont été tentées pour réduire le temps nécéssaire à l'envoi d'un message DDS:
923923+Quelques mesures ont été tentées pour réduire le temps nécessaire à l'envoi d'un message DDS:
924924925925-/ 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.
925925+/ 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.
926926/ Déplacer dans un autre thread: C'est ce qui a motivé la désynchronisation du thread "LowStateWriter" (cf @send-lowstate)
927927-/ 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`]
927927+/ 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`]
928928929929Ainsi que d'autres optimisations, qui ne sont pas en rapport avec cette phase d'un cycle:
930930···932932/ 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
933933934934935935-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.
935935+Après optimisations, on arrive à atteindre un RTF aux alentours des 30%. Des recherches supplémentaires sont nécessaires pour atteindre un RTF raisonnable.
936936937937== Enregistrement automatique de vidéos <video>
938938···984984985985=== Une image de base avec Docker
986986987987-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.
987987+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.
988988989989-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.
989989+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.
990990991991```dockerfile
992992FROM ubuntu:24.04
···10521052 [
1053105310541054 ==== Un environnement de développement contraignant
10551055- 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é.
10551055+ 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é.
1056105610571057 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.
10581058
···2233=== État dans le domaine de la programmation
4455-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.
55+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.
6677-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.
77+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.
8899-Mais, en contrepartie, on perd une équation qui est fondamentale en mathématiques:
99+Mais, en contrepartie, on perd une équation qui est fondamentale en mathématique:
10101111$
1212 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)
···29293030En dehors du besoin de vérifiabilité du monde de la recherche, la reproductibilité est une qualité recherchée en programmation @reproducibility.
31313232-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.
3232+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.
333334343535=== État dans le domaine de la robotique
36363737-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.
3737+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.
38383939De 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.
4040···148148149149=== Un ecosystème de dépendances
150150151151-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.
151151+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.
152152153153Ainsi, é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.
154154···234234- 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
235235236236Au 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.
237237-Des efforts supplémentaires sont nécéssaires pour empaqueter _gz-unitree_.
238238-237237+Des efforts supplémentaires sont nécessaires pour empaqueter _gz-unitree_.
+7-7
rapport/sdk2-study.typ
···57575858CycloneDDS 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.
59596060-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.
6060+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.
616162626363== Une base de code partiellement open-source
···9090 ```,
9191)
92929393-Compiler le SDK nécéssite l'existance de ces fichiers binaires:
9393+Compiler le SDK nécessite l'existance de ces fichiers binaires:
94949595#import "@preview/zebraw:0.6.0"
9696#let zebraw = (..args) => zebraw.zebraw(
···119119120120Ici 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.
121121122122-On confirme cette nécéssite en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` :
122122+On confirme cette nécessite en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` :
123123124124#{
125125 show regex(".*CMake Error.*"): set text(fill: red)
···173173...
174174```
175175176176-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?
176176+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?
177177178178Ces constats ont motivé une première tentative de décompilation de ces `libunitree_sdk2.a` pour comprendre le fonctionnement du SDK, via _Ghidra_ @ghidra.
179179180180-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.
180180+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.
181181182182== Un autre bridge existant: `unitree_mujoco`
183183···200200 }
201201}))
202202203203-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).
203203+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).
204204205205206206#figure(caption: [Fonctionnement via _unitree\_mujoco_ du SDK], diagram({
···264264 )[*API de Gazebo*])
265265}))
266266267267-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.
267267+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.
268268269269Le `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
270270
+2-2
rapport/thanks.typ
···11Je 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.
2233-Merci à Sylvie Chambon, Géraldine Morin et Ronan Guivarch, professeurs à l'ENSEEIHT, qui m'ont soutenue à travers des périodes parfois difficiles.
33+Merci à Sylvie Chambon, Géraldine Morin et Ronan Guivarch, professeurs à l'ENSEEIHT, qui m'ont soutenue pendant des périodes parfois difficiles.
4455-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.
55+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.