Chapitre 01
Introduction & outillage
Objectifs du chapitre
- Comprendre pourquoi Rust est un candidat sĂ©rieux pour la robotique temps rĂ©el, et oĂč il se situe par rapport au C/C++.
- Installer une chaĂźne d'outils Rust complĂšte avec
rustup(compilateur, gestionnaire de paquets, linter, formateur). - Créer, compiler et exécuter un premier projet avec
cargo, y compris en mode--release. - Découvrir
cargo clippyetcargo fmt, deux outils à utiliser dÚs le premier jour. - Présenter le projet fil rouge du cours : un mini moteur de calcul pour bras robotisé.
1. Pourquoi Rust pour le temps réel et la robotique ?
En robotique, le code qui tourne dans la boucle de contrĂŽle (lecture des capteurs, filtre de Kalman, calcul de cinĂ©matique inverse, envoi des consignes moteur) doit ĂȘtre rapide, prĂ©visible et fiable. Historiquement, ce terrain est occupĂ© par le C et le C++ : accĂšs direct Ă la mĂ©moire, pas de garbage collector, compilation vers du code machine optimisĂ©. Rust vise exactement la mĂȘme niche de performance, mais en corrigeant la plus grande source de bugs de ces deux langages : les erreurs mĂ©moire.
Le compilateur Rust (rustc) impose, via son systÚme de propriété (ownership) et
d'emprunts (borrowing) que nous détaillerons au chapitre 4, des rÚgles qui éliminent à la
compilation des familles entiĂšres de bugs : use-after-free, double free,
débordements de tampon, accÚs concurrents non protégés (data races). Ces bugs ne sont pas
juste gĂȘnants : sur un robot, un dĂ©rĂ©fĂ©rencement de pointeur invalide dans la boucle de commande
peut se traduire par un bras qui part dans une direction imprévue. Rust ne rend pas ces bugs
« moins graves », il empĂȘche le code de compiler tant qu'ils ne sont pas corrigĂ©s.
| CritĂšre | C / C++ | Rust |
|---|---|---|
| Performance brute | Excellente | Ăquivalente (LLVM, zĂ©ro coĂ»t d'abstraction) |
| Garbage collector | Non | Non |
| Sécurité mémoire | à la charge du développeur | Vérifiée à la compilation |
| Data races | Possibles | EmpĂȘchĂ©es Ă la compilation (code sĂ»r) |
| ĂcosystĂšme embarquĂ© / robotique | TrĂšs mature (ROS 1/2, drivers historiques) | En forte croissance (embedded-hal, ROS 2 via rclrust, nalgebra) |
| Courbe d'apprentissage | Douce au début, piÚges cachés ensuite | Plus raide au début (emprunteur), puis trÚs fiable |
no_std / bare-metal | Natif | Supporté (chapitre 14) |
Soyons honnĂȘtes : l'Ă©cosystĂšme C/C++ reste plus mature pour certains drivers bas niveau et pour ROS 1. Rust ne remplace pas magiquement des dĂ©cennies de bibliothĂšques existantes. Mais pour du code neuf â calcul numĂ©rique, algorithmes d'estimation d'Ă©tat, logique de contrĂŽle â Rust offre les mĂȘmes performances que le C++ avec une classe de bugs en moins, ce qui compte Ă©normĂ©ment quand ce code pilote un systĂšme physique.
Retiens cette phrase : Rust ne rend pas le code plus lent pour le rendre plus sûr. Le compilateur fait tout le travail de vérification avant l'exécution ; une fois compilé, le code sûr n'a (quasiment) aucun coût par rapport à l'équivalent C.
2. Installer Rust avec rustup
L'outil d'installation officiel est rustup. Il gĂšre les versions du compilateur, les cibles de compilation (utile plus tard pour cibler un microcontrĂŽleur) et les composants additionnels. Sous Linux ou macOS :
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Sous Windows, télécharge et exécute rustup-init.exe depuis rustup.rs. Dans les deux cas,
l'installateur pose quelques questions (garde l'option par défaut « installation standard ») puis
met en place trois outils essentiels :
rustcâ le compilateur.cargoâ le gestionnaire de paquets et de projets (l'Ă©quivalent d'unmake+apt+CMakerĂ©unis).rustuplui-mĂȘme â pour mettre Ă jour la chaĂźne d'outils (rustup update) ou installer des composants.
Ajoute ensuite deux composants indispensables au quotidien :
rustup component add clippy # linter
rustup component add rustfmt # formateur de code
Vérifie que tout est bien installé :
rustc --version
cargo --version
cargo clippy --version
cargo fmt --version
Tu devrais voir quelque chose comme rustc 1.8x.0 (xxxxxxxxx 2026-xx-xx). Si la commande
rustc n'est pas trouvée, redémarre ton terminal (l'installateur modifie le
PATH dans ton fichier de profil shell).
Contrairement au C/C++, il n'existe pas de « chaßne de compilateurs concurrents » à choisir
(gcc, clang, MSVCâŠ). rustc est le seul compilateur de rĂ©fĂ©rence, ce qui simplifie
beaucoup la portabilité d'un projet Rust d'une machine à l'autre.
3. Créer un premier projet avec cargo
Tout projet Rust sĂ©rieux passe par cargo, mĂȘme pour un seul fichier. CrĂ©e le projet qui
nous servira de point de départ pour le fil rouge du cours :
cargo new robot_kalman
cd robot_kalman
cargo new génÚre l'arborescence minimale suivante :
robot_kalman/
âââ Cargo.toml # mĂ©tadonnĂ©es du projet + dĂ©pendances (Ă©quivalent d'un CMakeLists.txt)
âââ .gitignore # ignore dĂ©jĂ le dossier target/
âââ src/
âââ main.rs # point d'entrĂ©e du programme
Cargo.toml ressemble à ceci juste aprÚs création :
[package]
name = "robot_kalman"
version = "0.1.0"
edition = "2021"
[dependencies]
La section [dependencies] est vide pour l'instant ; Ă partir du chapitre 9 nous y
ajouterons nalgebra pour l'algĂšbre linĂ©aire. Contrairement au C++ oĂč lier une
bibliothÚque implique souvent de bricoler un CMakeLists.txt, ajouter une dépendance
Rust se résume à une ligne dans ce fichier ; cargo télécharge, compile et lie la
bibliothĂšque automatiquement.
4. Compiler et exécuter
Trois commandes couvrent l'essentiel du cycle de développement :
cargo check # vérifie que le code compile, sans générer d'exécutable (rapide)
cargo run # compile (mode debug) puis exécute
cargo build --release # compile avec toutes les optimisations activées
cargo check est ton meilleur ami pendant l'écriture du code : il fait tourner
l'analyse du compilateur (types, emprunts, etc.) sans passer par la génération de code machine, donc
c'est nettement plus rapide qu'une compilation complÚte. Utilise-le en boucle pendant que tu écris,
et cargo run quand tu veux réellement exécuter le programme.
Pour du code de robotique temps réel, ne mesure jamais les performances en mode
debug. Par défaut, cargo build et cargo run compilent en mode
debug : assertions de dépassement d'entier actives, quasiment aucune optimisation,
code parfois 10 Ă 50 fois plus lent. Le mode --release active les optimisations
LLVM (inlining agressif, vectorisation, suppression des vérifications de debug) et c'est le seul
mode représentatif de ce qui tournera sur le robot. Réflexe à prendre : développe et teste la
logique en debug (messages d'erreur plus clairs, compilation plus rapide), mais benchmarke et
déploie toujours en --release.
Exécute le projet fraßchement créé :
cargo run
Tu devrais voir s'afficher :
Compiling robot_kalman v0.1.0 (/chemin/vers/robot_kalman)
Finished dev [unoptimized + debuginfo] target(s) in 0.42s
Running `target/debug/robot_kalman`
Hello, world!
5. Le premier programme, ligne par ligne
Ouvre src/main.rs : cargo y a déjà généré le programme le plus simple possible.
fn main() {
println!("Hello, world!");
}
fn main() déclare la fonction point d'entrée, exactement comme en C. Pas de type de
retour ici : une fonction qui ne retourne rien a le type unité (), implicite. Modifions
ce programme pour saluer notre projet :
fn main() {
println!("Bonjour, robot !");
}
Remarque le ! aprĂšs println : ce n'est pas une fonction, c'est une
macro. En Rust, tout identifiant suivi de ! est une macro, invoquĂ©e Ă
la compilation. println! vérifie à la compilation que le nombre et le type des
arguments correspondent au format demandĂ© â une erreur de format est donc dĂ©tectĂ©e avant mĂȘme
l'exĂ©cution, contrairement Ă printf en C oĂč un mauvais %d/%s
est un bug silencieux (voire une faille de sécurité) qui ne se révÚle qu'à l'exécution.
fn main() {
let angle_deg = 42.5;
let iteration = 7;
println!("ItĂ©ration {iteration} â angle mesurĂ© : {angle_deg} deg");
println!("{} et {} sont interpolés", 0.0, 1.0);
}
En C, printf("%d", ma_variable) ne vérifie rien à la compilation :
si le type de ma_variable ne correspond pas au format %d, c'est un
comportement indéterminé silencieux. En Rust, println!("{}", ma_variable) est vérifié
par le compilateur au moment de l'expansion de la macro : un type qui n'implémente pas l'affichage
(trait Display) donne une erreur de compilation claire, jamais un plantage surprise
en production.
6. Cargo.toml : dépendances et profils de compilation
Cargo.toml centralise la configuration du projet. Deux notions Ă connaĂźtre dĂšs
maintenant, mĂȘme si nous les creuserons plus tard :
[package]
name = "robot_kalman"
version = "0.1.0"
edition = "2021"
[dependencies]
# nalgebra = "0.33" # décommenté au chapitre 9, pour l'algÚbre linéaire
# Profil utilisé par "cargo build" et "cargo run" (sans --release)
[profile.dev]
opt-level = 0 # pas d'optimisation : compilation rapide, exécution lente
# Profil utilisé par "cargo build --release"
[profile.release]
opt-level = 3 # optimisations maximales
lto = true # link-time optimization : inlining inter-modules
codegen-units = 1 # meilleure optimisation au prix d'une compilation plus lente
panic = "abort" # pas de déroulement de pile en cas de panic : code plus petit et plus prévisible
Le bloc [profile.release] ci-dessus n'est pas obligatoire â Cargo a dĂ©jĂ de bonnes
valeurs par dĂ©faut pour opt-level et panic â mais c'est une configuration
courante en robotique embarquée : lto = true et codegen-units = 1
permettent au compilateur d'optimiser Ă travers les frontiĂšres de modules, ce qui compte pour du
code de boucle de contrÎle appelé des milliers de fois par seconde.
7. Outillage quotidien : clippy et rustfmt
Deux commandes Ă exĂ©cuter systĂ©matiquement avant de committer du code, y compris (surtout) sur un projet de robotique oĂč un avertissement ignorĂ© peut cacher un vrai bug numĂ©rique :
cargo clippy # linter : détecte les erreurs probables et les non-idiomes
cargo fmt # formate le code selon le style Rust standard
cargo clippy va bien au-delà d'un simple linter de style. Il détecte par exemple les
comparaisons de flottants avec == (dangereuses en calcul numĂ©rique â pense Ă un test
d'égalité sur un angle aprÚs une suite de rotations), les boucles réécrivables en itérateurs plus
idiomatiques et performants, ou les conversions de type risquées. Prends l'habitude de lancer
cargo clippy --all-targets -- -D warnings dans ta CI : cette option transforme tous
les avertissements en erreurs de compilation, ce qui empĂȘche un avertissement ignorĂ© de se glisser
en production.
8. Le fil rouge du cours
Tout au long de ce cours, nous allons construire progressivement un mĂȘme projet,
robot_kalman, celui que tu viens de créer. Il évoluera chapitre aprÚs chapitre pour
devenir un mini moteur de calcul pour bras robotisé, comprenant :
- des types reprĂ©sentant l'Ă©tat du robot (position articulaire, vitesse, pose de l'effecteur) â chapitre 5 ;
- de l'algÚbre linéaire maison puis via
nalgebra(vecteurs, matrices) â chapitres 6, 7, 9 et 10 ; - un calcul de cinĂ©matique directe Ă partir de paramĂštres de Denavit-Hartenberg â chapitre 11 ;
- un calcul de cinĂ©matique inverse par mĂ©thode numĂ©rique â chapitre 12 ;
- un filtre de Kalman linĂ©aire complet pour fusionner des mesures bruitĂ©es et estimer l'Ă©tat rĂ©el du bras â chapitre 13 ;
- des optimisations pensĂ©es pour tourner dans une boucle de contrĂŽle temps rĂ©el, sans allocation mĂ©moire â chapitre 14.
Garde ce projet ouvert au fil des chapitres : chaque nouvelle notion viendra s'y greffer, exactement comme un vrai projet de robotique grandit au fil du développement.
Exercices
Exercice 1 â CrĂ©er un projet
Si ce n'est pas déjà fait, crée un nouveau projet cargo nommé robot_kalman et
vérifie qu'il s'exécute avec cargo run. Inspecte le contenu de
Cargo.toml et de src/main.rs.
Voir la solution
cargo new robot_kalman
cd robot_kalman
cargo run
La sortie doit se terminer par Hello, world!. Le dossier target/ vient
d'apparaĂźtre : c'est lĂ que cargo place les artefacts de compilation (Ă ne jamais committer,
.gitignore s'en charge déjà ).
Exercice 2 â Personnaliser le message et afficher plusieurs lignes
Modifie src/main.rs pour qu'il affiche, sur plusieurs lignes distinctes : un message
de bienvenue avec le nom du projet, une ligne indiquant un nombre d'articulations (par exemple 6),
puis une ligne affichant ce nombre au carré à l'aide d'une variable intermédiaire.
Voir la solution
fn main() {
println!("Bienvenue dans robot_kalman !");
let nb_articulations = 6;
println!("Nombre d'articulations : {nb_articulations}");
let carre = nb_articulations * nb_articulations;
println!("Carré du nombre d'articulations : {carre}");
}
Chaque appel Ă println! ajoute automatiquement un saut de ligne final (comme
puts en C, contrairement Ă printf qui ne le fait pas). La syntaxe
{nb_articulations} directement dans la chaĂźne (interpolation) est Ă©quivalente Ă
println!("...: {}", nb_articulations) mais plus lisible.
Exercice 3 â Comparer debug et release
Compile ton projet en mode debug puis en mode release, et compare la taille des exécutables générés ainsi que le temps affiché par cargo pour chaque compilation.
Voir la solution
cargo build
ls -lh target/debug/robot_kalman
cargo build --release
ls -lh target/release/robot_kalman
Tu devrais constater que la compilation --release prend plus de temps (le
compilateur applique des optimisations LLVM coûteuses) mais que l'exécutable généré est en
général plus petit et surtout beaucoup plus rapide à l'exécution, une fois les optimisations
appliquées. Sur un vrai calcul numérique (boucle de filtre de Kalman par exemple), l'écart de
performance entre debug et release peut facilement atteindre un facteur 10 Ă 50.
Exercice 4 â Utiliser cargo clippy
Dans src/main.rs, ajoute volontairement une comparaison d'égalité entre deux
flottants avec ==, puis lance cargo clippy et observe l'avertissement
obtenu. Corrige le code en comparant plutÎt la valeur absolue de la différence à un petit seuil
(« epsilon »).
Voir la solution
fn main() {
let angle_mesure = 0.1 + 0.2; // en toute rigueur, ne vaut pas exactement 0.3 en binaire
let angle_cible = 0.3;
// Version fautive, détectée par clippy (clippy::float_cmp) :
// if angle_mesure == angle_cible { ... }
// Version correcte : comparaison Ă un epsilon prĂšs
let epsilon = 1e-6;
if (angle_mesure - angle_cible).abs() < epsilon {
println!("Angle atteint (Ă epsilon prĂšs) !");
} else {
println!("Ăcart rĂ©siduel : {}", angle_mesure - angle_cible);
}
}
cargo clippy signale ce genre de comparaison directe entre flottants via le lint
clippy::float_cmp, car l'arithmétique flottante en base 2 ne représente pas
exactement la plupart des nombres dĂ©cimaux (0.1 + 0.2 â 0.3 exactement). En estimation d'Ă©tat
(Kalman) comme en cinématique, compare toujours des flottants à un epsilon prÚs plutÎt qu'avec
==.
Récapitulatif
- Rust vise les mĂȘmes performances que le C/C++ tout en Ă©liminant Ă la compilation les erreurs mĂ©moire et les data races.
rustupinstallerustc,cargo, et permet d'ajouterclippyetrustfmt.cargo newcrée un projet standard (Cargo.toml+src/main.rs) ;cargo check,cargo runetcargo build --releasecouvrent le cycle de développement.- Le mode
--releaseest obligatoire pour évaluer ou déployer du code temps réel : le mode debug n'est pas représentatif des performances réelles. - Un identifiant suivi de
!commeprintln!est une macro, vĂ©rifiĂ©e Ă la compilation â contrairement Ăprintfen C. cargo clippyetcargo fmtfont partie du flux de travail normal, pas d'une Ă©tape optionnelle.- Le fil rouge du cours, le projet
robot_kalman, grandira chapitre aprÚs chapitre jusqu'à un filtre de Kalman et une cinématique directe/inverse complets.