Fauteuil roulant 2 moteurs DC, joystick, batterie lithium.

Ce fauteuil roulant a un moteur droit et gauche et se pilote comme un robot ou un char.
Par contre, il faut des accélérations et des décélérations faibles, avec des limitations de vitesses en fonction de l’handicap.
Les accélérations et les décélérations peuvent être gérer comme le sujet de la trottinette de puissance 500W
https://forum.arduino.cc/index.php?topic=473015.0
Les hacheurs abaisseurs qui commandent les moteurs ne permettent pas d’avoir une marche arrière, ni un freinage électrique. Par contre, il sera possible d’avoir une marche arrière avec des relais.
Il n’y a pas besoin de faire un asservissement de vitesse des moteurs, car il n’y a pas beaucoup de différence de vitesse entre les 2 moteurs donc c’est le pilotage du joystick qui fera cette correction.

Dans un premier temps, nous allons présenter quelques programmes simples qui permettent de gérer le joystick et les 2 moteurs.
Voici, le fauteuil roulant et le schéma ISIS qui permet de simuler l’afficheur LCD, le joystick et les 3 moteurs. Evidemment, le schéma électrique avec la commande des moteurs est bien différent.
La simulation permet de minimiser les erreurs de programmations.


On peut télécharger le fichier ISIS ici
nano fauteuil roulant.DSN - Google Drive

Il y a plusieurs façons de gerer le joystick et commander les moteurs:

  • un joystick précis et une manipulation fine de celui-ci permet de faire un commande linaire.
  • un joystick qui est manipulé assez brusquement donc en tout ou rien.
  • un joystick qui est manipulé moins brusquement donc utilise une matrice avec une plus ou moins grande précision.

Les PWMs des broches 11 et 3 seront utilisés utilisant le timer2, pour avoir une frequence de hachage de 32kHz qui permet de minimiser l’ondulation du courant dans les moteurs DC de 300W.
Le bon compromis est entre 20khz et 32 khz donc 64kHz c’est trop important, 8khz c’est trop faible.
Le timer1 sera utilisé pour faire une routine d’interruption pour faire des mesures tous les 0.01s et un affichage tous les 0.1s pour débuguer le programme.
https://playground.arduino.cc/Main/TimerPWMCheatsheet

Voici la façon linéaire de gérer le joystick à partir d’un Arduino nano

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>

LiquidCrystal lcd(2, 8, 4, 5, 6, 7);     // LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


#define motorD    3      //   timer2
#define motorG   11      //   timer2   

#define y_axis    A0      //  mesure du joystick en x
#define x_axis    A1      // mesure du joystick en y  
#define Led13     13


int x_hiz=0;
int y_hiz=0;
int PWMD=0;
int PWMG=0;
byte temps=0;
int xprecedent;
int yprecedent;
int increD;
int increG;

void setup() {

Serial.begin(9600);
  Timer1.initialize(10000);           // initialize timer1, and set a 0,1 second period =>  100 000     pour 0.01s  10 000
  Timer1.attachInterrupt(callback);   // attaches callback() as a timer overflow interrupt
  lcd.begin(20, 4);                   //modifier pour un afficheur 20x4
 TCCR2B = (TCCR2B & 0b11111000) | 0x01;         //pin 10  32khz    http://playground.arduino.cc/Main/TimerPWMCheatsheet
pinMode(motorD,OUTPUT);
pinMode(motorG,OUTPUT);
pinMode(Led13,OUTPUT); 
}


// Interruptions  tous les 0.01s fait par le timer1
void callback()  {
temps++;
//clignotement de la led 13  à 50Hz=2/0.01s
digitalWrite(Led13, !digitalRead(Led13));

//Bouton joystik est à (512,512) au milieu lorsqu'il n'est pas acivé, 
//il n'y a pas de marche arriere car les reducteurs freines assez rapidement le fauteuil
// mesure de joystick
x_hiz= analogRead(x_axis);
if (x_hiz<=512) {x_hiz=513;}      //petite zone morte entre 512 et 520 de la commande moteur
x_hiz= map(x_hiz,512,1023,0,255);  //converti le 10 bit du CAN en 8bit du PWM  
if (x_hiz>PWMD) {increD=1;} 
else {increD=-5;}    //decrementation plus rapide que l'incremetation


                 
y_hiz= analogRead(y_axis);
if (y_hiz<=512) {y_hiz=513;}
y_hiz= map(y_hiz,512,1023,0,255);


//limitation de la valeur PWM
if ((increD==1) && (PWMD<40))  {PWMD=40;}  //à cause du couple de charge ce n'est pas la peine d'avoir PWM<40 car le moteur ne bougera pas tant que le couple moteur n'est pas superieur à celui de la charge
if ((PWMD<254) && (increD==1))     {PWMD=PWMD+increD;}   //limitation de la PWM
if ((PWMD>5) && (increD==-5))     {PWMD=PWMD+increD;} 
   

//il faudrait faire l'acceleration et decleration sur la PWMG
PWMG=y_hiz;

analogWrite(motorD, PWMD);   //PWM  pin 3 comandé par x
analogWrite(motorG, PWMG);   //PWM  pin 11 comandé par y
 
}   //fin routine


void loop() {

//affiche tous les 0.1s mais mesure tous les O.O1s
if (temps>=10) {
temps=0;

lcd.setCursor(0,0);
lcd.print("JOY");
lcd.setCursor(5,0);
lcd.print("PWM");
lcd.setCursor(11,0);
lcd.print("inc");

lcd.setCursor(0,1);
lcd.print("x");
lcd.print(x_hiz);
lcd.print("  ");
lcd.setCursor(5,1);
lcd.print(PWMD);
lcd.print("  ");
lcd.setCursor(11,1);
lcd.print(increD);
lcd.print("  ");


lcd.setCursor(0,2);  
lcd.print("y");
lcd.print(y_hiz);
lcd.print("  ");
lcd.setCursor(5,2);
lcd.print(PWMG);
lcd.print("  ");


    }   //fin if (temps>10
  
}//fin loop

