Vistas de página en total

lunes, 27 de abril de 2020

Control MacroRail

MacroRail Scopos

   He recibido mensajes de cantidad de personas que me piden como realizar el control para mi macroRail que tengo en Thingiverse. 
   


   Por ello he decidido añadir esta entrada para enlazar con Thigiverse y completar el proyecto.
   
    Para empezar podemos hablar de como lo controlaríamos. En este caso el sistema es muy sencillo, se trata, en el modelo más completo de dos motores stepper. Uno para el giro del plato (opcional), y otro para el avance del macrorail.
     El macroRail lleva un final de carrera para realizar el home inicial, pero el giro del plato no lo necesita.
     Para el control de la cámara necesitaremos una salida optoacoplada con un cable a un jack macho.
    Y para la comunicación con el smartphone, una tarjeta bluetouth HC-06.

HADWARE

   El control, para no tener que realizar placas específicas lo he realizado con un arduino uno y una tarjeta CNC Shield.


    Ya solo queda saber como se conecta el jack de la cámara. Al menos para una Canon.
     Como optoacoplador podréis usar cualquiera, construyendo una pequeña placa, según este esquema.


 Pero para los que no quieran soldar y fabricar placas podréis usar esta. 

             dst-1r4p
Con este esquema



Para poder comunicar sin cables al smartphone, basta con montar un bluetouth conectado al puerto serie de la tarjeta. Tened en cuenta que este puerto es el mismo que el de carga y programación, por lo que no se puede tener el bluetouth conectado mientras se programa el arduino. 
   Eso no es problema porque se puede desconectar el bluetouth antes de recargar el gbrl.
  Aquí tenéis las conexiones entre el modulo bluetouth y la tarjeta CNC Shield.
     

Software

    Para el control usaré en este caso el gbrl v1.1. Yo particularmente prefiero usar código propio, pero entiendo que para muchos es más fácil acoplar un código libre. Por eso diseñe el sistema para que pudiera trabajar con esta tarjeta y el gbrl.
    Usar un código propio en Arduino te permitirá realiza la secuencia dentro del arduino y poder incluso comandarlo por web desde el explorador. Pero eso exige mucho más nivel de programación. Así que partiré de algo más simple.
   De esta forma no hace falta, aunque podemos, realizar un control específico en el arduino. Dejaremos, por lo tanto todo el control a una aplicación en alto nivel, que se puede realizar con cualquier lenguaje C,Java, Basic, o phyton.
   El gbrl lo bajaremos de github .   Dentro de este arduino instalaremos el GBRL 1.1
          https://github.com/gnea/grbl
Lo descargamos a un directorio propio como macroRail y lo descomprimimos.
No necesitamos todo lo que hay en el directorio, y de hecho no lo vamos a colocar en la librería de arduino, ya que lo prefiero separado para poder editarlo en el futuro si lo neceisto. Yo prefiero tomar el fichero gbrlupload de ..gbrl\examples\grblUpload y copiarlo en el raiz gbrl. 
   Luego lo renombramos de gbrlUpload a gbrl. y eliminamos la carpeta examples que ya no necesitamos.
   Hecho esto podremos abrir directamente el fichero gbrl del directorio principal y cargarlo en el arduino.
   Pero antes de compilarlo y transferirlo debemos cambiar en el #include los <> por ".
   Hecho esto podemos abrir el fichero de configuración default.h y editar las características de nuestro equipo. 
   Si hemos usado un husillo, como el que yo he usado de 2mm por vuelta de paso, y un driver puesto a 16 micropasos con un motor de 200 pasos por vuelta deberemos colocar como he puesto el número de step en cada punto.
    El eje X será usado para el avance del macroRail, y el eje Y para el giro de la mesa.
     Así habrá que dar 1600 pulsos por milimetro de avance del carro, lo que nos dará una precisión de 1/1600 = 0.6um. Esto con drivers de 1/16. con drivers 1/32 deber´na ser 3200 paso por mm.
     En el giro,vamos a hacer un truco, y es engañar al sistema. Aunque el sistema considera mm, vamos a hacer algo para considerarlo grados.      
    Sabemos que 200*16 pulsos son 360 grados, por lo que pondremos 3200, considerando que lo que vamos a indicar al sistema son vueltas completas. En caso de 32 micropasos pondremos 6400.
     Podríamos usar conversión a grados, pero 3200/360 no sale un número de pasos entero, por lo que es mejor considerar vueltas completas como medida base. Así,simplemente, el valor enviado en el eje Y deberán ser expresadas en decimales de vueltas. Por ejemplo, 10 grados son 10/360=0,027 que es lo que enviaríamos al sistema como parámetro Y.
     Ejemplo. para avanzar 10 um y girar 30 grados (30/360=0.0833) la mesa enviamos enviaríamos al gcode 
       Ejemplo:  G1X0.010Y0.0833.


Configuración

      Deberemos activa la busqueda de cero, el mantenimiento del encendido de los motores y algunos parámetros más, como la definición de los pasos de cada motor. Para todo ello editamos el default.h y lo modificamos así:

 #define DEFAULT_X_STEPS_PER_MM 1600.0
  #define DEFAULT_Y_STEPS_PER_MM 3200.0
  #define DEFAULT_Z_STEPS_PER_MM 250.0
  #define DEFAULT_X_MAX_RATE 500.0 // mm/min
  #define DEFAULT_Y_MAX_RATE 500.0 // mm/min
  #define DEFAULT_Z_MAX_RATE 500.0 // mm/min
  #define DEFAULT_X_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
  #define DEFAULT_Y_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
  #define DEFAULT_Z_ACCELERATION (10.0*60*60) // 10*60*60 mm/min^2 = 10 mm/sec^2
  #define DEFAULT_X_MAX_TRAVEL 70.0 // mm NOTE: Must be a positive value.
  #define DEFAULT_Y_MAX_TRAVEL 365.0 // mm NOTE: Must be a positive value.
  #define DEFAULT_Z_MAX_TRAVEL 200.0 // mm NOTE: Must be a positive value.
  #define DEFAULT_SPINDLE_RPM_MAX 1000.0 // rpm
  #define DEFAULT_SPINDLE_RPM_MIN 0.0 // rpm
  #define DEFAULT_STEP_PULSE_MICROSECONDS 10
  #define DEFAULT_STEPPING_INVERT_MASK 0
  #define DEFAULT_DIRECTION_INVERT_MASK 1
  #define DEFAULT_STEPPER_IDLE_LOCK_TIME 255 // msec (0-254, 255 keeps steppers enabled)
  #define DEFAULT_STATUS_REPORT_MASK 1 // MPos enabled
  #define DEFAULT_JUNCTION_DEVIATION 0.01 // mm
  #define DEFAULT_ARC_TOLERANCE 0.002 // mm
  #define DEFAULT_REPORT_INCHES 0 // false
  #define DEFAULT_INVERT_ST_ENABLE 0 // false
  #define DEFAULT_INVERT_LIMIT_PINS 0 // false
  #define DEFAULT_SOFT_LIMIT_ENABLE 0 // false
  #define DEFAULT_HARD_LIMIT_ENABLE 1  // false
  #define DEFAULT_INVERT_PROBE_PIN 0 // false
  #define DEFAULT_LASER_MODE 0 // falseA
  #define DEFAULT_HOMING_ENABLE 1 // false
  #define DEFAULT_HOMING_DIR_MASK 1 // move positive dir
  #define DEFAULT_HOMING_FEED_RATE 25.0 // mm/min
  #define DEFAULT_HOMING_SEEK_RATE 200.0 // mm/min
  #define DEFAULT_HOMING_DEBOUNCE_DELAY 250 // msec (0-65k)

  #define DEFAULT_HOMING_PULLOFF 1.5 // mm

   Para definir el orden del home y los ejes que deben actuar debemos ir al fichero config.h buscar las líneas  y redifinirlas así:
// NOTE: Defaults are set for a traditional 3-axis CNC machine. Z-axis first to clear, followed by X & Y.
//#define HOMING_CYCLE_0 (1<<Z_AXIS)                // REQUIRED: First move Z to clear workspace.
//#define HOMING_CYCLE_1 ((1<<X_AXIS)|(1<<Y_AXIS))  // OPTIONAL: Then move X,Y at the same time.
// #define HOMING_CYCLE_2                         // OPTIONAL: Uncomment and add axes mask to enable

// NOTE: The following are two examples to setup homing for 2-axis machines.
// #define HOMING_CYCLE_0 ((1<<X_AXIS)|(1<<Y_AXIS))  // NOT COMPATIBLE WITH COREXY: Homes both X-Y in one cycle. 

 #define HOMING_CYCLE_0 (1<<X_AXIS)  // COREXY COMPATIBLE: First home X

// #define HOMING_CYCLE_1 (1<<Y_AXIS)  // COREXY COMPATIBLE: Then home Y



     Dado que conectamos el disparo de la cámara al coolant, realizaremos el disparo activando y desactivando coolant mediante los comandos M8,M9, realizando una pausa entre medias mediante comando G4
     Ejemplo: 
                 M8
                G4 P100    *
                 M9

     * En gbrl p no va en ms si no en s. y además no está bien codificada, pues no espera siempre correctamente. Por lo tanto recomiendo que la pausa se haga en el código de envío de la secuencia.


    Si la dirección de movimiento es la contraria basta con girar el conector del motor 180 grados o cambiar el parámetro de mascara de dirección $3=0 por $3=1.
    También se puede cambiar en el fichero default.h el parámetro DEFAULT_DIRECTION_INVERT_MASK  antes de enviar el fichero.

     Ahora ya se podrá enviar el programa al arduino.

