Tutoriel · Débutant · 17 min de lecture
Machines à états finis pour le comportement robotique
Machine à états finis pour un robot : transitions explicites, temporisations, récupération, mises à jour non bloquantes, sécurité testable.
Les programmes de robots commencent souvent comme une boucle pleine de if : suivez la ligne sauf si un obstacle est proche, tournez jusqu’à dégagement, arrêtez quand la batterie est basse, et récupérez somehow quand un capteur dépasse son délai. Quand les comportements grandissent, la même condition peut déclencher des commandes contradictoires.
Une machine à états finis (FSM) rend le comportement actif explicite. À tout instant le robot est dans un état ; événements et gardes déterminent quand il transite vers un autre. Le motif est assez petit pour un Arduino et assez structuré pour être testé.
Séparez état, événement et action
Un état décrit ce que fait le robot dans la durée : FOLLOW_LINE, AVOID, SEARCH ou FAULT. Un événement est observé : un obstacle entre en zone d’arrêt, la ligne est perdue, une temporisation expire, ou l’opérateur appuie sur stop. Une action est la sortie : fixez les vitesses de roues, effacez un intégrateur, ou levez une alerte.
Évitez de créer des états pour des faits instantanés. OBSTACLE_DETECTED est généralement un événement ou une garde ; AVOIDING_OBSTACLE est un état car il persiste pendant que le robot exécute un comportement.
Commencez par une table de transitions avant le code :
| État courant | Garde ou événement | État suivant | Action d’entrée |
|---|---|---|---|
| Idle | Démarrage demandé et capteurs valides | Follow | Réinitialiser le contrôleur |
| Follow | Obstacle dans le seuil d’arrêt | Avoid | Arrêter la traction avant |
| Follow | Ligne manquante depuis 200 ms | Search | Mémoriser le dernier côté |
| Avoid | Chemin libre depuis 500 ms | Follow | Réinitialiser le contrôleur |
| Tout état actif | Temporisation capteur | Fault | Arrêter les moteurs |
| Fault | Reset opérateur et santé valide | Idle | Effacer la faute |
Le tableau expose les cas manquants et rend visible la priorité de sécurité.
Gardez la boucle de mise à jour non bloquante
Un état doit effectuer une petite mise à jour et revenir. Un delay() de deux secondes dans AVOID empêche le programme de voir une demande d’arrêt ou une panne capteur pendant ces deux secondes.
enum class State { Idle, Follow, Avoid, Search, Fault };
State state = State::Idle;
unsigned long enteredAt = 0;
void transitionTo(State next) {
state = next;
enteredAt = millis();
}
void loop() {
Sensors s = readSensors();
if (!s.healthy && state != State::Fault) transitionTo(State::Fault);
switch (state) {
case State::Idle: updateIdle(s); break;
case State::Follow: updateFollow(s); break;
case State::Avoid: updateAvoid(s); break;
case State::Search: updateSearch(s); break;
case State::Fault: updateFault(s); break;
}
}
Chaque mise à jour lit l’instantané capteur déjà capturé et écrit une seule commande d’actionneurs cohérente. Cela évite que différents états lisent des valeurs changeantes à différents moments dans un même cycle.
Placez les transitions à des frontières claires
Choisissez une règle de propriété. Une conception embarquée simple laisse chaque état demander ses transitions sortantes, tandis qu’un contrôle de sécurité de haut niveau peut outrepasser tout état actif.
void updateFollow(const Sensors& s) {
if (s.obstacleMm < stopThresholdMm) {
setWheels(0, 0);
transitionTo(State::Avoid);
return;
}
if (s.lineMissingForMs > 200) {
transitionTo(State::Search);
return;
}
followLine(s.lineError);
}
Revenez aussitôt après une transition pour que l’ancien état ne puisse pas aussi commander les actionneurs. À l’entrée d’un état de commande, réinitialisez l’historique qui ne doit pas fuir entre comportements, comme un intégral PID ou un échantillon dérivé.
Utilisez les actions de sortie avec parcimonie. La plupart du nettoyage peut se faire dans la fonction centralisée transitionTo() ou au chemin d’entrée de l’état suivant. Des fonctions séparées onEntry, onUpdate et onExit ne deviennent utiles que quand plusieurs états en ont vraiment besoin.
Utilisez temporisations et hystérésis comme gardes
Les événements physiques sont bruités. Si AVOID sort dès que la distance repasse au-dessus du même seuil d’entrée, le bruit de mesure peut causer un broutage rapide d’états. Utilisez deux seuils ou un temps de persistance : entrez sous 250 mm, mais ne sortez qu’après 350 mm tenus pendant 500 ms.
Chaque état qui attend un progrès exige une temporisation. Si le robot entre en SEARCH sans jamais trouver la ligne, il ne doit pas tourner indéfiniment. Transitez vers FAULT ou une récupération bornée après un intervalle testé.
Basez les minuteries sur le temps depuis l’entrée ou depuis le dernier progrès. Utilisez des soustractions comme now - enteredAt >= timeout, correctes au débordement du compteur non signé quand les types sont cohérents.
Donnez priorité aux transitions de sécurité
Définissez ce qui peut tout interrompre : arrêt d’urgence, données capteur critiques invalides, faute du pilote moteur, ou temporisation de chien de garde. Évaluez ces conditions avant les transitions propres au comportement et rendez explicite la sortie sûre.
Un état FAULT doit verrouiller quand une récupération automatique pourrait redémarrer un mouvement dangereux. Exiger un reset opérateur après disparition de la cause est souvent plus sûr que de resauter directement en FOLLOW.
Ne laissez pas deux machines à états indépendantes écrire les mêmes commandes moteurs sans règle d’arbitrage. Si navigation et gestion batterie touchent toutes deux au mouvement, un seul propriétaire doit choisir la commande finale et appliquer les limites de sécurité.
Testez les transitions sans faire tourner les moteurs
Déplacez les décisions dans des fonctions qui acceptent un instantané capteur et un temps, puis rendent un état suivant ou une commande. Nourrissez une séquence d’instantanés synthétiques :
- Ligne valide et chemin libre restent en
FOLLOW. - Un seul échantillon proche bruité ne déclenche pas si une persistance est exigée.
- Un obstacle proche soutenu entre en
AVOIDet commande d’abord l’arrêt. - Un chemin libre tenu pendant l’intervalle de sortie revient en
FOLLOW. - Une temporisation capteur atteint
FAULTdepuis chaque état actif.
Journalisez les changements d’état comme ancien → nouveau, l’événement déclencheur, et l’horodatage. Les journaux continus par boucle noient les transitions dans le bruit ; les journaux de transitions expliquent pourquoi le comportement a changé.
Sachez quand une FSM cesse d’être le modèle le plus simple
Une FSM convient quand le robot a un nombre modeste de modes mutuellement exclusifs. Elle devient difficile quand les états se multiplient pour représenter des combinaisons comme « naviguer tout en surveillant la batterie tout en réessayant la localisation ». Le nombre de transitions peut croître plus vite que les comportements eux-mêmes.
Les arbres de comportement composent des actions réutilisables avec structure de séquence, repli et récupération, d’où leur usage par des systèmes de navigation comme Nav2 pour des tâches complexes. N’introduisez pas un cadriciel d’arbres de comportement pour quatre états. Migrez quand la table de transitions devient vraiment difficile à relire ou que les comportements exigent une réutilisation hiérarchique.
Diagnostiquer les pannes de conception courantes
- Le robot ignore stop en tournant : un état contient une boucle bloquante ou un délai.
- Les moteurs reçoivent deux commandes par cycle : le code de transition continue après changement d’état.
- La machine broute : des gardes bruitées sans hystérésis ni persistance.
- La récupération ne finit jamais : l’état n’a ni condition de progrès ni temporisation.
- Une faute redémarre aussitôt le mouvement : les fautes de sécurité ne sont pas verrouillées ou les conditions de reset sont trop faibles.
- Tester exige tout le robot : décisions et accès matériel sont mélangés au lieu d’instantanés capteurs et sorties de commandes.
Le simulateur d’évitement d’obstacles fournit une séquence de comportements utile à modéliser : croisière, braquage autour d’une menace, arrivée, ou arrêt quand aucun mouvement sûr ne reste. Le simulateur de résolution de labyrinthe montre une autre séparation entre détection, mise à jour de carte, planification, et mouvement d’une case. Le robot mini sumo est le test le plus net de la règle de sécurité verrouillée ci-dessus : son réflexe de bord doit préempter tout autre état, depuis tout état, sinon le robot se sort lui-même de l’arène.
Explorer le graphe
Fait partie de ces réalisations
Les projets et parcours d'apprentissage qui incluent ce tutoriel.
- ProjetConstruire un mini robot sumo : adhérence et coin
- ProjetConstruire un robot de pick-and-place qui sait quand il rate
- ProjetConstruire un robot piloté au téléphone en Bluetooth ou Wi-Fi
- ProjetConstruire un robot ROS 2 qui voit et roule vers un marqueur
- Parcours d'apprentissageNavigation réactive : apprenez à un robot à éviter les obstacles
- Parcours d'apprentissagePréhenseurs et effecteurs : attrapez-le vraiment
Pour aller plus loin