Le problème est qu’il faut faire une calibration du joystick car le point central du joystick correspond rarement à 512.

Donc, voici la façon tout ou rien de gérer le joystick.
Il n’y a pas besoin de faire de calibration du joystick et cette gestion est plus sécuritaire par rapport à la gestion précédente.

void callback()  {
temps++;
//clignotement de la led 13  à 50Hz=2/0.01s
digitalWrite(Led13, !digitalRead(Led13));

//Bouton joystik est à (512,512) au milieu lorsqu'il n'est pas acivé, 
//il n'y a pas de marche arriere car les reducteurs freines assez rapidement le fauteuil
// mesure de joystick
x_hiz= analogRead(x_axis);   //lecture du joystick
y_hiz= analogRead(y_axis);

//gestion du joystick tout ou rien
if (x_hiz>900) {increD=1;} else {increD=-5;}    //decrementation plus rapide que l'incremetation
if (y_hiz>900) {increG=1;} else {increG=-5;}    //


//limitation de la valeur PWMD
if ((increD==1) && (PWMD<40))  {PWMD=40;}  //à cause du couple de charge ce n'est pas la peine d'avoir PWM<40 
if ((increD==-5) && (PWMD<40))  {PWMD=0;}  //car le moteur ne bougera pas tant que le couple moteur n'est pas superieur à celui de la charge
if ((PWMD<254) && (increD==1))     {PWMD=PWMD+increD;}   //limitation de la PWM
if ((PWMD>5) && (increD==-5))      {PWMD=PWMD+increD;} 
   

// limitation de la valeur PWMG
if ((increG==1) && (PWMG<40))  {PWMG=40;}
if ((increG==-5) && (PWMG<40))  {PWMG=0;}  
if ((PWMG<254) && (increG==1))     {PWMG=PWMG+increG;}   
if ((PWMG>5) && (increG==-5))      {PWMG=PWMG+increG;} 

analogWrite(motorD, PWMD);   //PWM  pin 3 comandé par x
analogWrite(motorG, PWMG);   //PWM  pin 11 comandé par y
 
}   //fin routine

voici la simulation avec la commande tout ou rien, ou il faut que la valeur de la conversion numerique analogique des voies x et Y soit supérieures à 900 pour que la PWM s'incremente.

Nous sommes en train de finir l’écriture de la présentation du matériel, des convertisseurs moteurs et de la gestion de la batterie qui est effectué par un BMS Bluetooth et affichage par smartphone. De même, comment est gérer les relais pour la marche arrière et la protection des moteurs et la limitation de courant dans les moteurs comme pour la trottinette.

les 2 programmes precedents en fichier attaché

dcmotor_joystick.ino (2.87 KB)

dcmotor_joystick_tout_ou_rien.ino.ino (3 KB)

Réalisation d’un fauteuil roulant pour personne à mobilité réduite
Présentation générale
De nos jours, 1 personne sur 200 utilise un fauteuil roulant. C’est pourquoi nous avons voulu créer un fauteuil roulant afin de faciliter les déplacements en ville. Nous avons pour but de le rendre « low cost » et de permettre aux utilisateurs de faire la maintenance en cas de dysfonctionnement comme par exemple changer la batterie à la place de changer le fauteuil entièrement en cas de problème de cette dernière.

Voici le matériel utilisé :

Ecran LCD

Arduino MEGA 2560

2 moteurs courant continu.

Joystick

Ventilateur x2

Carte hacheur abaisseur maison x2

Voici le schéma électrique simplifié de notre fauteuil avec affichage sur un écran LCD. Ici nos résistances variables représentent le joystick

Les cartes hacheurs ne sont pas représentées pour simplifier la simulation.

Notre but est donc de créer un fauteuil roulant capable de circuler en autonomie en ville.

L’écran LCD

Nous utilisons l’écran LCD ci-dessus mais vous pouvez utiliser celui-ci-dessous si vous avez 4 boutons disponible à côté.

https://fr.aliexpress.com/item/LCD-Board-2004-20-4-LCD-20X4-5V-Blue-screen-LCD2004-display-LCD-module-LCD-2004/1863464287.html?spm=a2g0w.search0204.3.28.4fc330f6Fe43NX&ws_ab_test=searchweb0_0,searchweb201602_5_10152_10151_10065_10344_10068_10342_10343_10340_10341_10696_10084_10083_10618_10305_10304_10307_10306_10302_5711215_10313_10059_10184_10534_100031_10103_10624_10623_443_10622_10621_10620,searchweb201603_25,ppcSwitch_2_ppcChannel&algo_expid=6f09b0dc-1bea-4aa2-8f12-c1868780ac0c-4&algo_pvid=6f09b0dc-1bea-4aa2-8f12-c1868780ac0c&priceBeautifyAB=0

Moteur DC :

Nous utilisons ces moteurs pour notre fauteuil roulant. Ci-joint les caractéristiques du moteur.

Les joysticks

Les joysticks que l’on utilise sont semblable à des joysticks de manette de consoles de salon. Un joystick fonctionne sur 2 axes. Il possède 2 potentiomètres, ce qui va permettre d’aller plus ou moins vite dans une direction en fonction de sa position.

Les hacheurs

Les cartes hacheurs sont des convertisseurs DC-DC qui permettent de moduler la tension. Elles vont nous permettre de faire varier la tension pour ne pas que les moteurs soient à fond ou à 0. Ces cartes ne permettent pas la marche arrière.

fauteuil roulant arduino 1.doc (943 KB)

fauteuil roulant arduino 2.doc (876 KB)

Pour bien comprendre le gestion du fauteuil roulant, nous avons simuler celui-ci avec Matlab simulink
avec la gestion du joystick à partir de look up table.
On peut observer la progression de l’augmentation de la PWM à partir de la discrétisation tous les 0.1s et sa régulation.
Puis, il y a la modélisation du moteur DC gauche. Le moteur droit n’a pas été modélisé pour ne pas surcharger le schéma. De même dans ce modèle, il n’y a pas la limitation de courant….

