You Have the Control – Estación de carga de gamepads motorizada

Proyecto realizado por Grupo 3: Sahar Yousefi para la asignatura de Diseño de Sistemas Empotrados.

Proyecto

You Have the Control es una estación de carga motorizada para gamepads.

Al acercarse el usuario, los mandos se mueven para poder ser recogidos con mayor facilidad. Al colocarlos de vuelta, las plataformas que sujetan los mandos se retraen y los cables de carga magnéticos se conectan automáticamente a los mandos.

Materiales

Arduino Uno R311,50€
Cables2€
2 servos SG905€
4 pulsadores switch2€
Registro de desplazamiento 74HC595 0,32€
Mini placa de prototipo1,30€
8 LEDs0,40€
Sensor ultrasónico SR042,70€
Filamento PETG y PLA10€
12 resistencias0,45€
TOTAL35,67€

Diseño de hardware

Todo el diseño es original, diseñado desde cero para el proyecto.

El prototipo inicial y simulación del circuito se realizó en TinkerCad antes de ser implementado con componentes reales.

Los servos están conectados a la placa Arduino Uno directamente, al usar sólo dos servos no hace falta una placa driver dedicada. Se utilizan dos pulsadores como detectores de fin de carrera para cada servo, con resistencias para obtener señales HIGH y LOW que pudiera leer el Arduino y poder ser utilizadas en el código de control.

El medidor de ultrasonidos se encarga de detectar objetos a menos de 25cm delante de la plataforma para activar los servos.

Para la iluminación, hizo falta utilizar un registro de desplazamiento conectado a través de una mini placa de prototipo. Al tener que manejar 8 LEDs, los pines disponibles en el Arduino no eran suficientes. Los LEDs están cableados en paralelo para poder activarlos de forma individual a través del registro de desplazamiento. El registro de desplazamiento y todo el cableado de los leds se externalizó en una mini placa de prototipo conectada al Arduino y se ocultó bajo la base.

Diseño de piezas

Todas las piezas han sido impresas con impresora 3D.

Se ha utilizado el siguiente modelo de actuador lineal basado en servo de Thingiverse:

https://www.thingiverse.com/thing:3170748

El resto de piezas han sido diseñadas desde cero en Microsoft 3D Builder.

Problemas encontrados

Servos sin rotación continua

El primer problema fue que los servos sólo giran 180 grados, y necesitamos que giren de forma continua para poder usarlos como actuadores lineales.

Los servos de rotación continua son más caros y difíciles de conseguir, así que se decidió modificar los servos SG90 para convertirlos en rotación continua.

Para ello, hay que abrir el servo y cortar la punta del eje que entra en el potenciómetro. Así el servo no recibe información sobre su posición, y girará continuamente dependiendo del ángulo que se envíe.

Una vez hecha la modificación, hay que encontrar el ángulo neutro del servo. Suele estar en los 90 grados, pero cada servo tiene una calibración un poco diferente. Para ello, se hizo un pequeño programa que fuese mandando ángulos de 0 a 180 y así poder encontrar el ángulo en el que el servo deja de moverse.

Para controlar la velocidad de rotación, cuanto más cercano sea el ángulo al neutro, más despacio se moverá el servo. Por ejemplo, si el ángulo neutro es 90, enviando 91 el servo se moverá muy despacio, 92 más rápido, y normalmente mandando 95 en adelante se mueve a la máxima velocidad.

Para que el servo gire en dirección contraria, se envían ángulos por debajo del ángulo neutro.

Sistema de parada de servos

Al convertir los servos a rotación continua, no tenemos información sobre la posición del servo, así que hizo falta pensar una manera de saber cuándo el servo debía parar al terminar el recorrido de cada plataforma.

Se decidió utilizar pulsadores como detectores de fin de carrera, con resistencias para conseguir señalas altas y bajas como inputs.

Resistencia de materiales

La mayoría de las piezas han sido impresas en plástico PETG, pero una vez montado, se descubrió que las cremalleras eran demasiado flexibles, por lo que se doblaban al estar extendidas con el peso de los mandos. Se decidió imprimir de nuevo las cremalleras en plástico PLA, que es más rígido, solucionando así el problema.

Errores de detección de objetos

Durante las pruebas del medidor de ultrasonidos, se obtenían mediciones muy erráticas de la distancia, que dificultaba poder detectar un objeto frente a la plataforma de forma consistente.

Investigando posibles soluciones, se encontró la librería NewPing https://bitbucket.org/teckel12/arduino-new-ping/wiki/Home que soluciona la mayoría de problemas con los sensores de ultrasonidos baratos.

Mejoras futuras

La idea original era que los mandos estuviesen custodiados por un dragón, que abriese las alas a la vez que los mandos se movieran. Pero no hubo tiempo para diseñar el modelo 3D del dragón, imprimirlo y montarlo.

Código

#include <Servo.h>
#include <NewPing.h>

// servos
#define BOTTOM_SERVO_PIN 8;
#define BOTTOM_SERVO_STOP_ANGLE 87;
#define BOTTOM_SERVO_FORWARD_ANGLE 92;
#define BOTTOM_SERVO_BACKWARD_ANGLE 84;
Servo bottomServo;

#define TOP_SERVO_PIN 12;
#define TOP_SERVO_STOP_ANGLE 89;
#define TOP_SERVO_FORWARD_ANGLE 85;
#define TOP_SERVO_BACKWARD_ANGLE 93;
Servo topServo;

