Um dos principais objectivos da robótica é articular objectos. E para isso, podemos também atuar sobre a articulação dos objectos através da ação do utilizador diretamente no sistema. Neste artigo, vamos ver como programar a placa Arduino para controlar servomotores com um joystick.
Pré-requisitos: Controlo de um servomotor com Arduino, Utilizar um joystick com o Arduino
Hardware
- Computador
- Placa Arduino Nano
- Cabo USB para ligar a placa Arduino ao PC
- 4x cabos fêmea
- 2x servomotor SG90 9G
- 1x joystick Modulo KYO-23
Esquema de ligação
Os servomotores são alimentados por uma tensão através dos fios castanhos da terra comum (GND) e dos fios vermelhos da fonte de tensão (5V). Aqui estão ligados diretamente à fonte de tensão de 5V do Arduino Nano e são controlados por sinais PWM enviados para os fios cor de laranja (terminal 5 e terminal 6). O joystick, também alimentado por uma tensão de 5V e terra comum (GND), devolve dois valores analógicos que iremos ligar às entradas analógicas do microcontrolador. Também devolve um valor digital, que ligamos ao pino 2 do microcontrolador.
Explicações e código
Vamos programar a placa Arduino Nano de modo a que o Joystick indique o sentido de rotação dos motores. Para isso, vamos interpretar a ligação dos nossos 2 servomotores como 2 rodas motorizadas cuja direção depende dos 2 valores analógicos devolvidos pelo Joystick. Estes dois valores correspondem às coordenadas YX do Joystick, cada uma variando de 0 a 1023. Para compreender a lógica do nosso programa, podemos representá-lo com um diagrama num plano YX:
Aqui, podemos ver que as coordenadas do Joystick correspondem a estados no nosso sistema, que por sua vez correspondem a directivas para controlar os nossos servomotores.
Este joystick tem um botão de pressão incorporado, que iremos simplesmente programar para exibir uma mensagem quando for premido. Para controlar os servomotores, vamos utilizar a biblioteca ServoLib.h (que pode encontrar no final deste artigo).
// headers for library #include <Arduino.h> //only used on PlatformIO IDE on VSCode. Not needed on Arduino IDE. #include <ServoLib.h> // function's prototypes used void printing_results(int x, int y); void motor_joystick_controlled(int xValue,int yValue); void MotorAvancer(); void MotorReculer(); void MotorDroite(); void MotorGauche(); void MotorAvancerDroite(); void MotorAvancerGauche(); void MotorReculerDroite(); void MotorReculerGauche(); void MotorStop(); // declaration of pins used #define joyX A1 #define joyY A2 const int joyBtn = 2; // Motors definition ServoLib motorG; const int motorGPin = 5; const int motorGZero = 1500; ServoLib motorD; const int motorDPin = 6; const int motorDZero = 1400; // Variables int Power = 400; //Motor velocity // initialization void setup() { Serial.begin(9600); // open to communicate via serial port motorG.associePin(motorGPin);// pins assignment ... motorD.associePin(motorDPin);// ... for right and left motors pinMode(joyBtn,INPUT_PULLUP); // button initialization (TOR = 0 or 1) MotorStop(); // stop motors as initial state } // main loop void loop() { int xValue = analogRead(joyX); // read joystick ... int yValue = analogRead(joyY); // ... position printing_results(xValue, yValue); // display of Joystick coordinates motor_joystick_controlled(xValue,yValue); // definition of the state of the motors //according to the coordinates of the joystick } // function to display the position of the joystick as well as the state of the button if pressed void printing_results(int x, int y) // only used for debugging/monitoring the response of the joystick { Serial.print(x); Serial.print("\t"); Serial.println(y); if (!digitalRead(joyBtn)) {Serial.println(F("Joy Button pressed"));}//if the button is pressed, display "Joy Button pressed" } // motor status montoring function based on XY coordinates void motor_joystick_controlled(int xValue,int yValue) { if(639<xValue && 382<yValue && yValue<639) { MotorAvancer(); Serial.println(F("avancer"));} //only used for debugging/monitoring the state of the motors else if(xValue<382 && 382<yValue && yValue<639) { MotorReculer(); Serial.println(F("reculer"));} //only used for debugging/monitoring the state of the motors else if(382<xValue && xValue<639 && 639<yValue) { MotorDroite(); Serial.println(F("tourner vers la droite"));} //only used for debugging/monitoring the state of the motors else if(382<xValue && xValue<639 && yValue<382) { MotorGauche(); Serial.println(F("tourner vers la gauche"));} //only used for debugging/monitoring the state of the motors else if(639<xValue && 639<yValue) { MotorAvancerDroite(); Serial.println(F("avancer et tourner a droite"));} //only used for debugging/monitoring the state of the motors else if(639<xValue && yValue<382) { MotorAvancerGauche(); Serial.println(F("avancer et tourner a gauche"));}//only used for debugging/monitoring the state of the motors else if(xValue<382 && 639<yValue) { MotorReculerDroite(); Serial.println(F("reculer et tourner à droite"));}//only used for debugging/monitoring the state of the motors else if(xValue<382 && yValue<382) { MotorReculerGauche(); Serial.println(F("reculer et tourner a gauche"));}//only used for debugging/monitoring the state of the motors else { MotorStop(); Serial.println(F("arret totale"));}//only used for debugging/monitoring the state of the motors } // function for forward state void MotorAvancer() { motorG.appliquerImpulsion(motorGZero + Power); motorD.appliquerImpulsion(motorDZero - Power);} // function for backward state void MotorReculer() { motorG.appliquerImpulsion(motorGZero - Power); motorD.appliquerImpulsion(motorDZero + Power);} // function for turn right state void MotorDroite() { motorG.appliquerImpulsion(motorGZero + Power); motorD.appliquerImpulsion(motorDZero + Power);} // function for turn left state void MotorGauche() { motorG.appliquerImpulsion(motorGZero - Power); motorD.appliquerImpulsion(motorDZero - Power);} // function for turn right and forward state void MotorAvancerDroite() { motorG.appliquerImpulsion(motorGZero + Power); motorD.appliquerImpulsion(motorDZero);} // function for turn left and forward state void MotorAvancerGauche() { motorG.appliquerImpulsion(motorGZero); motorD.appliquerImpulsion(motorDZero - Power);} // function for turn right and backward state void MotorReculerDroite() { motorG.appliquerImpulsion(motorGZero - Power); motorD.appliquerImpulsion(motorDZero);} // function for turn left and backward state void MotorReculerGauche() { motorG.appliquerImpulsion(motorGZero); motorD.appliquerImpulsion(motorDZero + Power);} // function for stop state void MotorStop() { digitalWrite(motorDPin,LOW); digitalWrite(motorGPin,LOW);}
Resultados
Uma vez carregado o código no microcontrolador, deve ver na porta série os valores de X e Y a mudar em função da posição do joystick, bem como os estados dos motores correspondentes e a mensagem apresentada quando o botão é premido.
Livraria ServoLib.h
Para utilizar esta biblioteca, é necessário criar e colocar os seguintes ficheiros numa pasta no diretório de bibliotecas do seu software de programação:
Se estiver a programar em ¨Visual Studio Code com PlatformIO IDE, pode encontrar a biblioteca para o seu projeto aqui: DocumentsPlatformIO\Projects\Servo_Joystick_Control\lib\ServoLib
Ficheiro ServoLib.cpp :
//Librairie #include <Servo.h> //Déclaration des constantes #define UPDATE_TIME 15 #define MAX_POS 180 #define MIN_POS 0 //Déclaration des paramètres int servoPin = 9; int pulse = 1500; //Déclaration des variables Servo myServo; // création d'un objet Servo // Sur la plupart des cartes, on peut créer jusqu'à douze objets int pos=0; // variable contenant la position du servomoteur void setup() { myServo.attach(servoPin); } void loop() { for (pos = MIN_POS; pos <= MAX_POS; pos += 1) { myServo.write(pos); delay(UPDATE_TIME); } for (pos = MAX_POS; pos >= MIN_POS; pos -= 1) { myServo.write(pos); delay(UPDATE_TIME); } }
Ficheiro ServoLib.h :
#include <ServoLib.h> ServoLib::ServoLib(){} void ServoLib::associePin(int pin){ servoPin=pin; pinMode(servoPin,OUTPUT); } void ServoLib::envoiePosition(int value){ int pulse=0; if (value<MIN_POS) value=MIN_POS; else if (value>MAX_POS) value=MAX_POS; value=map(value,MIN_POS,MAX_POS,MIN_PULSE_WIDTH,MAX_PULSE_WIDTH); pulse=this->convertirAngleEnImpulsion(value); this->appliquerImpulsion(pulse); } void ServoLib::appliquerImpulsion(int pulse){ digitalWrite(servoPin,HIGH); delayMicroseconds(pulse); digitalWrite(servoPin,LOW); delay(UPDATE_TIME); } int ServoLib::convertirAngleEnImpulsion(int ang){ float a = 2500/180; float b = 500; return int(a*ang+b); }
O ficheiro keyword.txt é opcional. É utilizado para alterar a cor dos nomes das funções no programa.
####################################### # Syntax Coloring Map ServoLib ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### ServoLib KEYWORD1 ServoLib ####################################### # Methods and Functions (KEYWORD2) ####################################### associePin KEYWORD2 envoiePosition KEYWORD2 convertirAngleEnImpulsion KEYWORD2 appliquerImpulsion KEYWORDS2 ####################################### # Constants (LITERAL1) ####################################### UPDATE_TIME LITERAL1 MIN_POS LITERAL1 MAX_POS LITERAL1 MIN_PULSE_WIDTH LITERAL1 MAX_PULSE_WIDTH LITERAL1
Aplicações
- Criar um controlo remoto para acionar o seu microcontrolador
- Utilizar um ou mais joysticks para controlar um braço de robô articulado por servomotores