viernes, 22 de noviembre de 2013

Control inalámbrico de un “hidrodeslizador” mediante Processing, Arduino y módulos Xbee.



Control II
Ingeniería Electrónica.
Universidad del Magdalena.
Ciro Rueda, Jose Gutierrez, Holman Ortega

Resumen del proyecto.

Para este proyecto se realizó una interfaz básica de usuario en Processing, mediante la cual se puede controlar el movimiento de nuestro hidrodeslizador, la comunicación inalámbrica entre el PC y el bote se lleva a cabo mediante comunicación serial por medio de un par de módulos Xbee configurados para comunicación punto a punto.

Que es un hidrodeslizador?

Un hidrodeslizador o bote pantanero, como algunos la conocerán es una embarcación impulsada únicamente por el flujo de aire provocado por una gran hélice, la dirección de un hidrodeslizador, normalmente corre por cuenta de unos timones que direccionan dicho flujo de aire, cumpliendo con estas premisas básicas se construyó nuestro modelo de hidrodeslizador, teniendo en cuenta un compartimiento para ubicar la electrónica del proyecto.

Prototipo de hidrodeslizador usado en el proyecto.

Control del hidrodeslizador.

Para el control de la dirección se planeó usar un controlador PID encargado de  la posición de los timones, por medio de un potenciómetro acoplado mecánicamente a un motor DC. 

Diagrama de control de posición por PID

Sin embargo debido a las características del sistema físico a controlar, que en este caso se trataría de  un sistema tipo 1 se encontró que para el control de este tipo de sistemas y para este en particular basta con a constante proporcional del controlador, es decir un controlador proporcional. Debido a esto la mejor opción, a nivel estructural de eficiencia y practicidad fue implementar un servomotor de modelismo, puesto que estos poseen el sistema de potenciómetro, motor y sistema de control proporcional en un montaje estándar, fácil y rápido de implementar, para más información del funcionamiento y la estructura interna de un servomotor pueden dirigirse a aquí ó aquí.

Micro servo Tower Pro usado en e proyecto


Para la propulsión se usó un motor brushless usado en aeromodelismo, el cual al tratarse de un motor AC, necesita su propio circuito de control y alimentación, en ultimas gracias a dicho sistema de control este motor puede implementarse de igual forma que un servomotor  común. Véase.

Motor brushless Turnigy empleado en el proyecto

Para el control de este “par de servos” se usó un Arduino Uno el cual activara y cambiara la señal de control de los mismos dependiendo de los datos que reciba a través de su puerto serial.

Tarjeta de desarrollo Arduino Uno

El encargado de enviar los datos mencionados anteriormente no es más que una pequeña interfaz en Processing, la cual transmite las pulsaciones de una serie de teclas  hechas por el usuario hacia el Arduino uno que es el encargado de traducirlas a comandos de control.

Interfaz implementada en Processing
Para lograr la transmisión inalámbrica de los datos, como se mencionó anteriormente se usaron un par de modulos Xbee configurados en comunicación punto a punto, uno de ellos conectado por usb al pc mediante una tarjeta Xbee Explorer, y el otro acoplado al Arduino uno mediante una tarjeta Xbee Shield.

Modulo Xbee

Tarjeta Xbee Explorer

Tarjeta Xbee Shield
Processing.

La idea de la interfaz en Processing no es más que enseñarle al usuario los controles para el manejo del hidrodeslizador y transmitir las acciones del usuario mediante el puerto serial.

Hay que tener en cuenta que la interfaz, no debe ser muy llamativa ni presentar demasiada información al usuario, puesto que este debe concentrarse principalmente en la posición y trayectoria actual del bote, partiendo de tales consideraciones y objetivos se creó el siguiente código:

import processing.serial.*;
Serial myPort;
PImage A;//Imagen para la tecla A
PImage D;//Imagen para la tecla D
PImage W;//Imagen para la tecla W
PImage M;//Imagen para la tecla M
PImage K;//Imagen para la tecla K
PImage Ap;//Imagen para la tecla A presionada
PImage Dp;//Imagen para la tecla D presionada
PImage Wp;//Imagen para la tecla W presionada
PImage Mp;//Imagen para la tecla M presionada
PImage Kp;//Imagen para la tecla K presionada
int v;