// ultrasonic
#define ULTRASONIC_TRIG_PING = 9;
#define ULTRASONIC_ECHO_PING = 10;
unsigned int distance = 0;
boolean objectDetected = false;
NewPing sonar(ULTRASONIC_TRIG_PING, ULTRASONIC_ECHO_PING, 150);

// buttons
#define BOTTOM_SERVO_RETRACTED_BUTTON_PIN 2;
#define BOTTOM_SERVO_EXTENDED_BUTTON_PIN 3;
boolean bottomServoRetracted = false;
boolean bottomServoExtended = false;
#define TOP_SERVO_RETRACTED_BUTTON_PIN 4;
#define TOP_SERVO_EXTENDED_BUTTON_PIN 5;
boolean topServoRetracted = false;
boolean topServoExtended = false;

// lights
#define SHIFT_REGISTER_DATA_PIN 7;
#define SHIFT_REGISTER_LATCH_PIN 6;
#define SHIFT_REGISTER_CLOCK_PIN 13;
#define LIGHT_DELAY 100
unsigned int currentLight = 0;
boolean enableLights = false;
boolean reverseLightDirection = false;

void ledWrite(int Led)
{
  shiftOut(SHIFT_REGISTER_DATA_PIN, SHIFT_REGISTER_CLOCK_PIN, LSBFIRST, Led);
  digitalWrite(SHIFT_REGISTER_LATCH_PIN, HIGH);
  digitalWrite(SHIFT_REGISTER_LATCH_PIN, LOW);
}

void setup()
{
  Serial.begin(115200);
  bottomServo.attach(BOTTOM_SERVO_PIN);
  topServo.attach(TOP_SERVO_PIN);
  bottomServo.write(BOTTOM_SERVO_STOP_ANGLE);
  topServo.write(TOP_SERVO_STOP_ANGLE);

  pinMode(BOTTOM_SERVO_RETRACTED_BUTTON_PIN, INPUT);
  pinMode(BOTTOM_SERVO_EXTENDED_BUTTON_PIN, INPUT);
  pinMode(TOP_SERVO_RETRACTED_BUTTON_PIN, INPUT);
  pinMode(TOP_SERVO_EXTENDED_BUTTON_PIN, INPUT);

  pinMode(ULTRASONIC_TRIG_PING, OUTPUT);
  pinMode(ULTRASONIC_ECHO_PING, INPUT);

  pinMode(SHIFT_REGISTER_DATA_PIN, OUTPUT);
  pinMode(SHIFT_REGISTER_LATCH_PIN, OUTPUT);
  pinMode(SHIFT_REGISTER_CLOCK_PIN, OUTPUT);
}

void loop()
{
  delay(50);
  distance = sonar.ping_cm();
  bottomServoRetracted = digitalRead(BOTTOM_SERVO_RETRACTED_BUTTON_PIN) == HIGH;
  bottomServoExtended = digitalRead(BOTTOM_SERVO_EXTENDED_BUTTON_PIN) == HIGH;
  topServoRetracted = digitalRead(TOP_SERVO_RETRACTED_BUTTON_PIN) == HIGH;
  topServoExtended = digitalRead(TOP_SERVO_EXTENDED_BUTTON_PIN) == HIGH;

  // only check for objects when servo is not moving
  if (bottomServoRetracted || bottomServoExtended)
  {
    if (25 > distance && distance != 0)
    {
      objectDetected = true;
    }
    else if (distance != 0)
    {
      objectDetected = false;
    }
  }

  // bottom servo control
  if (!bottomServoExtended && objectDetected == true)
  {
    bottomServo.write(BOTTOM_SERVO_FORWARD_ANGLE);
    reverseLightDirection = false;
    enableLights = true;
  }
  else if (bottomServoExtended && objectDetected == true)
  {
    // wait 4 seconds when extended to let user pick up gamepads
    // also turn on all the lights
    ledWrite(B11111111);
    currentLight = 0;
    delay(4000);
  }
  else if (!bottomServoRetracted && objectDetected == false)
  {
    bottomServo.write(BOTTOM_SERVO_FORWARD_ANGLE);
    // lights go in reverse direction when retracting
    reverseLightDirection = true;
    enableLights = true;
  }
  else
  {
    bottomServo.write(BOTTOM_SERVO_STOP_ANGLE);
    // restart the lights and turn off
    currentLight = 0;
    enableLights = false;
  }

  // top servo control
  if (!topServoExtended && objectDetected == true)
  {
    topServo.write(TOP_SERVO_FORWARD_ANGLE);
  }
  else if (!topServoRetracted && objectDetected == false)
  {
    topServo.write(TOP_SERVO_BACKWARD_ANGLE);
  }
  else
  {
    topServo.write(TOP_SERVO_STOP_ANGLE);
  }

  // light control
  if (enableLights == true)
  {
    if (reverseLightDirection)
    {
      ledWrite(B11000000 >> currentLight);
    }
    else
    {
      ledWrite(B00000011 << currentLight);
    }

    delay(LIGHT_DELAY);

    currentLight++;

    if (currentLight > 8)
      currentLight = 0;
  }
  else
  {
    ledWrite(B00000000);
  }
}

Vídeo explicativo

Memoria

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *