Outils pour utilisateurs

Outils du site


cppu_moteur

Objectifs et organisation des séances

Cette de ces deux séances est d'étudier un servomoteur de façon ludique. Cette étude sera faite à travers une maquette de moteur à courant continu réductée asservie par un Arduino. Les séances seront divisées en trois parties :

  • Partie 1 - Présentation générale de la maquette et son environnement
  • Partie 2 - Contrôle du moteur en vitesse, en sens et en position
  • Partie 3 - Consommation électrique du moteur

Cette page expliquera les différentes parties et donnera des liens utiles aux séances. Pour suivre ses séances, les stagiaires doivent avoir fini le cours d'initiation à la programmation et au shield E/S donné par M. Vandeportaele

Partie 1 - Présentation générale de la maquette

Définition - L'énergie

L'énergie est la somme dans le temps des contributions instantanées de la puissance. Cela veut dire que l'énergie est exprimé par le produit de la puissance et le temps :

<latex>

\begin{center}

\begin{equation} E_{X} = P_{X} \cdot temps \end{equation}

\end{center} </latex>

Où :

  • E c'est l'énergie en joule ou watt.heure
  • P c'est la puissance en watt
  • temps est le temps en secondes
  • X représente un domaine de la physique (électrique, chimique, mécanique et etc)

Définition - énergie électrique et énergie mécanique

Les génies électrique et mécaniques se consacrent à créer des sources qui délivrent leur puissance correspondante.

La puissance électrique est décrite par l'équation suivante :

<latex>

\begin{center}

\begin{equation} P_{électrique} = V \cdot i \end{equation}

\end{center} </latex>

Où :

  • V c'est la tension en volts
  • i c'est le courant en ampères

La tension électrique représente la différence d'accumulation des charges qui existent entre deux fils électriques. En courant continu on parle suivant du pôle positif (+) et du pôle négatif (-). Le courant électrique représente la quantité de charges qui sortent d'un fil pour aller vers l'autre. La puissance électrique est dite positive lorsque le courant sort du fil positif vers le négatif.

Une source électrique capable de délivrer un grand courant (donc une grande quantité de charges) sans pour autant présenter une diminution dans sa tension (la différence de concentration de charges) a une très forte puissance nominale. Si cette source est capable de délivrer cette puissance pendant un temps très long, cette source a une grande capacité d'énergie.

La puissance mécanique est décrite par l'équation suivante :

<latex>

\begin{center}

\begin{equation} P_{mécanique} = \Gamma_{mot} \cdot \omega_{mot} \end{equation}

\end{center} </latex>

Où :

  • ω c'est la vitesse angulaire en radian par secondes
  • Γ c'est le couple mécanique en Newton mètre

La vitesse angulaire représente l'allure dont un moteur tourne. Dans certains domaines on parle en rotations par minutes ou RPM. Le couple est la capacité d'un moteur à délivrer une force sur son axe. La puissance mécanique est dite positive lorsque le couple est dans le même sens que la vitesse.

