Es frecuente querer realizar cortes precisos en diversos materiales para realizar prototipos. Estos materiales suelen ser ligeros y maleables, como el aluminio, el plástico o la madera de balsa. En particular, estos dos últimos, tienen la capacidad de presentar un buen comportamiento ante un cortado láser.

Es por ello que pensamos que podría ser útil a la par que atractivo el realizar una cortadora láser para los laboratorios del departamento de Sistemas y Automática de la Universidad de Sevilla, ya que estos contaban con varias unidades de brazos robóticos Scorbot. Veamos cómo lo hicimos.

Introducción:

Un Scorbot no es más que un brazo robótico educativo que se puede encontrar en muchos talleres y escuelas universitarias. Posee 5 grados de libertad y es una herramienta muy práctica para aprender robótica y automatización industrial, aunque eso lo dejamos para otro artículo posterior.

Fase previa:

Sabíamos que teníamos que comunicar el Scorbot (su controladora) con el PC a través del puerto serie RS-232, aunque no teníamos mucha idea de cómo hacerlo. Por ello, navegando por la web, encontramos dos programas muy útiles: El look RS232 y el Virtual Serial Port Emulator. Con el empleo simultáneo de ambos conseguimos capturar las tramas enviadas desde el Scorbot hacia el PC y viceversa, visualizándolas en pantalla. ¿Y para qué necesitamos saber esto si podemos reproducir los comandos directamente? Pues, sencillamente, porque no tiene por qué haber un mapeado directo entre los caracteres que se pulsan en el teclado y los que son enviados por el puerto COM del ordenador, como realmente pasó. Además, hay una serie de caracteres que se mandan al inicio y que "despiertan" a la controladora del Scorbot, que son:

|echo. .....

Por otro lado, hay que configurar la tasa de transmisión y los parámetros, que en nuestro caso serán:

Tasa de transmisión (Baud rate): 9600
¿Bit de stop?: Sí, 1
¿Bits de datos?: 8
¿Bit de paridad?: No

Llegados a este punto, ya seremos capaces de mandar órdenes a nuestro Scorbot sin utilizar la pistola de programación o el interfaz gráfico de MS-DOS que nos proporciona el fabricante (llamado ATS). Basta con abrir una consola (o el HyperTerminal si estamos en Windows), seleccionar el puerto COM correspondiente a donde esté conectada la controladora (seleccionando los parámetros arriba indicados) y teclear el código para "despertar" al brazo: '|echo. .....'

Si tras esto escribimos cualquier orden comprensible para el robot como "HOME", teóricamente, la ejecutará. Y decimos teóricamente porque realmente (no se puede asegurar categóricamente, pero creemos que en todas las plataformas pasará igual) a nosotros no nos funcionó. La explicación es sencilla: La forma de decirle al Scorbot que hemos terminado de teclear una orden y queremos que la ejecute desde el terminal (o el HyperTerminal) es pulsar intro. Esto suele mandar el carácter '\n' o '\r', pero la controladora no espera ese carácter para ejecutar, sino que espera '.' La solución es sencilla: Le pasamos la cadena "HOME." y luego pulsamos intro. Ahora sí se ejecutará.

Las comunicaciones están listas. Primera fase concluida. Veamos cómo hacer los planos de corte.


¿Cómo crear los planos de la pìeza a cortar?

Podemos pensar que los planos de una pieza son complicados y necesitamos herramientas CAD sofisticadas. Nada más lejos de la realidad: Un simple fichero de texto plano (.txt) puede servirnos. Basta con que haya un protocolo o entendimiento entre el fichero que le pasemos y el tipo de corte que realice la cortadora (por ejemplo, en un fichero de texto plano, podemos crear "líneas de corte" con 5 columnas: las dos primeras para las coordenadas X, Y del inicio del corte, las dos segundas para las coordenadas del fin del corte y la última para el tipo de interpolación y velocidad -lineal al 50%, al 20%...-).

En nuestro proyecto nos hemos decidido por realizar los planos en una imagen que podamos editar con cualquier herramienta gráfica (como Paint, Gimp, Photoshop o similares) y a ésta, hacerle un procesado en MatLab de tal forma que, automáticamente, se detecten los puntos singulares de la pieza y ejecute los cortes adecuados.

En esta primera versión, no se ha contemplado que las piezas puedan contener huecos u oquedades. Si éste fuera el caso, se deberá hacer el corte en varias fases. En la primera se eliminará el contorno con un plano imagen que no contenga las oquedades, y para las fases posteriores se utilizarán máscaras con la forma de las oquedades que el brazo recortará sobre el patrón antes realizado.

Todo empieza a parecer complejo, pero es mucho más sencillo de lo que parece. Tal vez observando el código y comentándolo todo resulte más intuitivo.

 


Código de MatLab empleado:

% PROYECTO - CORTADOR DE PIEZAS AUTOMATIZADO
% Autores: Javier Galnares Arias y David Mejías Rivas.
% Lab. de Automatización y Robótica: Curso 2010 / 2011
% 5º Ingeniería de Telecomunicaciones (98)
% Escuela Superior de Ingeniería de Sevilla

