Site icon AranaCorp

Controlo de servomotores com um joystick Arduino

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

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:

Pour Arduino IDE.

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

Referências

Exit mobile version