lunes, 18 de febrero de 2013

Panorama Robot 1.7 , el programa



Quizás, y para estar un poco a la moda le podría llamar 2.0 y hasta 3.0 , aunque en realidad el programa es el mismo de la versión 2011-2012 del robot original, este ha sufrido ligeros cambios y correcciones. En realidad tampoco le debería llamar 1.7, le podría decir versión 2013 y no por el año en que estamos sino por los pequeños cambios que he hecho desde finales del 2011 semana tras semana hasta que funcionara y tuviera un  mejor aspecto. A dia de hoy creo que me funciona todo y que es posible que deba de corregir cosas, pero para los inquietos ya pueden ir copiando y haciendo sus modificaciones pertinentes.

Actualmente está volcado en un ChipKit 32 y si no recuerdo mal, excede de los 34kb.

Algunos de los errores corregidos respecto la versión inicial han sido :

·      Cuando el lcd mostraba el % del proyecto llegaba a un punto en que este valor de volvía loco. @engeeknyer me dio la solución : cambia el int por un long int .
·     * Ya no utilizo mi “keypad” casero, ahora todas las entradas de posicionamiento y validación funcionan mediante el LCD Shield con el que gano espacio, peso y orden.
·     * Antes el robot se pasaba un paso al final de cada línea , se ha corregido modificando el programa.
·      Antes cuando terminaba toda la secuencia de fotografías, el robot al cabo de un rato reiniciaba sólo el ciclo. Ahora se quita todo del loop y al final se queda el sistema bloqueado hasta que le provoquemos un reset.
·       * Optimización de tiempos de espera entre movimientos. Aproximadamente se ha “regateado” entre 1-1.5 segundos/foto.

Las nuevas implementaciones:
·      
*  * Se programa mediante el uso de varias subrutinas, trabajando el programa en diferentes bloques, según como se mire es más fácil de entender dónde empieza y dónde acaba cada cosa.
·      Se puede modificar la focal, antes era focal fija definida por variable.
·      Se puede elegir el formato de cámara entre 4 modelos diferentes y comunes  de formato que hay en el mercado. Viendo el programa es fácil modificar e incluir otros.
    Dado que tenemos más posibilidad de definir parámetros mediante el uso del Shield LCD, provoca que existan más variables “automatizadas” como:
·      Calculo automático del ángulo , provocando que se deba auto-definir los impulsos que deben dar hacia los motores paso a paso.
·      Calculo automático del tiempo de duración del proyecto ya que el tiempo entre capturas no es el mismo al variar los ángulos y tiempos de desplazamiento.

NOTA > Por si hay alguien que se quiere “copiar” el proyecto, informaros que debido a un problema con la Shield LCD tuve que insertar una resistencia entre el pulsador de SELECT y la entrada AnalogRead(A0) ya que no me respondía esta tecla.

Cabecera del programa: Sirve para anotar informaciones que no afectaran al funcionamiento del programa pero que nos puede servir para tenerlas junto al programa cuando lo grabamos. Muchas veces nos puede servir para hacernos recordar que es lo que hace el programa, sobretodo cuando no ponemos títulos muy claros a la hora de guardar el programa.

/*

 Photo Robot 2x L298N + Nema 17
 language: Wiring/Arduino

 This program drives two bipolar stepper motor.
 Photo-shots signals it's actuated by relay --
 Movement manual motor by array switchs of keypad

 Created 28 Dec. 2012
 Last updated 13 Feb. 2013
 v1.7

 by XavierGP

 */

Habilitar uso de librerias y constantes: Una librería es un conjunto de instrucciones que no viene por defecto en la programación de Arduino y que nos simplificaran mucho el trabajo y tamaño del proyecto. En este caso incluimos la librería Stepper.h que hace referencia al movimiento de los motores paso a paso , y la librería LiquidCrystal.h que es la encargada de facilitarnos la faena a la hora de mostrar caracteres en la Shield LCD.

También aprovechamos para definir las siguientes constantes:

·      _360div2xPI la utilizaremos para convertir los angulos de radianes a formato estandar de una vuelta de 360º.
·      motorStepsX es el numero de impulsos que debe dar nuestro motor paso a paso para dar una vuelta, es un valor que en principio nos lo debe dar el fabricante. En este caso lo usaremos para el motor del movimiento horizontal (eje X). En nuestro caso corresponde a 200 impulsos.
·      motorStepsY lo mismo que la anterior constante motorStepsX, pero en este caso es para el motor del movimiento horizontal.
·      fan se trata del numero de salida del Arduino que da orden de funcionamiento del ventilador de refrigeración de los drivers del motor (2x L298N)


#include <Stepper.h>        // incluimos la Stepper library
#include <LiquidCrystal.h>  // incluimos la LiquidCrystal LCD library
#define _360div2xPI     57.29577951
#define motorStepsX 200     // Pasos motor X 360º/1.8º
#define motorStepsY 200     // Pasos motor Y 360º/1.8º                     
#define fan 29              // Pin OUT 29 habilita tensión en ventilador disipador

Definición de variables: definimos las variables que vamos a utilizar. A diferencia de las constantes esto son valores que puede ser que se modifiquen en el transcurso del programa.
·      pulsosvueltaejeX : es el numero de pulsos que debemos dar al motor para que de una vuelta entera el eje X. En teoria deberia ser 200 multiplicado por la reducción que tengamos. Yo preferí generarme un programa para determinar con exactitud el numero de impulsos para dar una vuelta entera.
·       pulsosvueltaejeY: idem , como en pulsosvueltaejeX pero para el motor del movimiento vertical (eje Y).  En mi caso fueron 6444 impulsos.
·       impX es el numero de impulsos que corresponden a 1 grado del ejeX
·       impY es el numero de impulsos que corresponden a 1 grado del ejeY
·       pasoX son los impulsos que realizará entre capturas del eje X y que tiene relación entre los grados que hay en el eje horizontal entre diferentes capturas.
·       pasoY idem pasoX pero para el eje vertical.
·       xact posición actual en grados del eje X.
·       yact posición actual en grados del eje Y.
·       xmax hace referencia a la posición máxima de la captura expresada en grados del eje X.
·       xmin hace referencia a la posición mínima de la captura expresada en grados del eje X.
·       ymax hace referencia a la posición máxima de la captura expresada en grados del eje Y.
·       ymin hace referencia a la posición mínima de la captura expresada en grados del eje Y.
·       xtemp posición actual del robot en grados del eje X.
·       ytemp posición actual del robot en grados del eje Y.
·       nshot numero de foto actual del proyecto.
·       filasX (debería de ser columnas en realidad…) es el numero de fotos a realizar en cada fila horizontal.
·       filasXX usada como variable temporal.
·       columnasY es el numero de todas las filas.
·       columnasYY usada como variable temporal.
·       previsionShot numero total de fotografías a realizar durante el proyecto.
·       segundos duración del proyecto expresada en segundos.
·       minutos duración del proyecto en minutos.
·       horas duración del proyecto en horas.
·       pause variable para mantener o reanudar una pausa.
·       analogico0 es el valor correspondiente a la lectura analógica numero 0 correspondiente a los valores entregados por los diferentes pulsadores de la Shield LCD.
·       temperatura valor de temperatura corregido.
·       voltage es el valor entregado por la bateria usando un divisor de tensión para tener un control sobre la misma.
·       valorTemp lectura del LM35DT encargado de mostrarnos la temperatura del disipador de los drivers L298N.
·       progreso es el valor en % del proyecto realizado.
·       abajo es el valor de la entrada analogica 0 correspondiente al pulsador DOWN.
·       arriba es el valor de la entrada analogica 0 correspondiente al pulsador UP.
·       enter  es el valor de la entrada analogica 0 correspondiente al pulsador SELECT.
·       left es el valor de la entrada analogica 0 correspondiente al pulsador LEFT.
·       right es el valor de la entrada analogica 0 correspondiente al pulsador RIGHT.
·       margen es el valor de la tolerancia que le damos al valor de los pulsadores.
·       d1 es el valor del lado largo del sensor fotografico CMOS expresado en mm.
·       d2 es el valor del lado alto del sensor fotografico CMOS expresado en mm.
·       diagonal_sensor es la diagonal resultante (hipotenusa) del triangulo d1 d2.
·       grados_horizontales es el valor resultante a la variables focal y factor (multiplicación o recorte) en horizontal.
·       grados_verticales es el valor resultante a la variables focal y factor (multiplicación o recorte) en vertical
·       factor es el factor multiplicación o recorte del sensor CMOS.
·       focal es el valor de la focal del objetivo que utilizaremos.
·       paso es una variable que nos ayudará a ir más rápido a la hora de definir la focal. Cuanto mas grande es el factor focal , más incrementa el paso (para no subir todo el rato en pasos de un milímetro)
·       menu es una variable para memorizar el tipo de formato de cámara usado.
·       logo1a – logo1h sirven para definir el gif de la presentación del inicio, en grupos de 5 columnas x 8 filas de pixeles, dónde un 0 es píxel apagado, y un 1 píxel oscuro.


int pulsosvueltaejex=10180;           // 200impulsos x reduccion mecanica
int pulsosvueltaejey=6444;            // 200impulsos x reduccion mecanica
float impX=pulsosvueltaejex/360;      // número de impulsos que corresponden a 1º del eje X   51.9*200/360  
float impY=pulsosvueltaejey/360;      // número de impulsos que corresponden a 1º del eje Y   32.2*200/360
int pasoX;                            // pasos motor entre capturas eje X
int pasoY;                            // pasos motor entre capturas eje Y 
float xact;               // º actual eje X
float yact;               // º actual eje Y
float xmax;               // º posición máxima eje X
float xmin;               // º posición mínima eje X
float ymax;               // º posición máxima eje Y
float ymin;               // º posición mínima eje Y
int xtemp;
int ytemp;
int nshot;
int filasX;                 // número de filas eje X >> (xmax-xmin)/pasoX
int filasXX;
int columnasY;              // número de columnas eje Y    >> (ymax-ymin)/pasoY
int columnasYY;
int previsionShot;          // prevision número shots   filasX * columnasY
int segundos ;
int minutos ;
int horas ;
int pause;
int analogico0;     // variable to store the value coming from the keypad
float temperatura;  // variable to store the value of radiator
float voltage;      // variable to store the voltage battery supply
float valorTemp;
int progreso;

int abajo=450;      //definimos valores teclas
int arriba=210;
int enter=945;
int left=680;
int right=0;
int margen=50;    //abans era 30

float d1;         // distancia 1 sensor CMOS (ancho)
float d2;         // distancia 2 sensor CMOS (alto)
float diagonal_sensor;
float grados_horizontales;
float grados_verticales;
float factor;     // en su defecto : FF = 1, Nikon DX=1.5 , Canon DX=1.6 , Olympus= 2
float focal = 700; 
int paso;
int menu=2;

byte logo1a[8] = {B01111, B00110, B01111, B11111, B11111, B11111, B11110, B11110};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
byte logo1e[8] = {B11110, B11110, B11110, B11111, B11111, B11111, B11111, B01111};
byte logo1h[8] = {B01111, B01111, B01111, B11111, B11111, B11111, B11111, B11110};


Inicialización de comandos: Direccionamos las entradas/salidas que corresponden a cada librería y a cada salida. En este caso definimos las dos salidas de los Steppers y luego las salidas correspondientes al LCD.
variables que vamos a utilizar. Seguidamente le decimos que el pin 28 lo utilizaremos como una salida al igual que el pin fan (definido en variables como numero 29), y acto seguido le decimos que por defecto queremos que estén en LOW (salidas apagadas). Acto seguido habilitamos la velocidad de los dos steppers al valor de 20 y también “arrancamos” el LCD.

Stepper myStepperX(motorStepsX, 31, 32, 30, 33); //  initialize the library motor X
Stepper myStepperY(motorStepsY, 38, 41, 39, 40); // initialize the library motor Y

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);  // initialize the library with the numbers of the interface pins

void setup() {
pinMode(28, OUTPUT);      // Output digital  shot
pinMode(fan, OUTPUT);     // Output digital  fan
digitalWrite(28, LOW);
digitalWrite(fan,LOW);

myStepperX.setSpeed(20);  // set the motor X speed at 15 RPMS:
myStepperY.setSpeed(20);  // set the motor Y speed at 10 RPMS:
 
lcd.begin(16, 2);

Creamos los caracteres en base a las variables logo1a,1d,1e y 1h:

lcd.createChar(1, logo1a);
lcd.createChar(4, logo1d);
lcd.createChar(5, logo1e);
lcd.createChar(8, logo1h);

Ejecutamos las subrutinas presentacioninicio y defineparametro: lease presentacioninicio y defineparametro . Estas subrutinas se encargan de que nos aparezca el mensaje de bienvenida con el logo de la cámara fotográfica y de definir los parámetros por el usuario correspondiente al formato de cámara y focal que utilizaremos.

presentacioninicio();
defineparametro();

Determinación de los puntos inicio y final de la panorámica: mediante la ayuda de dos subrutinas buscaorigen y buscafinal servirá para determinar la cobertura de nuestra foto. El programa está pensado para que el valor final tanto en X como de Y sea superior al final , es decir si entendemos que la posición inicial seria la esquina inferior izquierda de la fotografía y el punto final seria la esquina superior derecha. El programa NO está pensado para trabajar en otras opciones (habría que mejorarlo o probarlo como responde). Empezamos borrando la pantalla y apareciendo mediante un scroll lateral las instrucciones para localizar el punto de inicio (esquina inferior izquierda): mover mediante flechas y validar con tecla Select. Una vez validado nos aparecerá un mensaje de confirmación, y nos invitará a buscar el punto final (esquina superior derecha) mediante la subrutina buscafinal.  De toda esta parte acabaremos obteniendo el valor de filas y columnas totales.


lcd.clear();
lcd.print("Define posicion origen mediante flechas");
delay (1000);
for (int positionCounter = 0; positionCounter < 23; positionCounter++) {
lcd.scrollDisplayLeft(); // scroll one position left:
delay(350);              // wait a bit:
}
delay (1000);
lcd.setCursor(23,1);
lcd.print("[SELECT]  valida");
delay (3000);
buscaorigen();
lcd.clear();
lcd.print("Define posicion final mediante flechas");
delay (1000);
for (int positionCounter = 0; positionCounter < 22; positionCounter++) {
lcd.scrollDisplayLeft();    // scroll one position left:
delay(350);                 // wait a bit:
}
delay (1000);
lcd.setCursor(22,1);
lcd.print("[SELECT]  valida");
delay (3000);
buscafinal();
xact=-xmin;
yact=-ymin;
filasX=((xmax-xmin)/pasoX)+1;
filasXX=filasX;
columnasY=((ymax-ymin)/pasoY)+1;
columnasYY=columnasY;

Una vez tenemos el numero de filas y columnas es fácil determinar el numero de capturas (previsionShot) pues es una simple multiplicación, y a continuación calcularemos el tiempo requerido para realizar el proyecto en segundos.

previsionShot=filasX*columnasY;
segundos=previsionShot+(0.5*(columnasY-1))+(0.04*(((columnasY-1)*pasoY)+((filasX-1)*columnasY*pasoX))); 


Nuestro robot se mueve hacia el punto de origen mientras el LCD se borra varias veces y nos informa del numero de capturas, filas, columnas y grados correspondientes.



lcd.clear();
lcd.print("Moviendo a posicion de inicio         ");
lcd.setCursor(0,1);
lcd.print("<<< << <   < < <    < <     <         ");
delay(500);
for (int positionCounter = 0; positionCounter < 23; positionCounter++) {
lcd.scrollDisplayLeft(); // scroll one position left:
delay(350);              // wait a bit:
}
lcd.clear();
lcd.print("espera...");
myStepperX.step(-(filasX-1)*pasoX);
myStepperY.step(-(columnasY-1)*pasoY);   

lcd.clear();
lcd.print("Home position !");
delay(3000);
lcd.clear();
lcd.print(previsionShot);
lcd.print(" capturas:");   
lcd.setCursor(0, 1);
lcd.print(filasX);
lcd.print("H * ");
lcd.print(columnasY);
lcd.print("V");
delay (5000);
lcd.setCursor(0,1);
  
lcd.print((-xmin+xmax)/impX);
lcd.print(char(223));
lcd.print("H ");
lcd.print((-ymin+ymax)/impY);
lcd.print(char(223));
lcd.print("V     ");
delay (5000);
 
Ejecutamos la subrutina conversiontiempo para que nos pueda expresar la duración del proyecto en horas, minutos y segundos . Léase conversiontiempo.

conversiontiempo();

Tenemos todas las variables definidas y nuestro robot a punto, sólo falta que pulsemos SELECT para iniciar. El LCD nos informa de cómo empezar y como pausar el proyecto.

lcd.clear();
lcd.print("Pulse [SELECT] para iniciar y para pausar ");
lcd.setCursor(0,1);
lcd.print("               ");
delay (1000);
for (int positionCounter = 0; positionCounter < 24; positionCounter++) {
lcd.scrollDisplayLeft();   // scroll one position left:
delay(350);                // wait a bit:
}

delay(2000);
lcd.clear();
lcd.setCursor(0,1);
lcd.print("[SELECT] = start");
pause=1;

while(pause!=0){
if((analogRead(0)/margen)==(enter/margen)){   // Tecla SELECT
pause=0;
}
}
xmax=xmax-xmin;
ymax=ymax-ymin;
xmin=0;
ymin=0;
xact=0;
yact=0;
 

Rutina de captura de fotografía: movimiento de motores: Empieza el ciclo disparando una foto y acto seguido el robot se mueve en sentido hacia la derecha un PasoX (grados resultantes de la focal y factor cámara definidos) y a continuación otra foto hasta llegar al valor máximo programado como valor máximo de X (numero de filasX), momento en que retrocederá el robot hasta el punto de inicio e incrementará el ángulo en el eje vertical un PasoY antes de empezar otra vez a realizar todas las fotos de la misma fila.

for(int i=0; i<columnasY; i++){      // empieza secuencia panoramica        
delay(1000);
for(int j=0; j<(filasX-1); j++){   //FILASX

Analizamos la temperatura del disipador (señal del LM35) y decidimos si activamos el ventilador (temperatura superior a 33ºC) o si lo paramos (temperatura inferior a 29ºC)

temperatura=analogRead(A6);         // comprobamos temperatura en disipador LM35DT
valorTemp = ((500 * temperatura * 5 )/1024.0);
if(valorTemp<290){digitalWrite(fan,LOW);}              // deshabilita tensión en ventilador - energy saving - si temperatura < 29ºC
if(valorTemp>330){digitalWrite(fan, HIGH);}            // habilita tensión en ventilador si temperatura > 33ºC

Activamos la señal hacia el disparador remoto de la camara durante 500 ms.

digitalWrite(28, HIGH);
delay(500);                         // original 500 aquest timer es pot reduir !!!!!!!!!!!!!!!
nshot=nshot++;

mientras ejecuta el proyecto, en el LCD nos muestra el progreso, numero de foto , la fila y la columna.

lcd.clear();
progreso=((nshot*100)/(previsionShot));
lcd.print(progreso);
lcd.print("%");
lcd.setCursor(7,0);
lcd.print("X:   Y:");
lcd.setCursor(0,1);
lcd.print(nshot);
lcd.setCursor(7,1);
lcd.print(j+1);
lcd.setCursor(12,1);
lcd.print(i+1);

desactivamos la señal del disparador remoto y analizamos si se está pidiendo de realizar una pausa en el proyecto (subrutina peticiopausa)

digitalWrite(28, LOW);
peticiopausa();  
delay(500);
if(j!=(filasX+0)){  
myStepperX.step(pasoX);
xact=xact+(pasoX);
}
delay(500);                                                                                 // original 750 aquest timer es pot reduir !!!!!!!!
}

nshot=nshot++;
digitalWrite(28, HIGH);
lcd.clear();
progreso=((nshot*100)/(previsionShot));
lcd.print(progreso);
lcd.print("%");
lcd.setCursor(7,0);
lcd.print("X:   Y:");
lcd.setCursor(0,1);
lcd.print(nshot);
lcd.setCursor(7,1);
lcd.print(filasX);
lcd.setCursor(12,1);
lcd.print(i+1);
delay(500);                         // original 500 aquest timer es pot reduir !!!!!!!!!!!!!!!
digitalWrite(28, LOW);
delay(500);

una vez llegamos a la ultima captura de cada fila, el LCD nos mostrará la temperatura del disipador y el voltage de la bateria mediante una llamada a la subrutina pantallatemperatura a la vez que el robot retorna al origen en X y acto seguido incrementa un paso en el eje Y.

pantallatemperatura();
      
myStepperX.step((-(filasXX)+1)*pasoX);   //////////////+0
xact=0;
if(i!=columnasY-1){myStepperY.step(pasoY);}                                
yact=yact+(pasoY);
delay(1000);
}

Una vez se han completado todas las filas, columnas y fotos, tan sólo nos queda esperar a que el robot vuelva a la posición inicial y nos aparezca el mensaje de “Panorámica Completa ! “ y acto seguido entraremos en un loop del cual es imposible salir hasta que no pulsemos el botón de reset, con esto evitaremos reinicios automáticos como hacia la versión anterior de este programa. Ahora ya sólo nos queda procesar todas las capturas en nuestro ordenador.

lcd.clear();
lcd.print("Panoramica");
lcd.setCursor(5,1);
lcd.print("completa !!");
myStepperY.step((-columnasYY+1)*pasoY);
}
void loop(){
}

Subrutina defineparametro: En esta subrutina vamos a definir el tipo de cámara réflex que usamos. En principio se han programado los 4 tipos más conocidos según el formato de recorte (Full Frame, Nikon DX, Canon DX y Olympus 4/3). Es muy fácil insertar más tipos, tan solo tocará buscar las medidas de los sensores CMOS y calcular su diagonal. En esta programación por defecto parto de que utilizaré una Nikon DX y un zoom de 700mm. Si queréis definir otra tan sólo hay que cambiar los valores en las variables al inicio del programa.


void defineparametro(){ // rutina definimos tipo de camara y focal
lcd.clear();
lcd.print(" Formato camara");
lcd.setCursor(2, 1);
lcd.print("[UP] & [DOWN]");
delay(1500);
while((analogRead(0))/margen!=(enter/margen)){ //enter
delay(300);
if((analogRead(0)/margen)==(arriba/margen)){  //arriba
menu=menu+1;
if(menu>=4){menu=4;}
lcd.clear();
}
if((analogRead(0)/margen)==(abajo/margen)){   //abajo
menu=menu-1;
if(menu<=1){menu=1;}
lcd.clear();
}
if(menu==1){ // FF
lcd.print("Full Frame 35 mm.");
d1=35.8;         //canon 5d
d2=23.9;         //canon 5d
factor=1;
diagonal_sensor=43.0447441623248;
}
if(menu==2){ // Nikon DX
lcd.print(" Nikon DX  1.5x");
factor=1.5;
d1=23.6;         // distancia 1 sensor CMOS (ancho)   Valor de la Nikon d90 !
d2=15.8;         // distancia 2 sensor CMOS (alto)    Valor de la Nikon d90 !
diagonal_sensor=28.40070421662111;
}
if(menu==3){ // Canon DX
lcd.print(" Canon DX  1.6x");
factor=1.6;
d1=22.2;         // eos1000d
d2=14.8;         // eos1000d
diagonal_sensor=26.68107943843352;
}
if(menu==4){ // Olympus 4/3
lcd.print("Olympus 4/3   2x");
factor=2;
d1=17.3;         // e-520
d2=13.0;         // e-520
diagonal_sensor=21.6400092421422;
}
lcd.setCursor(0, 1);
lcd.print("[SELECT]  valida");
}

En la segunda parte de esta subrutina, definiremos la focal que vamos a utilizar. Aquí se utiliza la variable paso que tiene la función de cambiar el incremento de la focal a medida que va creciendo o decreciendo el valor con la finalidad de tener velocidad a la hora de ajuste sin perder precisión en el valor.



lcd.clear();     // rutina definimos focal
lcd.print("Longitud  focal:");
lcd.setCursor(2, 1);
lcd.print("[UP] & [DOWN]");
delay(1500);
while((analogRead(0))/margen!=(enter/margen)){ //enter
if (focal<100){
paso=1;
delay(75);
}
if (focal>=100){
paso=25;
delay(120);
}
if (focal>399){
paso=50;
delay(150);
}
if((analogRead(0)/margen)==(arriba/margen)){  //arriba
focal=focal+(paso);
lcd.clear();
}
if((analogRead(0)/margen)==(abajo/margen)){   //abajo
if(focal>1){focal=focal-(paso);}
lcd.clear();
}
lcd.print(focal);
lcd.print(" mm.");
lcd.setCursor(0, 1);
lcd.print("[SELECT]  valida");
}
// diagonal_sensor=sqrt((d1*d1)+(d2*d2));
//factor =35/d1;

Seguidamente y haciendo uso de las variables de focal y el formato de cámara, el programa calculará la equivalencia en grados tanto en vertical como en horizontal (en principio la programación esta pensada en que la  cámara esté situada en horizontal!), y acto seguido recalculará cuantos impulsos debe ejercer en cada uno de los dos motores para lograr un solape optimo).