%%% Limpieza de figuras y shell

close all;
clear all;
clc;

msk = [1 1 1; 1 0 1; 1 1 1];

%%% ADQUISICIÓN DE IMÁGENES E INICIALIZACIÓN

% Se capta una imagen en formato BMP y se somete a un preprocesamiento

fprintf('Introduzca el nombre de la imagen con su extensión: ');
nombre_imagen = input('\n', 's');

[imagen, map] = imread (nombre_imagen); % en color RGB
imagen = double(imagen)/255;

[nf, nc, z] = size(imagen); % Tamaño de la imagen

if ( z == 3 )
imagen = rgb2gray(imagen);
end

% Parámetros para cuadrar los planos Scorbot-Imagen
x0 = 0;
y0 = 0;
x1 = 500;
y1 = 2700;

% Número de pasos de corte del Scorbot
disp('Introduzca el número de puntos de corte a interpolar según el');
puntos_max = input('plano imagen (valor típico: 100)\n');
% Tiempo estimado de movimiento entre pasos
t_mov = 3;

%%% Esquinas y fronteras

[puntos, bordes] = corner2(imagen);

%%% Ordena puntos de paso

x = puntos(1, 1);
y = puntos(1, 2);

encuentra = 0;
terminar = 0;
i=1;

camino = [];

bordes_aux = bordes;

while( sum(sum(bordes_aux)) > 0 && terminar == 0)
direccion = msk.*bordes_aux(((x-1):(x+1)), ((y-1):(y+1)));
if sum(sum(direccion)) == 0
disp('Error: Imagen no conexa');
terminar = 1;
end

for m=1:3
for n=1:3
if (direccion (m, n) == 1) && (encuentra == 0)
camino(i, 1) = x;
camino(i, 2) = y;
x = x + m - 2;
y = y + n - 2;
encuentra = 1;
end
end
end

if ((x == puntos(1, 2)) && (y == puntos(1, 1)))
terminar = 1;
end

bordes_aux(x, y) = 0;
encuentra = 0;
i = i + 1;
end

camino(i, 1) = x;
camino(i, 2) = y;

n_puntos = length(camino(:,1));

%%% Correspondencia plano Scorbot - plano imagen

% Calculamos el ángulo de rotación entre los planos imagen y Scorbot
a = atan2((y1 - y0), (x1 - x0)) - pi/2;
% Calculamos el factor de escala a aplicar
escalado = sqrt((x1 - x0)^2 + (y1 - y0)^2)/nc;

if n_puntos > puntos_max
paso = round(n_puntos / puntos_max);
else
paso = 1;
disp(' ');
disp('¡Atención!');
disp('Precisión insuficiente en la imagen para el exigido en el robot');
disp(' ');
end
camino_scorbot = [];
camino_scorbot(1, :) = camino(1, :);
i = 1;

while(n_puntos > paso)
camino_scorbot((i+1), :) = camino((i*paso), :);
n_puntos = n_puntos - paso;
i = i + 1;
end

camino_scorbot((i+1), 1) = camino(1, 1);
camino_scorbot((i+1), 2) = camino(1, 2);
n_movimientos = length(camino_scorbot(:, 1));

M = [cos(a) -sin(a); sin(a) cos(a)];
vec_imagen = zeros(2, 1);
vec_scorbot = zeros(2, 1);

for i=1:length(camino(:, 1))
vec_imagen(1, 1) = camino(i, 1);
vec_imagen(2, 1) = camino(i, 2);
plano_imagen(i, :) = vec_imagen';
end

for i=1:length(camino_scorbot(:, 1))
vec_imagen(1, 1) = camino_scorbot(i, 1);
vec_imagen(2, 1) = camino_scorbot(i, 2);
vec_scorbot = M*vec_imagen;
plano_scorbot(i, :) = vec_scorbot';
end

plano_imagen = escalado*plano_imagen;
plano_scorbot = escalado*plano_scorbot;

%%% Figuras e imágenes

m_corte = floor(t_mov*n_movimientos/60);
s_corte = mod(t_mov*n_movimientos, 60);
fprintf('\t Tiempo estimado del corte: \t\t %i min. %i s.\n', m_corte, s_corte);

figure(1)
subplot(2, 2, 1)
imshow(imagen),
title('Plano CAD de la pieza');

subplot(2, 2, 2)
imshow(bordes);
hold on
plot(puntos(:,2), puntos(:,1), 'r+'),
title('Detección de puntos singulares y bordes');

subplot(2, 2, 3)
plot(plano_imagen(:, 2), plano_imagen(:, 1), 'b'),
title('Plano imagen escalado (girado 180º)'),
xlabel('Décimas de milímetro'),
ylabel('Décimas de milímetro');

subplot(2, 2, 4)
plot(plano_scorbot(:, 2), plano_scorbot(:, 1), 'b'),
title('Plano Scorbot (girado 180º)'),
xlabel('Décimas de milímetro'),
ylabel('Décimas de milímetro');

%%% Comunicación serie

PS1=serial('COM1');
set(PS1,'Baudrate',9600); % Se configura la velocidad a 9600 Baudios
set(PS1,'StopBits',1); % Se configura bit de parada a uno
set(PS1,'DataBits',8); % Se configura que el dato es de 8 bits
set(PS1,'Parity','none'); % Se configura sin paridad

% Abrimos el puerto COM1 para comunicarnos con el Scorbot
fopen(PS1);

% Mandamos la cadena '|echo .....' como nos señaló la captura con el
% lookRS232.
fwrite(PS1, '|echo.');
fwrite(PS1, ' ');
for i=1:5
pause(2);
fwrite(PS1, '.');
end
% Hacemos un 'HOME' para calibrar el Scorbot
fwrite(PS1, 'HOME.');
disp('Realizando calibrado... Pulse ENTER cuando finalice');
pause;
% Ponemos la velocidad a 20
disp('Calibrado terminado. Velocidad 20. ¡Precaución! Comienza el corte');
fwrite(PS1, 'SPEED 20.');

% Llevamos al Scorbot a la posición (0, 0) del plano Scorbot
% Esto habría que recalibrarlo para cada Scorbot
fwrite(PS1, 'HERE POS_SCORBOT.');
fwrite(PS1, 'SHIFTC POS_SCORBOT BY X 1000.'); % 'X' 10 cms
fwrite(PS1, 'SHIFTC POS_SCORBOT BY Y 1500.'); % 'Y' 15 cms
fwrite(PS1, 'SHIFTC POS_SCORBOT BY Z 2500.'); % 'Z' 25 cms
fwrite(PS1, 'MOVELD POS_SCORBOT.');
% Guardamos la posición del origen de coordenadas en POS_INI
fwrite(PS1, 'HERE POS_INI.');
pause(5);

repetir = 1;
while(repetir == 1)
fwrite(PS1, 'HERE POS_SCORBOT.');
fwrite(PS1, 'SHIFTC POS_SCORBOT BY X ');
fwrite(PS1, int2str(plano_scorbot(1, 1)));
fwrite(PS1, '.');
fwrite(PS1, 'SHIFTC POS_SCORBOT BY Y ');
fwrite(PS1, int2str(plano_scorbot(1, 2)));
fwrite(PS1, '.');
fwrite(PS1, 'MOVELD POS_SCORBOT.');
pause(5);

% Activamos el láser poniendo la salida 5 ON o cerrando la garra,
% según el modelo de láser que estemos usando
fwrite(PS1, 'SET OUT[5] 1.'); % Láser alimentado por el controlador
% fwrite(PS1, 'CLOSE.'); % Láser alimentado a pilas

for i=2:n_puntos
fwrite(PS1, 'HERE POS_SCORBOT.');
auxX = plano_scorbot(i, 1) - plano_scorbot(i-1, 1);
fwrite(PS1, 'SHIFTC POS_SCORBOT BY X ');
fwrite(PS1,int2str(auxX));
fwrite(PS1, '.');
auxY = plano_scorbot(i, 2) - plano_scorbot(i-1, 2);
fwrite(PS1, 'SHIFTC POS_SCORBOT BY Y ');
fwrite(PS1,int2str(auxY));
fwrite(PS1, '.');
fwrite(PS1, 'MOVELD POS_SCORBOT.');
pause(3) % Espera 3 s.
end;

% Desactivamos el láser quitando la salida 5 o abrimos la garra,
% según el modelo de láser que estemos usando
fwrite(PS1, 'SET OUT[5] 0.'); % Láser alimentado por el controlador
% fwrite(PS1, 'OPEN.'); % Láser alimentado a pilas

fwrite(PS1, 'MOVED POS_INI.');
pause(5);

disp('¿Desea cortar otra pieza idéntica a la anterior? [s/n]:');
resp = input('\n', 's');
if (resp == 's' || resp == 'S')
repetir = 1;
else
repetir = 0;
end
end

% Cerramos el puerto, devolviéndole el control al SO
fclose(PS1);
delete(PS1);
clear PS1;


Resumen y tareas pendientes:

Con todo esto y todos los materiales preparados, sólo resta probarlo todo. Para ello:

  1. Editamos una imagen cualquier para probar nuestro proyecto.
  2. Arrancamos MatLab y llamamos a nuestra función de corte.
  3. Esperamos el resultado de la pieza terminada.

En esta primera versión no se ha tenido en cuenta los caracteres devueltos por la controladora del brazo tras ejecutar una orden por ciertas imposibilidades que surgieron en la captura. Estamos trabajando para poder solucionarlo, aunque lo que proponemos es hacer una espera activa con temporizadores (estimamos la duración de los movimientos de corte y, en base a esos tiempos, le indicamos al Scorbot que pare o se mueva).

Los vídeos con el resultado del proceso intentaremos ponerlos a vuestra disposición en cuanto podamos.