🩀 Rust pour la robotique · temps rĂ©el

Chapitre 01
Introduction & outillage

Objectifs du chapitre

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ĂšreC / C++Rust
Performance bruteExcellenteÉquivalente (LLVM, zĂ©ro coĂ»t d'abstraction)
Garbage collectorNonNon
SĂ©curitĂ© mĂ©moireÀ la charge du dĂ©veloppeurVĂ©rifiĂ©e Ă  la compilation
Data racesPossiblesEmpĂȘchĂ©es Ă  la compilation (code sĂ»r)
ÉcosystĂšme embarquĂ© / robotiqueTrĂšs mature (ROS 1/2, drivers historiques)En forte croissance (embedded-hal, ROS 2 via rclrust, nalgebra)
Courbe d'apprentissageDouce au début, piÚges cachés ensuitePlus raide au début (emprunteur), puis trÚs fiable
no_std / bare-metalNatifSupporté (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 :

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 :

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