Une source mécanique capable de délivrer un couple (une force sur son axe) sans pour autant perdre de la vitesse angulaire (l'allure de ses rotations) a une très forte puissance nominale. Si cette source est capable de délivrer cette puissance pendant très longtemps, cette source a une grande capacité d'énergie.

Définition - le moteur à courant continu

Un moteur à courant continu permet de convertir l'énergie électrique en énergie mécanique. Mathématiquement nous pouvons exprimer cela par le jeu d'équation ci-dessous :

<latex>

\begin{center}

\begin{equation} E_{elec} = E_{meca} \end{equation} \begin{equation} P_{elec} \cdot temps = P_{meca} \cdot temps \end{equation} \begin{equation} P_{elec} = P_{meca} \end{equation} \begin{equation} V_{mot} \cdot i_{mot} = \Gamma_{mot} \cdot \omega_{mot} \end{equation}

\end{center} </latex>

Où:

  • elec c'est électrique
  • meca c'est mécanique

Ce transfert de puissance est illustré par la figure ci-dessous :

A partir de maintenant nous pouvons réfléchir en terme de point d'opération ou point d'équilibre. Une fois que la source mécanique est connéctée à une charge, une puissance mécanique lui sera délivré. En pratique cela signifie que le point d'opération ou le point d'équilibre du système change. Cela implique des changements dans la tension, le courant, le couple et la vitesse. Nous pouvons vite être dans des situations très complexes à comprendre.

Pour simplifier l'analyse, nous pouvons fixer des grandeur physiques. La tension peut suivant être considérée comme fixe pour une source électrique. La vitesse nominal d'un moteur est aussi suivant fixée. Cela est répresenté par la figure ci-dessous :

Nous pouvons faire un parallèle entre les grandeur électriques et mécaniques.

La tension électrique est liée à la vitesse de rotation. Pour une tension $V_{mot}$ donnée, le moteur tournera à une vitesse $\omega_{mot}$ spécifique.

Le courant est lié au couple. Lorqu'un moteur est à vide, le couple sur son arbre est très faible (frottement internes), donc le courant délivré par la source électrique est aussi très faible. Lorsqu'un moteur est mis sous charge, le couple sur son axe augmente et la source électrique délivre un courant plus important.

Contexte - le moteur à courant continu commandé

Un moteur à courant continu a un point d'opération qui dépend de sa tension d'entrée et de son couple résistant. Normalement nous ne pouvons pas anticiper comment la charge mécanique va se comporter, donc on a très peu de contrôle sur celle-ci. Il nous reste à contrôler la tension d'entrée du moteur.

D'un point de vue global, nous pouvons illustrer un moteur commandé par le schémas blocs ci-dessous.

Le système appelé “commande” adapte la tension à l'entrée du moteur en fonction des signaux de commande qu'il reçoit. Il utilise une source d'énergie électrique extérieure dont la tension est constante et nommée VCC.

Le bloc appelé “reducteur” permet de changer le rapport de vitesse angulaire et couple sans pour autant changer la puissance. Dans notre cas le “réducteur” diminue la vitesse et augmente le couple.

Contexte - le servomoteur

Un servomoteur est un moteur à courant continu dont le mouvement de rotation est contrôlé en termes d'angle plutôt qu'en vitesse. Le schématique ci-dessous illustre ce type de moteur :

Un potentiomètre est installé sur l'axe du servomoteur et lui donne une mesure sur l'angle de cet axe. Cette mesure représente une tension électrique qui varie avec l'angle de l'axe. La mesure set récupérée par le bloc de commande et utilisée pour contrôler la tension du moteur.

Le fait de récupérer une mesure et l'utiliser pour contrôler un système est appelé “asservissement”. Dans le cas de cet enseignement l'asservissement sera numérique.

Schéma-blocs de la maquette à étudier

La maquette étudiée lors de ses deux séances peut être résumée dans le schémas-bloc ci-dessous :

Vous pouvez remarquer une différence par rapport au modèle de servomoteur présenté. Notre maquette découpe la commande dans deux parties, l'Arduino+E/S et le shield moteur.

Le bloc Arduino+E/S reçoit la mesure du potentiomètre et génère des signaux de commande. Le shield du moteur reçoit les signaux de commande de l'Arduino et pilote la tension et le courant du moteur en fonction de ce signaux.

Ce découplage s'impose du fait que la carte Arduino n'est pas capable de générer les courant nécessaires au pilotage du moteur.

Liste des composants

La maquette étudie lors de ces deux séances est composée de :

Vue d'ensemble de la maquette

Les composants de la maquette sont montrés dans l'image ci-dessous:

Mettre une image qui montre chaque composant

Les librairies nécessaires pour piloter la maquette

Pendant les séances, un certain nombre de libraries spécialisés seront nécessaires à l'étude de le maquette.

Partie 2 - Contrôle du moteur en vitesse, en sens et en position

Pour prendre en main la maquette et comprendre le fonctionnement d'un moteur, nous allons procéder par petit pas.

Contrôle de la vitesse du moteur

La première étape de notre étude est nous familiariser avec le contrôle de vitesse du moteur à courant continu.

Assemblage mécanique

Le schématique ci-dessus donne une vue d'ensemble du montage à faire pour cette étude.

Consignes en vitesse et en sens

La carte shield et sa librairie associé facilitent énormément l'envoi des consignes de vitesse. Les commandes les plus importantes sont résumées ci-dessous :

consignes_vitesse
#include <AFMotor.h>  //inclusion de la librairie du moteur à utiliser
 
motor_id = 1; //identifiant moteur
AF_DCMotor motor(motor_id,MOTOR12_1KHZ);//, MOTOR12_8KHZ); // déclaration du moteur à utiliser
motor.setSpeed(consigne_vitesse); // passage de la valeur de la vitesse à la variable
motor.run(FORWARD); // choix du sense : avant     
motor.run(RELEASE); // choix du sense : axe libre      
motor.run(BACKWARD); // choix du sense : arrière

Etude en vitesse et en sens

A l'aide de la carte Arduino, créez un programme qui vous permet :

  • d'envoyer une consigne en vitesse
  • d'envoyer une consigne de sens
  • de visualiser la consigne envoyé à l'aide de l'interface Serial
  • de modifier vos consignes à l'aide de l'interface Serial
  • de piloter la vitesse à l'aide d'un potentiomètre

A l'aide de votre programme étudiez :

  • la valeur de consigne pour l'amorçage de votre machine
  • la valeur de consigne pour l'arrêt de votre machine
  • la valeur de consignes ci-dessus en fonction du sens de rotation
  • de piloter la vitesse à l'aide d'un potentiomètre

Voici une vidéo exemple de fonctionnement :

mettre un lien vidéo ici

Contrôle de la position angulaire du moteur

Maintenant que nous pouvons contrôler la vitesse du moteur, nous allons étudier comment utiliser un potentiomètre et sa mesure de tension associée pour piloter la position angulaire de l'axe du moteur.

Assemblage mécanique

Le schématique ci-dessus donne une vue d'ensemble du montage à faire pour cette étude.

On remarque bien que le potentiomètre n'est pas encore connecté mécaniquement au moteur à courant continu. Nous allons utiliser ce découplage pour illustrer le fonctionnement de l'asservissement.

Un peut de théorie sur l'asservissement

L'idée centrale à tout asservissement est de minimiser l'erreur entre une mesure et une consigne.

S'il est possible d'asservir directement la variable d'intérêt (dans notre cas précédent c'était la vitesse) aucune astuce mathématique est nécessaire, comme nous avons vu précédement. Cela s'exprime par le schéma directe ci-dessous.

Si nous n'avons pas accès à la variable asservie, les choses sont plus compliqués. Ceci est le cas maintenant où nous souhaitons asservir un déplacement angulaire en agissant sur une vitesse angulaire. Cela s'exprime par le schéma indirect ci-dessous.

La difficulté est de trouver le moyen de “traduire” une consigne en déplacement dans une consigne en vitesse. Cette traduction est faite par un correcteur qui a pour but de minimiser l'erreur entre le déplacement de sortie et la consigne de déplacement. Cette “traduction” est illustrée par l'image ci-dessous.