void setup() {
  background(0);//fondo de interfaz en negro
  size(290, 420);//escala de la interfaz
  v=1;//Se establece una velocidad inicial para el Brushless
  //Se cargan las imagenes correspondientes a cada tecla
  A=loadImage("A.png");
  D=loadImage("D.png");
  W=loadImage("W.png");
  K=loadImage("K.png");
  M=loadImage("M.png");
  //Se cargan las imagenes correspondientes a cada pulsación
  Ap=loadImage("Apress.png");
  Dp=loadImage("Dpress.png");
  Wp=loadImage("Wpress.png");
  Mp=loadImage("Mpress.png");
  Kp=loadImage("Kpress.png");
  //Se dibuja en pantalla cada tecla en un arreglo vertical
  image(A, 10, 100, 60, 60);
  image(W, 10, 20, 60, 60);
  image(M, 10, 340, 60, 60);
  image(D, 10, 180, 60, 60);
  image(K, 10, 260, 60, 60);
  //Se pon un rotulo junto a cada tecla para notificar
  // al usuario sobre la funcion de cada una.
  textSize(32);
  fill(0, 102, 153);
  text("Avanza!!", 90, 60);
  text("Derecha!!", 90, 140);
  text("Izquierda!!", 90, 220);
  text("Acelera!!", 90, 300);
  text("Desacelera!!", 90, 380);
  //Se inicializa la comunicacion serial
  myPort = new Serial(this, Serial.list()[0], 9600);
}

//Se detecta cuando alguna de las
//teclas designadas se ha presionado
//cuando esto sucede se representa la pulsacion y
//se envia el caracter correspondiente a la tecla 
//por el puerto serial, el caracter se envia en mayusculas 
//para poder identificar que la tecla ha sido pulsada
void keyPressed() {

  if (key=='a') {

    myPort.write(65);
    image(Ap, 10, 100, 60, 60);
  }

  if (key=='w') {

    myPort.write(87);
    image(Wp, 10, 20, 60, 60);

    println(v);
    myPort.write("+v+");
  }

  if (key=='m') {

    //para las teclas k y m no se envian datos cuando son 
    //presionada, unicamente cuando se sueltan dichas teclas
    image(Mp, 10, 340, 60, 60);
  }

  if (key=='d') {

    myPort.write(68);
    image(Dp, 10, 180, 60, 60);
  }

  if (key=='k') {

    image(Kp, 10, 260, 60, 60);
  }
}

//Se detecta cuando alguna de las teclas designadas se ha 
//soltado cuando esto sucede se vuelve a la imagen de tecla por 
//defecto y se envia el caracter correspondiente a la tecla 
//por el puerto serial, el caracter se envia en minusculas 
//para poder identificar que la tecla ha sido soltada
void keyReleased() {

  if (key=='a') {

    myPort.write(97);
    image(A, 10, 100, 60, 60);
  }

  if (key=='w') {

    myPort.write(119);
    image(W, 10, 20, 60, 60);
  }

  if (key=='m') {//cada que se suelta m la velocidad 
    //actual se reduce, siempre que sea mayor que 1, 
    //ademas se envia el dato de la nueva velocidad.

    myPort.write(109); 
    image(M, 10, 340, 60, 60);
    if (v>1) {

      v=v-1;
      println(v);
      myPort.write("+v+");
    }
  }

  if (key=='d') {

    myPort.write(100); 
    image(D, 10, 180, 60, 60);
  }
  if (key=='k') {//cada que se suelta k la velocidad 
    //actual aumenta, siempre que sea menor que 9, 
    //ademas se envia el dato de la nueva velocidad.

    image(K, 10, 260, 60, 60);
    if (v<9) {

      v=v+1;
      println(v);
      myPort.write("+v+");
    }
  }
}


Algo que hay que tener en cuenta para este código es que si bien se podían enviar todos los datos en una sola trama, se prefirió enviar los datos de manera independiente, sin embargo solo cuando se produzca un evento en el teclado, lo cual se puede traducir en una mayor eficiencia en la comunicación, además de una mayor facilidad para la determinación y resolución de problemas.

Arduino.

