Lire le PWM, décoder l'entrée du récepteur RC et appliquer la sécurité intégrée
Composants et fournitures
| × | 1 | ||||
| × | 2 |
À propos de ce projet
Ce projet contient un code générique mais efficace qui peut être utilisé pour simplement lire un récepteur RC (ou tout autre signal PWM) sur n'importe quelle broche d'entrée Arduino, et également appliquer une sécurité intégrée en cas de perte du signal de l'émetteur.
Vous trouverez ci-dessous une vidéo montrant un Arduino uno agissant comme un servomélangeur à l'aide du code PWMread_RCfailsafe.ino disponible en bas de cette page.
Les fonctions de PWMread_RCfailsafe.ino s'occupent des registres d'interruption pour vous et peuvent être facilement déplacées entre différents projets utilisant différentes broches.
Dans l'exemple vidéo ci-dessous :
- La sécurité intégrée s'active lorsque le récepteur n'a aucun signal de l'émetteur. le canal de profondeur est réglé au maximum et le canal des ailerons est réglé au neutre
- La direction du servo bleu est définie par la position de l'accélérateur
- La plage de mouvement (taux) du servo bleu est définie par le curseur sur le côté de l'émetteur
- Le mixage est activé et désactivé à l'aide du commutateur de vitesse sur l'émetteur
Sommaire
- Comment les servos sont-ils contrôlés par PWM
- Exemples d'utilisation d'Arduino dans les modèles/robots RC
- Aperçu du code :décoder le PWM du récepteur RC avec sécurité intégrée
- Comment utiliser PWMread_RCfailsafe.ino
- Afficher la fréquence d'images et la fréquence du récepteur
- Exemple de mixage servo
- Raison d'être de l'approche adoptée
- Restrictions
Comment les servos et les contrôleurs de vitesse sont-ils contrôlés par PWM ?
Il est supposé dans le reste de ce projet que vous avez une compréhension des signaux PWM utilisés pour contrôler les servos et les contrôleurs de vitesse. Voici une bonne vidéo expliquant le fonctionnement de ces signaux de modulation de largeur d'impulsion (PWM).
Vous devez également avoir une connaissance pratique de :
- L'IDE Arduino
- Variables flottantes, booléennes et int
- Si boucles
- Pour les boucles
- Tableaux
- Bibliothèque de servos
Exemples d'utilisation d'Arduino dans les modèles / robots RC
Vous ne serez limité que par votre imagination :
- Appliquer le mélange servo, allumer/éteindre les lumières, contrôler les pompes/vannes, définir des séquences sur mesure...
- Créez un contrôleur (c'est-à-dire stabilisation de vol/pilote automatique, maintien de cap, maintien d'altitude/profondeur, nivellement automatique, détection et évitement, retour à la maison...)
- Demandez à votre modèle RC de réagir à une perte de signal ou à une tension de batterie faible...
- Utilisez le même émetteur pour plusieurs modèles/projets sans avoir à modifier les paramètres ou à utiliser une fonction de mémoire de modèle.
Présentation du code :décoder le PWM du récepteur RC avec sécurité intégrée
Ce code mesure les signaux PWM (Pulse Width Modulation) à l'aide d'interruptions de changement de broche. Les fonctions utilisées automatisent la mise en place des interruptions et l'extraction des données de n'importe quelle broche numérique ou analogique (hors A6 et A7), sur Arduino Uno, Nano ou Pro Mini. Cela rend le code facile à utiliser même pour les débutants.
L'objectif principal de ce projet était de créer un récepteur RC générique avec un "module" à sécurité intégrée qui peut être rapidement déplacé entre les projets. En tant que tel, l'exemple de code présenté dans la section « comment utiliser » peut simplement être utilisé comme un moyen pour parvenir à une fin.
Remarque : ce code ne fonctionnera pas avec le logiciel série ou toute autre bibliothèque qui utilise des interruptions de changement de broche.
Pour ceux qui s'intéressent au fonctionnement du code :
- Les broches d'entrée sont identifiées dans un tableau. Ce tableau peut avoir n'importe quelle longueur.
- Une fonction de configuration permet les interruptions de changement de broche en définissant les registres appropriés pour chaque broche répertoriée dans le tableau de broches.
- Un changement de tension sur l'une des broches sélectionnées déclenchera l'une des trois routes de service d'interruption (ISR) en fonction du registre de port auquel la broche appartient ISR(PCINT0_vect) -> Port B, ISR(PCINT1_vect) -> Port C ou ISR(PCINT2_vect) -> Port D.
- Dans chaque ISR, une boucle FOR et des instructions IF sont utilisées pour déterminer quelle broche a changé et à quel canal RC elle appartient. L'heure de l'interruption est notée via l'utilisation de micros() avant de revenir à la boucle principale().
- Les intervalles de temps entre les changements de broche sont utilisés pour calculer la largeur d'impulsion et la période de répétition.
- Des drapeaux sont placés dans chaque ISR pour indiquer quand de nouvelles impulsions ont été reçues
- Les drapeaux sont ensuite utilisés par les fonctions restantes pour extraire et traiter les données collectées par les ISR
La vidéo YouTube suivante réalisée par Joop Brokking parle d'un projet différent qui utilise la même méthode pour connecter un récepteur RC à arduino. Pendant les 8 premières minutes, Joopexplique clairement comment faire utilisez les interruptions de changement de broche pour mesurer les signaux PWM d'un récepteur RC.
Tous ces détails sont pris en charge par PWMread_RCfailsafe.ino qui peut être téléchargé au bas de cette page.
Des informations utiles sur la manipulation des ports peuvent également être trouvées ici : https://tronixstuff.com/2011/10/22/tutorial-arduino-port-manipulation/
En plus de la gestion des interruptions de changement de broche, une fonction dédiée RC_decode() a été écrite pour convertir une largeur d'impulsion (1000-2000uS) en un signal de contrôle de +-100 % à partir d'un émetteur. Une sécurité intégrée vérifie un signal d'émetteur valide en utilisant les tolérances de signal de 10 à 330 Hz et de 500 à 2 500 uS. Si le signal est perdu, alors RC_decode() renvoie une valeur de sécurité prédéfinie.
Les valeurs d'étalonnage pour un émetteur spécifique et les positions de sécurité peuvent être définies pour chaque canal dans PWMread_RCfailsafe.ino
Comment utiliser PWMread_RCfailsafe.ino
Étape 1 : Exemple matériel configuration avec Arduino Uno
Si vous suivez l'exemple de configuration avec un Arduino Uno, connectez votre récepteur comme suit :(sinon, si vous utilisez votre propre projet, passez directement à l'étape 2)
- Alimentation du récepteur à l'aide des broches 5v et GND
- Connectez les broches de signal du récepteur aux broches 2 à 7 de l'Arduino à l'aide de cavaliers femelles à mâles. (Si vous avez un récepteur à 2 canaux, connectez-vous uniquement aux broches 2 et 3)
- Pour l'exemple du servo-mélangeur, attachez un fil de signal d'asservissement à la broche 9 et l'autre à la broche 10.
Étape 2 :Copiez PWMread_RCfailsafe.ino dans le dossier de croquis
Un exemple de croquis RC_Read_Example a été inclus pour téléchargement au bas de la page. Vous pouvez l'utiliser comme croquis principal en suivant les étapes ci-dessous.
Copiez et collez le fichier PWMread_RCfailsafe.ino dans le dossier contenant votre croquis principal. Lors de la prochaine ouverture de l'esquisse dans l'IDE, un deuxième onglet apparaîtra contenant le code dans PWMread_RCfailsafe.ino.
Étape 3 :Précisez le saisie épingles
Ouvrez ou rouvrez l'esquisse principale dans l'IDE Arduino.
Cliquez sur l'onglet PWMread_RCfailsafe, faites défiler jusqu'au titre "VARIABLES DÉFINIES PAR L'UTILISATEUR" et entrez les broches d'entrée dans le tableau pwmPIN[].
Remarque : N'importe quel nombre de broches peut être utilisé, et dans n'importe quel ordre. Sachez simplement que plus vous avez d'entrées, plus le code passera de temps à traiter la routine d'interruption. Remarque A6 et A7 sont des broches analogiques uniquement et ne peuvent pas être utilisées.
L'Arduino MEGA n'est actuellement pas pris en charge, mais cela pourrait être facilement résolu s'il y avait de l'appétit pour cela.
Remarque : le premier élément de pwmPIN[] est le canal 1, le deuxième élément le canal 2, etc... si vous utilisez tous les canaux du récepteur, ce serait une bonne idée de vous assurer que les canaux du récepteur 1 correspondent au canal 1 dans pwmPIN[]...
Étape 4 : Révision le disponible fonctions dans PWMread_RCfailsafe.ino
Étape 5 : Imprimer le impulsion largeur données à série
Téléchargez le code RC_Read_Example, allumez votre émetteur et imprimez les données brutes de largeur d'impulsion en série.
La fonction RC_avail() doit être utilisée pour vérifier quand de nouvelles données ont été reçues sur tous les canaux, puis utilisez print_RCpwm() pour envoyer les données de largeur d'impulsion au série.
Étape 6 : Calibrer l'émetteur
Utilisation des données de largeur d'impulsion imprimées en série via print_RCpwm() pour modifier manuellement les valeurs dans les tableaux RC_min[], RC_mid[] et RC_max[] afin de calibrer chaque canal dans la plage +-100 %.
Étape 7 : Imprimez les canaux calibrés à série
Commentez la fonction print_RCpwm()
Utilisez la fonction RC_decode(channel) pour calibrer chaque canal dans la plage +-1.
Ensuite, imprimez chacun des canaux calibrés en série à l'aide de la fonction decimal2percentage() suivie de Serial.println("")
Étape 8 : Définir la sécurité intégrée
Ajustez les positions de sécurité dans le tableau RC_failsafe[] pour chaque canal (dans la plage +-1).
Allumez et éteignez l'émetteur pour vérifier que la sécurité intégrée fonctionne comme vous le souhaitez.
L'entrée RC peut maintenant être utilisée dans votre croquis.
Remarque : vous devrez peut-être désactiver toute fonction de sécurité intégrée dans le récepteur, sinon l'arduino ne pourra pas répondre à la perte du signal de l'émetteur.
Afficher la fréquence d'images et la fréquence du récepteur
La période de répétition des impulsions du récepteur et la fréquence peuvent être imprimées en série. Vérifiez que de nouvelles données sont disponibles sur le canal choisi en utilisant la fonction PWM_read(numéro de canal), avant d'utiliser PWM_period() et PWM_freq() pour extraire les données pour l'impression. Un exemple de code est disponible dans RC_FrameRate.ino.
Il est préférable d'utiliser le premier canal car ce sera la première impulsion envoyée dans chaque trame de réception. PWM_read() utilise les mêmes indicateurs que RC_decode(CH) alors assurez-vous que PWM_read() est appelé en premier.
Voir la capture d'écran ci-dessous :
La période de réception peut être utile à connaître car elle vous indique le temps dont dispose le code avant l'arrivée de la prochaine série de données. Si RC_avail () ne détecte pas de nouvelles données RC après un temps prédéterminé, c'est-à-dire 21 ms, exécutez RC_decode () afin de déclencher la sécurité intégrée et/ou de continuer à exécuter le programme (qui pourrait être un contrôleur PID) à une fréquence constante.
Ceci est réalisé dans le RC_Read_Example.ino par l'instruction if suivante.
now =millis();if(RC_avail() || now - rc_update> 21) rc_update =now; // mettre à jour les données d'entrée RC à l'aide de RC_decode() // exécuter un contrôleur PID // appliquer le mixage des servos // positionner les servos}
Exemple de mixage servo
J'ai inclus RC_ServoMixer_Example.ino pour montrer comment vous pouvez mélanger deux canaux de réception (dans ce cas, les canaux 2 et 3, la profondeur et l'aileron). Le croquis montre également une méthode de réglage de la direction, de la vitesse et du sous-trim du servo. La bibliothèque de servos est utilisée pour contrôler les servos via les broches 9 et 10.
Vous trouverez ci-dessous une capture d'écran de la section de mixage servo du code :
Le mixage est obtenu en ajoutant et en soustrayant simplement les deux canaux ensemble, et en limitant la sortie à la plage -1 à +1. Lorsque vous appliquez un mixage de profondeur et d'aileron, vous créez deux sorties, une pour chaque servo.
mix1 =canal 2 - canal3 (elv - ail)
mix2 =canal 2 + canal3 (elv - ail)
Avant de positionner les servos, vous devrez convertir le signal +-100% (+-1) en une largeur d'impulsion équivalente en microsecondes pour le servo. Dans le RC_ServoMixer_Example.ino, j'utilise une fonction calc_uS() pour ce faire. Cette fonction est placée au bas du croquis et est montrée dans la capture d'écran ci-dessous.
La direction, le taux et le sous-trim spécifiés pour chaque servo sont utilisés pour calculer une largeur d'impulsion appropriée pour le servo.
L'impulsion neutre standard est de 1500uS et la plage normale de chaque côté du neutre est de +-500uS. Cela donne une largeur d'impulsion min de 1000uS (-100%) et max de 2000uS (+100%). L'impulsion avec les taux, la direction et le sous-trim appliqués peut donc être calculée comme suit.
impulsion, uS =1500 + (servo_position_% * taux * direction + sous-trim) * 500
La direction, le taux et le sous-trim du servo peuvent être statiques ou modifiés dynamiquement par l'esquisse en réponse à une entrée d'un autre canal de réception, ou par d'autres moyens.
Justification de l'approche adoptée
Il est possible de lire un récepteur RC à l'aide de la fonction pulseIn(PIN, HIGH), cependant pulseIn() bloque le code en boucle() en attendant qu'une impulsion démarre puis se termine, ce qui fait perdre un temps de traitement précieux. S'il y a plus d'une entrée, les données peuvent également être perdues.
Pour la vitesse, il est préférable d'utiliser la fonction d'interruption de changement de broche de l'Arduino avec la manipulation directe du port pour permettre au code en boucle () de s'exécuter avec le minimum de retard. Cependant, cela est plus complexe et prend plus de temps que d'appeler simplement pulseIn(PIN, HIGH).
Par conséquent, je voulais profiter des avantages des deux mondes en écrivant du code générique que je peux déplacer entre les projets. Tout ce qui est nécessaire est de copier et coller un fichier .ino (contenant les fonctions et les routines d'interruption) dans le dossier de croquis principal, de spécifier les broches d'entrée, puis d'utiliser les fonctions dans le croquis.
Limites
Le fonction micros()
Le minutage de la microseconde sur l'arduino est effectué à l'aide de la fonction micros(). Cette fonction compte par pas de 4uS. Cela signifie que nous avons un niveau de précision de 4 microsecondes lorsque nous mesurons les impulsions 1000-2000uS. D'un point de vue pratique, c'est plus que suffisant.
Si vous le souhaitez, il est possible d'améliorer cette résolution à 0,5 µS en utilisant des interruptions de minuterie. voir le lien ci-dessous :
https://www.instructables.com/id/How-to-get-an-Arduino-micros-function-with-05us-pr/
Efficacité de PWMread_RCfailsafe.ino
Si vous utilisez PWMread_RCfailsafe.ino pour lire un récepteur à 6 ou 9 canaux, 1,4 à 2,0% du temps de traitement est consacré à l'exécution des routines d'interruption de changement de broche, ce qui, à mon avis, est plus qu'acceptable.
Cependant, il est toujours trop bon de comprendre les limites du code et comment il pourrait être accéléré si nécessaire.
Vous trouverez ci-dessous une liste du temps nécessaire pour exécuter chaque ISR en fonction du nombre de canaux d'entrée sélectionnés.
1 canal <8uS
2 canaux <12uS
3 canaux <16uS
4 canaux <20uS
5 canaux <20uS
6 canaux <24uS
Remarque : plus il y a de canaux utilisés, plus chaque ISR met de temps à s'exécuter. C'est parce qu'une boucle for traverse chaque canal chaque fois que l'ISR est appelé.
Ce temps supplémentaire (inefficacité) est négligeable lors de la mesure de signaux RC à basse fréquence (c'est-à-dire 50 Hz).
En plus de ce qui précède, il faut ~4uS pour entrer et sortir d'un ISR. Pour une impulsion, l'ISR s'exécute deux fois, une fois au début d'une impulsion (FAIBLE à ÉLEVÉ), puis à nouveau à la fin (ÉLEVÉE à FAIBLE).
Le temps nécessaire pour mesurer 1 impulsion lors de l'utilisation de 6 entrées RC est de
2 * (4us pour entrer ISR + 24uS pour exécuter ISR) =2 * 28 =48uS.
Remarque : il s'agit de la largeur d'impulsion minimale pouvant être mesurée.
Le temps nécessaire pour lire les 6 canaux est de 288uS (6 * 48uS)
En supposant que la période de répétition du récepteur est de 20 millisecondes, l'interruption s'exécutera pendant 1,44 % (0,000288/0,02) du temps. C'est nettement mieux que d'utiliser la fonction pulseIn(). pulseIn() bloquerait le code jusqu'à 20 millisecondes pour chaque broche.
Pour info : si l'arduino n'avait que 2 entrées RC, l'ISR ne fonctionnera que 0,16% du temps (0,00032/0,02)
Fréquence maximale pratique (Hz)
Si vous utilisez ce code à d'autres fins, je suggérerais que la fréquence maximale pratique soit de 2,5 kHz. Cela donne 100 pas de résolution à partir de la fonction micros() (+- 0,025 kHz).
Si vous utilisez une broche d'entrée à cette fréquence, 3% du temps est passé dans l'interruption, ce qui signifie que le service minimum qui peut être mesuré est de 0,03. Cela équivaut à une impulsion minimale de 12uS.
Pour des fréquences plus élevées, réécrivez les ISR en fonction de votre application.
Code
- PWMread_RCfailsafe
- RC_Read_Example
- RC_FrameRate
- RC_ServoMixer_Example
PWMread_RCfailsafeArduino
Ce fichier .ino contient les fonctions et les routines d'interruption de changement de broche (ISR) utilisées pour décoder un récepteur RC et appliquer une sécurité intégrée si le signal de l'émetteur est perdu. Copiez et collez ce fichier dans le même dossier que l'esquisse principale (lorsque vous ouvrez l'esquisse, ce code apparaîtra comme un deuxième onglet dans l'IDE Arduino). Suivez ensuite les instructions du fichier.RC_Read_ExampleArduino
An example sketch used to display raw data in order to calibrate your RC receiver and set your the fail safe. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update;const int channels =6; // specify the number of receiver channelsfloat RC_in[channels]; // an array to store the calibrated input from receiver void setup() { setup_pwmRead(); Serial.begin(9600);}void loop() { now =millis(); if(RC_avail() || now - rc_update> 25){ // if RC data is available or 25ms has passed since last update (adjust to be equal or greater than the frame rate of receiver) rc_update =now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i =0; iRC_FrameRateArduino
Example sketch that prints the frame rate and frequency of an RC Receiver. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.void setup() { setup_pwmRead(); Serial.begin(9600);}void loop() { // Print RC receiver frame length and frame rate if (PWM_read(1)){ // if a new pulse is detected on channel 1 Serial.print(PWM_period(),0);Serial.print("uS "); Serial.print(PWM_freq());Serial.println("Hz"); }}RC_ServoMixer_ExampleArduino
An servo mixing example. Two channels from a 6 channel are receiver are mixed and sent to two servos controlled using the servo library. The PWMread_RCfailsafe.ino file should be copied into the same folder in order for the functions to be available.// servo variables#include// include the servo library to control the servosServo servo1; // name each servo output for use with the servo library Servo servo2; // Each servo must be attached to a pin that has a PWM output// on the arduino uno, nano and pro mini these pins are 3, 5, 6, 9, 10 and 11const int servo1_pin =9; // identify the pins that each servo signal wire is connected toconst int servo2_pin =10;// Select Servo Direction, Rates and Sub-trim (the size of each array must match the number of servos)boolean servo_dir[] ={0,1}; // Direction:0 is normal, 1 is reversefloat servo_rates[] ={1,0.5}; // Rates:range 0 to 2 (1 =+-500us (NORMAL), 2 =+-1000us (MAX)):The amount of servo deflection in both directionsfloat servo_subtrim[] ={0.0,0.0}; // Subtrimrange -1 to +1 (-1 =1000us, 0 =1500us, 1 =2000us):The neutral position of the servoboolean servo_mix_on =true;unsigned long now; // timing variables to update data at a regular interval unsigned long rc_update;// Receiver variablesconst int channels =6; // specify the number of receiver channelsfloat RC_in[channels]; // an array to store the calibrated input from receiver void setup() { servo1.attach(servo1_pin, 500, 2500); // attach the servo library to each servo pin, and define min and max uS values servo2.attach(servo2_pin, 500, 2500); setup_pwmRead(); Serial.begin(9600);}void loop() { now =millis(); if(RC_avail() || now - rc_update> 25){ // if RC data is available or 25ms has passed since last update (adjust to> frame rate of receiver) rc_update =now; print_RCpwm(); // uncommment to print raw data from receiver to serial for (int i =0; i 1) mix1 =1; // limit mixer output to +-1 else if(mix1 <-1) mix1 =-1; if(mix2> 1) mix2 =1; // limit mixer output to +-1 else if(mix2 <-1) mix2 =-1; // Calculate the pulse widths for the servos servo1_uS =calc_uS(mix1, 1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS =calc_uS(mix2, 2); // apply the servo rates, direction and sub_trim for servo 2, and convert to a RC pulsewidth (microseconds, uS) } else{ // MIXING OFF servo1_uS =calc_uS(RC_in[1],1); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) servo2_uS =calc_uS(RC_in[2],2); // apply the servo rates, direction and sub_trim for servo 1, and convert to a RC pulsewidth (microseconds, uS) } servo1.writeMicroseconds(servo1_uS); // write the pulsewidth to the servo. servo2.writeMicroseconds(servo2_uS); // write the pulsewidth to the servo. }}int calc_uS(float cmd, int servo){ // cmd =commanded position +-100% // servo =servo num (to apply correct direction, rates and trim) int i =servo-1; float dir; if(servo_dir[i] ==0) dir =-1; else dir =1; // set the direction of servo travel cmd =1500 + (cmd*servo_rates[i]*dir + servo_subtrim[i])*500; // apply servo rates and sub trim, then convert to a uS value if(cmd> 2500) cmd =2500; // limit pulsewidth to the range 500 to 2500us else if(cmd <500) cmd =500; return cmd;}
Schémas
This RC Receiver is powered by 5v and ground from the ICSP pins with the 6 signal outputs connected to pins 2-7Micro servo 1 is powered by 5v pin and ground, with signal wire connected to pin 9
Micro servo 2 powered by 3.3v pin and ground, with signal wired connected to pin 10
Processus de fabrication
- C - Entrée et Sortie
- Animation LCD et jeux
- Voltmètre DIY utilisant Arduino et Smartphone
- Enregistreur de données de température et d'humidité
- Comment lire la température et l'humidité sur Blynk avec DHT11
- Communication Python3 et Arduino
- Automates cellulaires basés sur Arduino et OLED
- Radio FM utilisant Arduino et RDA8057M
- Une entrée analogique isolée pour Arduino