Le PID, un correcteur "magique"

La nature de la “traduction” faite par le correcteur dépend des types de variables en entrée en en sortie. Il existe une technique qui s'applique à tout cas de figure sans pour autant nécessiter de connaissances claires sur le système. Ce correcteur s'appelle PID.

Le correcteur PID applique un traitement mathématique directement à l'erreur. Il consiste en trois termes différents qui vont appliquer des corrections différentes. Ces termes sont Kp, Ki et Kd comme montre l'image ci-dessous.

Où :

  • Le terme KP s'applique directement à l'erreur: $K_p \cdot erreur$
  • Le terme Ki s'applique à la somme moyenne des erreurs: $K_i \cdot \dfrac{erreur[1]+…+erreur[n]}{n}$
  • Le terme Kd s'applique à la différence entre deux erreurs: $K_d \cdot (erreur[n]-erreur[1])$

En pratique nous allons utiliser un PID numérique. Il consiste dans une suite de valeurs d'erreur appelée “buffer circulaire”. Il est circulaire car le nombre de valeurs admissibles est limitées et lorsque le programme arrive à la fin de la suite il continue à enregistrer les valeurs par l'écrasement des premières valeurs mesurées. Le buffer circulaire est illustré par l'image ci-dessous.

Dans notre application, si nous sommes à la case 1 du buffer circulaire, les valeurs nécessaires à la correction son calculés comme montre l'image ci-dessous :

Il nous reste de choisir les valeurs de Kp, Ki et Kd. Cela se fait par une méthode itérative qui sera décrite ci-dessous.

Etude de l'asservissement angulaire

PID_program
//-------------PID CONTROL------------------------------
// PID CONTROL VARIABLES DECLARATION
#define TAB_SIZE 10
 
int16_t erreur_tab[TAB_SIZE];
 
uint8_t ind_ecr_erreur_tab=0;
int16_t integrale_erreur=0;
int16_t derivee_erreur=0;
 
int16_t Kd=0;  //de 0 à 50
int16_t Ki=0;  
int16_t Kp=10;
 
uint16_t consigne_posit =  0; // variable that holds the value of the position reference
uint16_t posit =  0;  //variable that holds the value of the position measurement
int16_t erreur = 0;   // variable that calculates the error
 
 
 
void update_tab_erreur(int16_t new_erreur)
{
  //calcul intégrale
  integrale_erreur=integrale_erreur-erreur_tab[ind_ecr_erreur_tab];
  integrale_erreur=integrale_erreur+new_erreur;
 
  //calcul dérivée
  uint8_t ind_lec_erreur_tab=ind_ecr_erreur_tab+5;
  if (ind_lec_erreur_tab>=TAB_SIZE )  //gestion modulo
  ind_lec_erreur_tab-=TAB_SIZE;
  derivee_erreur=new_erreur-erreur_tab[ind_lec_erreur_tab];
 
  //maj tableau 
  erreur_tab[ind_ecr_erreur_tab]=new_erreur;
  ind_ecr_erreur_tab++;
  if (ind_ecr_erreur_tab>=TAB_SIZE)
  ind_ecr_erreur_tab=0;  
}//end of update_tab_erreur
//-----------------------------------------------
 
void pid_correction(int16_t erreur){  
  int16_t commande=0;        //control signal
  int16_t commande_max=250;  //maximum control signal
 
  erreur  = erreur*Kp;
  erreur += Kd*derivee_erreur; //quand l'erreur diminue, ce terme est négatif et permet d'éviter le dépassement
  erreur += (Ki*integrale_erreur)/TAB_SIZE;
 
  commande=abs(erreur);  // updates the control signal    
  if (commande>commande_max) commande=commande_max; // checks for saturation
 
  if (commande<3)
  {
    motor.run(RELEASE);
    motor.setSpeed(0);
  }
  else
  if (erreur<0)
  {
    motor.run(FORWARD);       
    motor.setSpeed(commande);
  }
  else
  {
    motor.run(BACKWARD);       
    motor.setSpeed(commande);
  }  
}//end of pid_correction
//------------------------------------------------
PID_usage
void setup() 
{ 
  // setup control table
  for (int i=0;i<TAB_SIZE;i++)
  erreur_tab[i]=0;
}
 
void loop()  
{ 
    consigne_posit =  analogRead(A0);  //reads the position reference
    posit =  analogRead(A1);           // reads the position
    erreur = posit - consigne_posit;   // calculates the error
    update_tab_erreur(erreur);         // updates the tab
    pid_correction(erreur);            // updates the control
 
    // Prints system variables
    Serial.print(consigne_posit,DEC);  //prints the position reference
    Serial.print("\t");
    Serial.print(erreur,DEC);          //prints the erro
    Serial.print("\t");
    Serial.print(posit,DEC);           //prints the position
    Serial.print("\t");
    Serial.println("");
    delay(10);
 
 
}

A l'aide de la carte Arduino, et le squelette de programme avec le PID intégré écrivez un programme que vous permet:

  • de changer les paramètres Kp, Ki et Kd
  • d'envoyer une consigne de déplacement angulaire
  • de monitorer l'angle par l'interface série
  • de modifier vos consignes à l'aide de l'interface série
  • de monitorer l'erreur par l'interface série
  • de récupérer la mesure d'angle à travers un deuxième potentiomètre

A l'aide de votre programme étudiez :

  • le comportement de votre moteur lorsque vous lui donnez une consigne angulaire
  • le comportement de votre moteur lorsque vous agissez sur le deuxième potentiomètre
  • l'erreur