Control

   Cuando se cargue el programa este estará listo para operar, pero se ha de tener en cuenta que el homing previsto hará que se bloquee el equipo al inicio. Por lo que hay que desbloquearlo con el comando $X.
    Para realizar la busqueda de cero se enviará luego el comando $H



    Si se modifica algún parámetro de forma directa mediante el comando $, este quedará grabado incluso aunque se recargue el programa de nuevo.

  Para restaurar los parámetros a los originales cargados desde con la transferencia de programa se deberá enviar. $RST=$
  Como al arrancar estará bloqueado se deberá enviar $X $RST=* $X.


Informe

    El gbrl devuelve un informe cada vez que se manda "?". Este informe tiene el formato

     <Idle|WPos:0.000,0.000,0.000|Bf:15,127|FS:0,0|Pn:Z>

  Pero los datos enviados pueden ser configurados mediante $10. Entre ellos los improtantes son el estado Idle/Run, la posición actual, necesaria para programar las posiciones inicial y final del recorrido del apilado, y el bufer. Este último es muy improtante para no sobrepasar el bufer de comandos. Los comandos deben de ser enviados solo mientras haya espacio en el buffer.
      BF:15,127 indica que hay espacio para 15 comandos y 127 caracteres en el buffer.
     Para asegurar que el formato es el puesto en el ejemplo debemos enviar $10=2

Comandos

     Ahora ya se podrán enviar los comandos para controlar el equipo. Igualmente se podrá hacer, con el lenguaje que se desee, el programa para el manejo del equipo.


Para desbloquear el grbl al conectar deberemos enviar  $X
Una vez hecho esto para tomar la refrencia correcta deberemos hacer una busqueda de cero con $H
Esto hace que se busque el sensor de home, pero las coordenadas no será las correctas, pues el home estará en -68.5. Para que este punto sea el 0 hay que deginir el G54. Para ello, cuando recibamos en la linea de estado el estado "Home" deberemos mandar "G10L2X-68.5"

           if(State.equals("Home")) Send("G10L2P1X-68.5");



        Mover en jog: 
                   Desplazador: $J=G21G91X0.754F500
                   Giro Mesa: $J=G21G91X0.054F500

        Disparo de cámara:   
                          M8
                        G4 P100
                        M9
        Avance de una distancia.
                       G91G1XnnFvvv        Avanza nn mm en el eje x. G91G0X0.001  avanzará 1 a una  velocidad vvv
        Avance a una posición absoluta.
                       G90G1XnnFvv       Avanza nn mm en el eje x. G91G0X0.001  avanzará 1 um a una velocidad vvv

EJEMPLO

     Para ayudar un poco pondré el código de algunas partes claves del programa.

     El programa deberá ser capaz de recoger el estado después de enviar "?", para conocer la posición y el estado de ejecucion.
    Para ello se deberá de enviar periódicamente "?" y analizar las cadenas recibidas.
    El código para analizar las cadenas sería este. En este caso escrito en Java.
    Esta rutina analizará la cadena recibida montando variables para definir el estado actual, marcando una bandera para indicar que se han actualizado los datos.

void GesEstado(String buf)
{ int ini,fin;
  //println(buf); 
   //Recibido Ok
  if(buf.substring(0, 2).equals("Ok")) .Ok=true;
  else Ok=false;

  //Report
  if(buf.substring(0, 1).equals("<")&&buf.indexOf(">")>0)   //se HA RECIBIDO EL REPORT COMPLETO
    {  
       
     // Estados
     ini=1;
     fin=buf.indexOf("|"); 
      DatosScreen.State="";
      if(buf.substring(1, fin).equals("Run")) State="Run";
      if(buf.substring(1, fin).equals("Idle")) State="Idle"; 
      if(buf.substring(1, fin).equals("Home")) State="Home"; 

           //POSICION X
                 ini=buf.indexOf("Pos:")+4;
                 fin=buf.indexOf(",");  //primer parámetro

                 //X
                  X=float(buf.substring(ini,fin)); 
                  //Y
                  ini=fin+1;
                 fin=buf.substring(ini).indexOf(",")+ini; 
                  Y=float(buf.substring(ini,fin)); 
                  //Z
                 ini=fin+1;
                 fin=buf.substring(ini).indexOf(",")+ini; 
                 Z=float(buf.substring(ini,fin)); 

                //Buffer
                ini=buf.indexOf("|Bf:")+4;
                 fin=buf.substring(ini).indexOf(",")+ini; 
                 Buffer_Inst=int(buf.substring(ini,fin)); 

       Update=true;
    }                 
         


 } 



Como complemento a este código deberá de existir un código que envíe los comandos incluido el "?" periódicamente. Este código puede ser así y debe de ejecutarse periódicamente en el bucle principal del programa. 
   Para poder usar el retardo delay del sistema no podemos enviar más que una linea de ejecución.
   Así mismo aqui se envía la corrección G54 en caso que el sistema nos indique que ha hecho un "Home".

void Run()
{  if(Running&&Update)
       { if(pBuffer<LonBuffer) 
           {
            if(DatosScreen.State.equals("Idle"))
                {                 
                 if(Buffer[pBuffer].substring(0, 2).equals("G4")) delay(int(Buffer[pBuffer++].substring(3)));
                 
                 else Send(Buffer[pBuffer++]);
                 
                 }
           }
          else  {Running=false; pBuffer=0; LonBuffer=0;} // finaliza la ejecución del buffer
             
         Update=false;
       }
    if(DatosScreen.State.equals("Home")) Send("G10L2P1X-68.5");
   //Envía orden de refresco de datos cada 200 ms
     if(frameCount%10==0) Send("?"); //Solicita actualizar los datos cada 50*5=200ms
}




Por último queda como generamos el programa de secuencia de movimientos para realizar el apilado de fotos. Para ello podemos usar el siguiente código. Como antes está en Java.
     Los datos necesarios para definir el proceso son:
                              float posIni      Posición inicial del apilado
                              float posFin      Posición finaldel apilado
                              int T_Est          Tiempo espera estabilización de cámara antes del disparo en ms
                              int T_Shoot      Tiempo de duración del pulso de disparo en ms
                              int T_Grab       Tiempo espera grabación en SD en ms. Esto dependerá de la rapidez de escritura de la cámara
                              int  Avance      Avance por cada fotograma en um
                              int AvGiro         Avance del giro del plato por cada secuencia en º.

void wait(int t)
{Buffer[indice++]="G4P"+t;
}


void Shoot(int Tdisparo)
{
Buffer[indice++]="M8";
 wait(Tdisparo);
 Buffer[indice++]="M9";
}

void SendMove(String s)
{Buffer[indice++]=s;



void Go(float posFin,float posFin,int T_Est,int T_Shoot, int T_Grab,int Avance,int AvGiro)
{ int nsecuencias;  //numero de secuencias satacker
   float av;

nsecuencias=1;
indice=0;

//si se dio un angulo se calcula el numero de secuencias necesarias
if(AvGiro.getPos()>0) {
  nsecuencias= 360/AvGiro; //Secuencias necesarias para completar un circulo
    SendMove ("G90G0Y0F180"); //se coloca al inicio la mesa de giro

}
  
  av=float(Avance)/1000;

 for (int i=0;i<nsecuencias;i++)
 {  //al siguiente movimiento
     SendMove ("G90G1X"+posIni+"F200");
  if(posFin>.posIni) 
    for(float p=posIni+av;p<.posFin;p+=av) 
      {    
        wait(T_Est);
        Shoot(T_Shoot);
        wait(T_Grab);
        SendMove("G90G1X"+p+"F200");
      }
     
  else
     {  SendMove("G90G1X"+posIni+"F200");
      for(float p=posFin;i>posIni;i-=av) 
      {  wait(T_Est);
        Shoot(T_Shoot);
        wait(T_Grab);
        SendMove( "G90G1X"+p+"F200");
      }
     }
       SendMove("G91G1Y"+AvGiro+"F100");
 }
  SendMove("G90G0X0Y0F200");
 LonBuffer=indice;
 Running=true;

}




Procedimiento de trabajo

     Al conectar y encender el equipo deberemos resetearlo, enviando $X y $H, para que busque el cero. Esto último es opcional.
     Después para con el equipo se deberá definir los tiempos, según las características de peso de la cámara y de tiempo de respuesta en grabado de cada foto y disparo.
     Luego se moverá la cámara en el desplazador por jogging a la posición inicial de la secuencia de apilado. Se almacenará esta posición, que será tomada del estado enviado por grbl. Después se avanza a la zona final del apilado y se graba la posición como posición final.
     Si deseamos que la mesa gire y se realice una secuencia de apilados con vistas de ángulos diferentes, basta con indicar un Angulo distinto de cero. En ese caso acabada la primera secuencia la mesa girará ese ángulo y repetirá la secuencia de apilado. Así hasta completar 360º. 
   Con estas secuencias se puede hacer múltiples apilados que generan fotos Macros con pequeños ángulos de giro. Puestos todos en una secuenca, podremos realizar un Vìdeo-Macro
    Hecho esto ya tendremos todo listo para lanzar la secuencia Go.


Ejecutables

     Podéis descargar una versión ejecutable para su control en windows aquí.
                        ScoposMacroRail ws32
                        ScoposMacroRail ws64

No hay comentarios :

Publicar un comentario