On peut observer que la PWM augmente de 1Temps d’échantillonnage qui est de 0.1seconde. Par conséquent, il faut 12.7seconde pour atteindre un rapport cyclique de 127.
La vitesse augmente lorsque le couple moteur est supérieur au couple résistant. Donc, il faut que la valeur de la PWM suivante pour que la vitesse devienne supérieure à 0.
PWM (pour Vitesse=0)=Couplechargesec
Resistance255/(contantedecouple24V)
PWM (pour Vitesse=0)=0.08N.m1.7ohms255/(0.05*24V)=29

On peut observer grâce à la rampe de la PWM, il n’y a pas le pic de courant demandé par les moteurs DC lorsqu’on leurs applique une tension. De plus, il y a bien une régulation de la PWM autour de la valeur souhaité qui est ici de 127.
De plus, on peut observe que le courant augmente lorsque la vitesse augmente car le couple de charge augmente vitesse à cause du coefficient de frottement visqueux qui est proportionnel à la vitesse.

Sur la figure suivante, on peut observer les dynamiques supplémentaires avec la valeur de la consigne de joystick qui demande une PWM 255 pour le temps de 30 secondes mais avec une période d’échantillonnage de 0.01s. On peut observer que la PWM mais une 1.27s pour atteindre 127 mais provoque des pointes de courants au moteur qui sont limites admissibles par le moteur.

D’ailleurs, si la période d’échantillonnage repasse à 0.1seconde
On observera que le courant augmente car le couple de charge augmente en fonction de la vitesse à cause du coefficient de frottement visqueux qui est proportionnel à la vitesse.

Remarques :

  • on retrouve bien les dynamiques précédentes lors de nos tests en réels.
  • la vitesse n’est pas très rapide pour un fauteuil roulant pour faire de grande distance.
  • la puissance perdue dans le réducteur des moteur (40W) est relativement importante par rapport à puissance demandé sur du plat 30 Watt.
    Des moteurs brushless sans reducteur seraient plus interressant à utiliser

On est en train d’écrire la présentation du programme Arduino avec de nombreuses explications

Nous avons d'abord réaliser un programme de test afin de vérifié si nos cartes hacheurs étaient capables de recevoir une PMW de 255. Nous utilisons un écran LCD afin de savoir quel hacheur recevait quelle PWM.

Bibliothèques :

// Bibliothèques Arduino utilisées
#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>

Dans la réalisation du projet, nous utiliserons trois bibliothèques, la première permettra la gestion de l’écran LCD. La seconde < SoftwareSerial> sera utilisé en cas d’utilisation d’une liaison série entre la carte arduino et un ordinateur via un câble USB par exemple. Pour finir, on utilisera la bibliothèque que l’on utilisera en tant que routine d’interruption.

Broches et variables :

// Définition des broches utilisaient sur la carte arduino

#define BP1     30      // 21 BP1
#define BP2     31      // 20 BP2           
#define BP3     32      // 19 BP3
#define PWM1    10      // 10 PWM1
#define PWM2    9       // 9 PWM2
#define LED     13      // 13 LED

//définition des broches du lcd : RS, Enable, D4, D5, D6, D7

LiquidCrystal lcd(27, 28, 25, 24, 23, 22);

// Déclaration des variables

int PWM_1=0;            // valeur de la PWM sur le moteur 1 (gauche)
int PWM_2=0;            // valeur de la PWM sur le moteur 2 (droit)
byte temps;

Ici, on assigne tous les pins utilisaient à son utilisation que ça soit pour des boutons, des PWM ou un écran LCD où celui-ci utilisera une fonction présent dans la bibliothèque . Pour finir, on déclarera les variables globales nécessaires au projet notamment nos deux PWM.

Configuration initiale :

void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000001;  // Configure le Timer1 pour une fréquence de PWM à 31372.55 Hz http://sobisource.com/arduino-mega-pwm-pin-and-frequency-timer-control/
  lcd.begin(20, 4);     // Définir la taille de l'écran LCD (x,y)
  Timer1.initialize(100000);         // Configure le timer1 pour 0,1 seconde de période =>  100 000
  Timer1.attachInterrupt(callback);  // Fonction utilisé lors de l'interruption
  pinMode(10, OUTPUT);  // Définit le pin 10 en sortie ( PWM 1 )
  pinMode(9, OUTPUT);   // Définit le pin 9 en sortie ( PWM 2 )
  pinMode(13, OUTPUT);  // Définit le pin 13 en sortie ( LED )
  pinMode(BP1, INPUT);  // Définit le bouton 1 en entrée
  pinMode(BP2, INPUT);  // Définit le bouton 2 en entrée
  pinMode(BP3, INPUT);  // Définit le bouton 3 en entrée
}

Dans cette partie, nous définissons les paramètres des PWM (fréquence), du timer d’interruption (temps de la routine et fonction appelé lors de l’interruption), et le sens des pins (entrée ou sortie).

Fonction :

// Fonction Clignotement de la lED
void callback()  
{
temps++;
if ( digitalRead(LED)== 1 ) {digitalWrite(LED,LOW);}
    else {digitalWrite(LED,HIGH);}
}

// Fonction réinitialisation PWM
void raz()
{
  PWM_1=0;
  PWM_2=0;
}

Pour le programme de test, nous utiliserons deux fonctions, une première qui utilisera le timer1 en faisant clignoter afin d’être sûr que le programme tourne et n’a pas planté. La seconde permettra une remise à zéro des PWM en cas de problème.

Programme principal :