Voici une vidéo exemple de fonctionnement :

mettre un lien vidéo ici

Partie 3 - Consommation électrique du moteur

La consommation électrique

Consommation à vide

Décrire en pas à pas :

  • L'assemblage mécanique (avec de potentiomètre de position)
  • Quelques principes de code à faire pour étudier la consommation
  • Des idées libres d'étude de la consommation
    • Un contrôle par le potentiomètre de signal
    • Un contrôle par les entrées de la carte E/S
    • Un contrôle automatique par génération de consigne par l'Arduino
  • Une vidéo de fonctionnement

Consommation avec une charge mécanique légère

Décrire en pas à pas :

  • L'assemblage mécanique (ficelle et petit poids)
  • Quelques principes de code à faire pour étudier la consommation
  • Des idées libres d'étude de la consommation
    • Un contrôle par le potentiomètre de signal
    • Un contrôle par les entrées de la carte E/S
    • Un contrôle automatique par génération de consigne par l'Arduino
  • Une vidéo de fonctionnement

Consommation avec une charge mécanique forte

Décrire en pas à pas :

  • L'assemblage mécanique (ficelle et gros poids)
  • Quelques principes de code à faire pour étudier la consommation
  • Des idées libres d'étude de la consommation
    • Un contrôle par le potentiomètre de signal
    • Un contrôle par les entrées de la carte E/S
    • Un contrôle automatique par génération de consigne par l'Arduino
  • Une vidéo de fonctionnement

Ancienne version à intégrer

Vidéo de démonstration de la maquette

vidéo de la manip en fonctionnement: https://www.youtube.com/watch?v=3goxHKO2iuE

Controle du moteur en vitesse et en sens

bvdp.inetdoc.net_files_cppu_img_20200918_002851_crop_resize.jpg

Controle du moteur en position

bvdp.inetdoc.net_files_cppu_img_20200924_130008_crop_resize.jpg

Installation de la librarie pour le shield moteur

Schéma du shield

Asservissement PID

Mettre ici des liens sur les fonctions de librairies Arduino à utiliser

Exemple de code - Consigne en vitesse

motor_shield_speed.ino
//B. Vandeportaele 09/2020
//L. Villa 10/2020
 
//Todo gestion du temps pour que l'asservissement ait des paramètres fixes/ contenu du programme
 
#include <AFMotor.h>  
#include <Wire.h>
 
//adresses codées sur 7bits
#define SLAVE_ADDR_8574_A 0x38+6
#define SLAVE_ADDR_8574_B 0x38+7
 
 
AF_DCMotor motor(1,MOTOR12_1KHZ);//, MOTOR12_8KHZ);
 
int led = 13; //numéro de la broche Arduino pilotant la LED sur le shield PERIPH
 
//////////////////////////////////////////
char readPort8574(char addr, char * ptr_value)
/*addr, l'adresse du PCF8574
ptr_value, pointeur pour renvoyer la valeur lue sur le port
retourne -1 si échec 0 sinon*/
{
    Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique
    if (Wire.available()==1) //si l'octet est disponible
    {
        (* ptr_value) = Wire.read(); // lire l'octet
        return 0;
    }
    else
    {
        (* ptr_value) =0; //valeur par défaut si le composant n'a pas acquité
        return -1;
    }
}
//////////////////////////////////////////
char writePort8574(char addr, char value)
/*addr, l'adresse du PCF8574
value, la valeur à écrire sur le port
retourne -1 si échec 0 sinon */
{
    Wire.beginTransmission(addr);//démarre la transmission avec l'adresse du pérpiphérique
    Wire.write((byte)value);     //envoie la donnée
    if (Wire.endTransmission()==0)      //stoppe la transmission
        return 0;
    else
        return -1;
}
//////////////////////////////////////////
 
//unité en milliseconde 
uint32_t cadence_it=3;
uint32_t time_next_it=0;
 
 
 
void setup() 
{ 
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);   
  Serial.begin(115200);  // start serial port at 9600 bps:
 
  Wire.begin();        // joindre le bus i2c en tant que maître
  writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée
  writePort8574(SLAVE_ADDR_8574_A,0); //allume les leds
  delay(1000);
  writePort8574(SLAVE_ADDR_8574_A,~0); //eteind les leds
 
}
 
 
void loop()  
{
 
  controleur();
 
}
 
 
void controleur()  
{  
byte consigne_vitesse;  // une variable pour récupérer la valeur de la vitesse
 
readPort8574(SLAVE_ADDR_8574_B,&consigne_vitesse); // contrôle de la valeur de la vitesse
 
writePort8574(SLAVE_ADDR_8574_A,~consigne_vitesse); // affichage de la valeur de la vitesse 
 
Serial.println(consigne_vitesse); // affichage de la valeur de la vitesse
 
motor.setSpeed(consigne_vitesse); // passage de la valeur de la vitesse à la variable
 
 
motor.run(FORWARD); // choix du sense : avant     
 
delay(1000);  
 
motor.run(RELEASE); // choix du sense : lâcher l'axe      
 
delay(1000); 
 
motor.run(BACKWARD); // choix du sense : arrière       
 
delay(1000); 
 
motor.run(RELEASE); // choix du sense : lâcher l'axe      
 
delay(1000); 
 
} 

CODE DE TEST Bertrand

motor_shield.ino
//B. Vandeportaele 09/2020
 
//done: gestion du temps pour que l'asservissement ait des paramètres fixes/ contenu du programme
 
#include <AFMotor.h>  
//Servo myservo;
 
   #include <Wire.h>
    //adresses codées sur 7bits
