Asservissement en position moteur DC

bonjour,
j'ai récupéré une optique motorisée de caméra de surveillance, il y avait a l'origine un boitier de commande qui a disparue.
sur les 3 commande : zoom , point , iris ,ce sont des moteur DC avec un potentiomètre de 5k pour la position.
j' ai une Nano avec un shield HW130 (2x L193D) , pour le zoom c'est un asservissement en vitesse et ça fonctionne , deux trois perfectionnement a ajouter sans doute.
pour le focus et l'iris c'est une autre histoire, impossible de stabiliser la commande, ça oscille d'un coté et de l'autre de la consigne quoi que je fasse.
voici une des nombreuses versions testé :wink:


// arduino UNO
// ADS 1115 multiplexeur analogique 4 entrées/sorties
// DollaTek L293D Motor Drive Shield

#include <AFMotor.h>
#include "Variables.h"
#include "ADS1X15.h"  // ADS1115

ADS1115 ADS(0x48);  // ADDR relier au 0V

AF_DCMotor Zoom(1);
AF_DCMotor Focus(2);
AF_DCMotor Iris(3);


void setup() {
  Serial.begin(115200);  //
  ADS.begin();
  ADS.setMode(0);      // mesures en continu (0 = CONTINUOUS, 1 = SINGLE)
  ADS.setDataRate(7);  // vitesse de mesure de tension, de 0 à 7
  ADS.readADC(0);      // lecture à vide, pour envoyer les paramètres

  pinMode(2, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);

  // active les moteurs
  Zoom.setSpeed(200);
  Zoom.run(RELEASE);
  Focus.setSpeed(200);
  Focus.run(RELEASE);
  Iris.setSpeed(200);
  Iris.run(RELEASE);

  // determine la vitesse du Focus et de l'Iris
  Focus.setSpeed(70);
  Iris.setSpeed(80);
  setpoint = 90;
  myPID.SetMode(AUTOMATIC);
}

void loop() {

  //////////// ZOOM ////////////

  Z = ADS.readADC(0);             // lecture du pot de Zoom via le multiplexeur
  SZ = analogRead(A0);            // lecture du pot de Speed Zoom
  LZ = analogRead(A1);            // lecture de la Limite du Zoom
  SZ = map(SZ, 0, 1023, 0, 255);  // maping de la valeur de speed (0,255)

  int invZoom = digitalRead(2);
  if (invZoom == 0) {
    SENSZOOM();
  }
  if (invZoom == 1) {
    Z = map(Z, 26600, 0, 0, 26600);  // inverse la valeur du pot de Zoom
    SENSZOOM();
  }

  //////////// FOCUS ////////////


  F = ADS.readADC(1);
  LF = analogRead(A2);
  F = map(F, 25500, 0, 863, 52);

  int invFocus = digitalRead(2);
  if (invFocus == 0) {
    SENSFOCUS();
  }
  if (invFocus == 1) {
    Z = map(Z, 26600, 0, 0, 26600);  // inverse la valeur du pot de Focus
    SENSFOCUS();
  }
}

void SENSZOOM() {
  //////////// DEZOOM ///////////
  if (Z <= 12000) {                // si déZoom 12774
    CZ = map(Z, 12000, 0, 0, SZ);  // 12774: millieux de la valeur maxi (multiplexeur) 25550
    Zoom.run(BACKWARD);            // fait tourner le moteur dans la sens indiqué
    Zoom.setSpeed(CZ);             // determine la vitesse du moteur
    if (LZ <= 190) {               // 176 - 990
      Zoom.run(RELEASE);
    }
  }
  ////////////// ZOOM ///////////
  if (Z >= 13520) {                    // si Zoom 12775
    CZ = map(Z, 13520, 25550, 0, SZ);  // valeur maxi 276000
    Zoom.run(FORWARD);
    Zoom.setSpeed(CZ);
    if (LZ >= 970) {
      Zoom.run(RELEASE);
    }
  }
}