void loop() 
{
  if(temps=1);
  {
   if ((digitalRead(BP1))==1) {	// Si bouton1 appuyé alors
   PWM_1++;
   delay(200);		// Délai de 200ms pour éviter les rebonds du bouton
   }
   if ((digitalRead(BP2))==1) {
   PWM_2++;
   delay(200);
   }
   if ((digitalRead(BP3))==1) {
   raz();
   delay(200);
   }
   if(PWM_1>=255){PWM_1=255;}   // Limitation (8bits = 255 max)
   if(PWM_2>=255){PWM_2=255;}   // Limitation (8bits = 255 max)
   analogWrite(PWM1, PWM_1);    // Ecrit la valeur de PWM_1 sur la PWM1
   analogWrite(PWM2, PWM_2);    // Ecrit la valeur de PWM_2 sur la PWM2
   lcd.clear();          // Remise à zéro de l'écran LCD
   lcd.setCursor(0,1);
   lcd.print("g:");
   lcd.setCursor(0,2);
   lcd.print("d:");
   lcd.setCursor(4,0);
   lcd.print("PWM");
   lcd.setCursor(4,1);
   lcd.print(PWM_1);     // Afficher la valeur de la PWM_1
   lcd.setCursor(4,2);
   lcd.print(PWM_2);     // Afficher la valeur de la PWM_2
   delay(10);
   temps=0;
  }
}

Ici, on utilise nos trois boutons définit précédemment afin d’incrémenter ou remettre à zéro nos PWM. Le premier bouton servira à l’incrémentation de la PWM_1 (moteur gauche), le deuxième bouton servira à l’incrémentation de la PWM_2 (moteur droit) et le troisième bouton servira à remettre les deux PWM à zéro. On limitera la PMW à 255 au maximum car notre hacheur est sur 8bits, ensuite on utilisera la fonction « analogWrite » pour affecter notre valeur analogique en sortie. Pour visualiser tout cela, on affichera sur notre écran LCD, la valeur instantanée des PWM.

Pour finir, voici le programme complet que l'on a utilisé pour faire nos tests.

Programme complet :

// Bibliothèques Arduino utilisées

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>

// Définition des broches utilisaient sur la carte arduino

#define BP1     30      // 21 BP1
#define BP2     31      // 20 BP2           
#define BP3     32      // 19 BP3
#define PWM1    10      // 10 PWM1
#define PWM2    9       // 9 PWM2
#define LED     13      // 13 LED

//définition des broches du lcd : RS, Enable, D4, D5, D6, D7

LiquidCrystal lcd(27, 28, 25, 24, 23, 22);

// Déclaration des variables

int PWM_1=0;            // valeur de la PWM sur le moteur 1 ( gauche )
int PWM_2=0;            // valeur de la PWM sur le moteur 2 ( droit )
byte temps;


void setup() {
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000001;  // Configure le Timer1 pour une fréquence de PWM à 31372.55 Hz http://sobisource.com/arduino-mega-pwm-pin-and-frequency-timer-control/
  lcd.begin(20, 4);     // Définir la taille de l'écran LCD (x,y)
  Timer1.initialize(100000);         // Configure le timer1 pour 0,1 seconde de période =>  100 000
  Timer1.attachInterrupt(callback);  // Fonction utilisé lors de l'interruption
  pinMode(10, OUTPUT);  // Définit le pin 10 en sortie ( PWM 1 )
  pinMode(9, OUTPUT);   // Définit le pin 9 en sortie ( PWM 2 )
  pinMode(13, OUTPUT);  // Définit le pin 13 en sortie ( LED )
  pinMode(BP1, INPUT);  // Définit le bouton 1 en entrée
  pinMode(BP2, INPUT);  // Définit le bouton 2 en entrée
  pinMode(BP3, INPUT);  // Définit le bouton 3 en entrée
}

// Fonction Clignotement de la lED
void callback()  
{
temps++;
if ( digitalRead(LED)== 1 ) {digitalWrite(LED,LOW);}
    else {digitalWrite(LED,HIGH);}
}

// Fonction réinitialisation PWM
void raz()
{
  PWM_1=0;
  PWM_2=0;
}

void loop() 
{
  if(temps=1);
  {
   if ((digitalRead(BP1))==1) {
   PWM_1++;
   delay(200);
   }
   if ((digitalRead(BP2))==1) {
   PWM_2++;
   delay(200);
   }
   if ((digitalRead(BP3))==1) {
   raz();
   delay(200);
   }
   if(PWM_1>=255){PWM_1=255;}   // Limitation (8bits = 255 max)
   if(PWM_2>=255){PWM_2=255;}   // Limitation (8bits = 255 max)
   analogWrite(PWM1, PWM_1);    // Ecrit la valeur de PWM_1 sur la PWM1
   analogWrite(PWM2, PWM_2);    // Ecrit la valeur de PWM_2 sur la PWM2
   lcd.clear();          // Remise à zéro de l'écran LCD
   lcd.setCursor(0,1);
   lcd.print("g:");
   lcd.setCursor(0,2);
   lcd.print("d:");
   lcd.setCursor(4,0);
   lcd.print("PWM");
   lcd.setCursor(4,1);
   lcd.print(PWM_1);     // Afficher la valeur de la PWM_1
   lcd.setCursor(4,2);
   lcd.print(PWM_2);     // Afficher la valeur de la PWM_2
   delay(10);
   temps=0;
  }
}

Voici maintenant le programme final, celui que nous avons utilisé avec notre joystick, celui qui permet de diriger le fauteuil ou bon nous semble. Pour le ce programme, les bibliothèques, les broches et le setup resterons inchangées par rapport au programme de test.

Variables :

int valeur_x;       // valeur analogique de x sur 10bits
  int valeur_y;       // valeur analogique de y sur 10bits
  byte PWM_g;          // valeur de la PWM sur le moteur 1 ( gauche )
  byte PWM_d;          // valeur de la PWM sur le moteur 2 ( droit )
  byte PWM_g_reel;     // valeur analogique réel de x sur 8bits
  byte PWM_d_reel;     // valeur analogique réel de y sur 8bits
  byte profil=1;       // profil du fauteuil, par défault : 1
  byte vue=1;          // numéro de la vue, par défault : 1
  byte temps;