#define SLAVE_ADDR_8574_A 0x38+6
#define SLAVE_ADDR_8574_B 0x38+7
 
 
//AF_DCMotor motor(3,MOTOR34_1KHZ);//, MOTOR12_8KHZ);
AF_DCMotor motor(1,MOTOR12_1KHZ);//, MOTOR12_8KHZ);
 
int led = 13; //numéro de la broche Arduino pilotant la LED sur le shield PERIPH
 
//////////////////////////////////////////
char readPort8574(char addr, char * ptr_value)
/*addr, l'adresse du PCF8574
ptr_value, pointeur pour renvoyer la valeur lue sur le port
retourne -1 si échec 0 sinon*/
{
    Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique
    if (Wire.available()==1) //si l'octet est disponible
    {
        (* ptr_value) = Wire.read(); // lire l'octet
        return 0;
    }
    else
    {
        (* ptr_value) =0; //valeur par défaut si le composant n'a pas acquité
        return -1;
    }
}
//////////////////////////////////////////
char writePort8574(char addr, char value)
/*addr, l'adresse du PCF8574
value, la valeur à écrire sur le port
retourne -1 si échec 0 sinon */
{
    Wire.beginTransmission(addr);//démarre la transmission avec l'adresse du pérpiphérique
    Wire.write((byte)value);     //envoie la donnée
    if (Wire.endTransmission()==0)      //stoppe la transmission
        return 0;
    else
        return -1;
}
//////////////////////////////////////////
 
 
 #define TAB_SIZE 10
 int16_t erreur_tab[TAB_SIZE];
 uint8_t ind_ecr_erreur_tab=0;
 int16_t integrale_erreur=0;
 int16_t derivee_erreur=0;
 
void update_tab_erreur(int16_t new_erreur)
{
  //calcul intégrale
 integrale_erreur=integrale_erreur-erreur_tab[ind_ecr_erreur_tab];
 integrale_erreur=integrale_erreur+new_erreur;
//calcul dérivée
uint8_t ind_lec_erreur_tab=ind_ecr_erreur_tab+5;
if (ind_lec_erreur_tab>=TAB_SIZE )  //gestion modulo
  ind_lec_erreur_tab-=TAB_SIZE;
derivee_erreur=new_erreur-erreur_tab[ind_lec_erreur_tab];
//maj tableau 
 erreur_tab[ind_ecr_erreur_tab]=new_erreur;
 ind_ecr_erreur_tab++;
 if (ind_ecr_erreur_tab>=TAB_SIZE)
    ind_ecr_erreur_tab=0;  
 }
 //////////////////////////////////////////
void setup() 
{ 
// myservo.attach(9);   
  // initialize the digital pin as an output.
  pinMode(led, OUTPUT);   
       Serial.begin(115200);  // start serial port at 9600 bps:
       // Serial.print("Bonjour");
 
        Wire.begin();        // joindre le bus i2c en tant que maître
        writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée
        writePort8574(SLAVE_ADDR_8574_A,~0); //eteind les leds
 
for (int i=0;i<TAB_SIZE;i++)
erreur_tab[i]=0;
 
}
 
//unité en milliseconde 
uint32_t cadence_it=3;
uint32_t time_next_it=0;
 
 
 
