Accueil > La technologie > Arduino > Manœuvre des aiguilles avec des servo-moteurs > Amélioration et mémorisation des réglages

Manœuvre des aiguilles en analogique avec des servo-moteurs

Amélioration et mémorisation des réglages

lundi 2 décembre 2013, par Jean-Luc

Comme indiqué dans la vidéo au bas de l’article « Réglage des butées des servomoteurs », un problème lié à l’inertie mécanique subsiste. Nous avons aussi le problème de la non mémorisation des réglages. Nous allons voir comment régler ces deux problèmes.

Prise en compte de l’inertie mécanique

Quand la consigne de position, via la durée de l’état haut de la PWM, est envoyé au servomoteur, celui-ci met quelques instants à appliquer cette consigne. Or, le programme actuel coupe la PWM est appelant la fonction detach() dès que la consigne atteint l’angle maximum ou l’angle minimum. Le servo n’a donc pas le temps de répondre à cette consigne et pour régler le problème il faut différer le détachement du servo.

Au lieu de détacher immédiatement le servo, il faut initialiser un compteur, que nous appelleront cptArret, qui quand il atteint 0 provoque le détachement du servo. Une fois le servo détaché, le compteur est mis à -1 pour éviter de détacher le servo de manière répétitive. Actuellement dans gereServo() en plusieurs endroits, la vitesse est mise à 0 et le servo est détaché.

Il faut remplacer les objetServo.detach() par l’initialisation du compteur. Pour rationaliser nous allons mettre le vitesse = 0; et l’initialisation du compteur dans une fonction membre séparée :

void arreteServo()
{
    vitesse = 0;
    cptArret = 30; 
}    

Avec le délai de 5ms dans loop(), une valeur de 30 engendre un délai de 150ms ce qui est suffisant pour que la mécanique rattrape l’électronique.

Il faut maintenant, dans la fonction gereServo(), décrémenter ce compteur tant qu’il est ≥ à 0 et détacher le servo quand ce compteur est égal à 0. De plus, à chaque fois que nous avons la mise de la vitesse à 0 et le détachement du servo, nous le remplaçons par l’appel à la fonction arreteServo(). gereServo() devient :

void gereServo()
{
    objetServo.writeMicroseconds(angle);
 
    if (vitesse == 0) {
        if (cptArret == 0)
            objetServo.detach();
        if (cptArret >= 0)
            cptArret--;
    }
    else
        angle += vitesse;
    
    if (! reglageEnCours ||
        etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MIN ||
        etatServo == SERVO_EN_MOUVEMENT_VERS_ANGLE_MAX) {
        if (angle > angleMax) {
            angle = angleMax;
            arreteServo();
            etatServo = SERVO_A_ANGLE_MAX;
        }
        else if (angle < angleMin) {
            angle = angleMin;
            arreteServo();
            etatServo = SERVO_A_ANGLE_MIN;
        }
    }
    else {
        if (etatServo == SERVO_A_ANGLE_MIN) {
            if (vitesse > 0 && angle >= angleMin) {
                angle = angleMin;
                arreteServo();
            }
            else if (vitesse < 0 && angle <= angleMin) {
                angle = angleMin;
                arreteServo();
            }
        }
        else if (etatServo == SERVO_A_ANGLE_MAX) {
            if (vitesse > 0 && angle >= angleMax) {
                angle = angleMax;
                arreteServo();
            }
            else if (vitesse < 0 && angle <= angleMax) {
                angle = angleMax;
                arreteServo();
            }
        }
    }
}

Il faut également garantir que le servo ne sera pas détaché intempestivement. En effet, si l’utilisateur demande un mouvement du servo dans l’intervalle de temps entre le passage de la vitesse à 0 et le passage du compteur à 0, il faut passer directement le compteur à -1 dans evenementServo() car sinon le détachement du servo interviendra pendant le mouvement, ce qui est un dysfonctionnement.

Mémorisation des butées

La plupart des microcontrôleurs dispose d’une mémoire permanente, c’est à dire qui ne s’efface pas à la mise hors tension, sous forme d’EEPROM (Electrically Erasable Programmable Read-Only Memory). La bibliothèque de l’Arduino permet de lire et d’écrire l’EEPROM de manière très simple via deux fonctions : EEPROM.read(...) et EEPROM.write(..., ...). EEPROM.read(...) prend comme argument une adresse d’octet (byte) et retourne l’octet qui se trouve à cet adresse et EEPROM.write(..., ...) prend deux arguments. Le premier est l’adresse de l’octet que l’on souhaite écrire et le second est la valeur que l’on souhaite y écrire. L’EEPROM de l’ATMega328 qui équipe l’Arduino Uno a une taille de 1ko.

