← Retour au portfolio English

Installation du pendule

Vue d'ensemble de l'architecture firmware

Firmware d'une grande installation cinétique en forme de pendule, construite à partir d'une chaîne de segments de bras motorisés et d'un propulseur électromagnétique rotatif à la base. Quatre projets PlatformIO indépendants ciblent des cartes Teensy 4.1 — un par segment de bras (petit, moyen, long) plus un pour le propulseur — partageant la même couche de communication OSC mais chacun avec sa propre géométrie physique, son nombre de moteurs, et son espace de noms d'adresses OSC.

Topologie matérielle

Les commandes arrivent sur le réseau sous forme de messages OSC, qui atteignent une unité NGIMU de x-io Technologies faisant office de pont OSC-vers-série. Chaque contrôleur Teensy 4.1 reçoit l'OSC à la fois directement via son port série USB et via le NGIMU sur son port série auxiliaire (Serial1), décodé par une couche OSC encadrée en SLIP (Receive.cpp/Send.cpp, basée sur la bibliothèque OSC99 de Seb Madgwick avec une couche EventScheduler/EventTrigger pour les envois périodiques ou déclenchés par événement).

Chaque Teensy pilote son propre matériel d'actionnement :

Topologie matérielle de l'installation du pendule Les commandes OSC arrivent sur le réseau vers un pont NGIMU x-io, qui les relaie via une liaison série vers quatre contrôleurs Teensy 4.1 indépendants, chacun pilotant son propre matériel moteur ou bobine et ses actionneurs physiques. Réseau de contrôle Messages OSC via UDP Pont NGIMU x-io Technologies, OSC ⇄ série 4× contrôleurs Teensy 4.1 Petit/moyen/long bras + propulseur Pilotes moteur / bobine Cartes Cytron ou bobine PWM+DIR Actionneurs physiques Moteurs de chariot / électroaimants Encodeurs et fins de course renvoient la position à leur Teensy
Topologie matérielle : réseau → NGIMU → contrôleurs → pilotes → actionneurs

Boucle firmware partagée

Les quatre variantes de firmware partagent la même structure de boucle globale, construite à partir de la même paire générique Receive.cpp / Send.cpp :

  1. Entrée OSC (SLIP) — ReceiveDoTasks() récupère des octets à la fois depuis le port série USB et le port série auxiliaire du NGIMU, alimentant deux décodeurs SLIP indépendants.
  2. Répartition par adresse — chaque message OSC décodé est comparé à un motif d'adresse littéral (p. ex. /smallArm/destinationM1, /booster/activeZone) et met à jour l'état pertinent du contrôleur : la destination d'un moteur, un indicateur comme brakeState ou broadcast, ou déclenche une action comme calibrate().
  3. Boucle de contrôle — les contrôleurs de bras vérifient les interrupteurs de fin de course (anti-rebond via Bounce), exécutent la machine à états de calibrage si active, et sinon pilotent les moteurs vers leurs destinations ; le contrôleur du propulseur lit son encodeur rotatif, détermine la direction du balancement, et évalue quelle « zone » d'électroaimants est active.
  4. Entrées/sorties matérielles — les signaux PWM/direction sortent vers le pilote Cytron ou le pilote de bobines ; l'état des encodeurs et interrupteurs de fin de course revient en entrée.
  5. Télémétrie planifiée — SendDoTasks() exécute un EventScheduler qui empaquette périodiquement l'état (positions des moteurs, statut de calibrage, déclenchements de fin de course, erreurs, etc.) en messages OSC renvoyés via Serial1 vers le NGIMU, sous réserve de l'indicateur broadcast/broadcastState propre à chaque contrôleur.

Cette boucle se répète indéfiniment dans le loop() de chaque .ino.

Structure de la boucle firmware partagée Chacun des quatre firmwares Teensy exécute la même forme de boucle : décoder l'OSC entrant via la liaison série, le répartir par adresse pour mettre à jour l'état local, exécuter la boucle de contrôle par rapport aux interrupteurs de fin de course et aux encodeurs, piloter le matériel, puis renvoyer la télémétrie planifiée, en répétant à chaque itération de la boucle principale. Entrée OSC (SLIP) Série USB + sérieauxiliaire NGIMU Répartition par adresse Met à jour destinations et indicateurs Boucle de contrôle Fins de course, calibrage, mouvement en boucle fermée Entrées/sorties matérielles PWM+DIR en sortie, encodeurs/fins de course en entrée Télémétrie planifiée Position, statut, erreurs envoyés via série ↻ se répète à chaque itération de la boucle principale
Boucle firmware partagée, commune aux quatre variantes de contrôleur

Chaîne des segments de bras

Les trois firmwares de bras partagent la même mécanique sous-jacente de motor/interrupteurs de fin de course/calibrage, mais décrivent des points différents d'une même chaîne mécanique, de la base rotative jusqu'à l'extrémité :

SegmentMoteursRôle de chaque moteur
Propulseur (base)— (anneau de bobines)Entraîne électromagnétiquement le balancement rotatif du pendule
Bras longM1, M2, M3M1 = chariot amortisseur ; M2 = récepteur, se connecte à l'axe du propulseur/de la base ; M3 = émetteur, se connecte au récepteur du bras moyen
Bras moyenM1, M2, M3M1 = chariot amortisseur ; M2 = récepteur, se connecte à l'émetteur du bras long ; M3 = émetteur, se connecte au récepteur du petit bras
Petit bras (extrémité)M1, M2M1 = chariot amortisseur ; M2 = récepteur, se connecte à l'émetteur du bras moyen

À l'intérieur de chaque bras, des constantes SAFE_DISTANCE_* imposent un écart minimal (en tics d'encodeur) entre chariots adjacents afin qu'ils ne puissent jamais entrer en collision physique, et les min_pos/max_pos de chaque chariot sont recalculés en continu par rapport à ses voisins dans update_motor_positions().

Chaîne des segments de bras du pendule, de la base à l'extrémité Le propulseur forme la base rotative, relié mécaniquement en séquence au bras long, puis au bras moyen, puis au petit bras à l'extrémité. Base → extrémité Propulseur Pilote de bobines,base rotative Bras long 3 chariots Bras moyen 3 chariots Petit bras (extrémité) 2 chariots
Chaîne mécanique de la base (propulseur) à l'extrémité (petit bras)

Spécificités par bras

Petit brasBras moyenBras long
Moteurs233
Interrupteurs de fin de course3 (haut, mi-haut, bas)4 (haut, mi-haut, mi-bas, bas)4 (haut, mi-haut, mi-bas, bas)
FreinOui (brakeControlPin, checkBrakeState())Non — gestionnaire OSC de frein commenté, aucune vérification dans la boucleOui (brakeState rétabli, vérifié via OSC et checkBrakeState())
Tics pleine échelle300 000 (CALIBRATE_MAX_SMALL_ARM)800 000 (CALIBRATE_MAX_MIDDLE_ARM)6 791 754 (CALIBRATE_MAX_LONG_ARM), ≈137 cm du haut au bas
Espace de noms OSC/smallArm/.../middleArm/.../longArm/...
Type de structurependulum_small_armpendulum_mid_long_arms (instance middleArm)pendulum_mid_long_arms (instance longArm)

La routine de calibrage de chaque bras pilote ses moteurs extrêmes jusqu'à leurs interrupteurs de fin de course pour remettre les encodeurs à zéro, puis enregistre min_pos/max_pos pour le reste de l'exécution. Une étape move_motors() réduit la vitesse PWM par paliers de distance fixes à mesure qu'un chariot approche de sa destination (motor_movement()), et donne un court « boost » PWM si un moteur semble bloqué (position inchangée pendant environ 5000 itérations de boucle).

Reprise après coupure d'alimentation (petit bras / bras moyen / bras long)

Les trois firmwares de bras conservent les dernières positions connues des moteurs en EEPROM (structure à nombre magique, saveLastKnownPositions() / loadLastKnownPositions()) :

Propulseur : entraînement rotatif électromagnétique

Le propulseur est architecturalement différent des trois bras — au lieu de faire avancer des chariots le long d'un rail, il pulse un anneau de bobines électromagnétiques pour ajouter de l'énergie au balancement rotatif du pendule, synchronisé avec un unique encodeur rotatif :

Référence OSC

Contrôleurs de bras (smallArm / middleArm / longArm)

AdresseSensFonction
/*/destinationM1, /*/destinationM2, /*/destinationM3*entréeDéfinit la position cible d'un moteur (échelle 0–1000, convertie en tics d'encodeur)
/*/calibrateentréeDémarre (ou redémarre) la routine de calibrage
/*/stopAllMotorsentréeMet à zéro toutes les sorties PWM et fige la position actuelle comme destination
/*/brakeStateentréeEngage/relâche le solénoïde de frein (petit bras et bras long uniquement)
/*/broadcastentréeActive/désactive la télémétrie de ce contrôleur
/*/positionM1, /*/positionM2, /*/positionM3*sortiePosition actuelle du moteur
/*/calibrationStatussortieIndique si le calibrage est terminé
/*/resumeReliabilitysortieÉtat de reprise EEPROM : aucune / propre / approximative
/*/maxPossiblePositionM1, /*/minPossiblePositionM2, etc.sortieBornes de plage sûre en direct, dérivées des chariots voisins
/*/limitSwitch0N TriggeredsortieNotification ponctuelle qu'un interrupteur de fin de course s'est déclenché
/teensy/errorsortieChaîne d'erreur générique (adresse OSC invalide, échec de décodage, etc.)

* destinationM3/positionM3 n'existent que sur le firmware du bras moyen et du bras long (3 moteurs).

Contrôleur du propulseur

AdresseSensFonction
/booster/onentréeActive/désactive l'activation des bobines (met aussi le PWM à zéro immédiatement si désactivé)
/booster/strengthentréeAmplitude PWM appliquée à une bobine active
/booster/activeZoneentréeSélectionne combien de paires d'électroaimants sont actives (0–3)
/booster/brakeStateentréeEngage/relâche le frein du propulseur
/rotary/broadcastentréeActive/désactive la télémétrie
/rotary/valuesortiePosition brute de l'encodeur rotatif (envoyée toutes les 60 s)
/teensy/errorsortieChaîne d'erreur générique

Compilation

Chaque variante est son propre projet PlatformIO ciblant un Teensy 4.1 :

[env:teensy41]
platform = teensy
board = teensy41
framework = arduino

monitor_port = /dev/ttyACM0
monitor_speed = 9600