void loop()  
{
  if (millis()>=time_next_it)
  {
  time_next_it=time_next_it+cadence_it;
  controleur()  ;  
  //test si  la cadence peut etre tenue
    if (millis()>=time_next_it)
       writePort8574(SLAVE_ADDR_8574_A,~0xFF);
  }
}
 
 
void controleur()  
{  
 
/*
  digitalWrite(led, HIGH);    // turn the LED off by making the voltage LOW
  delay(1000);               // wait for a second
  digitalWrite(led, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);   
 
  */
 
     //Serial.write('X');
//     delay(100); 
//motor.setSpeed(255);
 
/*delay(1000); 
motor.run(BACKWARD);      
delay(1000);  
motor.run(RELEASE);       
delay(1000); 
*/
static uint8_t vitesse=100;
static double t=0;
t=t+0.01;
 
 
 
//vitesse+=155+(100*sin(t));
//Serial.println(vitesse);
vitesse=vitesse+1;
if (vitesse==255)
  vitesse=100;
 
 
  byte port_in;
  readPort8574(SLAVE_ADDR_8574_B,(char*)&port_in);
//writePort8574(SLAVE_ADDR_8574_A,~port_in);
 
 
int16_t sens;
 
uint16_t consigne_position;
if ((port_in&1)!=0)
{
//lecture consigne sur potentiomètre
consigne_position=  analogRead(A0);
}else{
//génération échelons périodiques
unsigned long time;
  time = millis();
  if(((time/1000)%2)==0)
    consigne_position=512-250;
  else
    consigne_position=512+250;
}
 
 
uint16_t position=  analogRead(A1);
 
int16_t erreur=position-consigne_position;
 
 
 
update_tab_erreur(erreur);
 
/*
//type commande toute simple en proportionnel avec valeur min pour vaincre les frottements
int16_t commande_min=60; //pour vaincre les frottements
int16_t commande=abs(erreur)+commande_min;
int16_t commande_max=250;
if (commande>commande_max)
  commande=commande_max;
*/
 
int16_t Kd=10;  //de 0 à 50
int16_t Ki=2;  
int16_t Kp=1;
 
//erreur=erreur+4*derivee_erreur+integrale_erreur/8;
if ((port_in&2)!=0)
{
  erreur=erreur*Kp;
}
if ((port_in&4)!=0)
{
erreur+=Kd*derivee_erreur; //quand l'erreur diminue, ce terme est négatif et permet d'éviter le dépassement
}
if ((port_in&8)!=0)
{
  erreur+=(Ki*integrale_erreur)/TAB_SIZE;
}
int16_t commande=abs(erreur);
int16_t commande_max=250;
if (commande>commande_max)
  commande=commande_max;
 
 
 
 
 
if (abs(erreur)<3)
{
    motor.run(RELEASE);
motor.setSpeed(0);
sens=0;
}
else
if (erreur<0)
{
  motor.run(FORWARD);       
  motor.setSpeed(commande);
  sens=-1;
}
else
{
  motor.run(BACKWARD);       
 motor.setSpeed(commande);
   sens=1;
} 
/*
 
Serial.print("consigne_position: ");
Serial.print(consigne_position,DEC);
Serial.print("   position: ");
Serial.print(position,DEC);
Serial.print("   erreur: ");
Serial.print(erreur,DEC);
Serial.print("   commande: ");
Serial.print(commande,DEC);
 
Serial.print("   sens: ");
Serial.print(sens,DEC);
 
Serial.println("");
*/
 
 
 
 
Serial.print(consigne_position,DEC);
Serial.print("\t");
Serial.print(position,DEC);
Serial.print("\t");
 
Serial.print(erreur,DEC);
Serial.print("\t");
 
 
 
Serial.print(0,DEC);
Serial.print("\t");
Serial.print(1024,DEC);
Serial.print("\t");
 
/*Serial.print(erreur,DEC);
Serial.print("\t");
Serial.print(commande,DEC);
Serial.print("\t");
Serial.print(sens,DEC);
*/
Serial.println("");
//delay(10);
 
 
 
/*
  motor.run(BACKWARD);       
 motor.setSpeed(120);
 
/*
uint16_t adc=  analogRead(A0);
if (adc>=512)
  {
    if (adc>1023) adc=1022;
    vitesse=(adc-512)/2;
    motor.run(FORWARD);       
    motor.setSpeed(vitesse);
  }
  else
  {
        if (adc<2) adc=2;
     vitesse=(512-adc)/2;
    motor.run(BACKWARD);       
    motor.setSpeed(vitesse);
  }
/*
  vitesse=analogRead(A0)/4;
 
   motor.run(FORWARD);       
motor.setSpeed(vitesse);
Serial.println(vitesse);
*/
 
/*
    char  val;
if (readPort8574(SLAVE_ADDR_8574_B,&val)==0)
    {
      Serial.print("lecture  8574  OK, ");
        if (writePort8574(SLAVE_ADDR_8574_A,val)==0)
              Serial.println("ecriture 8574  OK");
        else
        Serial.println("ecriture 8574 NOK");
        }
        else
      Serial.println("lecture  8574 NOK,");    
*/      
    //writePort8574(SLAVE_ADDR_8574_A,~vitesse);
 
 
} 
interaction_serial.ino
#include <Wire.h>
//adresses codées sur 7bits
#define SLAVE_ADDR_8574_A 0x3E
#define SLAVE_ADDR_8574_B 0x3F
 
//////////////////////////////////////////
char readPort8574(char addr, char * ptr_value)
/*addr, l'adresse du PCF8574
  ptr_value, pointeur pour renvoyer la valeur lue sur le port
  retourne -1 si échec 0 sinon*/
{
  Wire.requestFrom((byte)addr, (byte)1);// demande la lecture d'1 octet depuis l'adresse du pérpiphérique
  if (Wire.available() == 1) //si l'octet est disponible
  {
    (* ptr_value) = Wire.read(); // lire l'octet
    return 0;
  }
  else
  {
    (* ptr_value) = 0; //valeur par défaut si le composant n'a pas acquité
    return -1;
  }
}
//////////////////////////////////////////
char writePort8574(char addr, char value)
/*addr, l'adresse du PCF8574
  value, la valeur à écrire sur le port
  retourne -1 si échec 0 sinon */
{
  Wire.beginTransmission((byte)addr);//démarre la transmission avec l'adresse du pérpiphérique
  Wire.write((byte)value);     //envoie la donnée
  if (Wire.endTransmission() == 0)    //stoppe la transmission
    return 0;
  else
    return -1;
}
//////////////////////////////////////////
void setup()
{
  Serial.begin(9600);  // start serial port at 9600 bps:
  Serial.print("Bonjour");
  Wire.begin();        // joindre le bus i2c en tant que maître
  writePort8574( SLAVE_ADDR_8574_B , 0xff); //configure le composant B en entrée
}
//////////////////////////////////////////
void loop()
{
  char  val;
 
  if (Serial.available() > 0) {
    val = Serial.read();
    writePort8574(SLAVE_ADDR_8574_A, &val);
    switch (val) {
      case '0':
        break;
      case '1':
        break;
      case '2':
        break;
      default:
        break;
 
    }
    Serial.println((int)val, DEC);
  }
  /*
 
    if (readPort8574(SLAVE_ADDR_8574_B,&val)==0)
      {
    //      Serial.print("lecture  8574  OK, ");
         writePort8574(SLAVE_ADDR_8574_A,val);
      }
 
    delay(100);*/
 
}

Modèles des pièces 3D

connecteur_moteur_potar_2.scad
//B. Vandeportaele 09/2020
//jeu=0.5; //pour connecteur
jeu=0.3; //pour chape
 
/////////////////////////////
module tige_biseau(){ // coté moteur
difference(){        
cylinder(h=20,d=5.5+jeu,$fn=120);
        //3.8mm en elevant les 2 biseaux  5.5-2*biseau=3.8=> biseau=0.85
        for (i=[0:1]){ //2 biseaux
            echo(i);
            rotate([0,0,i*180])
             translate([((5.5/2)-0.85)+jeu/2,-5,-1])
                cube([10,10,25]);
        }
    }
}
 
/////////////////////////////
 module tige_biseau2(){ //coté potar
difference(){        
cylinder(h=20,d=6+jeu,$fn=120);
        //4.5mm en elevant le  biseau
             translate([1.5+jeu/2,-5,-1])
                cube([10,10,25]);        
    }
}
/////////////////////////////
module connecteur(){
    rotate([180,0,0])
difference(){
cylinder(h=16,d=11,$fn=120);
    translate([0,0,-12])
    //tige_biseau2();
    #cylinder(h=20,d=6.40,$fn=120);
     translate([0,0,+7.95])
        tige_biseau();
}
}
/////////////////////////////
module chape(angle=0,ep=3,long=30){
difference(){
hull()
{
cylinder(h=ep,d=10,$fn=120);
  translate([long,0,0])
cylinder(h=ep,d=4,$fn=120);
}
translate([0,0,-10])
rotate([0,0,angle])
  tige_biseau();
translate([long,0,-10])
cylinder(h=20,d=2,$fn=120);
}
}
/////////////////////////////
 
 
module equerre(){
//distance entre axe vis fixation et potar=22mm
eps=0.01;
h=21.4;
ep=2;
entraxe=11.5;
diamvis=2.8;
hauteur=21;
union(){
    difference(){
            translate([-eps,-h/2,0])        
                cube([16,h,ep]);
            for (i=[-1:2:1]){
                translate([13.5,i*entraxe/2,-5])        
                #cylinder(h=10,d=diamvis,$fn=30);                
            }
            translate([2,0,-5])        
              #cylinder(h=10,d=4.5,$fn=30);                
    }
    translate([0,-h/2,ep-eps])     
        cube([ep,h,hauteur]);
    translate([-22+ep,-h/2,ep+hauteur-eps-eps])     
        difference(){
            cube([22,h,ep]);
             translate([33.5-22,h/2,-50])        
                 #cylinder(h=100,d=8,$fn=180);       
            translate([33.5-22,h/2+8,-50])        
                 #cylinder(h=100,d=2.9,$fn=18);       
        }
}
}
/////////////////////////////
 
 
/////////////////////////////
//pour 75mm de long+15mm partie male et - 7mm partie femelle
module rallonge(longueur){
union(){
    difference(){
    cylinder(h=longueur,d=13,$fn=120);
    translate([0,0,+longueur-7])
        //scale([1,1,5])
             tige_biseau();
}
 translate([0,0,-15])
        tige_biseau(); //de base il fait 20 de long
}
 
}
/////////////////////////////
//rotate([90,0,0])  rallonge(75);
   //tige_biseau();
    //tige_biseau2();
//connecteur();
//chape();
 
 
chape(90,4,40);
/*for  (i=[1:1:10]){
translate([0,i*15,0])
chape(90,4,20+i*10);
}
*/
//rotate([90,0,0]) equerre();
 
//connecteur();

Informations pratiques

Emplacement et horaires

Cette formation aura lieu à deux endroits différents, à savoir le département GEII de l'IUT Paul Sabatier et au CPPU 3 Rue Jules Raimu: https://www.google.fr/maps/place/3+Rue+Jules+Raimu,+31200+Toulouse/@43.6049444,1.4020718,11z/data=!4m5!3m4!1s0x12aea32fbaa81f85:0xa1533a58a9f6f9b!8m2!3d43.6358301!4d1.4743211

Les enseignements auront lieu au département GEII de l'IUT de Toulouse 3 dans les salles Objets Connectés et Micro 2, situées au rez de chaussée. Le rendez vous est dans le hall d'entrée du département à 7h50. https://www.google.com/maps/place/IUT+Paul+Sabatier+:+G%C3%A9nie+Electrique+et+Informatique+Industrielle/@43.5649614,1.4547926,17z/data=!3m1!4b1!4m5!3m4!1s0x12aebc16a031ce37:0x7c224bf38a08976!8m2!3d43.5649575!4d1.4569813

Pour se garer, les stagiaires pourront utiliser le parking du restaurant universitaire de médecine:

https://www.google.com/maps/place/Restaurant+Universitaire+3/@43.5639049,1.4584812,17.5z/data=!4m8!1m2!2m1!1sRU+m%C3%A9decine,+toulouse!3m4!1s0x0:0x77e34ea9b183ed2f!8m2!3d43.5630965!4d1.4594141

Séances faites par Bertrand

mardi 20/10

8h-12h: GEII Salle objets connectés

13h30-17h30: GEII Salle micro 2 (ce sera l'occasion de leur montrer les manips de la salle ER2AU)

mercredi 21/10 CPPU

Séances faites par Luiz

mardi 27/10 (marqué en congés sur Celcat, ce n'est pas un problème?)

8h-12h: GEII Salle objets connectés

13h30-17h30: GEII Salle objets connectés

mercredi 28/10

8h-12h: CPPU

13h30-17h30: CPPU

Séances faites par Thomas

Jeudi 12/11

13h30-17h30: GEII Salle objets connectés

Jeudi 19/11

13h30-17h30: CPPU

Jeudi 26/11

13h30-17h30: GEII Salle objets connectés

Jeudi 3/12

13h30-17h30: CPPU

Achat de matériel

6x Alim - option 1 https://www.amazon.fr/Adaptateur-Chargeur-LEICKE-diff%C3%A9rents-appareils/dp/B01I1LRCXW/ref=sr_1_13?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=1MCV2GIQ5M48O&dchild=1&keywords=5v+power+supply&qid=1601451769&sprefix=5V+po%2Caps%2C167&sr=8-13

2x Servo option 1 https://www.amazon.fr/Diymore-Digital-H%C3%A9licopt%C3%A8re-Voiture-commandes/dp/B07DQJ1JXY/ref=sr_1_6?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=159NDCTN6FY4Z&dchild=1&keywords=servo+moteur&qid=1601451841&sprefix=servo+moteu%2Caps%2C175&sr=8-6

Alternativement: Servo option 2 https://www.amazon.fr/ALLOMN-H%C3%A9licopt%C3%A8re-Beaucoup-Ambition-V%C3%A9hicule/dp/B07Q1GJJZS/ref=sr_1_5?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&crid=159NDCTN6FY4Z&dchild=1&keywords=servo+moteur&qid=1601451911&sprefix=servo+moteu%2Caps%2C175&sr=8-5

1x Alim option 2 (pour le robot): Alimentation de laboratoire 10A (prévoir cable pour distribution 5V): https://www.amazon.fr/Alimentation-Laboratoire-KAIWEETS-stabilis%C3%A9e-Commutation/dp/B085S34NNW/ref=sr_1_3_sspa?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=alimentation+laboratoire&qid=1600841001&sr=8-3-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUFVUzNISUhVTlRYVE4mZW5jcnlwdGVkSWQ9QTAyMzY4NzQzTzJOU09RT0VYQUJUJmVuY3J5cHRlZEFkSWQ9QTA0NzEwNzdRMUNGTFBFU0lRWlkmd2lkZ2V0TmFtZT1zcF9hdGYmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl

2x Carte shield motor L293D (16euros les 5): https://www.amazon.fr/AZDelivery-L293D-4-Channel-Diecimila-Duemilanove/dp/B07YWX7S9T/ref=pd_day0_107_6/259-9773987-6397531?_encoding=UTF8&pd_rd_i=B07YWX6N8B&pd_rd_r=09fb95fc-47a3-4712-9977-1d5df52c6ae9&pd_rd_w=Eyhez&pd_rd_wg=atieP&pf_rd_p=95f2fa2e-1e8f-4cae-bf89-355fdf5bbe96&pf_rd_r=BRJ769C5N9Q23VS6Y1ZJ&refRID=BRJ769C5N9Q23VS6Y1ZJ&th=1

3x Potentiomètre rotatif linéaire 10KOhm (7 euros les 5, en prendre 2 par manip): https://www.amazon.fr/potentiom%C3%A8tre-Arduino-Raspberry-dautres-projets/dp/B07B2TSDVF/ref=sr_1_8?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=arduino+potentiometre&qid=1600845741&sr=8-8

alternativement: https://www.sparkfun.com/products/9939

1x moteur avec réducteur (13 euros les 8) : https://www.amazon.fr/Gebildet-DC3V-6V-Voiture-motrices-robotique/dp/B07Z4PYJY4/ref=sr_1_16?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=arduino+motor&qid=1600845585&sr=8-16

1x moteur avec réducteur + roues (7.6 euros les 4) : https://www.amazon.fr/Nrpfell-Arduino-Intelligente-Plastique-Motoreducteur/dp/B07LGYTM57/ref=pd_sbs_60_5/260-0495266-0214753?_encoding=UTF8&pd_rd_i=B07LGYTM57&pd_rd_r=87808a76-98a5-4d65-be1b-011ed5a578e0&pd_rd_w=4KoNy&pd_rd_wg=qElsb&pf_rd_p=5df3b724-4d3e-4605-89d0-6cb31bf88ddc&pf_rd_r=YT9XJBA5JFAPWW2B31SJ&psc=1&refRID=YT9XJBA5JFAPWW2B31SJ

3x jeu de fils dupont (9.99euros les 3*3*40pièces): https://www.amazon.fr/AZDelivery-Jumper-Cavalier-C%C3%A2ble-Arduino/dp/B074P726ZR/ref=sr_1_7?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=dupont+wire&qid=1601414268&s=industrial&sr=1-7

1x petite centrale inertielle (18euros pièce): https://www.amazon.fr/MPI9250-CJMCU-117-dattitude-pr%C3%A9cision-communication/dp/B08289XC6V/ref=sr_1_2?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=CJMCU+117&qid=1601414477&sr=8-2

1x module récepteur GPS (7euros pièce): https://www.amazon.fr/Semoic-navigateur-positionnement-Satellite-Remplacement/dp/B07MZJSGKN/ref=sr_1_5?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=GPS+ATGM336H&qid=1601414739&sr=8-5

bobine de pla 1.75mm 1kg (18euros) : https://www.amazon.fr/AmazonBasics-Filament-pour-imprimante-Bobine/dp/B07T2R1BQJ/ref=sr_1_1_sspa?__mk_fr_FR=%C3%85M%C3%85%C5%BD%C3%95%C3%91&dchild=1&keywords=pla&qid=1601414577&sr=8-1-spons&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEzQzZVRFlSNFQ2QUc3JmVuY3J5cHRlZElkPUEwOTUzNTc5WldIVVhIS1o0TlJPJmVuY3J5cHRlZEFkSWQ9QTAyMzI4OTUyR01JMVlOU0QzQzEyJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==

cppu_moteur.txt · Dernière modification : 2021/02/19 20:20 de 127.0.0.1