Pour chaque servomoteur, il faut stocker les deux butées. Chaque butée est stockée dans un int. Sur l’Arduino Uno, un int prend 2 octets. Par conséquent il faut 4 octets pour chaque servos et donc 32 octets au total. En démarrant à l’adresse 0 de l’EEPROM, l’adresse de stockage des butée d’une servo sera donc égale au numéro du servo × 4. Nous allons donc stocker le premier octet de angleMin à numéro du servo × 4, le second à numéro du servo × 4 + 1, le premier octet de angleMax à numéro du servo × 4 + 2 et le 2e octet à numéro du servo × 4 + 3. Nous allons donc ajouter une donnée membre à la classe DescripteurServo pour y mémoriser cette adresse. Cette donnée est initialisée dans le constructeur.

Il est nécessaire d’être prudent avec les EEPROM. En effet, elle supporte un nombre de cycle d’effacement/écriture réduit. Par exemple, celle de l’ATMega328 qui équipe l’Arduino Uno supporte 100 000 cycles d’effacement/écriture. Cela peut sembler une valeur importante mais si nous écrivons imprudemment les butées des servos en EEPROM à chaque passage dans loop(), l’EEPROM sera fichue en 5ms × 100 000 = 500s = 8 minutes et 20 secondes.

Il faut donc n’écrire les butées que si un réglage a été effectué et si la valeur a changé. Nous allons donc ajouter un booléen membre de la classe DescripteurServo, aSauver, qui sera mis à vrai dans regleServo() si l’une des butées a été changée.

Enfin, deux fonctions membres, ecrireEEPROMSiDifferent(...) et enregistre() sont ajoutées pour mémoriser les butées dans l’EEPROM. Le travail n’est effectué que si aSauver est vrai et si les valeurs ont changé.

void ecrireEEPROMSiDifferent(int adresseEcriture, byte valeur)
{
    if (EEPROM.read(adresseEcriture) != valeur)
        EEPROM.write(adresseEcriture, valeur);
}
    
void enregistre()
{
    if (aSauver) {
        ecrireEEPROMSiDifferent(adresse, angleMin & 0xFF);
        ecrireEEPROMSiDifferent(adresse + 1, angleMin >> 8);
        ecrireEEPROMSiDifferent(adresse + 2, angleMax & 0xFF);
        ecrireEEPROMSiDifferent(adresse + 3, angleMax >> 8);
        aSauver = false;
    }            
}

Enfin, au démarrage du système dans connecte(), il faut initialiser la position des servos aux valeurs enregistrées. La valeur lue est recadrée entre ANGLE_MIN et ANGLE_MAX car l’EEPROM peut contenir des valeurs écrites par une autre application ou bien être vierge [1].

angleMin = EEPROM.read(adresse) | ((int)EEPROM.read(adresse + 1)) << 8;
angleMax = EEPROM.read(adresse + 2) | ((int)EEPROM.read(adresse + 3)) << 8;
if (angleMin < ANGLE_MIN || angleMin > ANGLE_MAX) angleMin = ANGLE_MIN;
if (angleMax < ANGLE_MIN || angleMax > ANGLE_MAX) angleMax = ANGLE_MAX;

Le sketch de l’application améliorée

Sketch Arduino de l’application avec réglage des butées amélioré

Et enfin une petite vidéo qui montre l’effet de ces deux améliorations.

Démonstration de l’amélioration et de la mémorisation des butées

[1Un octet d’EEPROM qui n’a jamais été écrit est à la valeur 255 en décimal ou 0xFF en hexadécimal (tous les bits de l’octet sont à 1).

Messages

  • Bonsoir,

    Félicitations pour votre article sur la commande des servos . Un grand merci de partager vos immenses compétences que ce soit sur votre site ou sur locoduino

    J’essaie de comprendre votre logiciel afin d’essayer de le modifier , mais je n’y arrive pas

    mon but est de commander un servo pas un inter ( à l’identique du décodeur pour aiguillage à solénoïdes site locoduino ) en gardant votre programme (manœuvre de servos )

    Je suis depuis des heures sur cette modification , mais je bloque

    Pouvez vous m’aider

    Cordialement
    Philippe 31 ( forum du N)

  • donc je suppose qu’il faut faire sa avec les huit servo et donc le num 4 ont refait la même manipulation que pour le zéro ?
    merci et super site

Un message, un commentaire ?

Qui êtes-vous ?
Votre message

Pour créer des paragraphes, laissez simplement des lignes vides.

Lien hypertexte

(Si votre message se réfère à un article publié sur le Web, ou à une page fournissant plus d’informations, vous pouvez indiquer ci-après le titre de la page et son adresse.)