this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Continue rapport

+35 -85
+9
bib.yaml
··· 1118 1118 date: '2025-11-05' 1119 1119 value: https://github.com/Gepetto/gz-unitree/blob/main/CMakeLists.txt 1120 1120 1121 + ufw: 1122 + type: repository 1123 + title: ufw 1124 + author: Jamie Strandboge 1125 + publisher: Launchpad 1126 + url: 1127 + date: '2025-11-05' 1128 + value: https://launchpad.net/ufw 1129 +
+1 -1
pages_count
··· 1 - 50 1 + 49
+6 -6
rapport/gz-unitree.typ
··· 16 16 17 17 == Établissement du contact 18 18 19 - Une première tentative a été de suivre la documentation de CycloneDDS pour écouter sur le canal @cyclonedds-helloworld `rt/lowcmd`, en récupérant les définitions IDL des messages, disponibles sur le dépot `unitree_ros2`#footnote[`unitree_mujoco` n'avait pas encore été découvert] @unitree_ros2 19 + Une première tentative a été de suivre la documentation de CycloneDDS @cyclonedds-helloworld pour écouter sur le canal `rt/lowcmd`, en récupérant les définitions IDL des messages, disponibles sur le dépot `unitree_ros2`#footnote[`unitree_mujoco` n'avait pas encore été découvert] @unitree_ros2 20 20 21 - Malheureusement, cette solution s'est avérée infructueuse, à cause d'une inadéquation sur les domaines DDS (ce qui sera compris plus tard). 21 + Malheureusement, cette solution s'est avérée infructueuse, à cause d'une erreur sur les domaines DDS utilisés (ce qui sera compris plus tard). 22 22 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) 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_ 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 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, du débuggage du traffic RTPS (le protocole sur lequel est construit DDS @dds), _Wireshark_ @wireshark s'est avéré utile. 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], 38 38 39 39 40 - C'est notamment grâce à ce traçage des paquets que le problème d'ID de domaine a été découvert: notre _subscriber_ DDS était réglé sur le domaine anonyme (ID 0) alors que le SDK d'Unitree communique sur le domaine d'ID 1. 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. 41 41 42 42 C'est aussi Wireshark qui nous a permis de voir quels étaient les types IDL utilisés pour les messages. 43 43 ],
+19 -78
rapport/sdk2-study.typ
··· 45 45 unsigned long reserve[4]; 46 46 unsigned long crc; 47 47 }; 48 - ```) 49 - 50 - 51 - #grid( 52 - columns: 2, 53 - ```python 54 - @dataclass 55 - @annotate.final 56 - @annotate.autoid("sequential") 57 - class MotorCmd_(idl.IdlStruct, typename="MotorCmd"): 58 - mode: types.uint8 59 - q: types.float32 60 - dq: types.float32 61 - tau: types.float32 62 - kp: types.float32 63 - kd: types.float32 64 - reserve: types.uint32 65 - 66 - @dataclass 67 - @annotate.final 68 - @annotate.autoid("sequential") 69 - class LowCmd_(idl.IdlStruct, typename="Cmd"): 70 - mode_pr: types.uint8 71 - mode_machine: types.uint8 72 - motor_cmd: types.array['MotorCmd_', 35] 73 - reserve: types.array[types.uint32, 4] 74 - crc: types.uint32 75 - 76 - ```, 77 - ```cpp 78 - class LowCmd_ 79 - { 80 - private: 81 - uint8_t mode_pr_ = 0; 82 - uint8_t mode_machine_ = 0; 83 - std::array<::MotorCmd_, 35> motor_cmd_ = { }; 84 - std::array<uint32_t, 4> reserve_ = { }; 85 - uint32_t crc_ = 0; 86 - 87 - public: 88 - LowCmd_() = default; 89 - 90 - explicit LowCmd_( 91 - uint8_t mode_pr, 92 - uint8_t mode_machine, 93 - const std::array<::MotorCmd_, 35>& motor_cmd, 94 - const std::array<uint32_t, 4>& reserve, 95 - uint32_t crc) : 96 - mode_pr_(mode_pr), 97 - mode_machine_(mode_machine), 98 - motor_cmd_(motor_cmd), 99 - reserve_(reserve), 100 - crc_(crc) { } 101 - 102 - uint8_t mode_pr() const { return this->mode_pr_; } 103 - uint8_t& mode_pr() { return this->mode_pr_; } 104 - void mode_pr(uint8_t _val_) { this->mode_pr_ = _val_; } 105 - uint8_t mode_machine() const { return this->mode_machine_; } 106 48 ``` 107 - ) 108 - )) 49 + ) 109 50 110 - DDS groupe les mesages dans des _topics_. Les messages sont échangés sur un topic de la manière suivante 51 + DDS groupe les messages dans des _topics_. Les messages sont échangés sur un topic de la manière suivante 111 52 112 53 / Lecture: En s'abonnant au topic, on reçoit en temps réel les messages qui sont envoyés dessus 113 54 / Écriture: En publiant des messages sur le topic, on les rend disponibles aux abonnés 114 55 115 56 #import "@preview/unify:0.7.1": qty 116 57 117 - 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 messages entre #qty("0.9", "kB") et #qty("1.3", "kB") dans le cas des échanges commandes/état avec le robot 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. 118 59 119 - Et enfin, les _topics_ peuvent être isolés d'autres topics via des _domain_#[s]. 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. 120 61 121 62 122 63 == Une base de code partiellement open-source 123 64 124 - Le code source du SDK d'unitree est disponible sur Github @sdk2_source_today. Cependant, le dépôt git comprend des fichiers binaires déjà compilés: 65 + Le code source du SDK d'Unitree est disponible sur Github @sdk2_source_today. Cependant, le dépôt git comprend des fichiers binaires déjà compilés: 125 66 126 67 #figure( 127 - caption: [Résultat de `tree lib thirdparty` dans le dépot git], 68 + caption: [Résultat de `tree lib/ thirdparty/` dans le dépot git], 128 69 ``` 129 70 lib 130 71 ├── aarch64 ··· 159 100 ) 160 101 161 102 #figure( 162 - caption: [Extrait de `cmake/unitree_sdk2Targets.cmake`], 103 + caption: [Extrait de `cmake/unitree_sdk2Targets.cmake` @unitree_sdk2], 163 104 kind: raw, 164 105 zebraw( 165 106 numbering-offset: 63 - 1, ··· 176 117 ), 177 118 ) 178 119 179 - 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. 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. 180 121 181 - On confirme ceci en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` : 122 + On confirme cette nécéssite en lançant `mkdir build && cd build && cmake ..` après avoir supprimé le répertoire `lib/` : 182 123 183 124 #{ 184 125 show regex(".*CMake Error.*"): set text(fill: red) ··· 234 175 235 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? 236 177 237 - Ces constats ont motivé une première tentative de décompilation de ces `libunitree_sdk2.a` pour comprendre le fonctionnement du SDK2, via _Ghidra_ @ghidra. 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. 238 179 239 - Cependant, la découverte de l'existance d'un bridge officiel SDK $arrows.lr$ Mujoco @unitree_mujoco a rendu 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écéssaire. 240 181 241 182 == Un autre bridge existant: `unitree_mujoco` 242 183 ··· 247 188 #figure(caption: "Fonctionnement usuel du SDK", diagram({ 248 189 node((0, 0), $Pi$) 249 190 node((1, 0), "SDK") 250 - node((2, 0), "Robot") 191 + node((2, 0), "robot") 251 192 252 193 edge((0, 0), (0, 0), "<-", bend: 130deg, loop-angle: 180deg)[] 253 194 edge((2.25, 0), (2.25, 0), "->", bend: -130deg, loop-angle: -180deg)[] ··· 259 200 } 260 201 })) 261 202 262 - Un bridge se substitue au Robot physique, interceptant les ordres du SDK et les traduisants en des appels de fonctions utilisant 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 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). 263 204 264 205 265 206 #figure(caption: [Fonctionnement via _unitree\_mujoco_ du SDK], diagram({ ··· 323 264 )[*API de Gazebo*]) 324 265 })) 325 266 326 - 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) renvoyé par le robot. 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. 327 268 328 - Le `low` indique que ce sont des messages bas-niveau. Par exemple, `rt/lowcmd` correspond directement à des ordres de tension 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 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 329 270 330 - Les ordres dans `rt/lowcmd` sont ensuite traduits en appels de fonctions de Mujoco pour mettre à jour l'état du robot simulé, et de messages `rt/lowstate` sont créés à partir des données fournies par Mujoco 271 + Les ordres dans `rt/lowcmd` sont ensuite traduits en appels de fonctions de Mujoco pour mettre à jour l'état du robot simulé, et de messages `rt/lowstate` sont créés à partir des données fournies par Mujoco. 331 272 332 - Étant donné le modèle _pub/sub_ de DDS, on parle de _pub(lication)_ de message, et de _sub(scription)_#footnote[abonnement] aux messages d'un canal (pour les recevoir) 273 + Étant donné le modèle _pub/sub_ de DDS, on parle de _pub(lication)_ de message, et de _sub(scription)_#footnote[abonnement] aux messages d'un canal (pour les recevoir). 333 274 334 275 #figure( 335 276 caption: [Cycle de vie de la simulation avec le bridge pour Mujoco], ··· 354 295 355 296 356 297 edge(<sdk>, <lowcmd>, "->", bend: 30deg)[pub] 357 - edge(<lowcmd>, (1, 2), "..>", bend: 20deg)[via sub] 298 + edge(<lowcmd>, (1, 2), "-->", bend: 20deg)[via sub] 358 299 edge((1, 2), <mujoco>, "->", bend: 20deg, `data->ctrl[i] = ...`) 359 300 360 - edge(<sdk>, <lowstate>, "<..", bend: -30deg)[via sub] 301 + edge(<sdk>, <lowstate>, "<--", bend: -30deg)[via sub] 361 302 edge(<lowstate>, (-1, 2), "<-", bend: -20deg)[pub] 362 303 edge((-1, 2), <mujoco>, "<-", bend: -20deg, `... = data->sensordata[i]`) 363 304