ABLANA.               

Velocista para Cosmobot 2008. Marzo.



Nombre: Ablana.
Tipo: Velocista, giro con servo.
Objetivo: seguir una línea lo más rápido posible.
Base: coche rc escala 1/28, motor dc, servomotor.
Microcontrolador: PIC18F452 programado en C.
Driver: mosfet smd.
Sensores: CNY70.
Alimentación: 4 pilas AAA.

Ablana fue el segundo robot que preparamos para el Cosmobot 2008 (más acerca del concurso en la descripción de VIT), su diseño se realizó en tan sólo una semana. Teníamos la idea de hacer este robot con un coche a escala 1/28 (un miniz), pero en principio nos decidimos por VIT, escala 1/18 que resultó ser demasiado grande para el radio de curvatura mínimo del circuito.

Un poco desilusionados por la mala elección y sin mucha esperanza en obtener buenos resultados porque sólo quedaba una semana, nos pusimos a montar el miniz, Silent_ (mi compañero de equipo) se lo llevo una tarde, por la noche hizó la placa del pic, y al día siguiente estaba sobre la pista adelantando al 1/18. El resultado de este tipo de coche rc para la prueba de velocistas es muy bueno ya que su radio de giro es pequeño, en el Cosmobot hicimos de media 131 cm/s (nosotros llegamos a medir 140 cm/s ya que utilizabamos el carril exterior).

EL diseño al igual que VIT tiene muchos fallos, tanto en hardware como en el programa. Pero es una escala de la que esperamos poder realizar un velocista competitivo (2 m/s) para próximos concursos, yo ahora mismo me encuentro buscando componentes para su nuevo diseño.

El coche es de la marca MiniZ (modelo RM015), su precio en tienda ronda los 70 euros. Este es un coche que tiene muchos aficionados que compiten de manera casi profesional, por lo que hay muchos upgrades disponibles que poner: piezas de aluminio, distintos motores, suspensiones, tipos de rueda etc... Así que quien se quiera gastar más dinero puede acabar con una base muy buena para un velocista, un coche de estos preparado puede superar los 100 km/h, con 8 km/h nosotros seríamos más que felices.

El coche trae una placa de electrónica, donde lleva el receptor y la etapa de potencia con Mosfet, por lo que no vamos a poder usar nada. Además este modelo de coche la eletrónica del servomotor la lleva también en la placa anterior, por lo que se la tendremos que añadir nosotros, y aquí es donde tuvimos bastante suerte.. ya que le pudimos acoplar la electrónica de un microservo de radiocontrol, que si no lo llegamos a tener probablemente no nos habría dado tiempo a tenerlo preparado para el concurso. Por este motivo es preferible comprar otros coches a escala 1/28 antes que un miniz, por ejemplo los iwaver, que llevan un microservo que podemos cambiar por el que queramos, de esta forma podemos poner un servo más rápido que es fundamental para el giro del coche.

La electrónica es muy sencilla, se tuvo que hacer deprisa y corriendo y no tuvimos tiempo de cambiar nada. La placa de sensores está realizada con CNY70,  los leemos en digital como en los robots anteriores. A diferencia del otro velocista del Cosmobot esta vez no utilizamos un trigger smicht externo (aunque el robot lleva una entrada con trigger externo que no acabamos utilizando), conectamos los sensores a entradas del pic con este trigger interno, y no tuvimos ningún tipo de problema con la luz en el concurso.

Electrónica montada en el coche:



La placa de sensores no la tengo ya que era una de las mias anteriores que perdí, pero Silent me ha mandando la placa que diseño para el PIC.

Esquema placa del pic:



PCB placa del pic:


Como se puede observar en el esquema el diseño es muy sencillo. Los ocho sensores delanteros entran al portD del pic, el cual cuenta con un trigger smichtt interno. Dependiendo de la lectura de estos sensores asignamos una velocidad para el motor y una posición para el servo. El driver del motor va en la misma placa del pic, al igual que en VIT tenemos el error de no utilizar un puente en H (no había tiempo para hacerlo), hemos usado un Mosfet de canal N smd, que aguanta algo más de 3 Amperios, y un diodo smd de potencia para la bobina del motor. En la puerta de este transistor tenemos una señal pwm generada por el módulo CCP1 del pic.

Lleva un trigger smicht externo con una entrada al pic, que no usamos para nada, así que lo mejor es quitarlo. Lo pusimos para leer un sensor trasero para saber cuando el coche estaba centrada con la línea, pero al final no lo acabamos usando. Y en el caso de que se use tampoco es necesario, ya que se puede usar otra entrada del pic con trigger interno.

Un error grave que nos afecto en el concurso fue no haber puesto unos microinterruptores para poder seleccionar distintas opciones del programa una vez que el coche estuviese sobre la pista. No tuvimos tiempo a hacer más, pero esto lo aprendimos bien, desde ahora todos con microinterruptores.

Poco más que comentar sobre este diseño, un diodo led smd que indicaba si la placa tenía alimentación, un diodo D1 de protección (yo no suelo ponerlos) ya que no utilizamos fuente, la alimentación llega directa desde las pilas. Una conexión para ser programado a través de ICD2 y los típicos condensadores para eliminar transitorios. Los trazos azules (pista en el top) son puentes, la placa es de una sola cara. Arriba en donde pone sens está cambiado el - por el +.

