===== 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 [[arduinoisr|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 : \begin{center} \begin{equation} E_{X} = P_{X} \cdot temps \end{equation} \end{center} 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 : \begin{center} \begin{equation} P_{électrique} = V \cdot i \end{equation} \end{center} 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 : \begin{center} \begin{equation} P_{mécanique} = \Gamma_{mot} \cdot \omega_{mot} \end{equation} \end{center} 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 : \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} Où: * elec c'est électrique * meca c'est mécanique Ce transfert de puissance est illustré par la figure ci-dessous : {{https://bvdp.inetdoc.net/files/cppu/source_elec_meca_1.png}} 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 : {{https://bvdp.inetdoc.net/files/cppu/source_elec_meca_charge_1.png}} 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. {{https://bvdp.inetdoc.net/files/cppu/moteur_1.png}} 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 : {{https://bvdp.inetdoc.net/files/cppu/servo_mot_1.png}} 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 : {{https://bvdp.inetdoc.net/files/cppu/maquette_1.png}} 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 : * Une carte Aduino Uno (lien arduino) * [[tdcom2|Une shield Entrée/Sortie]] * [[http://wiki.sunfounder.cc/index.php?title=L293D_Motor_Driver_Shield|Un shield de pilotage pour le moteur]] * Un moteur à courant continu réducté * Deux potentiomètres * Des pièces imprimées en 3D pour coupler le potentiomètre au moteur * Une aiguille imprimée en 3D ==== 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. * Librairie du shield de pilotage du moteur: https://learn.adafruit.com/adafruit-motor-shield/library-install * Librairie de lecture des données à l'entrée analogique d'Arduino : https://www.arduino.cc/reference/en/language/functions/analog-io/analogread/ * Référence générale des librairies d'Arduino : https://www.arduino.cc/reference/fr/ ===== 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. {{https://bvdp.inetdoc.net/files/cppu/etude_vitesse_1.png}} === 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 : #include //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. {{https://bvdp.inetdoc.net/files/cppu/etude_angle_1.png}} 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. {{https://bvdp.inetdoc.net/files/cppu/asservissement_direct_1.png}} 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. {{https://bvdp.inetdoc.net/files/cppu/asservissement_indirecte_1.png}} 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. {{https://bvdp.inetdoc.net/files/cppu/asservissement_total_1.png}} === 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. {{https://bvdp.inetdoc.net/files/cppu/theorie_PID_1.png}} 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. {{https://bvdp.inetdoc.net/files/cppu/buffer_circulaire_1.png}} 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 : {{https://bvdp.inetdoc.net/files/cppu/buffer_circulaire_pid_1.png}} 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 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 //------------------------------------------------ void setup() { // setup control table for (int i=0;i 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=== {{https://bvdp.inetdoc.net/files/cppu/IMG_20200918_002851_crop_resize.jpg}} ===Controle du moteur en position=== {{https://bvdp.inetdoc.net/files/cppu/IMG_20200924_130008_crop_resize.jpg}} ====Installation de la librarie pour le shield moteur==== https://learn.adafruit.com/adafruit-motor-shield/library-install ====Schéma du shield==== http://wiki.sunfounder.cc/index.php?title=L293D_Motor_Driver_Shield ====Asservissement PID==== https://fr.wikipedia.org/wiki/R%C3%A9gulateur_PID ====Mettre ici des liens sur les fonctions de librairies Arduino à utiliser==== === Exemple de code - Consigne en vitesse === //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 #include //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==== //B. Vandeportaele 09/2020 //done: gestion du temps pour que l'asservissement ait des paramètres fixes/ contenu du programme #include //Servo myservo; #include //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=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); } #include //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==== //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==