Pilotage de 2 moteurs DC avec un Joystick virtuel en JAVA
Dans le but de créer un robot thermique, nous souhaitons piloter le Raspberry en direct au travers d’une interface développée en Java Swing. Pour cela, la partie la plus importante concerne le pilotage de 2 moteurs DC avec le GPIO du Raspberry. Après avoir fait le nécessaire au niveau des installations de Pi4J sur le Raspberry (voir page) et sur le PC Windows (voir page), nous pouvons maintenant développer une application en nous appuyant sur le précédent test réalisé avec le contrôle d’une LED (voir page précédente).
Le robot que nous utilisons est un robot conçu par Anthony pour son TPE de Première S. Une page résume la construction de ce dernier (voir onglet Matériel). Pour ce projet, le Raspberry était programmé en Python (projet Raspi Car) et c’est donc ici l’occasion de faire le code équivalent en Java.
Branchement des moteurs avec la puce L293D
Le L293D est un composant indispensable pour piloter 2 moteurs à courant continu. Il permet de contrôler le sens et la vitesse de rotation d’un ou deux moteurs en inversant la polarité du câblage. Les moteurs sont en effet branchés sur le L293D, qui lui est connecté au GPIO du Raspberry. Le plan de câblage de la puce avec les 2 moteurs est le suivant :
Les ports GPIO utilisés pour le branchement de la L293D sont listés ci-dessous, avec leur correspondance en librairie WiringPi :
- Enable 1 : SCLK 11 du GPIO, WiringPi 14
- Input 1 : MISO 9 du GPIO, WiringPi 13
- Output 1 : moteur B (câble noir)
- Ground : masse du Raspberry
- Ground : masse du Raspberry
- Output 2 : moteur B (câble rouge)
- Input 2 : MOSI 10 du GPIO, WiringPi 12
- Vs : alimentation en 5 Volt de la batterie externe
- Vss : alimentation en 5 Volt du Raspberry Pi
- Input 4 : MOSI 20 du GPIO, WiringPi 28
- Output 4 : moteur A (câble noir)
- Ground : masse du Raspberry
- Ground : masse du Raspberry
- Output 3 : moteur A (câble rouge)
- Input 3 : 16 du GPIO, WiringPi 27
- Enable 2 : SCLK 21 du GPIO, WiringPi 29
Branchement du support amovible Pan Tilt
Le support Pan/Tilt est un support amovible que nous utiliserons pour faire pivoter le module thermique et ainsi connaître la chaleur à différents endroits. Le support se compose en 2 parties :
- en bas : le Pan qui pivote de gauche à droite, branché sur la pin 5 du GPIO, WiringPi 21
- en haut : le Tilt qui pivote de haut en bas, branché sur la pin 6 du GPIO, WiringPi 22
Le plan de cablâge du support Pan/Tilt est celui ci-dessous. Le + 5 Volt provient de la batterie externe.
Conception de l’interface Java Swing avec Joystick virtuel
Pour le codage, nous avons créé un projet « RaspiThermoCam » sur Eclipse. Nous travaillons dans le package par défaut. Ensuite, nous créons 5 classes selon la méthode MVC et nous ajoutons les librairies Pi4J au projet. De plus, nous prenons soin de compiler le code en version Java 11, comme expliqué sur la page Développement sur Eclipse pour Raspberry (voir ici). Les 5 classes sont les suivantes :
- AppJoystick.java
- Controller.java
- Joystick.java
- Robot.java
- View.java
Le projet est téléchargeable avec le lien ci-dessous. L’archive contient les fichiers binaires, les fichiers sources au format Java et les données de l’application.
Projet Java - Raspi Thermo Cam
17 files 144 KB 06/05/2021
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
// Anthony LE PORS - Pilotage d'un Robot Raspi Thermo Cam - Avril 2021 // VIEW : Interface graphique import javax.swing.*; import java.awt.*; import java.awt.event.*; public class View { // View utilise Swing pour afficher interface utilisateur // définit les attributs : les composants de l'inteface private JFrame fenetre; // fenetre private JPanel contenu; // panneau private int hauteurFenetre, largeurFenetre; private int espacement, espacementHorizontal; private JLabel labelTitre; private JLabel labelDirection; private JLabel labelRotation; private JButton btnParabilis; private JButton btnGetThermal; private JSeparator separateur; private Joystick joystickDirection; private JSlider sliderVertical; private JSlider sliderHorizontal; // Instanciation : constructeur de la fenetre public View(String title) { ////////////////////////////// PARAMETRAGE DE LA FENETRE ////////////////////////////////// fenetre = new JFrame(title); fenetre.setIconImage(new ImageIcon("data/logo.png").getImage()); fenetre.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); // ne fait rien a la fermeture fenetre.setSize(350,300); // taille de la fenetre fenetre.setMinimumSize(new Dimension(350,300)); fenetre.setLocation(Toolkit.getDefaultToolkit().getScreenSize().width - 350, Toolkit.getDefaultToolkit().getScreenSize().height - 300); fenetre.setVisible(true); fenetre.getContentPane().setLayout(null); largeurFenetre = fenetre.getContentPane().getBounds().width; hauteurFenetre = fenetre.getContentPane().getBounds().height; espacement = hauteurFenetre/35; espacementHorizontal = largeurFenetre/50; ////////////////////////////// PARAMETRAGE DU PANNEAU ///////////////////////////////////// contenu = new JPanel(); contenu.setBounds(0, 0, largeurFenetre, hauteurFenetre); fenetre.getContentPane().add(contenu); contenu.setBackground(new Color(203,224,233)); // bleu contenu.setLayout(null); contenu.setVisible(true); ////////////////////////////// AJOUT DES ELEMENTS AU PANNEAU ////////////////////////////// //////////////////////////////////// LABEL TITRE ////////////////////////////////////////// labelTitre = new JLabel("Pilotage du Raspi Thermo Cam"); labelTitre.setBounds(0,5,largeurFenetre,hauteurFenetre/10); labelTitre.setHorizontalAlignment(JLabel.CENTER); labelTitre.setVerticalAlignment(JLabel.CENTER); contenu.add(labelTitre); //////////////////////////////////// LABEL DIRECTION ////////////////////////////////////// labelDirection = new JLabel("Direction du robot"); labelDirection.setBounds(0,hauteurFenetre/12 + espacement,largeurFenetre*2/3,30); labelDirection.setHorizontalAlignment(JLabel.CENTER); labelDirection.setVerticalAlignment(JLabel.CENTER); contenu.add(labelDirection); //////////////////////////////////// LABEL ROTATION /////////////////////////////////////// labelRotation = new JLabel("Rotation de la caméra"); labelRotation.setBounds(0,hauteurFenetre/10 + espacement + hauteurFenetre/3 + espacement*3,largeurFenetre*2/3,30); labelRotation.setHorizontalAlignment(JLabel.CENTER); labelRotation.setVerticalAlignment(JLabel.CENTER); contenu.add(labelRotation); //////////////////////////////////// SEPARATEUR /////////////////////////////////////////// separateur = new JSeparator(JSeparator.VERTICAL); separateur.setBounds(largeurFenetre*2/3 + 2, 5 + hauteurFenetre/10 + 5, 2, hauteurFenetre - (5 + hauteurFenetre/10 + 5 + espacement)); contenu.add(separateur); ////////////////////////////// PANEL FOND JOYSTICK DIRECTION ////////////////////////////// joystickDirection = new Joystick((largeurFenetre*2/3)/2 - (hauteurFenetre/3)/2, hauteurFenetre/12 + espacement + 30, hauteurFenetre/3); contenu.add(joystickDirection.getFondJoystick()); joystickDirection.getFondJoystick().setVisible(false); joystickDirection.getFondJoystick().setVisible(true); ////////////////////////////// JSLIDER ROTATION HORIZONTALE ////////////////////////////// sliderHorizontal = new JSlider(-50, 90, 0); sliderHorizontal.setBounds(espacementHorizontal, hauteurFenetre/10 + espacement*6 + hauteurFenetre/3 + 10 + hauteurFenetre/6 -25, largeurFenetre*10/21, 50); sliderHorizontal.setPaintTrack(true); sliderHorizontal.setPaintTicks(true); sliderHorizontal.setPaintLabels(true); sliderHorizontal.setMajorTickSpacing(25); sliderHorizontal.setMinorTickSpacing(5); contenu.add(sliderHorizontal); sliderHorizontal.setVisible(false); sliderHorizontal.setVisible(true); ////////////////////////////// JSLIDER ROTATION VERTICALE //////////////////////////////// sliderVertical = new JSlider(JSlider.VERTICAL,-30, 80, 0); sliderVertical.setBounds(largeurFenetre*10/21 + espacementHorizontal*2, hauteurFenetre/10 + espacement*6 + hauteurFenetre/3 + 10, 50, hauteurFenetre/3); sliderVertical.setPaintTrack(true); sliderVertical.setPaintTicks(true); sliderVertical.setPaintLabels(true); sliderVertical.setMajorTickSpacing(30); sliderVertical.setMinorTickSpacing(5); contenu.add(sliderVertical); sliderVertical.setVisible(false); sliderVertical.setVisible(true); /////////////////////////////// BOUTON PARABILIS THERMAL ////////////////////////////////// btnParabilis = new JButton("ParabilisThermal"); btnParabilis.setBounds(largeurFenetre *2/3 + espacementHorizontal,hauteurFenetre*1/2 - 45, largeurFenetre*1/3 - 2*espacementHorizontal, 30); contenu.add(btnParabilis); btnParabilis.setVisible(false); btnParabilis.setVisible(true); ////////////////////////////////// BOUTON GET THERMAL ///////////////////////////////////// btnGetThermal = new JButton("GetThermal"); btnGetThermal.setBounds(largeurFenetre *2/3 + espacementHorizontal,hauteurFenetre*1/2 + 15, largeurFenetre*1/3 - 2*espacementHorizontal, 30); contenu.add(btnGetThermal); btnGetThermal.setVisible(false); btnGetThermal.setVisible(true); fenetre.validate(); //////////////////////////////// REDIMENSIONNEMENT DE LA FENETRE ////////////////////////// fenetre.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { largeurFenetre = fenetre.getContentPane().getBounds().width; hauteurFenetre = fenetre.getContentPane().getBounds().height; espacement = hauteurFenetre/35; espacementHorizontal = largeurFenetre/50; contenu.setBounds(0, 0, largeurFenetre, hauteurFenetre); labelTitre.setBounds(0,5,largeurFenetre,hauteurFenetre/10); labelDirection.setBounds(0,hauteurFenetre/12 + espacement,largeurFenetre*2/3,30); labelRotation.setBounds(0,hauteurFenetre/10 + espacement + hauteurFenetre/3 + espacement*3,largeurFenetre*2/3,30); separateur.setBounds(largeurFenetre*2/3 + 2, 5 + hauteurFenetre/10 + 5, 2, hauteurFenetre - (5 + hauteurFenetre/10 + 5 + espacement)); btnParabilis.setBounds(largeurFenetre *2/3 + espacementHorizontal,hauteurFenetre*1/2 - 45, largeurFenetre*1/3 - 2*espacementHorizontal, 30); btnGetThermal.setBounds(largeurFenetre *2/3 + espacementHorizontal,hauteurFenetre*1/2 + 15, largeurFenetre*1/3 - 2*espacementHorizontal, 30); joystickDirection.redimensionnement((largeurFenetre*2/3)/2 - (hauteurFenetre/3)/2, hauteurFenetre/12 + espacement + 30, hauteurFenetre/3); sliderHorizontal.setBounds(espacementHorizontal, hauteurFenetre/10 + espacement*6 + hauteurFenetre/3 + 10 + hauteurFenetre/6 -25, largeurFenetre*10/21, 50); sliderVertical.setBounds(largeurFenetre*10/21 + espacementHorizontal*2, hauteurFenetre/10 + espacement*6 + hauteurFenetre/3 + 10, 50, hauteurFenetre/3); } }); } // methodes // on génère les getters et les setters pour chaque élément // getter permet de recuperer un element private avec une methode publique public Joystick getJoystickDirection() { return joystickDirection; } public JFrame getFenetre() { return fenetre; } public JSlider getSliderVertical() { return sliderVertical; } public JSlider getSliderHorizontal() { return sliderHorizontal; } public JButton getBtnParabilis() { return btnParabilis; } public JButton getBtnGetThermal() { return btnGetThermal; } } |
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
// Anthony LE PORS - Pilotage d'un Robot Raspi Thermo Cam - Avril 2021 // Classe joystick : JPanel pour afficher un joystick import javax.swing.*; import java.awt.*; import java.awt.event.*; public class Joystick { // attributs private JPanel fondJoystick; private JLabel labelJoystick; private ImageIcon imageJoystick; private ImageIcon imageJoystickScaled; private int tailleFond, tailleJoystick, valeurMax; private int[] positionCurseur = new int[2]; private boolean depasseGauche = false; private boolean depasseDroite = false; private boolean depasseBas = false; private boolean depasseHaut= false; private int[] pointMilieu = new int[2]; private int[] puissance = new int[2]; // constructeur Joystick (int x, int y, int cote){ this.tailleFond = cote; this.tailleJoystick = cote/2; creationJoystick(x, y, tailleFond); } // methodes public void creationJoystick(int x, int y, int cote){ ////////////////////////////////// PANEL FOND JOYSTICK //////////////////////////////////// fondJoystick = new JPanel(); // creation du plateau fondJoystick.setLayout(null); fondJoystick.setBounds(x, y, cote, cote); fondJoystick.setBackground(new Color(255,255,255)); // blanc fondJoystick.setVisible(false); fondJoystick.setVisible(true); this.tailleFond = fondJoystick.getBounds().width; this.tailleJoystick = tailleFond*2/5; this.valeurMax = tailleFond/2 - tailleJoystick/2; ///////////////////////////////////// CREATION DES IMAGES ///////////////////////////////// imageJoystick = new ImageIcon("data/joystick.png"); imageJoystickScaled = new ImageIcon(imageJoystick.getImage().getScaledInstance(tailleJoystick, tailleJoystick, Image.SCALE_SMOOTH)); ///////////////////////////////////// LABEL JOYSTICK ////////////////////////////////////// labelJoystick = new JLabel(imageJoystickScaled, JLabel.CENTER); labelJoystick.setBounds(tailleFond/2 - tailleJoystick/2, tailleFond/2 - tailleJoystick/2, tailleJoystick, tailleJoystick); fondJoystick.add(labelJoystick); labelJoystick.setVisible(false); labelJoystick.setVisible(true); //////////////////////////////////// DRAG DU JOYSTICK ///////////////////////////////////// dragJoystick(); } public void redimensionnement(int x, int y, int cote){ fondJoystick.setBounds(x, y, cote, cote); fondJoystick.remove(labelJoystick); this.tailleFond = fondJoystick.getBounds().width; this.tailleJoystick = tailleFond*2/5; this.valeurMax = tailleFond/2 - tailleJoystick/2; imageJoystickScaled = new ImageIcon(imageJoystick.getImage().getScaledInstance(tailleJoystick, tailleJoystick, Image.SCALE_SMOOTH)); labelJoystick = new JLabel(imageJoystickScaled, JLabel.CENTER); labelJoystick.setBounds(tailleFond/2 - tailleJoystick/2, tailleFond/2 - tailleJoystick/2, tailleJoystick, tailleJoystick); fondJoystick.add(labelJoystick); dragJoystick(); labelJoystick.setVisible(false); labelJoystick.setVisible(true); } public void dragJoystick(){ labelJoystick.addMouseListener(new MouseAdapter(){ @Override public void mouseReleased(MouseEvent e){ labelJoystick.setBounds(tailleFond/2 - tailleJoystick/2, tailleFond/2 - tailleJoystick/2, tailleJoystick, tailleJoystick); pointMilieu[0] = tailleFond/2 - (labelJoystick.getX() + tailleJoystick/2); pointMilieu[1] = tailleFond/2 - (labelJoystick.getY() + tailleJoystick/2); System.out.println("Position Joystick : " + pointMilieu[0] + " ; " + pointMilieu[1]); puissance[0] = 0; puissance[1] = 0; } }); labelJoystick.addMouseMotionListener(new MouseAdapter(){ @Override public void mouseDragged(MouseEvent e){ positionCurseur[0] = e.getXOnScreen() - fondJoystick.getLocationOnScreen().x; positionCurseur[1] = e.getYOnScreen() - fondJoystick.getLocationOnScreen().y; pointMilieu[0] = tailleFond/2 - (labelJoystick.getX() + tailleJoystick/2); pointMilieu[1] = tailleFond/2 - (labelJoystick.getY() + tailleJoystick/2); for (int i = 0; i < 2; i++) { puissance[i] = pointMilieu[i] * 100/valeurMax; // valeur entre -100 et 100 if (puissance[i] < 0) { puissance[i] = puissance[i]/2 - 50; // valeur dans [-100;-50] } else if (puissance[i] > 0) { puissance[i] = puissance[i]/2 + 50; // valeur dans [50;100] } } System.out.println("Curseur X : " + positionCurseur[0]); System.out.println("Curseur Y : " + positionCurseur[1]); System.out.println("Position Joystick : " + pointMilieu[0] + " ; " + pointMilieu[1]); System.out.println("Puissance Joystick : " + puissance[0] + " ; " + puissance[1]); depasseGauche = false; depasseDroite = false; depasseHaut = false; depasseBas = false; if (positionCurseur[0] < tailleJoystick/2 ) depasseGauche = true; if (positionCurseur[0] > tailleFond - tailleJoystick/2 ) depasseDroite = true; if (positionCurseur[1] < tailleJoystick/2 ) depasseHaut = true; if (positionCurseur[1] > tailleFond - tailleJoystick/2 ) depasseBas = true; // position par defaut labelJoystick.setBounds(positionCurseur[0] - tailleJoystick/2, positionCurseur[1] - tailleJoystick/2, tailleJoystick, tailleJoystick); // blocage a gauche HAUT et BAS if (depasseGauche){ if (depasseHaut) labelJoystick.setBounds(0, 0, tailleJoystick, tailleJoystick); else if (depasseBas) labelJoystick.setBounds(0, tailleFond - tailleJoystick, tailleJoystick, tailleJoystick); else labelJoystick.setBounds(0, positionCurseur[1] - tailleJoystick/2, tailleJoystick, tailleJoystick); } // blocage a droite HAUT et BAS if (depasseDroite){ if (depasseHaut) labelJoystick.setBounds(tailleFond - tailleJoystick, 0, tailleJoystick, tailleJoystick); else if (depasseBas) labelJoystick.setBounds(tailleFond - tailleJoystick, tailleFond - tailleJoystick, tailleJoystick, tailleJoystick); else labelJoystick.setBounds(tailleFond - tailleJoystick, positionCurseur[1] - tailleJoystick/2, tailleJoystick, tailleJoystick); } // blocage en haut GAUCHE et DROITE if (depasseHaut){ if (depasseGauche) labelJoystick.setBounds(0, 0, tailleJoystick, tailleJoystick); else if (depasseDroite) labelJoystick.setBounds(tailleFond - tailleJoystick, 0, tailleJoystick, tailleJoystick); else labelJoystick.setBounds(positionCurseur[0] - tailleJoystick/2, 0, tailleJoystick, tailleJoystick); } // blocage en bas GAUCHE et DROITE if (depasseBas){ if (depasseGauche) labelJoystick.setBounds(0, tailleFond - tailleJoystick, tailleJoystick, tailleJoystick); else if (depasseDroite) labelJoystick.setBounds(tailleFond - tailleJoystick, tailleFond - tailleJoystick, tailleJoystick, tailleJoystick); else labelJoystick.setBounds(positionCurseur[0] - tailleJoystick/2, tailleFond - tailleJoystick, tailleJoystick, tailleJoystick); } } }); } // getters et setters public JPanel getFondJoystick(){ return this.fondJoystick; } public JLabel getLabelJoystick() { return labelJoystick; } public int getTailleFond(){ return this.tailleFond; } public int getTailleJoystick(){ return this.tailleJoystick; } public int[] getPointMilieu() { return pointMilieu; } public int[] getPuissance() { return puissance; } } |
Ensuite, nous passons au code de la classe Robot. Les missions principales de cette classe sont la création des fonctions de déplacement des moteurs et de rotation du support amovible.
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
// Anthony LE PORS - Pilotage d'un Robot Raspi Thermo Cam - Avril 2021 // Classe robot : controle du Raspberry import com.pi4j.io.gpio.*; import com.pi4j.util.CommandArgumentParser; import com.pi4j.wiringpi.SoftPwm; import java.io.*; public class Robot { // attributs : elements du robot private GpioController gpio; private int moteurGA = 27; private int moteurGB = 28; private int moteurDA = 13; private int moteurDB = 12; private GpioPinDigitalOutput moteurGaucheE; private GpioPinDigitalOutput moteurDroiteE; private Pin pinPan; private GpioPinPwmOutput pwmPan; private Pin pinTilt; private GpioPinPwmOutput pwmTilt; // constructeur du robot public Robot(){ this.gpio = GpioFactory.getInstance(); this.moteurGaucheE = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_29); this.moteurDroiteE = gpio.provisionDigitalOutputPin(RaspiPin.GPIO_14); // creation des sorties PWM SoftPwm.softPwmCreate(moteurGA, 0, 100); SoftPwm.softPwmCreate(moteurGB, 0, 100); SoftPwm.softPwmCreate(moteurDA, 0, 100); SoftPwm.softPwmCreate(moteurDB, 0, 100); moteurGaucheE.high(); // active moteur Gauche moteurDroiteE.high();// active moteur Droite pinPan = CommandArgumentParser.getPin(RaspiPin.class, RaspiPin.GPIO_21); pwmPan = gpio.provisionSoftPwmOutputPin(pinPan); pwmPan.setPwmRange(100); pinTilt = CommandArgumentParser.getPin(RaspiPin.class, RaspiPin.GPIO_22); pwmTilt = gpio.provisionSoftPwmOutputPin(pinTilt); pwmTilt.setPwmRange(100); } // methodes public void deplacement(int[] puissance) { int vitesse = Math.max(Math.abs(puissance[0]), Math.abs(puissance[1])); if (puissance[1] > 0) { if (puissance[0] > 0) { // zone 1 SoftPwm.softPwmWrite(moteurGA, vitesse - puissance[0]); SoftPwm.softPwmWrite(moteurGB, 0); SoftPwm.softPwmWrite(moteurDA, vitesse); SoftPwm.softPwmWrite(moteurDB, 0); } else if (puissance[0] < 0) { // zone 2 SoftPwm.softPwmWrite(moteurGA, vitesse); SoftPwm.softPwmWrite(moteurGB, 0); SoftPwm.softPwmWrite(moteurDA, vitesse + puissance[0]); SoftPwm.softPwmWrite(moteurDB, 0); } else if (puissance[0] == 0) { avancer(puissance[1]); } } else if (puissance[1] < 0) { if (puissance[0] < 0) { // zone 3 SoftPwm.softPwmWrite(moteurGA, 0); SoftPwm.softPwmWrite(moteurGB, vitesse); SoftPwm.softPwmWrite(moteurDA, 0); SoftPwm.softPwmWrite(moteurDB, vitesse + puissance[0]); } else if (puissance[0] > 0) { // zone 4 SoftPwm.softPwmWrite(moteurGA, 0); SoftPwm.softPwmWrite(moteurGB, vitesse - puissance[0]); SoftPwm.softPwmWrite(moteurDA, 0); SoftPwm.softPwmWrite(moteurDB, vitesse); } else if (puissance[0] == 0) { avancer(puissance[1]); } } else if (puissance[1] == 0) { if (puissance[0] == 0) { stop(); }else { // pivot droite ou gauche tourner(puissance[0]); } } } public void avancer(int vitesse) { if (vitesse > 0) { System.out.println("Avant"); SoftPwm.softPwmWrite(moteurGA, vitesse); SoftPwm.softPwmWrite(moteurGB, 0); SoftPwm.softPwmWrite(moteurDA, vitesse); SoftPwm.softPwmWrite(moteurDB, 0); } else { System.out.println("Arriere"); SoftPwm.softPwmWrite(moteurGA, 0); SoftPwm.softPwmWrite(moteurGB, -vitesse); SoftPwm.softPwmWrite(moteurDA, 0); SoftPwm.softPwmWrite(moteurDB, -vitesse); } } public void tourner(int vitesse) { if (vitesse > 0) { System.out.println("Gauche"); SoftPwm.softPwmWrite(moteurGA, 0); SoftPwm.softPwmWrite(moteurGB, vitesse); SoftPwm.softPwmWrite(moteurDA, vitesse); SoftPwm.softPwmWrite(moteurDB, 0); } else { System.out.println("Droite"); SoftPwm.softPwmWrite(moteurGA, -vitesse); SoftPwm.softPwmWrite(moteurGB, 0); SoftPwm.softPwmWrite(moteurDA, 0); SoftPwm.softPwmWrite(moteurDB, -vitesse); } } public void stop() { System.out.println("Stop"); SoftPwm.softPwmWrite(moteurGA, 0); SoftPwm.softPwmWrite(moteurGB, 0); SoftPwm.softPwmWrite(moteurDA, 0); SoftPwm.softPwmWrite(moteurDB, 0); } public void rotationPan(int angle) { System.out.println("Rotation du Pan"); pwmPan.setPwm(angle); System.out.println("Angle choisi " + pwmPan.getPwm()); try { Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace();} System.out.println("pwm end"); } public void rotationTilt(int angle) { System.out.println("Rotation du Tilt"); pwmTilt.setPwm(angle); System.out.println("Angle choisi " + pwmTilt.getPwm()); try { Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace();} System.out.println("pwm end"); } public void lancerParabilis() { try { ProcessBuilder builder = new ProcessBuilder("bash", "-c", "/home/pi/RaspiThermoCam/data/ParabilisThermal.sh"); builder.start(); }catch (IOException ie) {ie.printStackTrace();} } public void lancerGetThermal() { try { ProcessBuilder builder = new ProcessBuilder("bash", "-c", "/home/pi/RaspiThermoCam/data/GetThermal.sh"); builder.start(); }catch (IOException ie) {ie.printStackTrace();} } public void resetGPIO() { rotationPan(18); // tout droit rotationTilt(17); // tout droit gpio.shutdown(); moteurGaucheE.low(); // eteint moteur Gauche moteurDroiteE.low();// eteint moteur Droite } } |
Il s’en suit la création de la classe Controller qui ordonne les actions du Raspberry en fonction des événements sur l’interface.
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
// Anthony LE PORS - Pilotage d'un Robot Raspi Thermo Cam - Avril 2021 // Controller : lien entre Robot et Interface import java.awt.event.*; import javax.swing.event.*; public class Controller { // attributs private View view; private Robot robot; // constructeur public Controller(View v, Robot r) { view = v; robot = r; } // methodes // action des boutons public void initController() { actionFermeture(); actionJoystickDirection(); view.getFenetre().addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { actionJoystickDirection(); } }); view.getSliderHorizontal().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { robot.rotationPan((int) (view.getSliderHorizontal().getValue()*(-0.105) + 19.066)); } }); view.getSliderVertical().addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { robot.rotationTilt((int) (view.getSliderVertical().getValue()*(-0.1091) + 16.727)); } }); view.getBtnParabilis().addActionListener(event -> robot.lancerParabilis()); view.getBtnGetThermal().addActionListener(event -> robot.lancerGetThermal()); } // evenements lors d'un appui sur bouton private void actionFermeture() { view.getFenetre().addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { robot.resetGPIO(); view.getFenetre().dispose(); } }); } private void actionJoystickDirection() { view.getJoystickDirection().getLabelJoystick().addMouseMotionListener(new MouseAdapter(){ @Override public void mouseDragged(MouseEvent e){ robot.deplacement(view.getJoystickDirection().getPuissance()); } }); view.getJoystickDirection().getLabelJoystick().addMouseListener(new MouseAdapter(){ @Override public void mouseReleased(MouseEvent e){ robot.stop(); } }); } } |
Enfin, le main permet de coordonner toutes les classes, toujours selon la méthode MVC.
Java
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Anthony LE PORS - Pilotage d'un Robot Raspi Thermo Cam - Avril 2021 // MAIN - Méthode MVC public class AppJoystick { public static void main(String[] args) { // on assemble les pièces de la méthode MVC (ici RVC) View fenetreInterface = new View("Raspi Thermo Cam"); Robot raspiThermoCam = new Robot(); Controller controlActions = new Controller(fenetreInterface, raspiThermoCam); controlActions.initController(); } } |
Compilation et exécution du pilotage des moteurs avec le Joystick virtuel Java
Pour compiler, nous exécutons le main sur Eclipse, toujours sur le PC Windows. Evidemment, des erreurs apparaissent puisque ce fichier ne peut pas s’exécuter sur Windows sans les ports GPIO. Mais les fichiers compilés apparaissent cette-fois dans le répertoire /bin du projet. En effet, les fichiers « .class » compilés sont ajoutés dans ce répertoire. Avec WinSCP, nous transférons le dossier du projet sur le Raspberry. Il ne nous reste plus qu’à lancer le programme avec la commande suivante :
Terminal
1 2 |
cd /home/pi/RaspiThermoCam # on se place dans le répertoire qui contient les fichiers compilés sudo java -classpath .:classes:/opt/pi4j/lib/'*' AppJoystick # commande pour exécuter l'appli via le main et en utilisant les librairies Pi4J |
Le résultat du pilotage des moteurs du Raspberry avec un joystick est visible sur la vidéo ci-dessous :
L’utilisation de ce robot en tant que robot thermique est en ligne sur une page en cliquant ici.