Estuvimos probando este circuito durante la semana y no tuvimos ningún problema, así que lo damos por bueno.

Para su diseño usamos Eagle de cadsoft, para mí el mejor soft disponible para diseñar pcbs, tiene en su página web muchas librerias subidas por sus usuarios, por lo que no perderemos tiempo haciendo los footprints de la mayoría de componentes que vayamos a usar, Hay una versión de prueba gratuita que permite hacer placas de hasta 8 cm x10 cm. Quién tenga dudas sobre que soft usar para diseñar circuitos pues recomendarle que pruebe este.

El programa que pongo al final es el que utilizamos en el concurso, teníamos dos, uno con cambios de carril como VIT y otro para correr por el carril de fuera. Como el de cambios de carril no tuvimos casi tiempo de probarlo nos decidimos por usar el que habíamos probado. El programa es lo más simple que se puede hacer, en función del sensor leído se controla la velocidad y el giro del servo. El programa es reciclado de otro robot que teníamos por lo que alguna variables y trozo de código de los que están sobran, pero cuando tienes una semana para hacer un robot y dos programas pues no tuvimos tiempo para más.

Tanto el hard como el programa están probados (muchas vueltas) y funcionan perfectamente.

Para próximos diseños de velocistas vamos a volver a usar esta escala, con una base buena de aluminio, y un buen motor y servo. Respecto al hard y soft no vamos a reutilizar nada de este, la placa de sensores y la del pic van a ser hechas de nuevo, hay que poner microinterruptores. También vamos a usar un puente en H, y el programa es el principal punto donde trabajar y mejorar, ya que es donde menos hemos trabajado y donde más se puede mejorar con un control PD. Pero todo esto para nuestro proyecto de Velocista 2009.

Bueno por último un video del coche funcionando (no tenemos los del concurso)


Para cualquier comentario, duda o corrección...

Gracias por pasar por aquí





Código del pic.


#include <p18f452.h>
#include <timers.h>
#include <delays.h>
#include <pwm.h>

#pragma config WDT=OFF, LVP=OFF, OSC=HS

void inter (void);        // declaración de la función de la interrupción
void inic (void);

#pragma code high_vector=0x08        // para que coloque este código en la
                    // dirección a la que apunta el vector de interrupción
void interrupt_at_high_vector(void)
{
    _asm GOTO inter _endasm
}

#pragma code        // vuelvo a colocar el código en el sitio normal


//Declaracion de variables globales

int contador=0;

/********** Definiciones *******/

//definimos las variables de PWM y sentido de los motores
#define motor1PWM PORTCbits.RC2

#define servo1 PORTCbits.RC1

//sensores delantero
#define L0 PORTDbits.RD4
#define L1 PORTDbits.RD5
#define L2 PORTDbits.RD6
#define L3 PORTDbits.RD7
#define R0 PORTDbits.RD3
#define R1 PORTDbits.RD2
#define R2 PORTDbits.RD1
#define R3 PORTDbits.RD0

//sensor trasero

#define TRASERO PORTCbits.RC0

//400
#define vel0f 390
#define vel1f 375
#define vel2f 365
#define vel3f 355
#define vel4f 350
#define vel5f 340
#define vel6f 330 
#define vel7f 320 

//Valores servomotor.

#define posR3 930
#define posR2R3 1010
#define posR2 1074
#define posR1R2 1138
#define posR1 1202
#define posR0R1 1266
#define posR0 1330      //1335
#define posL0R0 1350    // centro servo
#define posL0 1370      //1365
#define posL1L0 1430
#define posL1 1490
#define posL2L1 1550
#define posL2 1610
#define posL3L2 1670
#define posL3 1730

#define desgaste 30
//Variables

unsigned int duty1=posL0R0 ; //marca que el tiempo por defecto sea centrado

unsigned int vel, vel0, vel1, vel2, vel3, vel4, vel5, vel6, vel7, velocidad=0;

enum {D1, NO_D1, RESTO} estado; //variable de estado para saber en qeu estado esta el contador

//Programa principal