void SENSFOCUS() {

  //////////// FOCUS ////////////
  if (F < LF) {

    Focus.run(FORWARD);  // vers le mini
    if (LF == F) {
      Focus.run(RELEASE);
    }
    if (LF >= 60) {  // limite mini
      Focus.run(RELEASE);
    }

    if (F > LF) {
      Focus.run(BACKWARD);  // vers le maxi
      if (LF == F) {
        Focus.run(RELEASE);
      }
      if (LF >= 800) {
        Focus.run(RELEASE);
      }
    }
  }

  void IRIS() {
  }

Bonjour @imaljl1

pour le focus et l'iris c'est une autre histoire, impossible de stabiliser la commande, ça oscille d'un coté et de l'autre de la consigne quoi que je fasse.

Y compris abec un régulateur PID ajusté ?

Des bibliothèques PID pour Aduino existent

j'ai essayer avec celle ci " #include <PID_v1_bc.h>"
la vitesse de déplacement fait que l’action dépasse la consigne et reste dans ce processus et se balade de quelque degrés de chaque coté de la consigne.
mais je n'ai pas assez de connaissance en programmation pour l’intégrer dans mon programme.
je ne sais pas comment gérer les variables.
il y a :
-F- valeur du pot de Focus
-LF- valeur du pot de copie du déplacement de la bague de focus
-SF- la valeur de vitesses (0 a 255)

  • FORWARD et BACKWARD - pour le sens

Un sujet récent sur un PID avec des liens vers une librairie et de la littérature sur le sujet.

je viens de tester avec ce que j'ai compris de la bibli, c'est toujours pareil, ça oscille autour de la consigne sans s’arrêter.
j'ai bien sur testé plein de possibilités de réglage du PID.

// arduino UNO
// ADS 1115 multiplexeur analogique 4 entrées/sorties
// DollaTek L293D Motor Drive Shield

#include <AFMotor.h>
#include "Variables.h"
#include "ADS1X15.h"  // ADS1115

ADS1115 ADS(0x48);  // ADDR relier au 0V

AF_DCMotor Zoom(1);
AF_DCMotor Focus(2);
AF_DCMotor Iris(3);

/**********************/
#include <PID_v1_bc.h>
double F = 0;   // setpoint
double LF = 0;  // input
double output = 0;
double Kp = 5, Ki = 3, Kd = 1; 
PID motor(&LF, &output, &F, Kp, Ki, Kd, DIRECT);
/***********************/

void setup() {
  Serial.begin(115200);  //
  ADS.begin();
  ADS.setMode(0);      // mesures en continu (0 = CONTINUOUS, 1 = SINGLE)
  ADS.setDataRate(7);  // vitesse de mesure de tension, de 0 à 7
  ADS.readADC(0);      // lecture à vide, pour envoyer les paramètres

  pinMode(2, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);

  // active les moteurs
  Zoom.setSpeed(200);
  Zoom.run(RELEASE);
  Focus.setSpeed(200);
  Focus.run(RELEASE);
  Iris.setSpeed(200);
  Iris.run(RELEASE);

  // determine la vitesse du Focus et de l'Iris
  //Focus.setSpeed(50);
  // Iris.setSpeed(80);
  motor.SetMode(AUTOMATIC);
  motor.SetOutputLimits(65, 853);
}

void loop() {
  //////////// ZOOM ////////////
  Z = ADS.readADC(0);             // lecture du pot de Zoom via le multiplexeur
  SZ = analogRead(A0);            // lecture du pot de Speed Zoom
  LZ = analogRead(A1);            // lecture de la Limite du Zoom
  SZ = map(SZ, 0, 1023, 0, 255);  // maping de la valeur de speed (0,255)

  int invZoom = digitalRead(2);
  if (invZoom == 0) {
    SENSZOOM();
  }
  if (invZoom == 1) {
    Z = map(Z, 26600, 0, 0, 26600);  // inverse la valeur du pot de Zoom
    SENSZOOM();
  }

  //////////// FOCUS ////////////
  F = ADS.readADC(1);   //set point
  LF = analogRead(A2);  // input // limites 853 - 65
  F = map(F, 25500, 0, 853, 65);
  motor.Compute();
  SENSFOCUS(output);
  //////////// IRIS ////////////
  IRIS();
}

void SENSZOOM() {
  //////////// DEZOOM ///////////
  if (Z <= 12000) {                // si déZoom 12774
    CZ = map(Z, 12000, 0, 0, SZ);  // 12774: millieux de la valeur maxi (multiplexeur) 25550
    Zoom.run(BACKWARD);            // fait tourner le moteur dans la sens indiqué
    Zoom.setSpeed(CZ);             // determine la vitesse du moteur
    if (LZ <= 190) {               // 176 - 990
      Zoom.run(RELEASE);
    }
  }
  ////////////// ZOOM ///////////
  if (Z >= 13520) {                    // si Zoom 12775
    CZ = map(Z, 13520, 25550, 0, SZ);  // valeur maxi 276000
    Zoom.run(FORWARD);
    Zoom.setSpeed(CZ);
    if (LZ >= 970) {
      Zoom.run(RELEASE);
    }
  }
}

void SENSFOCUS(int out) {
  if (out > 394) { // millieu de la plage focus
    Focus.run(BACKWARD);
    Focus.setSpeed(out);  
  } else {
    Focus.run(FORWARD);  
    Focus.setSpeed(out);
  }
}

void IRIS() {
}

J'ai bien sur testé plein de possibilités de réglage du PID.

au pif ou de manière méthodique : Kp seul d'abord puis Ki et enfin Kd ?

comme indiqué par exemple ici au chapître 3

méthodique comme indiqué dans la doc de Brett Beauregard

et ça ne permet pas d'atteindre relativement vite la consigne (Kp) , d'en rester proche (Ki) avec des oscillations faibles (Kd) ?

si les oscillations sont de très grande amplitude ('pompage , une valeur excessive de Kp peut en être la cause le système fonctionnant alors quasiment en 'Tout ou Rien' sans 'bande proportionnelle'

ça arrive vite a la consigne, c'est assez très proche mais oscillation importantes

constates-tu un dépassement très important avant d'osciller autour de la consigne ?

pas évident a déterminé car ça oscille tout le temps mais je trouve que non.
Kp,Ki,Kp c'est a règler sur quel plage: 0 a 10 , 0 a 100 ???

Un asservissement de position fait avec un moteur est un système du second ordre et instable par nature.

L'approche est assez difficile.

Tout d’abord, mettre Ki et Kd à 0.
Mettre kp à n'importe quelle valeur et constater :

  • Si ça oscille : baisser kp
  • Si ça ne fait rien ou que ça réagit très lentement : augmenter kp

Lorsqu'on a atteint quelque chose qui tend vers un asservissement avec de faibles oscillations, on augmente kd jusqu'à ce que les oscillations disparaissent.

NOTA : le terme kd a un effet stabilisant (tendre à rendre le système vers un premier ordre). Cependant son action agissant sur les fréquences hautes, il augmente les bruits du système. De ce fait il possible que vous n'arriviez à rien. Il est alors nécessaire d'ajouter un filtre passe bas dans la boucle .. pas simple.

Dès lors , en boucle :

  • ré-augmenter légèrement kp : ça devrait se remettre à osciller
  • ré_augmenter légèrement kd jusqu'à ce que les oscillations disparaissent

On doit arriver à un point où il n'est plus possible d'annuler les oscillations. Revenir alors un peu sur kp et kd.

Augmenter maintenant ki jusqu'à ce que des oscillations reviennent. A ce moment, re-diminuer légèrement ki : vous êtes arrivé à un équilibre satisfaisant ... que vous pouvez toujours essayer d'affiner.

Cordialement.

Pierre.

merci je vais faire ça.
j'aimerai savoir quand même si le code est correct avant de passer du temps a régler le système.

Quel est le domaine de tension que vous obtenez aux bornes de vos potentiomètres lorsqu'ils sont aux extrémités des positions d'iris et de focus ?

Est-ce que ça reste strictement positif ou bien est-ce ça passe en négatif ?

NOTA : l'ADS1X15 ne supporte que des tensions positives sur ses entrées.

Cordialement.

Pierre.

tension entre 0 et 4,9v

Parfait, pouvez-vous nous monter le morceau de code qui permet l'asservissement du moteur d'iris ou de focus.

Edit : non je n'ai rien dit.

Cordialement.

Pierre.

Vous pouvez nous fournir votre bibliothèque "Variables.h" en indiquant, si ne n'est déjà fait, à quoi correspond chaque variable.

Cordialement.

Pierre.

voici "Variables.h"


// attribution des entrées //
// ADS (A0) = Zoom
// ADS (A1) = Focus
// ADS (A2) = Iris
// A0 = Speed Zoom
// A1 = Limite Zoom
// A2 = Limite Focus
// A3 = Limite Iris
// A4 = SDA ADS multiplexeur analog
// A5 = SCL ADS multiplexeur analog
// D2 = inverseur du sens du Zoom
// D13 = inverseur du sens du Focus

int16_t Z;  // Entrée consigne Zoom
//int16_t F;  // Entrée consigne Focus_ remplacé: par double F pour le test PID
int16_t I;  // Entrée consigne Iris

uint8_t CZ;  // commande Zoom  ( 0,255 )
//int CF;  // commande Focus 
//int CI;  // commande Iris 

int LZ;  // Limite Zoom
// int LF;  // Limite Focus_ remplacé: par double LF pour le test PID
int LI;  // Limite Iris

int SZ;  // Speed Zoom
//int SF;  // Speed Focus

bool InvZ;  // invertion Zoom
bool InvF;  // invertion Focus

const int invZoom = 2;    // inverseur de sens Zoom
//const int invFocus = 13;  // inverseur de sens Focus

J'ai ajouté votre bibliothèque à votre sketch *.ino. Il manque tout un tas de déclarations.

Pouvez-vous donner un sketch qui, même s'il ne fonctionne pas, passe la compilation.

Cordialement.

Pierre

code .ino, qui compile

// arduino UNO
// ADS 1115 multiplexeur analogique 4 entrées/sorties
// DollaTek L293D Motor Drive Shield

#include <AFMotor.h>
#include "Variables.h"
#include "ADS1X15.h"  // ADS1115

ADS1115 ADS(0x48);  // ADDR relier au 0V

AF_DCMotor Zoom(1);
AF_DCMotor Focus(2);
AF_DCMotor Iris(3);

/**********************/
#include <PID_v1_bc.h>
double F = 0;   // setpoint
double LF = 0;  // input
double output = 0;

double Kp = 1, Ki = 1, Kd = 1;  
PID motor(&LF, &output, &F, Kp, Ki, Kd, DIRECT);
/***********************/

void setup() {
  Serial.begin(115200);  //
  ADS.begin();
  ADS.setMode(0);      // mesures en continu (0 = CONTINUOUS, 1 = SINGLE)
  ADS.setDataRate(7);  // vitesse de mesure de tension, de 0 à 7
  ADS.readADC(0);      // lecture à vide, pour envoyer les paramètres

  pinMode(2, INPUT_PULLUP);
  pinMode(13, INPUT_PULLUP);

  // active les moteurs
  Zoom.setSpeed(200);
  Zoom.run(RELEASE);
  Focus.setSpeed(200);
  Focus.run(RELEASE);
  Iris.setSpeed(200);
  Iris.run(RELEASE);

  // determine la vitesse du Focus et de l'Iris
  Focus.setSpeed(50);
  // Iris.setSpeed(80);
  motor.SetMode(AUTOMATIC);
  motor.SetOutputLimits(65, 853);
}

void loop() {
  /*
  Serial.print("Zoom: ");
  Serial.print(Z);
  Serial.print("  LimitZoom: ");
  Serial.print(LZ);
  //Serial.print("  CommandeZoom: ");
  //Serial.print(CZ);
  Serial.print("   Focus: ");
  Serial.print(F);
  Serial.print("  LimitFocus: ");
  Serial.print(LF);
  //Serial.print("  CommandeFocus: ");
  //Serial.print(CF);
  //Serial.print("  SpeedFocus: ");
  //Serial.print(SF);
  Serial.print("   Iris: ");
  Serial.print(I);
  Serial.print("  LimitIris: ");
  Serial.print(LI);
  //Serial.print("  invZoom:");
  //Serial.print(invZoom);
  //Serial.print("   Speed: ");
  //Serial.print(SZ);

  Serial.println("");
*/
  //////////// ZOOM ////////////
  Z = ADS.readADC(0);             // lecture du pot de Zoom via le multiplexeur
  SZ = analogRead(A0);            // lecture du pot de Speed Zoom
  LZ = analogRead(A1);            // lecture de la Limite du Zoom
  SZ = map(SZ, 0, 1023, 0, 255);  // maping de la valeur de speed (0,255)

  int invZoom = digitalRead(2);
  if (invZoom == 0) {
    SENSZOOM();
  }
  if (invZoom == 1) {
    Z = map(Z, 26600, 0, 0, 26600);  // inverse la valeur du pot de Zoom
    SENSZOOM();
  }

  //////////// FOCUS ////////////
  F = ADS.readADC(1);   //set point
  LF = analogRead(A2);  // input
  F = map(F, 25500, 0, 869, 30);
  //LF = map(LF, 850, 23, 840, 360);
  motor.Compute();
  SENSFOCUS(output);
  /*
  int invFocus = digitalRead(2);
  if (invFocus == 0) {
    SENSFOCUS();
  }
  if (invFocus == 1) {
    Z = map(Z, 26600, 0, 0, 26600);  // inverse la valeur du pot de Focus
    SENSFOCUS();
  }
*/

  //////////// IRIS ////////////
  IRIS();
}

void SENSZOOM() {
  //////////// DEZOOM ///////////
  if (Z <= 12000) {                // si déZoom 12774
    CZ = map(Z, 12000, 0, 0, SZ);  // 12774: millieux de la valeur maxi (multiplexeur) 25550
    Zoom.run(BACKWARD);            // fait tourner le moteur dans la sens indiqué
    Zoom.setSpeed(CZ);             // determine la vitesse du moteur
    if (LZ <= 50) {               // 176 - 990
      Zoom.run(RELEASE);
    }
  }
  ////////////// ZOOM ///////////
  if (Z >= 13520) {                    // si Zoom 12775
    CZ = map(Z, 13520, 25550, 0, SZ);  // valeur maxi 276000
    Zoom.run(FORWARD);
    Zoom.setSpeed(CZ);
    if (LZ >= 960) {
      Zoom.run(RELEASE);
    }
  }
}

void SENSFOCUS(int out) {

  //////////// FOCUS ////////////

  if (out > 400) {
    Focus.run(BACKWARD);
    Focus.setSpeed(out);  // drive motor CW
  } else {
    Focus.run(FORWARD);  // drive motor CCW
    Focus.setSpeed(out);
  }
}

void IRIS() {
  //////////// IRIS ////////////
  I = ADS.readADC(3); 
  LI = analogRead(A3);
  I = map(I, 26400, 35, 1023, 0);
  //IF = map(LF, 1023, 0, 840, 360);
}