grados_horizontales= 2* (atan(diagonal_sensor/(2*focal*factor))*_360div2xPI);
grados_verticales = grados_horizontales * d2 / d1;

pasoX=(pulsosvueltaejex*(grados_horizontales/360));
pasoY=(pulsosvueltaejey*(grados_verticales/360));
lcd.clear();
lcd.print ("Focal equivalente");
lcd.setCursor(2, 1);
lcd.print(focal*factor);
lcd.setCursor(10, 1);
lcd.print("mm.");
delay(3000);
}


Subrutina presentacioninicio: básicamente es la presentación del inicio dónde nos da la bienvenida el LCD y acto seguido muestra la versión del programa y un gif animado de una cámara haciendo una foto. Dentro de esta subrutina, llamaremos a otras dos logo2 y logo3 que es dónde contiene parte de la programación del gif. Según mis pruebas sólo pueden haber 8 caracteres definidos por el usuario a la vez, es por ello que utilizo subrutinas para acortar el programa.



void presentacioninicio(){
lcd.clear();
lcd.print(" Hola XavierGP!");
delay(3000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Panorama");
lcd.setCursor(0,1);
lcd.print("Robot");
logo3();
delay(800);
logo2();
delay(400);
logo3();
lcd.setCursor(6,1);
lcd.print("v1.7");
delay(800);
logo2();
delay(400);
logo3();
delay(3000);  // fin GIF
}

void logo2(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00111, B01111, B01111, B11111};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B11100, B11110, B11110, B11111};
byte logo1f[8] = {B11110, B11111, B01111, B01111, B00111, B10001, B11100, B11111};
byte logo1g[8] = {B01111, B11111, B11111, B11110, B11100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

void logo3(){
byte logo1b[8] = {B00000, B00111, B11100, B10001, B00110, B01100, B01000, B10000};
byte logo1c[8] = {B00000, B11100, B00111, B10001, B01100, B00110, B00010, B00001};
byte logo1f[8] = {B10000, B10000, B01000, B01100, B00110, B10001, B11100, B11111};
byte logo1g[8] = {B00001, B00001, B00011, B00110, B01100, B10001, B00111, B11111};
byte logo1d[8] = {B00000, B00000, B11110, B11111, B11111, B11111, B11111, B11111};
lcd.createChar(2, logo1b);
lcd.createChar(3, logo1c);
lcd.createChar(6, logo1f);
lcd.createChar(7, logo1g);
lcd.createChar(4, logo1d);
lcd.setCursor(12,0);
lcd.write(1);
lcd.setCursor(13,0);
lcd.write(2);
lcd.setCursor(14,0);
lcd.write(3);
lcd.setCursor(15,0);
lcd.write(4);
lcd.setCursor(12,1);
lcd.write(5);
lcd.setCursor(13,1);
lcd.write(6);
lcd.setCursor(14,1);
lcd.write(7);
lcd.setCursor(15,1);
lcd.write(8);
}

Subrutina conversiontiempo: Tal y como dice el nombre, esta subrutina sólo tiene la finalidad de convertir el tiempo de duración del proyecto que teníamos en formato segundos a formato horas, minutos y segundos.


void conversiontiempo(){  // convertimos tiempo necesario en realizar proyecto
while(segundos>=3600){  
horas = horas++;
segundos = segundos - 3600;
}
while(segundos>=60){
minutos = minutos++;
segundos = segundos - 60;
}
lcd.clear();
lcd.print("Duracion prevista");
lcd.setCursor(0,1);
lcd.print(horas);
lcd.print("hrs ");
lcd.print(minutos);
lcd.print("min ");
lcd.print(segundos);
lcd.print("seg");
delay(5000);
}


Subrutina pantallatemperatura: esta es la pantalla que nos mostrará el LCD cuando está retornando el robot a la columna de inicio. Podremos ver la temperatura del disipador y el voltaje de la batería a modo de información. En el valor de voltaje he tenido de corregirlo para que se asemejara lo más posible a mi realidad.


void pantallatemperatura(){
lcd.clear();
lcd.print("Voltage ");
voltage=analogRead(A7)*1.212;
lcd.print(voltage/100);  
lcd.print(" v.");
lcd.setCursor(0,1);
lcd.print("Temp.:  ");
lcd.print(valorTemp/10);
lcd.print(" ");
lcd.print(char(223));
lcd.print("C");
}


Subrutina peticiopausa: preguntamos al pic si hay la entrada analógica 0 en valor de Select durante 10 veces y si es asín provocamos una pausa forzada de 10 segundos antes no podamos volver a pulsar la misma tecla para reprender con nuestro proyecto fotográfico. El por que de las 10 veces es porque me he encontrado que o el pic corre mucho y “no se entera” o el valor no era el exacto al programar la pausa.


void peticiopausa(){
for (int k=0; k<10; k++){ //prova pausa
if((analogRead(0)/margen)==(enter/margen)){     // tecla SELECT si apretem provoquem una pausa fins que no apretem ENTER
lcd.clear();
lcd.print("Pausa!");
lcd.setCursor(0,1);
lcd.print("[SELECT]=reStart");
delay(10000);  //abans no hi era i em rearrancaba
pause=2;
while(pause>1){
if((analogRead(0)/margen)==(enter/margen)){   // tecla SELECT
pause=0;
lcd.clear();
lcd.print("SHOT   X:   Y:");
delay(1000);
}
}
}
} //prova pausa
}

Subrutina buscaorigen: hasta que no pulsemos select no nos validará la posición que deseemos y que podemos desplazar mediante el uso de los pulsadores up, down, left y right mediante la subrutina movimiento. A continuación memorizará estos valores como la xmin y la ymin.

void buscaorigen(){
while((analogRead(A0)/margen)!=(enter/margen)){
movimiento();
}
xmin=xact;
ymin=yact;
lcd.clear();
lcd.print("Posicion  origen");
lcd.setCursor(0, 1);
lcd.print(" - CONFIRMADA - ");
delay(3500);
}


Subrutina buscafinal: lo mismo que en buscainicio pero definiremos el punto final de nuestras capturas, memorizándolos como xmax e ymax.


void buscafinal(){
while((analogRead(A0)/margen)!=(enter/margen)){
movimiento();
}
xmax=xact;
ymax=yact;
lcd.clear();
lcd.print("Posicion  final");
lcd.setCursor(0, 1);
lcd.print(" - CONFIRMADA - ");
delay(3500);
}
 
Subrutina movimiento: tiene la función de hacer mover el robot manualmente mediante los pulsadores up, down, left y right y acto seguido informándonos en el LCD la posición del mismo.



void movimiento(){
analogico0 = analogRead(A0);   
if((analogRead(A0)/margen)==(right/margen)) {xact=xact+pasoX;}    //  derecha +X
if((analogRead(A0)/margen)==(left/margen))  {xact=xact-pasoX;}    //  izquierda -X  
if((analogRead(A0)/margen)==(abajo/margen)) {yact=yact-pasoY;}    //   abajo -Y
if((analogRead(A0)/margen)==(arriba/margen)){yact=yact+pasoY;}    //   arriba +Y
lcd.clear();
lcd.print("X:      Y:");
lcd.setCursor(0, 1);
lcd.print(xact/impX);
lcd.print(char(223));
lcd.setCursor(8, 1);
lcd.print(yact/impY);
lcd.print(char(223));
myStepperX.step(xact-xtemp);
xtemp=xact;
myStepperY.step(yact-ytemp);
ytemp=yact;
delay(20);
                     }


Con todo esto ya tenemos el programa enterito y funcional, aunque seguro que muchos de vosotros podéis optimizarlo mucho más y corregir errores, todo es cuestión de pasarse horas pensando , modificando y probando.

Lógicamente no lo he probado con todas las focales y ni mucho menos con los 4 formatos de factor recorte , pero en teoría y repito: en teoría debería ir…

Personalmente creo que hay un error al no definir el solape entre capturas pero después de varias pruebas, aun no lo veo claro ! y como que es un proyecto personal sin ningún animo de lucro ni de proyecto de carrera ni nada que se le parezca creo que ya está bastante bien, al menos era la intención , pero todo conlleva mucho tiempo, horas ya sea haciendo agujeros, buscando material por eBay, documentando y buscando fórmulas, haciendo videos y luego editarlos, fotos, entrada al blog…na que hay un buen curro que espero que algunos sepáis valorar.

Finalmente os dejo un video en que podéis medio ver como funciona el tema de pantallas, deseo que os guste.


Tengo previsto alojar el programa entero en Internet, pero aun no tengo claro dónde para poder tener un control a título personal del número de descargas…¿alguna idea/propuesta?

¿ Alguien se anima a crear la versión 2.0 ?

Saludos a todos los visitantes!





domingo, 27 de enero de 2013

Electrónica y control del Robot Giga panorámico



Empiezo de nuevo toda la parte de electrónica y control del Robot. Esta vez en vez de usar Arduino UNO , elijo un PIC ChipKit UNO32 comercializado por Digilent . Elegí este PIC que tenia por casa por la simple razón que en el anterior robot me quedé corto de entradas y salidas, y con este prácticamente doblaba. Actualmente tengo también un Arduino Mega2560 R3 pero actualmente está funcionando como gestor de mi estación meteorológica-web visible en http://77.231.64.193:80 o seguirla vía tweeter @MeteoXavierGP .

Algunas pequeñas diferencias pero notables son: 

·        * El Mega 2560R3 es prácticamente el doble de grande que el UNO32 y como la intención era quitar peso y tamaño…
·        * Las salidas del UNO32 son a 3.3 v en vez de 5v! Esto nos puede dar algún que otro problema añadido. En mi caso tuve que buscar unos relés a 3VDC en USA, ya que en Europa na de ná!
·        * El UNO32 no acepta todas las librerías de Arduino ! Cuidadín con vuestros proyectos, yo me volví telele una vez, menos mal que tenia Arduino’s en casa y comprobé que lo que falla era el hardware y no mi programación.
·        * El software de programación del UNO32 es propio. Se llama mpide y funciona igual que el Arduino.exe, pero cada placa con su software!

En casa tenia una Shield LCD con pulsadores, esto me ahorraría cableados y botoneras externas, consiguiendo otra vez reducir volumen y peso.


Busqué vía eBay dos shields L298N para utilizarlo como drivers de los motores y empecé a jugar, creándome un pequeño programa de simples giros de los motores. Mediante una fuente de alimentación regulable observé que cuando debía aplicar unos 6v para obtener un giro sin tembleques en los motores y que la pantalla Shield del Arduino se quedara bién iluminada.

Aprovecho este momento para pedir vía eBay un step down con salida a 6v y 7 amperios para aprovechar las baterías de 12v que tengo en casa.

Cuando creía que toda la parte motora podía funcionar bién, elegí una caja de plástico de dimensiones justas-optimizadas y después de hacer con ella un colador para fijar diferentes shields ya tenia de nuevo todo a punto para probar.

Cual fue mi sorpresa…una vez cableado en principio estos elementos dentro de la caja cuando aplico tensión me doy cuenta que el LCD me pierde intensidad, y que los botones de la Shield no me responden correctamente.

Inicialmente había conectado la alimentación del UNO32 por el jack lateral…después de pensar me dí cuenta que la tensión caía a unos 4voltios en el pin de 5v. Esto es debido al regulador interno que tiene del tipo 7805 , el cual tiene una caída de tensión interna de unos 2v. , cosas de aquellas que en principio sabemos pero de vez en cuando olvidamos. Miro el dataste del UNO32 y lo deja claro: alimentación entre 7-12VDC.

En este momento decido que NO puedo alimentar el PIC por este conector , y decido bruscamente alimentarlo por el USB…pero claro…el USB va a 5v y yo tengo 6!

En eso que llega mi Step-down (los que compráis a China, ya sabéis que tardan las cosas entre 2-3 semanas normalmente). Conecto todo de nuevo , pruebo y la cosa va aun peor !

Cojo el tester y observo que a la salida del step-down hay 5.03v en vez de 6v! Calentón rápido de mi cuerpo, lo miro varias veces con carga, y sin. Observo que la pegatina dice salida 6 v, pero yo no los tengo. Analizo los datos del pedido de eBay y son correctos, acto seguido entro a la web del fabricante y empiezo a buscar mi modelo…y NO EXISTE !


Aprovecho para ir a la tienda de electrónica local y adquiero unos diodos y ya de paso una batería de 6v con la que alimentaré el invento en mis salidas campestres.

Acto seguido, inserto un diodo a la salida de la batería para observar la caída de tensión , y acto seguido lo intercalo entre el positivo del conector mini usb. La cosa parece que empieza a ir… pero las lecturas en los pulsadores es muy inestable.

Cojo el UNO32 + LCD lo conecto al USB del PC y la cosa cambia mucho. Pruebo de nuevo con la batería y oscila... está claro: si cambiando la alimentación del PIC me falla, el problema es la alimentación.

Sé que tengo 6v en las baterías y necesito regular a 5, y también que con un 7805 no me sirve porque tiene una caída de tensión de 2v, por lo que debería alimentar a 7v. Se me pasa por la cabeza montar un diodo Zener, pero @engeeknyer no me lo recomienda, y me dice hay una cosa que se llaman Step-down … finalmente: cambio de planes: utilizaré la batería de 12v, un step down regulable de 1A para alimentar el UNO32 (lo ajusto con una salida de 7v) y luego pruebo con un StepDown de 5v/10A para los drivers de los motores. Lo monto todo y por fin empieza a funcionar ! Ya era el segundo cambio de diseño en la alimentación, esta claro que los diseñadores nunca aciertan a la primera.


En este punto ya tengo la fase de lógica y control de movimiento de los motores funcionando.


Aprovecho para complicarme más la vida y le monto un conector tipo puerto paralelo a la caja para poder cacharrear con toda la electrónica sin necesidad de arrastrar todo el invento.


Ahora sólo falta montarle el sensor remoto de la cámara fotográfica, el control de temperatura del los drivers , el ventilador para refrigerar y un divisor de tensión para analizar la capacidad de la batería.

Como que vengo de la rama de la electricidad, y no domino la electrónica, soy un poco reacio a montar aun que dispositivos electrónicos. Soy de los que prefiere montar un relé para aislar señales y cuando se trata del control remoto de una réflex, y los euros que vale, no me la juego, axial que acabo montando una plaquita agujereada al cual le monto unos relés de 3VDC.


También monto un LM-35 y lo fijo a una pletina de aluminio que es la que disipa el calor hacia el radiador exterior, en el cual monto un ventilador de 12v que será el encargado de enfriar los L298N.


Finalmente hay un divisor de tensión para analizar el estado de las baterías. En este caso reduzco 1/3 parte el voltaje de las baterías , pasando de 12 v a unos 4v para aplicarlos luego a una entrada analógica (sólo acepta 5v. si no queremos freir el PIC). Una vez luego mediante programación ya multiplicaremos el valor otra vez por 3 para que el LCD nos marque el valor correcto, pero esto ya es programación del PIC que lo dejamos para otro día.


Ya sabéis, si tenéis dudas o más detalles del montaje, no dudéis en postear. Gracias!


  





sábado, 26 de enero de 2013

Porque un grado importa (III)

Seguimos con las matemáticas aplicada a la fotografía. Muchas veces cuando nos hablan de focales de objetivos nos quedamos con la idea de que si el número es grande es un tele-objetivo, que representa que tiene mucho “zoom”, pero no sabemos cual es el ángulo de visión, y de lo contrario, si el número es pequeño se tratará de un angular o gran angular.

Pero ¿ a cuantos grados equivale esa focal ?
Para ello deberemos ayudarnos de la trigonometría. Primero de todo , necesitaremos conocer las dos siguientes variables:

Focal a utilizar (aquí no hay mucho que explicar, tan sólo hay que ver a cuanto tenemos ajustado la focal en nuestro objetivo en mm). Si utilizamos un duplicador o tele convertidor deberemos multiplicar para obtener la focal resultante. Por ejemplo si tenemos un objetivo ajustado a 300 mm y utilizamos un tele convertidor de 1.4x, la focal resultante seria de 300 x 1.4 = 420mm. Otra opción es disparar una foto y acto seguido visualizar mediante la pantalla de la cámara la información referente a la focal en mm.


Diagonal del tamaño del sensor en milímetros. Si no sabemos como encontrar las dimensiones del sensor, y siempre hablando de cámaras del tipo bridge o réflex, podemos utilizar la siguiente tabla:

 

Si sois de los que os gusta calcular todo:
Diagonal sensor, una vez buscado en los manuales de la cámara, en las características técnicas suele decirlo, no seáis brutos y midáis con un pié de rey esas cotas si queréis evitar el riesgo de dañar el CMOS !  Aquí es tan fácil como aplicar el teorema de Pitágoras . En nuestro caso :

Diagonal sensor = raíz cuadrada de( ( distancia1*distancia) + (distancia2*distancia2) )

Realizaremos un ejemplo con el sensor de la d-90 que según caracteristicas técnicas de Nikon, el sensor mide 23,6 x 15,8 mm. , entonces :


Diagonal sensor = raíz ( ( 23,6*23,6) + (15,8*15,8) )
Diagonal sensor = raíz ( ( 556,96 ) + ( 249,64 ) )
Diagonal sensor = raíz ( 806,6 )
Diagonal sensor = 28,84 mm


Aprovecho para mostraros el efecto recorte producido por el factor conversión:


Imaginemos que utilizamos una Nikon d-90, un 500 mm de objetivo y un con un tele-conversor de 1.4x (no confundir con el factor del sensor!) . En principio estos son los datos que utilizaré en mi próximo proyecto Giga panorámico

Ya tenemos las dos variables: focal equivalente y tamaño de la diagonal del sensor, ahora ya solo falta aplicar la fórmula final:

Diagonal sensor = 28,84 mm
Focal = 500 x 1,4 (tele-conversor ) = 700 mm
Focal equivalente = Focal x Factor recorte = 700 x 1,5 = 1050 mm
Angulo de visión horizontal =2 * arc tangente ( diagonal sensor / 2 * focal  equivalente)
Angulo = 2 * arc tangente (28,84/ 2*1050)
Angulo = 2 * arc tangente (28,84/2010)
Angulo = 2 * arc tangente (0,013733)
Angulo = 2 * 0,78679
Angulo de visión horizontal = 1,57º

NOTA = Si no tenéis una calculadora científica, y disponéis de la calculadora de Windows, podéis calcular el arc tangente haciendo click en INV previo a tan.


Finalmente y ya puestos, calcularemos el ángulo de visión en vertical
Angulo de visión vertical = ángulo de visión horizontal * distancia2 / diastancia1
Angulo Vertical = 1,57 * (15,8 / 23,6)
Angulo Vertical = 1,57 * (0,6695)
Angulo Vertical = 1,05 º
  
Finalmente y para los usuarios de Arduino, aprovecho un pequeño código para calcular los grados y poder utilizarlo en vuestros proyectos:

// Calculo ángulo de visión de un objetivo por XavierGP

#define _360div2xPI     57.29577951

float d1=23.6;         // distancia 1 sensor CMOS (ancho)   Valor de la Nikon d90 !
float d2=15.8;         // distancia 2 sensor CMOS (alto)    Valor de la Nikon d90 !
float diagonal_sensor=sqrt((d1*d1)+(d2*d2));
float grados_horizontales;
float grados_verticales;
float factor =35/d1;     // en su defecto : FF = 1, Nikon DX=1.5 , Canon DX=1.6 , Olympus= 2
float focal = 700;       // valor zoom a utilizar (en mi caso 500 x 1.4)

void setup() { 
  Serial.begin(9600); // 9600 bps
}

void loop(){
 grados_horizontales= 2* (atan(diagonal_sensor/(2*focal*factor))*_360div2xPI);
 grados_verticales = grados_horizontales * d2 / d1;
 
  Serial.print("Diagonal sensor: ");
  Serial.print(diagonal_sensor);
  Serial.println(" mm.");
  Serial.print("Con una focal de ");
  Serial.print(focal);
  Serial.print(" mm. y un factor ");
  Serial.print(factor);
  Serial.print("x , el resultante es de ");
  Serial.print(grados_horizontales);
  Serial.print(" grados en Horizontal, y de ");
  Serial.print(grados_verticales);
  Serial.println(" grados verticales.");
}

Con todo esto ya puedo seguir con mi proyecto, para que sea más moldeable y versátil en cuanto a programación y poder definir in situ la focal a utilizar, ahora sólo falta adaptar esto al programa general.

domingo, 13 de enero de 2013

Porque el tamaño importa (II)


No es una leyenda urbana, ni una manera de autojustificar, pero el tamaño muchas veces importa. ¿ Os acordáis de los primeros teléfonos “móviles” ? Eran grandes, pesados, con poca autonomía (la cobertura es otro tema!), y a grandes pasos la cosa ha ido empequeciendo, hasta llegar a unos tamaños increíbles, todo y que en el futuro quien sabe como serán…

El año pasado con el robot panorámico logré juntar varios temas para llegar a un resultado: La mecánica, la electrónica y la fotografía para obtener una fotografía Gigapanorámica, con un resultado para mí de alta satisfacción, es por ello que la gran cantidad de horas empleadas me llenaron al ver los resultados esperados.

También es de cultura popular que quién algo quiere ver, algo tiene que andar. En el caso de fotografías panorámicas siempre he tenido de hacer varias excursiones previas para analizar el terreno, y determinar el que creí como mejor punto optimo (aquí entraba la parte de fotografía y la parte de transporte).

Uno de los problemas detectados durante las tres excursiones para la toma de fotografías es que cada vez tocaba andar más y por peores accesos con toda la artillería a cuestas, y parece que no pero hay sus quilitos. Para hacernos una idea: cuerpo Nikon d-90 ronda 700 gramos, el objetivo Sigma 150-500, unos 1800 gramos, dos baterías de 12v 7Ah, cada una debe pasar de los 1500 gramos, unas herramientas de supervivencia… ya pasamos de los 5 kilos , y me he dejado el robot !

El robot anterior calculo que rondaba los 15-20 kilos, habían varios engranajes de hierro puro y duro, rodamientos, platinas de 10 mm de hierro, perfilería de unos 60 mm. Era muy robusto, pero muy pesado .

En más de una ocasión estuve buscando posibles sitios para hacer panorámicas pero siempre lo debía descartar por la dificultad de acceso de todos los materiales. Soy una persona que no me gusta molestar a la gente y no creo que sea ético contratar sherpas para que me trasladen mis trastos para hacer una cosa en que el resultado final es un reto mío.

Una de mis últimas ascensiones en solitario hasta el Collbaix , sirvieron para que remontara la idea de intentar hacer una foto desde allí arriba. Analicé los diferentes accesos para determinar el mejor, pero todo y eso el tema peso pintaba complicado y allí empezó el reestiling del robot panorámico. La vista que se ve desde allí arriba la podeis ver en pequeño . La foto un poco más grande aqui


Como que la idea la tenia bastante clara, sólo me faltó la ayuda incondicional de mi compañero de trabajo que siempre me aporta grandes soluciones a mis grandes problemas, y dicho y hecho, 4 esbozos , recopilación de hierros , 4 herramientas básicas y hacer bricomanía.

Esta vez se puede decir que excepto un pequeño engranaje de 12z que es metálico, el resto son de nylon-resina, mucho más ligeros, fáciles de trabajar, y menos pesados. Se ha reducido al mínimo las dimensiones, para quitar tanto peso como sea posible , al igual que las pletinas de aluminio, hierro o madera DM se han reducido a planchas de poco espesor.
También en el tema engranajes/motor, se ha sustituido el conjunto de rotación horizontal por un motor nema con reductor incluido de 51:1 . Aquí se redujo el peso drásticamente a base de incrementar el coste…


Si quereis más información podeis encontrarla en la web de Stepperonline

Y el resultado fue este: El robot de la derecha es el nuevo en proceso de montaje, mientras que el de la izquierda es el antiguo con varias piezas desmontadas para su reutilización.


La idea básicamente es la misma que el anterior, pero esta vez es más fácil de montar y desmontar, el ajuste es prácticamente nulo, cosa que el anterior tenía serios problemas para que los engranajes de los reductores no tuvieran mucho juego entre ellos. Cabe recordar que entre tomas habían aproximadamente 2.7º, y un movimiento producido por un mal acoplamiento entre engranajes podía provocar serios problemas en el resultado final. Cada vez que debía desmontar los reductores del eje horizontal me ponía malo en pensar en el siguiente ajuste. Ahora y gracias a este motor y a la precisión de otras medidas, el hecho de montar el robot es como un juego, os invito a ver el siguiente video que deseo que os guste, todo y que hay alguna parte que no se aprecia correctamente, lo siento !


Tiempo real utilizado para desmontarlo tranquilamente = 25 minutos, mientras que para su montaje y ajuste completo fueron unos 45 si no recuerdo mal. El peso del robot actual, diria que menos de 3 kilos ! Ahora la cosa empieza a ser más autotransportable.

domingo, 6 de enero de 2013

Resumen GigaPans 2012

Empezamos el año, nuevos proyectos, nuevas ilusiones, y nuevos retos. El reto más importante del pasado año fue la creación de una Gigapan que tuviera un tamaño digno (lo siento pero eso de entrar a la web de www.gigapan.com y encontrarme panorámicas de 30 Mpixels creo que no es serio cualitativamente).

Después de haber realizado tres gigapans en este pasado 2012 , he ido aprendiendo detalles y detectar varias cosas que se pueden mejorar. En un par de posibles destinos a retratar tuve que tirarme para atrás por la gran dificultad que había para acceder con todos los “trastos” necesarios, no era sólo una cuestión de volumen, sino del peso.

 En la primera Gigapan  (08260-Súria) junto a Emma pude descargar los trastos a 10 metros del punto desde dónde realizaría el proyecto. En la segunda (Solsona) ya tuvimos que arrastrarlo todo 100 metros, por lo que previamente ya le había adaptado dos ruedas al robot, todo y eso había un motor NEMA-17 que va a pocos centímetros del suelo y hay que ir con mucho cuidado sorteando todos los bachecitos, por suerte no sufrió ningún accidente.


 Finalmente, en la última Gigapan del Pont de Vilomara tuve la colaboración de @engeeknyer y su sobrino, y en esa ocasión el recorrido que había entre el lugar desde dónde podíamos acceder con el coche y el sitio desde dónde tomaríamos la fotografía.
de cerca de 1 Km.  

Unos enlaces a las tres giga panorámicas del 2012:

Fecha: 21 Abril 2012
1.098 capturas (61 horizontales x 18 verticales)
Resolución final = 11.43GPx
Exposición = 1/650”
Diafragma = f11




26 mayo 2012
1.827 capturas (87 horizontales x 21 verticales)
Resolución final = 19.42 GPx




08254 Pont de Vilomara www.gigapan.com/gigapans/109009
03 Julio 2012
1.196 capturas ( 46 horizontales x 26 verticales )
Resolución final = 15.68 GPx
Exposición = 1/400”
Diafragma = f9



Lo que quedo claro es que una sola persona no podía acarrear todo el peso del robot (unos 20 kg) más toda la parte de fotografía, por lo que el próximo objetivo era reducir peso y volumen. Si los fabricantes profesionales tenían robots más pequeños , yo también podía intentarlo...

martes, 1 de enero de 2013

El gran dia (Gigapan Súria)

21 de abril del 2012, después de muchos problemas y quebraderos es el día fijado para salir al campo y probar el invento y todo lo que vendría después…

Salimos de casa con el robot, mesita, cámara, objetivo,… y nos desplazamos hasta Súria, una población minera situada en el corazón de Catalunya.

Empezamos descargando todos los “trastos”,  en una pista de acceso entre Can Reguant y Costafreda y empezamos a colocar la mesa de suportación. Mediante el uso del teléfono móvil utilizo una aplicación para nivelar la mesa con el uso de piedras (sistema rupestre, pero efectivo).

Una vez la mesa está a nivel , se coloca el robot y vuelvo a verificar el nivel justo antes de empezar a montar la cámara Nikon d90 , su grip MB-80 (2 baterías EN-EL3e cargaditas) y el objetivo Sigma Bigmos 150-500mm. f5-6.3 .

Desactivo el estabilizador del objetivo y pongo el sistema óptico en modo enfoque manual, y fijo el zoom en 400 mm aproximadamente (conocedores de este objetivo dicen que es la mejor relación calidad/zoom de este objetivo).

Previamente y mediante el uso de varios softwares (Google Earth, TPE y DOF Master) ya tenia decidido el punto óptimo de enfoque para buscar un compromiso , en este caso rondaba los 750 metros (aproximadamente en la iglesia situada en centro del pueblo). Para fijar tanto el anillo del zoom como el de enfoque utilizo cinta aislante al ser un material eficiente, ligero y que no deja marcas.

Como que parte de la gracia de este tipo de fotografía es que quede más o menos todo dentro de foco, tenia claro que una apertura de f11 seria “buena” para tener la profundidad de campo deseada.

Como que el zoom equivalente es de 600 mm aprox. y para evitar trepidación ajusto la velocidad a 1/650”.

Realizo varios barridos para determinar con esta apertura de f11 y velocidad de 1/650” cual es la mejor velocidad e ISO y me sale lo mejor como ISO 640.

Aprovecho para fijar el modo de captura en RAW (NEF Nikon), y desactivo parámetros de visualización previa en pantalla para optimizar los consumos y no poner en riesgo el proyecto.

Se fijan todos los parámetros en la cámara en modo manual. Acto seguido empieza el momento Arduino.

Decido el ángulo de visión (aproximadamente 162º en horizontal  x 32º en vertical lo que da una resultante de 61 x 18  = 1.098 capturas, el numero me gusta , me recuerda a un modelo de motos de la marca Ducati !).

Con este zoom obtendré capturas de 2,7º x 1,8 º, y un solape de aproximadamente un 25% entre fotos tanto en vertical como en horizontal.

En la pantalla del Arduino me va dando información…una de ellas es la previsión del tiempo que asciende a 1 hora y 31 minutos, ya que acaba realizando una foto cada 5 segundos de media.

Una vez se mueve el robot, hay un tiempo de 1 segundo antes y después de realizar las capturas para que nada se mueva…

La cosa empieza y parece que todo responde, entre nervios voy tweeteando con el hashtag #GigapanSuria y realizamos capturas de video. Todo parece que funciona según lo previsto. El ventilador para refrigerar los 2 drivers de potencia L298N para los motores Nema-17 funciona correctamente y sólo se activan cuando el calor en el disipador excede de los 25ºC

Pasa el rato , hasta que aparece en el LCD de 2x16 la frase “Panorámica Completa !!!”.

En ese momento, verifico algunas capturas mediante la pantalla de la cámara, en principio las baterías han aguantado, la SD , todo bien!

Recogemos trastos y una vez de vuelta en casa hay que probar el software AutoPano Giga, que en pequeñas panorámicas me funcionaba bién pero que nunca había probado con GigaPans…

Ahora empieza el paso de convertir los 11.72 Gb RAW en JPG y tras realizar una operación en bloque y darle el tono requerido, manipular algunas de ellas (cielo). El paso de conversión NEF > JPG tarda una hora con mi iMac core i5 3.6GHz con 12 Gb de RAM…

Ahora abro el PanoramaGiga 2.6 y empiezo a renderizar, la vista previa promete… y tras 3 horas 42 minutos aparece el archivo resultante de 91.48 Gb en formato PSB que me es imposible de manipular desde PS. El software me informa que el factor RMS = 2.53 que es un índice de calidad de solape.

Tengo el archivo pero no puedo abrirlo correctamente, el pobre PhotoShop sufre a cada click ! por lo que decido subirlo a Gigapan mediante el  Upload  1.2.0086 y tras 66.346 segundos ya está subido a Internet, momento en que abro el enlace
www.gigapan.com/gigapans/103601 y ya puedo ver la culminación de todo el proyecto
Os dejo el link del making off.


Si alguien necesita datos o ayuda, no dudéis en pedir !

Feliz 2013 !

domingo, 12 de febrero de 2012

Malas costumbres y mi eje de pupila

Una de las malas costumbres que tenemos es la de enfocar a un punto X (usando la medición en el centro de la imagen que es lo más habitual) y luego girar la cámara unos grados para que la composición sea “perfecta” , esto solemos hacerlo cuando nos encontramos que enfocando directamente hacia dónde queremos hacer la foto no se nos enfoca el elemento X. Esta costumbre nos la han explicado a todos como si fuera un buen truco, pero aquí veremos que nos hemos llevado un chasco y quizás entenderemos el porqué aquella vez no logré dejar el elemento X enfocado.


En este caso nosotros queremos fotografiar el recuadro rosa pero quedemos que la chica quede enfocada totalmente, y lo que muy mal hacemos es enfocar a la chica (recuadro de color rojo) y luego girar la cámara unos grados antes de terminar de pulsar el disparador.

A lo tonto, nos creemos que hemos “engañado” a la maquina, pero en realidad nos hemos engañado nosotros mismos (mira que somos tontos). Al hacer click en el disparador obtendremos la foto, pero quizás la chica no salga tan enfocada como habíamos pensado.


Hemos creado a lo tonto un triangulo entre nosotros , la distancia que ha enfocado la cámara, y el punto dónde esta la chica.
Para que nos hagamos una idea , mejor la siguiente imagen y tabla

El circulo rosa es la distancia a la que hemos enfocado inicialmente, pero vemos que si giramos 15 º la cámara, la distancia que hay entre nosotros y la perpendicular de la modelo es de 4,83 metros en vez de los 5, este error lo llamaré e en la tabla para que nos hagamos una idea más directa y observaremos que el error va creciendo cada vez en forma exponencial.
Muchas veces el error es casi inapreciable en nuestra captura, pero eso no quita de que hagamos las cosas bien. Cuanto menos profundidad de campo más se notará el error.

Para evitar esto, la  solución es fácil: definir correctamente el punto de enfoque manualmente y no andar girando la cámara. Esto es muy fácil desde la mayoría de réflex digitales con varios puntos de enfoque, donde tenemos un pequeño joystick en cruz que nos sirve para definir el punto de enfoque: Encuadramos la escena y mediante el Joystick elegimos nuestro punto de enfoque , y a continuación click ! A que es fácil hacer las cosas bien y utilizar los recursos que nos proporciona nuestros equipos ( me doy cuenta que la gente no quiere leer, se piensan que lo saben todo usando su intuición….a ver si alguien se lee los manuales algún día junto los aparatos ! ) . Luego para evitar movimientos en nuestro punto de enfoque deberemos bloquearlo usando el interruptor de bloqueo del “joystick” , en este caso seria coincidiendo en punto y la ralla , quedando en modo Lock.

Todo esto viene relacionado con el Punto Nodal y la pupila de entrada del objetivo.

Si lo que deseamos hacer es una panorámica correctamente este punto no nos puede pasar por alto, debemos conocer dónde se encuentra  realmente el eje óptico de rotación de nuestra cámara (no está en el sensor del cuerpo, lo localizaremos en el objetivo). Cuidadín ¡ que esto no quiere decir que las que hemos hecho hasta ahora no se vean bien pero quizás podían haber salido mal….sobretodo en aquellas en que los elementos fotografiados estaban muy cerca. Todo y tener el software apropiado de edición nos puede llegar a dar fatales sorpresas que no pueda corregir aparte de ponérselo más difícil.

Antes de empezar y si no queremos ser altamente perfeccionistas, sólo un consejo : deberemiamos quitarnos la costumbre de movernos junto a la cámara cuando hacemos panorámicas.
En este caso hemos obtenido un muy buen ángulo de solape entre las 3 capturas rotando la cámara desde el mismo eje (usando trípode), en cambio lo que hacemos habitualmente es quedarnos quietos nosotros en un mismo punto e ir girando nuestro cuerpo con la cámara , lo que provocamos dos cosas: que el centro de las tres capturas sea diferente y que el ángulo de solape sea mucho inferior respecto cuando rotamos sobre el eje de pupila del objetivo.
El eje de pupila es un punto que esta a la altura del centro del objetivo y a una distancia que deberíamos calcular, o bien haciendo pruebas con el uso de rotulas panorámicas que nos dejan avanzar/retrasar el conjunto de cámara+objetivo respecto al eje de rotación del trípode.

Pero si lo que queremos es calcularlo de verdad, deberemos jugar antes .Aqui es dónde directamente os recomiendo el Blog de Manuel Portillo dónde nos cuenta como hacerlo en el siguiente link:

En el siguiente link hay unas tablas dónde nos dan el resultado calculado, por si os sirve de algo: http://wiki.panotools.org/Entrance_Pupil_Database

Nos crucemos unos mails con el importador de Sigma en España informándome de unas distancias pero yo aun no lo veo claro. También leí en algún lugar que el punto de eje de pupila es dónde está situado el diafragma.

Después de varias lecturas y de liarme aun más, tomé una decisión: establecer mi eje de pupila a la altura de la fijación del Sigma 150-500 a la zapata del trípode, eso si, algún día realizaré las pruebas pero hacerlo dentro de casa es todo un espectáculo. De momento he adaptado mi invento para que el eje de rotación del movimiento vertical sea siempre en el mismo punto (ahora rota en el centro del objetivo) por lo que he tenido de modificar un poco la soportación. En cambio para el plano horizontal he retrasado un poco la soportación de la perfilaría vertical para que los ejes de los rodamientos verticales y horizontales coincidieran obteniendo un punto en el aire que se mueva tanto la base horizontal como el mecanismo vertical mi punto de pupila de entrada del objetivo sea el mismo
Originalmente cuanto más se inclinaba la cámara, el diámetro generado en el plano horizontal de rotación, se hacia más grande (círculos naranjas) , y también variaba la posición del centro del objetivo formando la línea verde . Si interaccionábamos las dos circunferencias generábamos un eje de pupila que rondaba entre los 7 cms de alto por 13 de ancho en forma de pelota de rugby.

Tras retrasar el eje de rodamientos del movimiento vertical para que coincidiera con el mismo centro del eje de rotación horizontal, conseguí "cargarme" la línea naranja original obteniendo un solo punto de rotación independientemente de la posición de la mesa.

Y ahora un video en el que podeis ver como se mantiene en todo momento el extremo del destornillador en el mismo punto indistintamente de la inclinación o la rotación de la mesa.