void main()
    {
    int resultado=0; //resetear estas variables
    inic();
 
//inicializanción  de variables

    velocidad= 120;  //100
    vel0= vel0f+velocidad;
    vel1= vel1f+velocidad;
    vel2= vel2f+velocidad;
    vel3= vel3f+velocidad;
    vel4= vel4f+velocidad;   
    vel5= vel5f+velocidad;
    vel6= vel6f;
    vel7= vel7f;

    SetDCPWM1(vel0);

//Bucle principal

    while(1)
        {
    resultado=0;

//Leer sensores si no hay cambio de carril obligado.

/*****************************************************************/

    if (L3==0)
        resultado+=160;

    if (L2==0)
        resultado+=40;

    if (L1==0)
        resultado+=10;

    if (L0==0)
        resultado+=3;

    if (R0==0)
        resultado+=1;

    if (R1==0)
        resultado+=5;

    if (R2==0)
        resultado+=20;

    if (R3==0)
        resultado+=80;



//********************************SWITCH*****************************


    switch(resultado)
    {
        case 160:     //l3   
        {
            duty1=posL3;
//        if (TRASERO==0)
//            SetDCPWM1(vel7);
//        else
            SetDCPWM1(vel7);
        }break;
/*******************************************************************/
        case 200:        //l3l2
        {
            duty1=posL3L2;
//        if (TRASERO==0)
//            SetDCPWM1(vel6);
//        else
            SetDCPWM1(vel6);
        }break;
/*******************************************************************/
        case 40:        //l2
        {
            duty1=posL2;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel5);
        }break;
/*******************************************************************/
        case 50:        //l2l1
        {
            duty1=posL2L1;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel4);
        }break;
/*******************************************************************/
        case 10:        //l1
        {
            duty1=posL1;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel3);
        }break;
/*******************************************************************/
        case 13:        //l1l0
        {
            duty1=posL1L0;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel2);
        }break;
/*******************************************************************/
        case 3:        //l0
        {   
            duty1=posL0;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel1);
        }break;
/******************************************************************/
        case 4:        //l0r0
        {
            duty1=posL0R0;
            SetDCPWM1(vel0);
        }break;
/*******************************************************************/
        case 1:        //r0
        {
            duty1=posR0;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel1);
        }break;
/*******************************************************************/
        case 6:        //r0r1
        {
            duty1=posR0R1;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel2);
        }break;
/*******************************************************************/
        case 5:        //r1
        {
            duty1=posR1;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel3);
        }break;
/*******************************************************************/
        case 25:        //r1r2
        {
            duty1=posR1R2;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel4);
        }break;
/*******************************************************************/
        case 20:        //r2
        {   
            duty1=posR2;
//        if (TRASERO==0)
//            SetDCPWM1(vel0);
//        else
            SetDCPWM1(vel5);
        }break;
/*******************************************************************/
        case 100:    //r2r3   
        {
            duty1=posR2R3;
//        if (TRASERO==0)
//            SetDCPWM1(vel6);
//        else
            SetDCPWM1(vel6);
        }break;
/*******************************************************************/
        case 80:    //r3   
        {
            duty1=posR3;
//        if (TRASERO==0)
//            SetDCPWM1(vel7);
//        else
            SetDCPWM1(vel7);
        }break;

//*********************************************************************************
   
        default:break;
    }//end de switch principal
    }//end while(1)
    }//end main

//******************************FUNCIONES*******************************************
void inic(void)
{//Funcion inicializacion
//configuración de los puertos, declarando como salida los pines necesarios
      TRISA=  0b11111111; // Todo el puerto A como entradas (sensores traseros)
      TRISB = 0b11101001;        // pongo todo el puerto B como salidas (ICD y LED)
      TRISC = 0b11111001;    // Pulsador y microruptores como entradas. PWM y servo salidas
    TRISD = 0b11111111;    // Todo el puerto D como entrada (sensores delanteros)
      TRISE = 0b00000111;  // Puerto E como entradas  (sensores traseros)

 
    ADCON1 = 0b00001111; //todo puerto A como digital4es.


  OpenTimer0( TIMER_INT_ON & T0_16BIT & T0_SOURCE_INT & T0_PS_1_4 );
        // inicializo el timer0 como timer de 16 bits, preesc 1:4 y
        // habilitando la interrupción

// inicializo pwms con periodo máximo
    OpenPWM1(255);

    servo1=1;                // inicializo las salidas
    estado=D1;                // inicializo el estado
    WriteTimer0(0xffff-duty1); //precargo el timer

    // se establecen los valores de los pwms
    vel=275;
    SetDCPWM1(vel);

    INTCONbits.GIEL=0;            //Deshabilito todas las interrupciones -creo-
    INTCONbits.GIEH=0;
    INTCONbits.PEIE=0;            // deshabilito interrupción de periféricos
    INTCON2bits.TMR0IP = 1;        //Para habilitar la prioridad alta en el tmr0?
    INTCONbits.GIEH=1;            // habilito interrupciones globales


}

#pragma interrupt inter //coloco la funcion de la interrupción en su sitio

void inter(void)
{
    INTCONbits.GIEH=0;  //Deshabilito interrupciones altas
    INTCONbits.TMR0IF=0;        //bajo el flag de la interrupción

    switch (estado)
    {
        case D1:
            servo1=1;
            WriteTimer0(0xffff-3125+duty1); //temporizo los 2,5ms correspondientes a este servo
                                            // menos el tiempo de pulso
            estado=NO_D1;
            break;
        case NO_D1:
            servo1=0;
            WriteTimer0(0xffff-(duty1));        //temporizo el resto de los 2,5 ms que no son tiempo
                                            //de pulso (3125-duty1)
            estado=RESTO;
            break;
        case RESTO:
            servo1=0;
            WriteTimer0(0xffff-21875);        //resto hasta los 20 ms (25000-3125=21875)
            estado=D1;
    }
    INTCONbits.GIEH=1;  //Deshabilito interrupciones altas
}