Afin d’éviter que notre moteur passe de l’arrêt à la vitesse maximale en un court instant, on rajoutera une variable PWM_reel pour chaque moteur sur laquelle, on ajoutera une pente pour l’augmentation de la PWM.
On rajoutera aussi une variable « profil » pour choisir la vitesse maximale du moteur et une variable « vue » qui permettra de changer l’affichage de LCD grâce à un bouton.

Routine d'interruption

void callback()  
{
 temps++;
 if ( digitalRead(LED)== 1 ) {digitalWrite(LED,LOW);}
   else {digitalWrite(LED,HIGH);}
 valeur_x=analogRead(A0);  // Mesure la tension sur la broche A0 en valeur analogique sur 10bits
 valeur_y=analogRead(A1);  // Mesure la tension sur la broche A1 en valeur analogique sur 10bits
 valeur_y=1023-valeur_y;
 if(PWM_g_reel<PWM_g){PWM_g_reel=PWM_g_reel+10;}   // rampe accélération
 if(PWM_g_reel>=254){PWM_g_reel=254;}
 if(PWM_d_reel<PWM_d){PWM_d_reel=PWM_d_reel+10;}   // rampe accélération
 if(PWM_d_reel>=254){PWM_d_reel=254;}
 if(PWM_g_reel>PWM_g){PWM_g_reel=PWM_g_reel-50;}   // rampe décélération
 if(PWM_g_reel<=0){PWM_g_reel=0;}
 if(PWM_d_reel>PWM_d){PWM_d_reel=PWM_d_reel-50;}   // rampe décélération
 if(PWM_d_reel<=0){PWM_d_reel=0;}
}

On mettra notre programme principal dans la routine d’interruption ainsi notre rampe sera de 0.1 seconde. Pour l’accélération, on aura une incrémentation de 10 toutes les 0.1 seconde alors que pour la décélération, on aura une décrémentation de 50 toutes les 0.1 seconde.

Fonction :

//fonction ecran pour le choix de la vue
void ecran()
{
  vue++;                      // Incrémente la vue
  if(vue>=3)vue=1;            // nombre de vue = 2
}

//fonction incrémentation
void incrementation() 
{
  if(vue==1)
  {
    profil++;                 // Incrémente le profil
    if(profil>=5)profil=5;    // valeur max du profil = 5
  }
}

//fonction décrémentation
void decrementation() 
{
  if(vue==1)
  {
  profil--;                   // Décrémente la profil
  if(profil<=1)profil=1;      // valeur min du profil = 1
  }
}

Ici, on utilisera nos 3 boutons pour l’incrémentation, la décrémentation et le changement de la vue de l’écran LCD.

Programme principal :

void loop() 
{
  if(temps=1);
  {
   if ((digitalRead(BP1))==1){    // Gestion bouton 1
    incrementation();
    delay(200);
   }
   if ((digitalRead(BP2))==1){    // Gestion bouton 2
    decrementation();
    delay(200);
   }
   if ((digitalRead(BP3))==1){    // Gestion bouton 3
    ecran();
    delay(200);
   }
  if(vue==1)
  {
    lcd.clear();              // Remise à 0 de l'écran LCD 
    lcd.setCursor(0,1);       // Positionnement du curseur (x,y)
    lcd.print("g:");
    lcd.setCursor(0,2);
    lcd.print("d:");
    lcd.setCursor(0,0);
    lcd.print("profil=");   
    lcd.setCursor(7,0);
    lcd.print(profil);        // Affiche la valeur du profil
    lcd.setCursor(5,1);
    lcd.print(valeur_x);      // Affiche la valeur de x
    lcd.setCursor(5,2);
    lcd.print(valeur_y);      // Affiche la valeur de y
    lcd.setCursor(10,0);
    lcd.print("cons"); 
    lcd.setCursor(10,1);
    lcd.print(PWM_g);         // Affiche la valeur de la PWM gauche
    lcd.setCursor(10,2);
    lcd.print(PWM_d);         // Affiche la valeur de la PWM droit
    lcd.setCursor(16,0);
    lcd.print("PWM"); 
    lcd.setCursor(16,1);
    lcd.print(PWM_g_reel);    // Affiche la valeur réel de la PWM gauche
    lcd.setCursor(16,2);
    lcd.print(PWM_d_reel);    // Affiche la valeur réel de la PWM droit
    delay(100);               // Délai de 100ms pour éviter le clignotement de l'afficheur LCD
  }
  if(vue==2)
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("profil=");
    lcd.setCursor(7,0);
    lcd.print(profil);        // Afficher la valeur du profil
    lcd.setCursor(0,1);
    lcd.print("PWM_g=");
    lcd.setCursor(7,1);
    lcd.print(PWM_g_reel);    // Affiche la valeur  réel de la PWM gauche
    lcd.setCursor(0,2);
    lcd.print("PWM_d=");
    lcd.setCursor(7,2);
    lcd.print(PWM_d_reel);    // Affiche la valeur réel de la PWM droit
    delay(100);
  }
  if(valeur_y<660)
  {
    PWM_g=0;PWM_d=0;          // zone 1,2 et 3 : pas de marche arrière
  }
  if(valeur_y>=660&&valeur_y<=720)
  {
    if(valeur_x<440)
    {
      switch(profil)
      {           
        case 1: PWM_g=0;PWM_d=50;break;
        case 2: PWM_g=0;PWM_d=100;break;
        case 3: PWM_g=0;PWM_d=150;break;
        case 4: PWM_g=0;PWM_d=200;break;
        case 5: PWM_g=0;PWM_d=250;break;
      }
    }
    if(valeur_x>=440&&valeur_x<=560){PWM_g=0;PWM_d=0;}
    if(valeur_x>560)
    {      
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=0;break;
        case 2: PWM_g=100;PWM_d=0;break;
        case 3: PWM_g=150;PWM_d=0;break;
        case 4: PWM_g=200;PWM_d=0;break;
        case 5: PWM_g=250;PWM_d=0;break;
      }
    }
  }
  if(valeur_y>720)
  {
    if(valeur_x<440)
    {
      switch(profil)
      {           
        case 1: PWM_g=25;PWM_d=50;break;
        case 2: PWM_g=50;PWM_d=100;break;
        case 3: PWM_g=75;PWM_d=150;break;
        case 4: PWM_g=100;PWM_d=200;break;
        case 5: PWM_g=125;PWM_d=250;break;
      }    
    }
    if(valeur_x>=440&&valeur_x<=560)
    {
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=50;break;
        case 2: PWM_g=100;PWM_d=100;break;
        case 3: PWM_g=150;PWM_d=150;break;
        case 4: PWM_g=200;PWM_d=200;break;
        case 5: PWM_g=250;PWM_d=250;break;
      }
    }
    if(valeur_x>560)
    {
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=25;break;
        case 2: PWM_g=100;PWM_d=50;break;
        case 3: PWM_g=150;PWM_d=75;break;
        case 4: PWM_g=200;PWM_d=100;break;
        case 5: PWM_g=250;PWM_d=125;break;
      }    
    }
  }
  analogWrite(PWM1, PWM_g_reel);    // Ecris sur la PWM1 (moteur gauche)
  analogWrite(PWM2, PWM_d_reel);    // Ecris sur la PWM2 (moteur droit)
  temps=0;
  }
}