Quien realiza la mayor parte de trabajo es nuestra placa de Arduino, pues tiene que tomar los datos enviados por Processing vía serial e interpretarlos para el proceso de control, debido a que la transmisión se lleva a cabo únicamente cuando ocurre un evento de teclado en el pc, será muy difícil que se sature el buffer del módulo receptor  Xbee o de la placa Arduino, por lo que bien podemos dejar la tarjeta leyendo datos constantemente o esperando hasta recibir un dato por el puerto, sin embargo se recomienda leer el puerto solo cuando hay datos disponibles en el buffer, con el fin de ahorrar batería. Lo cual es mucho más indispensable en el receptor que en el transmisor puesto que el primero funciona con baterías, mientras que el último depende de la alimentación del puerto USB del PC.
Según o anterior el código para la placa Arduino fue el siguiente:

#include <Servo.h>//se incluye la libreria servo para controlar
//la direccion y la velocidad


Servo motor;//se define una variable servo para el Brushless
Servo paletas;//se define la variable servo para la direccion
int v;//se declara una variable temporal para el valor de velocidad
//puesto que el dato de velocidad es enviado solo cuando ocurre un
//cambio en esta.
int w;//se declara una variable temporal para la accion de avance.

char rx=' ';//se declara una variable temporal para almacenar
//el dato entrante y poder evaluarla.

void setup(){ 
  Serial.begin(9600);//se inicializa la comunicacion serial.

  //se declaran los pueros correspondientes a las señales de
  //control de direccion y velocidad.
  motor.attach(6);
  paletas.attach(9);

void loop(){

  if (Serial.available()) {//se procede solo si hay datos en el buffer del puerto

    rx = Serial.read();//se lee el dato recibido y se almacena temporalmente

    //si el dato es D se mueven los timones hacia la derecha (servo en 30 grados)   
    if(rx=='D'){
      paletas.write(30);
    }

    //si el dato es d o a se mueven los timones hacia el centro (servo en 100 grados)
    if(rx=='d'||rx=='a'){
      paletas.write(100);
    }

    //si el dato es d o a se mueven los timones hacia a izquierda(servo en 170 grados) 
    if(rx=='A'){
      paletas.write(170);
    }

    //si el dato esta comprendido entre 1 y 9 significa que es un dato de velocidad, por lo que segun que valor ha llegado
    //se le asigna un valor en milisegundos a la señal de contro del motor brushless
    if(rx=='1'){ 
      v=1600;  
    }
    if(rx=='2'){
      v=1650;
    }
    if(rx=='3'){
      v=1700;
    }
    if(rx=='4'){
      v=1750;
    }
    if(rx=='5'){
      v=1800;
    }
    if(rx=='6'){
      v=1850;
    }
    if(rx=='7'){
      v=1900;
    }
    if(rx=='8'){
      v=1950;
    }
    if(rx=='9'){
      v=2000;
    }

    //si el dato es W significa que el usuario 
    //ha oprimido W con el fin de hacer avanzar el bote
    //esto se almacena como una variable temporal con valor de 1
    //si por el contrario es w(minuscula) significa que el usuario 
    //ha soltado la tecla y por consiguiente el bote ha de detenerse
    if(rx=='W'){
      w=1;
    }
    if(rx=='w'){ 
      w=0;
    }

    if(w==1){//si la variable de avance es 1 se escribe el valor correnspondiente
      //a la velocidad actual en la señal de control del brushless
      motor.writeMicroseconds(v);
    }
    if(w==0){//si la variable de avance es 0 se escribe 1500ms para la duracion del
      //pulso de control, lo que se traduce en detener el motor brushless
      motor.writeMicroseconds(1500);
    }
    delay(10);//un periodo de espera para estabilidad de la lectura y control
  }
}

Conclusiones.

Con la implementación de estos códigos y la estructura de control establecida, se logró el control inalámbrico del hidrodeslizador, de una manera fácil práctica, y sobre todo eficiente tanto energéticamente como a nivel de comunicaciones, además se consiguió que este proyecto sea fácil de comprender y re-implementar en un proyecto a mayor escala, esto último debido principalmente a las herramientas de software y hardware libre implementadas tales como Arduino y Processing.


No hay comentarios:

Publicar un comentario