La première partie du programme principal permet la gestion des boutons et de l’écran LCD avec l’affichage de la valeur du profil et des PWM.
Ensuite la seconde partie constituée de plusieurs « if » et de « switch ». Elle permet définir la PWM suivant la position du joystick par exemple si le joystick est en avant alors les deux moteurs reçoivent une PWM de 255. La partie « switch » quant à elle permet de limité la vitesse du moteur suivant sa valeur. Et pour on injectera la valeur analogique dans les hacheurs avec la fonction « analogWrite ».

Le programme complet est mis dans le post suivant à cause de la limitation de caractère.

fauteuil roulant arduino 3.doc (457 KB)

Programme complet :

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>
#include <TimerOne.h>

#define BP1     30      // 21 BP1
#define BP2     31      // 20 BP2           
#define BP3     32      // 19 BP3
#define PWM1    10      // 10 PWM1 moteur gauche 
#define PWM2    9       // 9  PWM2 moteur droit
#define LED     13      // 13 LED

LiquidCrystal lcd(27, 28, 25, 24, 23, 22); //définition des broches du lcd : RS, Enable, D4, D5, D6, D7

  int valeur_x;       // valeur analogique de x sur 10bits
  int valeur_y;       // valeur analogique de y sur 10bits
  byte PWM_g;          // valeur de la PWM sur le moteur 1 ( gauche )
  byte PWM_d;          // valeur de la PWM sur le moteur 2 ( droit )
  byte PWM_g_reel;     // valeur analogique réel de x sur 8bits
  byte PWM_d_reel;     // valeur analogique réel de y sur 8bits
  byte profil=1;       // profil du fauteuil, par défault : 1
  byte vue=1;          // numéro de la vue, par défault : 1
  byte temps;

void setup() 
{
  TCCR2B = (TCCR2B & 0b11111000) | 0b00000001;  // set timer 1 divisor to     1 for PWM frequency of 31372.55 Hz http://sobisource.com/arduino-mega-pwm-pin-and-frequency-timer-control/
  lcd.begin(20, 4);
  Timer1.initialize(100000);          // Initialise le timer 1 et le définit pour 0,1 seconde =>  100 000
  Timer1.attachInterrupt(callback);   // Nomme le timer 1
  pinMode(10, OUTPUT);  // définit le pin 10 en sortie ( PWM 1 )
  pinMode(11, OUTPUT);  // définit le pin 11 en sortie ( PWM 2 )
  pinMode(13, OUTPUT);  // définit le pin 12 en sortie ( LED )
  pinMode(BP1, INPUT);  // définit le bouton 1 en entrée
  pinMode(BP2, INPUT);  // définit le bouton 2 en entrée
  pinMode(BP3, INPUT);  // définit le bouton 3 en entrée
  
}
void callback()  
{
  temps++;
  if ( digitalRead(LED)== 1 ) {digitalWrite(LED,LOW);}
    else {digitalWrite(LED,HIGH);}
  valeur_x=analogRead(A0);  // Mesure la tension sur la broche A0 en valeur analogique sur 10bits
  valeur_y=analogRead(A1);  // Mesure la tension sur la broche A1 en valeur analogique sur 10bits
  valeur_y=1023-valeur_y;
  if(PWM_g_reel<PWM_g){PWM_g_reel=PWM_g_reel+10;}   // rampe accélération
  if(PWM_g_reel>=254){PWM_g_reel=254;}
  if(PWM_d_reel<PWM_d){PWM_d_reel=PWM_d_reel+10;}   // rampe accélération
  if(PWM_d_reel>=254){PWM_d_reel=254;}
  if(PWM_g_reel>PWM_g){PWM_g_reel=PWM_g_reel-50;}   // rampe décélération
  if(PWM_g_reel<=0){PWM_g_reel=0;}
  if(PWM_d_reel>PWM_d){PWM_d_reel=PWM_d_reel-50;}   // rampe décélération
  if(PWM_d_reel<=0){PWM_d_reel=0;}
}

//fonction ecran pour le choix de la vue
void ecran()
{
  vue++;                      // Incrémente la vue
  if(vue>=3)vue=1;            // nombre de vue = 2
}

//fonction incrémentation
void incrementation() 
{
  if(vue==1)
  {
    profil++;                 // Incrémente le profil
    if(profil>=5)profil=5;    // valeur max du profil = 5
  }
}

//fonction décrémentation
void decrementation() 
{
  if(vue==1)
  {
  profil--;                   // Décrémente la profil
  if(profil<=1)profil=1;      // valeur min du profil = 1
  }
}

void loop() 
{
  if(temps=1);
  {
   if ((digitalRead(BP1))==1){    // Gestion bouton 1
    incrementation();
    delay(200);
   }
   if ((digitalRead(BP2))==1){    // Gestion bouton 2
    decrementation();
    delay(200);
   }
   if ((digitalRead(BP3))==1){    // Gestion bouton 3
    ecran();
    delay(200);
   }
  if(vue==1)
  {
    lcd.clear();              // Remise à 0 de l'écran LCD 
    lcd.setCursor(0,1);       // Positionnement du curseur (x,y)
    lcd.print("g:");
    lcd.setCursor(0,2);
    lcd.print("d:");
    lcd.setCursor(0,0);
    lcd.print("profil=");   
    lcd.setCursor(7,0);
    lcd.print(profil);        // Affiche la valeur du profil
    lcd.setCursor(5,1);
    lcd.print(valeur_x);      // Affiche la valeur de x
    lcd.setCursor(5,2);
    lcd.print(valeur_y);      // Affiche la valeur de y
    lcd.setCursor(10,0);
    lcd.print("cons"); 
    lcd.setCursor(10,1);
    lcd.print(PWM_g);         // Affiche la valeur de la PWM gauche
    lcd.setCursor(10,2);
    lcd.print(PWM_d);         // Affiche la valeur de la PWM droit
    lcd.setCursor(16,0);
    lcd.print("PWM"); 
    lcd.setCursor(16,1);
    lcd.print(PWM_g_reel);    // Affiche la valeur réel de la PWM gauche
    lcd.setCursor(16,2);
    lcd.print(PWM_d_reel);    // Affiche la valeur réel de la PWM droit
    delay(100);               // Délai de 100ms pour éviter le clignotement de l'afficheur LCD
  }
  if(vue==2)
  {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("profil=");
    lcd.setCursor(7,0);
    lcd.print(profil);        // Afficher la valeur du profil
    lcd.setCursor(0,1);
    lcd.print("PWM_g=");
    lcd.setCursor(7,1);
    lcd.print(PWM_g_reel);    // Affiche la valeur  réel de la PWM gauche
    lcd.setCursor(0,2);
    lcd.print("PWM_d=");
    lcd.setCursor(7,2);
    lcd.print(PWM_d_reel);    // Affiche la valeur réel de la PWM droit
    delay(100);
  }
  if(valeur_y<660)
  {
    PWM_g=0;PWM_d=0;          // zone 1,2 et 3 : pas de marche arrière
  }
  if(valeur_y>=660&&valeur_y<=720)
  {
    if(valeur_x<440)
    {
      switch(profil)
      {           
        case 1: PWM_g=0;PWM_d=50;break;
        case 2: PWM_g=0;PWM_d=100;break;
        case 3: PWM_g=0;PWM_d=150;break;
        case 4: PWM_g=0;PWM_d=200;break;
        case 5: PWM_g=0;PWM_d=250;break;
      }
    }
    if(valeur_x>=440&&valeur_x<=560){PWM_g=0;PWM_d=0;}
    if(valeur_x>560)
    {      
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=0;break;
        case 2: PWM_g=100;PWM_d=0;break;
        case 3: PWM_g=150;PWM_d=0;break;
        case 4: PWM_g=200;PWM_d=0;break;
        case 5: PWM_g=250;PWM_d=0;break;
      }
    }
  }
  if(valeur_y>720)
  {
    if(valeur_x<440)
    {
      switch(profil)
      {           
        case 1: PWM_g=25;PWM_d=50;break;
        case 2: PWM_g=50;PWM_d=100;break;
        case 3: PWM_g=75;PWM_d=150;break;
        case 4: PWM_g=100;PWM_d=200;break;
        case 5: PWM_g=125;PWM_d=250;break;
      }    
    }
    if(valeur_x>=440&&valeur_x<=560)
    {
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=50;break;
        case 2: PWM_g=100;PWM_d=100;break;
        case 3: PWM_g=150;PWM_d=150;break;
        case 4: PWM_g=200;PWM_d=200;break;
        case 5: PWM_g=250;PWM_d=250;break;
      }
    }
    if(valeur_x>560)
    {
      switch(profil)
      {           
        case 1: PWM_g=50;PWM_d=25;break;
        case 2: PWM_g=100;PWM_d=50;break;
        case 3: PWM_g=150;PWM_d=75;break;
        case 4: PWM_g=200;PWM_d=100;break;
        case 5: PWM_g=250;PWM_d=125;break;
      }    
    }
  }
  analogWrite(PWM1, PWM_g_reel);    // Ecris sur la PWM1 (moteur gauche)
  analogWrite(PWM2, PWM_d_reel);    // Ecris sur la PWM2 (moteur droit)
  temps=0;
  }
}

La batterie & BMS

Nous utilisons une batterie sur laquelle nous avons branché un BMS (Battery Management System). Ce dernier nous permet de récupérer toutes les informations de la batterie, sa consommation, sa capacité restante, etc…

Voici le lien pour télécharger l’application mobile (communication par Bluetooth) et l’application ordinateur via une liaison série (usb). /!\ Applications en anglais

J’ai d’ailleurs réalisé un word sur l’application mobile pour comprendre son fonctionnement (disponible ici : http://velorizontal.bbfr.net/t20802-bms-et-depannage-ou-maintenance-de-pack-de-batterie )

De nos jours, la plupart des BMS est analogique (sans microcontrôleur) pour avoir plus de fiabilité mais ils n’ont pas de communication série pour faire un diagnostic.

Nous avons 2 possibilités. Soit laissé le BMS géré grâce aux différentes applications, soit le faire avec une carte Arduino. Nous avons choisi la première solution car plus facile.

En Bluetooth

Elle permet à l’utilisateur de voir en direct sur son téléphone portable l’autonomie restante.
On y retrouve différentes pages permettant de connaitre l’état de la batterie, de la régler, … Pour vous connecter en Bluetooth, vous devrez avoir activé votre Bluetooth (évidemment) et mettre la géolocalisation (pour les calculs de distances parcouru)


Figure 1 : Page principale


Figure 2 : accès rapide à quelques informations


Figure 3 : Les différentes pages


Figure 4 : Page d’informations de la batterie


Figure 6 : Tension par cellule de la batterie (12 cellules ici)


Figure 7 : Courbes de tension et de courant pour une cellule


Figure 8 : Page de réglage du BMS


Figure 9 : Page de connexion au BMS

Sur les différentes pages que vous avez vu ci-dessus, on retrouve les pages d’informations qui permettent de connaître avec précision l’état de la batterie avec entre autres l’état de chaque cellule, les tensions de coupure (surtension ou sous tension), la capacité nominale, l’autonomie restante, la capacité restante, … A vous d’explorer les différentes pages !

L’avantage du BMS est qu’il permet aux personnes à mobilité réduite d’avoir un œil permanant sur l’état de leur batterie pendant leur trajet. Cependant, la page de réglage reste légère comparé à l’application sur ordinateur.

L’application sur ordinateur

Vous devez brancher le câble de communication série dans l’un de vos port usb. Une fois fait, allez dans votre gestionnaire de périphériques et regardé son port. Cela permet d’indiquer l’application sur quel port communiquer les informations. Pour le régler, cliquez sur « COM » et mettez le bon port.

Vous pouvez voir ici la page d’information de la batterie. Vous voyez entres autres la tension de chaque cellule, la charge restante (en %) de la batterie la tension de la cellule la plus haute, la plus basse, le courant et d’autres informations complémentaires.

Cette page nous permet de régler le BMS comme bon nous semble. Nous pouvons choisir les seuils de tension à ne pas dépasser pour protéger la batterie. Vous pouvez entres autres régler la tension de surtension, de sous-tension, le nombre d’éléments de votre batterie. Une fois fait, vous devez cliquer sur « Write EEPROM » afin de l’implémenter dans le BMS.
Ce qui est pratique avec cette application, c’est que vous pouvez lire la configuration actuelle du BMS et sauvegarder votre propre configuration. De plus, et assez important, vous pouvez sauvegarder les informations de votre batterie sous forme de tableur Excel : toutes les données telle que la capacité nominale, la tension par cellule, … Cela va permettre d’étudier ses données si besoin.

OK, mais pourquoi faire tout ça ?

L’étude d’une batterie avec un BMS permet de faire la maintenance de la batterie. Cela permet de vérifier si la batterie est hors service ou s’il s’agit juste d’un problème un élément. Cela permet aussi de faire des économies car on peut juste changer la batterie si nécessaire à la place de changer entièrement le fauteuil. Il est tout aussi possible de changer le seul élément manquant.

Nous allons prendre comme exemple une batterie contenant 13 éléments

Nous obtenons un tableau semblable à celui-ci (la dernière colonne a été rajouté « à la main » )
A partir de ce tableau nous pouvons tracer les courbes que nous voulons. Dans notre cas nous avons tracé 2 graphes :

Nous pouvons voir ici 2 courbes. Elles représentent respectivement la courbe de la tension de la cellule 1 (en bleu) et de la cellule 13 (en mauve) en fonction de la capacité restante. On observe que la capacité de la cellule 13 est tombé plus rapidement que celle de la cellule 1. Au début, les cellules on chacune 6500mAH de capacité. Puis on observe que plus la capacité disponible baisse, plus on aperçoit un écart entre les tensions de chaque cellule.

On remarque qu’il y a une différence de tension de 0.6V. On remarque aussi que les cellules 1 et 2 sont quasiment équivalentes alors que la cellule 13 perd beaucoup plus de tension. Nous avons aussi calculé la résistance interne de la cellule 13 et de la cellule1 : La résistance interne de la cellule 13 vaut 0,04518664Ω alors que celle de la cellule 1 vaut 0,018664047Ω. Il va donc falloir changer la cellule 13 de la batterie. La cellule 13 consomme plus que les autres cellules

Nous voyons ici que la tension de la cellule 13 chute plus vite que celle des deux autres cellules. Nous aurions pus tracer les courbes des autres cellules mais cela n’était pas nécessaire car le logiciel nous donne les tensions par cellule et nous avons vu que la tension de la cellule 13 était la plus basse à la fin.

Bonjour
je suis technicien fauteuil, votre projet est super.
Mais plusieurs choses sont à prendre en compte :
la marche arrière (ok relais inverseur....TRIAC )
le freinage ... il doit pouvoir être géré car en cas d'urgence le fauteuil doit s’arrêter très vite.

le top du top serait une récupération de l'énergie au freinage.

le calibrage du joystick, le mieux est de faire se que font tout les fabricant d'électronique;
c'est un de blocage, où temps que le système ne lis pas un valeur proche du milieu il est impossible de démarrer
Et enfin une zone neutre
x=512-(10)
-x=-512+(10)

la gestion de la batterie est plutôt sympa. il faudrait avoir l'apk pour la traduire et surtout pour pérenniser l'app car un fauteuil se garde minimum 5 ans et vous visez plus longtemps.
un retour avec une valeur (en %) sur l'écran est obligatoire

la demande de nos jours est quelque chose entre le tout terrains et le fauteuil de salon.
c'est à dire capable de monté un vrai trottoir (+15cm) avec la possibilité de terrain moue (boue sable)
et qui peut passé dans des portes de 70 (68 soit 61 max châssis )
Vu qu'il est compliqué de tout faire il faudra choisir....

j'ai beaucoup de ressource sur se domaine
Techniques , sécurité , obligation légal ....

Et si je peu aidé sa sera avec grand plaisir, j’espère que votre projet aboutira.
Car de nos jours sachez que se domaine est sclérosé ,
Il y à très peu de revendeurs qualifiés et seul la marge compte et pas le handicap