Informe - Escuela de Ingeniería Eléctrica

Anuncio
Universidad de Costa Rica
Facultad de Ingeniería
Escuela de Ingeniería Eléctrica
IE – 0502 Proyecto Eléctrico
“Desarrollo de un modelo y simulador tridimensional
del brazo robot Stäubli RX 90 L”
Por:
David Cuenca Alpízar
Ciudad Universitaria Rodrigo Facio
Julio del 2006
“Desarrollo de un modelo y simulador tridimensional
del brazo robot Stäubli RX 90 L”
Por:
David Cuenca Alpízar
Sometido a la Escuela de Ingeniería Eléctrica
de la Facultad de Ingeniería
de la Universidad de Costa Rica
como requisito parcial para optar por el grado de:
BACHILLER EN INGENIERÍA ELÉCTRICA
Aprobado por el Tribunal:
_________________________________
Ing. Federico Ruiz Ugalde, Lic.
Profesor Guía
_________________________________
_________________________________
Ing. Francisco Siles Canales, Lic.
Ing. Andrés Díaz Soto
Profesor lector
Profesor lector
ii
DEDICATORIA
A mi familia y amigos.
iii
ÍNDICE GENERAL
ÍNDICE DE FIGURAS ................................................................................vi
ÍNDICE DE CUADROS ........................................................................... viii
GLOSARIO ..................................................................................................ix
RESUMEN....................................................................................................xi
CAPÍTULO 1: Introducción.........................................................................1
1.1 Justificación ..................................................................................................................1
1.2 Objetivos.......................................................................................................................2
1.2.1 Objetivo general.................................................................................................2
1.2.2 Objetivos específicos .........................................................................................2
1.3 Metodología ..................................................................................................................2
CAPÍTULO 2: Desarrollo teórico ................................................................4
2.1 El sistema operativo GNU/Linux .................................................................................4
2.2 El lenguaje de programación C ....................................................................................5
2.3 Las interfaces de programación de aplicaciones (API’s)..............................................5
2.3.1 La interfaz OpenGL ............................................................................................5
2.3.2 La interfaz DIRECTX.........................................................................................9
2.3.3 Comparación general entre OpenGL y Direct3D ...............................................9
2.4 Teoría de modelado tridimensional aplicada a computadoras....................................11
2.4.1 Las coordenadas homogéneas...........................................................................12
2.4.2 Los sistemas de coordenadas Cámara, Global y Objeto...................................12
2.4.3 Trasladar, rotar y escalar objetos ......................................................................16
2.4.4 Sistemas gráficos ..............................................................................................19
2.4.5 Arquitectura Gráfica (PIPELINE) ....................................................................20
2.5 Introducción a OpenGL, GLUT y GTK .....................................................................22
2.5.1 La ventana de despliegue utilizando GLUT .....................................................22
2.5.2 La ventana de despliegue utilizando GTK........................................................23
2.5.3 Primitivas y vértices..........................................................................................24
2.5.4 La matriz CTM .................................................................................................25
2.5.5 La matriz de transformación del modelo ..........................................................26
2.5.6 Transformación de la visión y proyección de la escena. ..................................27
iv
CAPÍTULO 3: La robótica .........................................................................30
3.1 Las bases de los robots................................................................................................30
3.2 El brazo robot..............................................................................................................31
3.2.1 El brazo robot Stäubli RX90.............................................................................32
3.2.2 Geometría de brazo robot Stäubli RX90...........................................................33
CAPÍTULO 4: Desarrollo de la aplicación de simulación ........................36
4.1 Simulador del brazo robot Stäubli RX90 L ................................................................38
4.1.1 El brazo robot Stäubli como un modelo jerárquico ..........................................39
4.1.2 El modelado del brazo robot Stäubli RX90 L...................................................42
4.1.3 El método de detección de colisiones ...............................................................51
CAPÍTULO 5: La interfaz gráfica de usuario de la aplicación ................64
5.1 Los botones de configuración del simulador .............................................................68
5.2 Creación, edición y ejecución de rutinas ....................................................................70
5.3 Envío de ordenes al simulador por medio del teclado y el ratón................................72
CAPITULO 6: Pruebas finales y sus resultados........................................74
6.1 Pruebas destructivas....................................................................................................74
6.2 Pruebas de velocidad ..................................................................................................74
6.3 Pruebas de memoria....................................................................................................77
CAPITULO 7: Conclusiones y Recomendaciones .....................................78
7.1 Conclusiones...............................................................................................................78
7.2 Recomendaciones .......................................................................................................80
BIBLIOGRAFÍA.........................................................................................81
APÉNDICES ...............................................................................................82
v
ÍNDICE DE FIGURAS
Figura 2.1 Plano 2D y espacio 3D ....................................................................................11
Figura 2.2 La cámara y el mundo .....................................................................................13
Figura 2.3 La cámara, el mundo y el objeto .....................................................................13
Figura 2.4 Sistemas de coordenadas Cámara, Global y Objeto........................................15
Figura 2.5 Rotación y Traslación......................................................................................18
Figura 2.6 Elementos típicos de un sistema gráfico .........................................................19
Figura 2.7 Arquitectura Gráfica (pipeline) .......................................................................20
Figura 2.8 Polígono y puntos............................................................................................25
Figura 2.9 Analogía entre cámara y computadora ............................................................28
Figura 2.10 Volumen de visión especificado por gluPerspective() ..................................29
Figura 3.1 Ejemplo de brazo robot con cuatro grados de libertad ....................................32
Figura 3.2 Partes del brazo robot Stäubli RX90 ...............................................................33
Figura 3.3. Medidas geométricas del brazo robot Stäubli RX90......................................34
Figura 3.4 Distribución de trabajo de las amplitudes de giro ...........................................35
Figura 4.1 Diagrama de flujo de la aplicación de simulación virtual ...............................37
Figura 4.2 Modelo de brazo robot de tres grados de libertad ...........................................39
Figura 4.3 Primer modelo creado del brazo robot. ...........................................................42
Figura 4.4 Principales componentes del brazo Stäubli RX90. .........................................43
Figura 4.5 Esfera con iluminación y sin iluminación. ......................................................49
Figura 4.6 Modelo del brazo robot Stäubli RX90 L. ........................................................50
Figura 4.7 Tipos de figuras de control ..............................................................................51
Figura 4.8 Figuras de control en el modelo del brazo robot .............................................52
Figura 4.9 Figuras de control (esquemático) ....................................................................52
Figura 4.10 Zonas de precaución ......................................................................................56
Figura 4.11 Orillas del hombro del brazo robot................................................................58
vi
Figura 4.12 Prueba de colisión de orillas (1) ....................................................................59
Figura 4.13 Prueba de colisión de orillas (2) ....................................................................59
Figura 4.14 Prueba de colisión de orillas (3) ....................................................................60
Figura 5.1 La aplicación gráfica de usuario del simulador...............................................64
Figura 5.2 Los botones de configuración del simulador...................................................68
Figura 5.3 Introducción de una base externa ....................................................................69
Figura 5.4 Introducción del margen de error ....................................................................70
Figura 5.5 Ventana 1 de la aplicación...............................................................................70
vii
ÍNDICE DE CUADROS
Cuadro 3.1 Amplitudes de giro y velocidades nominales y máximas ..............................35
Cuadro 4.1 Puntos y figuras de control.............................................................................53
Cuadro 4.2 Niveles de jerarquía de las figuras de control ................................................54
Cuadro 4.3 Condiciones y resultados de la prueba de colisión de orillas.........................61
Cuadro 5.1 Envío de ordenes por medio del teclado ........................................................73
viii
GLOSARIO
GNU/Linux:
Denominación para el sistema operativo que utiliza kernel
<<linux>>. Es un sistema multitarea multiusuario para PC´s.
Linux es una implementación del sistema operativo UNIX.
UNIX:
Sistema operativo multitareas, multiusuario y portable. Unix fue
desarrollado a finales de los sesenta en los laboratorios Bell y
hasta principios de los ochenta su uso estuvo restringido
fundamentalmente al entorno académico.
OpenGL:
OpenGL es una biblioteca gráfica desarrollada originalmente por
Silicon Graphics Incorporated (SGI). OpenGL significa Open
Graphics Library o en español: biblioteca abierta de gráficos. Se
utiliza para la creación y despliegue y manejo de primitivas
geométricas.
API:
Un API (Application Programming Interface) o interfaz de
programación de aplicaciones, es un conjunto de especificaciones
de comunicación entre componentes software. Generalmente está
compuesto por un grupo de funciones dedicadas un área
específica, por ejemplo la comunicación con el sistema de
ventanas.
GLUT:
GLUT (OpenGL Utility Toolkit) es una API multiplataforma que
provee una reducida funcionalidad para el manejo de ventanas e
interacción por medio de teclado y ratón.
GTK:
Inicialmente creado para construir el programa gráfico GIMP,
GTK es la abreviatura de GIMP toolkit y es muy usada por los
programadores de sistemas Linux para desarrollar interfaces
gráficas de usuario.
ix
RGBA:
RGBA es el termino utilizado para definir el modelo de color, en
el cual los colores primarios (rojo, verde y azul: <<Red Blue
Green>>) son combinados en forma específica con el fin de
obtener algún color determinado. La A en RGBA se refiere al
termino alfa, que define la transparencia de los colores.
Buffer:
Un buffer es un espacio de memoria, en el que se almacenan
datos, con el fin de evitar que el recurso que los requiere, ya sea
hardware o software, se quede en algún momento sin datos. Los
buffers también son llamados almacenadores intermedios.
Direct3D:
Es una interfaz de programación de aplicaciones, desarrollada
por Microsoft, que facilita el manejo y trazado de primitivas
gráficas elementales, así como la utilización de transformaciones
geométricas sobre las primitivas desplegadas.
textview:
Objeto de la librería Gtk que se utiliza para desplegar texto
dentro de una ventana.
IDE:
Un IDE es un entorno integrado de programación, generalmente
consiste en un editor de código, un compilador, un depurador y
un constructor de interfaz gráfica.
x
RESUMEN
La creación de un modelo tridimensional y un simulador virtual del brazo robot
Stäubli RX90 L es el objetivo principal de este proyecto. Para el desarrollo del programa de
simulación se empleó OpenGL como herramienta principal de trabajo. El código de la
aplicación fue desarrollado en su totalidad en el lenguaje de programación C.
La primera fase del proyecto la comprendió el estudio de la teoría de modelado
tridimensional en computadoras. Con ayuda de la información obtenida durante esta etapa,
se comenzó la búsqueda y análisis de las herramientas necesarias para el desarrollo de la
aplicación.
Se eligió la interfaz de programación de aplicaciones gráficas OpenGL, como la
librería principal para la creación del programa de simulación virtual. Se estudió y comenzó
a experimentar con las funciones brindadas por la librería. A partir de este punto, en el cual
ya se tenía un mejor conocimiento de la teoría de modelado y de las herramientas al
alcance, se dio inicio a la estructuración de la aplicación a desarrollar.
El siguiente paso fue analizar la geometría del brazo robot Stäubli RX90 L, con el
fin de determinar cuales características geométricas de éste son las más influyentes en los
procesos de colisión. Después del estudio del objeto real, se procedió a crear un modelo
virtual tridimensional de éste.
A partir de este punto se establecieron dos objetivos principales: la creación de un
motor de ejecución de espacio virtual, en el cual se pudiera manejar el modelo del brazo
robot Stäubli y la implementación de un método de detección de colisiones al motor de
ejecución.
Para alcanzar el segundo objetivo se tuvo que desarrollar un método nuevo de
detección de colisiones, el cual resultó ser, además de altamente efectivo y flexible,
verdaderamente simple en comparación con otros métodos existentes.
Una vez creados el motor de ejecución y los algoritmos de detección de colisiones,
se inició la estructuración de una interfaz gráfica de usuario, que no solo facilitara el uso de
la aplicación, sino que permitiera una futura unión con otras aplicaciones, en especial con
otros programas desarrollados para el mismo brazo robot. La interfaz se desarrolló
utilizando la librería GTK.
Una vez finalizada la aplicación, se llevó a cabo una serie de pruebas mediante las
cuales se descubrieron algunos problemas, solucionados posteriormente.
Se logró cumplir con el objetivo de crear una aplicación de simulación para el
sistema operativo GNU/Linux. La librería gráfica OpenGL y el lenguaje de programación
C demostraron poseer las cualidades necesarias para el desarrollo de aplicaciones gráficas
complejas. Los métodos de detección de colisiones implementados en el programa,
mostraron un buen funcionamiento durante la etapa de prueba.
xi
CAPÍTULO 1: Introducción
1.1
Justificación
El campo de la robótica se encuentra en constante crecimiento. Los robots se han
convertido en asistentes imprescindibles en muchas áreas. Se encargan de tareas que
pueden ser tediosas, pesadas, difíciles e inclusive imposibles para un ser humano.
Los avances tecnológicos de los últimos años han permitido que los sistemas
electromecánicos controlables puedan ser aplicados a diferentes áreas. Desde la industria
del entretenimiento, hasta la exploración espacial, desde la industria automotriz, hasta las
ciencias médicas, los robots afectan nuestras vidas muchas veces sin darnos cuenta.
Una parte imprescindible en el desarrollo de nuevas tecnologías robóticas y la
implementación de éstas, es el modelado y la simulación. Saber como se comportará el
sistema ante diferentes situaciones es indispensable tanto para el diseñador como para el
usuario. Mediante el uso de simuladores, los desarrolladores pueden detectar errores y
corregirlos antes del proceso de fabricación. Por otro lado los usuarios pueden utilizar
modelos virtuales del equipo para verificar previamente si las órdenes que se enviarán
tendrán el resultado deseado. Simular con anterioridad los movimientos que se le ordenarán
a un robot, incrementa no solo la eficiencia del proceso, sino que también ayuda a
resguardar la seguridad de los usuarios y del robot mismo, ya que mediante la
representación virtual se puede determinar si la ejecución de cierta orden por parte del
equipo, presenta algún riesgo para las personas alrededor o para el equipo mismo.
El uso de sistemas operativos propietarios no solo encarece el proceso de desarrollo
de aplicaciones, sino que también resta flexibilidad al producto final, ya que muchas de las
herramientas y aplicaciones desarrolladas sobre sistemas propietarios no pueden ser
implementadas en otros sistemas. El hecho de utilizar una plataforma abierta, para crear
modelos virtuales de equipos electromecánicos, presenta ventajas tanto para el
desarrollador como para el usuario.
Las observaciones anteriores representan algunas de las razones para el desarrollo
de este proyecto. La creación de un motor de ejecución y un modelo tridimensional del
brazo robot Stäubli RX90 L, además de la implementación de un método de detección de
colisiones, ayudará a incrementar la eficiencia y seguridad de los movimientos del brazo
robot. Además, la utilización del sistema operativo GNU/Linux y la biblioteca gráfica de
código abierto OpenGL, como bases del simulador virtual, incrementa la versatilidad y
estabilidad de la aplicación y permite que el código desarrollado pueda ser portado, sin
mayor complicación, a proyectos futuros e implementado en otras aplicaciones.
Aun cuando el objetivo específico de este proyecto sea la creación de un simulador
virtual del brazo Robot Stäubli RX90 L, el trabajo aquí desarrollado podría utilizarse como
base para crear modelos virtuales de otros sistemas eléctricos controlables.
1
2
1.2
Objetivos
1.2.1
Objetivo general
Crear un modelo tridimensional y un simulador virtual del brazo robot Stäubli
RX90 L.
1.2.2
1.3
Objetivos específicos
•
Investigar teoría del modelado tridimensional aplicado en computadoras.
•
Investigar acerca de las interfaces para programación de aplicaciones (API’s)
gráficas.
•
Crear un motor de ejecución de un espacio tridimensional virtual, para
implementar el modelo del brazo robot Stäubli RX90 L, capaz de acatar órdenes
de movimiento enviadas por el usuario.
•
Implementar al motor de ejecución un método de detección de colisiones capaz
de indicar cuando el brazo choca contra el piso o consigo mismo.
•
Utilizar el sistema operativo de licencia libre GNU/Linux para desarrollar la
aplicación gráfica.
•
Desarrollar la estructura de la aplicación de manera que se facilite una futura
unión entre ésta y el programa desarrollado en [4].
Metodología
La metodología empleada para la realización del proyecto fue la siguiente:
•
Se determina el objetivo general y los objetivos específicos del proyecto y se
utilizan como base para crear una estructuración de éste.
•
Se investiga acerca del sistema operativo GNU/Linux y de las interfaces de
programación de aplicaciones gráficas en tercera dimensión, principalmente
OpenGL y DirectX.
•
Se realiza un estudio profundo de la teoría del modelado tridimensional aplicado
en computadoras. Con base en la información obtenida durante las
3
investigaciones bibliográficas referentes al tema, se decide utilizar la interfaz de
programación OpenGL.
•
Se investiga acerca del brazo robot Stäubli RX90L y se lleva a cabo una
recopilación y un estudio de los proyectos y tesis anteriores, referentes al tema.
•
Se desarrolla un modelo inicial del brazo robot y un motor de ejecución, que
permite controlar el modelo utilizando el teclado y el ratón de la computadora.
•
Se investiga acerca de los métodos de detección de colisiones
virtuales, utilizados en aplicaciones gráficas.
•
Se desarrolla un algoritmo de detección de colisiones para el brazo robot virtual
y se implementa el algoritmo al motor de ejecución del espacio virtual.
•
Se lleva a cabo varias pruebas, para comprobar la eficiencia del método de
detección de choques.
•
Se crea una interfaz de usuario para la aplicación y se adapta ésta, de manera
que en un futuro pueda funcionar de forma conjunta a la aplicación desarrolla en
[4].
•
Se lleva a cabo algunas pruebas finales del funcionamiento de la aplicación
desarrollada en el proyecto.
•
De forma simultánea al desarrollo de los puntos anteriores, se lleva a cabo el
desarrollo del informe final del proyecto.
de objetos
CAPÍTULO 2: Desarrollo teórico
2.1
El sistema operativo GNU/Linux
GNU/Linux o Linux son los nombres que suele recibir el sistema operativo de
distribución libre y código abierto desarrollado en un principio por Linus Torvalds a
principio de los años noventa (1991) y el proyecto GNU fundado por Richard Stallmann en
1983. El término Linux se refiere estrictamente al núcleo o kernel creado en un principio
por Linus Torvalds y GNU/Linux al sistema operativo final, que utiliza bibliotecas y
herramientas desarrolladas por el proyecto GNU y muchos otros proyectos y grupos de
software. La palabra Linux también es usada comúnmente para referirse a las diferentes
distribuciones Linux, las cuales son colecciones de software que suelen contener grandes
cantidades de paquetes además del núcleo.
La colección de utilidades para la programación de GNU es por mucho la familia de
compiladores más utilizada en Linux. Tiene la capacidad de compilar C, C++, Java, Ada
entre muchos otros lenguajes. Existen varios ambientes integrados de desarrollo disponibles
para Linux, entre ellos están Anjuta, Kdevelop, NetBeans IDE y Eclipse.
Aunque originalmente Linux fue diseñado solamente para soportar procesadores
Intel 80386, actualmente soporta una gran variedad de arquitecturas, convirtiéndose en uno
de los sistemas operativos con mayor portabilidad.
El soporte técnico para usuarios de Linux normalmente se provee a través de foros
en línea, grupos de noticias y listas de correos electrónicos. Los grupos de usuarios Linux
LUG’s (por sus siglas en inglés: Linux Users Groups) tradicionalmente prestan soporte
local para los usuarios de Linux y para las personas que desean introducirse en el mundo
del software libre.
Su alta eficiencia, gran portabilidad, variada gama de software libre compatible y
muchas otras características, hacen que el sistema Linux represente una gran plataforma de
investigación y desarrollo de aplicaciones.
Al ser un sistema operativo de código abierto, el sistema Linux ha sufrido un
crecimiento acelerado. Programadores provenientes de diferentes lugares del mundo se han
unido al desarrollo y mejoramiento del sistema y las aplicaciones que este soporta. Para el
desarrollo del kernel del sistema Linux se utiliza el lenguaje de programación C.
4
5
2.2
El lenguaje de programación C 1
El lenguaje C, el cual fue creado en 1963 por Ken Thompson y Dennis M. Ritchie
en los laboratorios Bell, está basado en los lenguajes de programación BCPL y B. C está
orientado a la implementación de sistemas operativos, concretamente Unix. C es el
lenguaje de programación más utilizado para crear software de sistemas, se aprecia por la
eficiencia del código que produce.
Es un lenguaje de medio nivel, pero con muchas características de bajo nivel.
Dispone de las estructuras típicas de los lenguajes de alto nivel, pero también dispone de
construcciones del lenguaje que permiten un control a muy bajo nivel.
Entre algunas de las ventajas de C se encuentra el hecho de que es un lenguaje muy
eficiente, ya que es posible utilizar características de bajo nivel para realizar
implementaciones óptimas. A pesar de su bajo nivel es el lenguaje más portado en
existencia, existiendo compiladores para casi todos los sistemas operativos conocidos.
Proporciona facilidades para realizar programas modulares y además de la posibilidad de
utilizar código o bibliotecas existentes.
2.3
Las interfaces de programación de aplicaciones (API’s)
2.3.1
La interfaz OpenGL
OpenGL es una especificación estándar, la cual define un API multilenguaje y
multiplataformas para desarrollar aplicaciones gráficas con objetos de dos y tres
dimensiones. La interfaz consiste en más de 250 funciones que pueden ser utilizadas para
crear complejos gráficos tridimensionales a partir de simples primitivas geométricas.
OpenGL se extiende mas allá de las PCs y computadoras marca Apple, a muchos tipos de
sistemas UNIX.
Desde un punto de vista básico, OpenGL es una especificación, es decir que es
simplemente un documento que describe un conjunto de funciones y el comportamiento
preciso que éstas deben tener. A partir de esta especificación, los desarrolladores de
hardware crean implementaciones, bibliotecas de funciones desarrolladas de manera que
respeten las especificaciones descritas por OpenGL. Los productores de hardware deben
someter sus programas a diferentes pruebas, para poder calificar sus implementaciones
como implementaciones de OpenGL.
En comparación a Direct3D 2, OpenGL no es un API de alto nivel. Su propósito es
dibujar objetos. Las tareas de edición de objetos y entradas y salidas de archivos
(<<Input/Output>>) son relegadas a otras aplicaciones, como por ejemplo Open Inventor,
propiedad de Silicon Graphics. La interfaz OpengGL es multiplataforma, es decir que
1
2
Fuente: Programación en C y C++, http://www.cprogramming.com/
Direct3D: Biblioteca gráfica desarrollada por Microsoft.
6
puede ser utilizada sobre diferentes sistemas operativos, como por ejemplo Windows, Mac
OS-X, PlayStation 3, Linux y otros sistemas en base UNIX. Las funciones de OpenGL
pueden ser llamadas desde varios lenguajes, entre ellos C/C++, FORTRAN, Ada y Java.
El API OpenGL, comenzó como una iniciativa de la empresa Silicon Graphics para
crear una sola interfaz de programación gráfica, independiente de desarrolladores de
hardware. Antes de la introducción de OpenGL, muchos vendedores de hardware
utilizaban diferentes librerías gráficas, lo cual encarecía el proceso de crear programas
compatibles con diferentes plataformas de hardware. Esto llevo a Silicon Graphics a crear
OpenGL, aplicación cuya base original es la biblioteca gráfica IRIS. OpenGL empezó
como una especificación, luego Silicon Graphics creó una implementación, que podía se
utilizada por los creadores de hardware para desarrollar sus propias implementaciones. Los
desarrolladores de software no necesitan una licencia para utilizar OpenGL en sus
aplicaciones, mientras que los desarrolladores de hardware si deben adquirir una licencia
para poder crear una implementación de OpenGL. Por ejemplo, Mesa 3D es un API cuyo
código es compatible con OpenGL, sin embargo para evitar el pago de licencias por
creación de una implementación de OpenGL, no es llamada como una implementación
directa de OpenGL, sino como un API muy similar.
Una de la características más interesantes de OpenGL es que utiliza un mecanismo
de servidor y cliente para procesar los gráficos. El cliente gráfico utiliza OpenGL y el
sistema operativo para trasmitir las primitivas gráficas, coordenadas de vértices, colores y
texturas al servidor. En el servidor se utiliza la información obtenida para generar los
píxeles, los cuales luego son sometidos a las pruebas de mapeo de texturas, pruebas de
profundidad, pruebas de unión de colores , entre otras.
Aunque normalmente el cliente y el servidor se encuentran en la misma
computadora, la ventaja de la separación del proceso es que hace posible que una máquina
de bajo costo transmita, a través de una red, comandos de OpenGL a una máquina más
costosa y de mayor eficiencia, sobre la cual corre el proceso del servidor, luego las
imágenes finales pueden ser devueltas a la máquina de menor eficiencia para su despliegue.
OpenGL se puede manejar bajo dos tipos de modos, modo inmediato en el cual una
aplicación envía un comando a OpenGL y ésta los ejecuta inmediatamente y el modo de
retención, en el cual secuencias de comandos gráficos son almacenadas en estructuras de
datos conocidas como lista de despliegue o listas de exhibición. El uso de estas listas
presenta dos ventajas importantes. Si se debe graficar un objeto complicado de manera
frecuente, solamente es necesario referirse a la lista que posee la información del objeto,
además al estar la información de objeto en una lista, ésta se puede enviar rápidamente a
través de una red. La desventaja de las listas de exhibición es que si un objeto está
destinado a sufrir modificaciones frecuentemente, se deben generar nuevas descripciones
del objeto de igual manera.
OpenGL elimina la complejidad que representa para el usuario interactuar con
diferentes tarjetas gráficas, presentando al programador una interfaz única y uniforme para
7
el desarrollo y programación de gráficos tridmensionales. Además esconde las diferentes
capacidades que poseen diversas plataformas de hardware, ya que es requerido que todas
las implementaciones oficiales soporten todo el conjunto de funciones de OpenGL,
inclusive utilizando emulación de software si es necesario.
OpenGL es un API de proceso de bajo nivel, por lo que requiere que el
programador le dicte los pasos necesarios para crear una escena. Esto contrasta con las
API’s descriptivas, las cuales solo necesitan que el programador describa la escena y la
librería se encarga del resto de la representación de la escena. Tal característica de OpenGL
hace necesario que el programador posea un buen conocimiento del modelado
tridimensional, pero también da un cierto nivel de libertad para implementar nuevos
algoritmos de representación gráfica.
OpenGL fue desarrollada como una aplicación multiplataforma, por lo que trabaja
independiente del sistema de ventanas, razón por la cual no contiene comandos que
interactúen con éste. Funciones como abrir, escalar, dar forma y cerrar ventanas, determinar
la posición del mouse o cursor y determinar las entradas del teclado, son acciones que
deben ser relevadas a otras librerías. Existen varias librerías que han sido desarrolladas con
el propósito de brindar funcionalidad extra a los programadores que utilizan OpenGL.
GLUT, una librería que contiene utilidades y herramientas desarrolladas para trabajar en
conjunto con OpenGL, se encarga de todos los procesos de comunicación entre las
aplicaciones OpenGL y el sistema de ventanas del sistema operativo. Al igual que GLUT
existen muchas otras librerías, por ejemplo SDL, GLU, GLUI y FLTK, entre muchas otras,
que se encargan de añadir funcionalidad extra al API OpenGL.
La especificación de OpenGL es desarrollada y vigilada actualmente por ARB
(OpenGL Architecture Review Board), junta que fue formada en el año 1992. ARB está
constituida por diferentes compañías, las cuales están interesadas en crear un API
consistente, que se mantenga disponible durante un largo periodo. En abril del año 2006,
algunos miembros de ARB con poder de voto eran: Silicon Graphics, 3DLabs, ATI
Technologies, NVIDIA, Intel, IBM, Apple Computer, Dell y Sun Microsystems. Microsoft
quien fuera uno de los miembros fundadores, abandonó el grupo en marzo del año 2003. A
parte de estas corporaciones, otras compañías son invitadas a formar parte del grupo
OpenGL ARB durante periodos de un año. Con tantas compañías involucradas,
representando intereses tan diversos, OpenGL se ha convertido en un API con un amplia
gama de capacidades. Está planeado que la especificación de OpenGL pase a ser
controlada por el grupo Khronos, a finales del año 2006, esto con el fin de incrementar el
mercado y eliminar algunas barreras en el desarrollo de OpenGL y OpenGL ES, la cual es
un API derivada de la original, creada para ser implementada en sistemas móviles, tales
como teléfonos celulares, PDA’s y consolas de juegos de video.
Gran parte de la popularidad de OpenGL se debe a la excelente documentación
oficial existente. OpenGL ARB ha publicado una serie de manuales, los cuales son
actualizados constantemente. Esto manuales son conocidos por los colores de sus portadas.
8
Algunas de las opciones implementadas en OpenGL son las siguientes:
•
Las primitivas geométricas, las cuales permiten construir descripciones
matemáticas de objetos. Las primitivas que actualmente se encuentran
implementadas en OpenGL son: puntos, polígonos, imágenes y mapas de bits
(<<bitmaps>>).
•
OpenGL permite la codificación de color en los modos RGB y el modo de color
indexado.
•
Existe la posibilidad de mover la cámara por el espacio de la escena.
•
Se pueden utilizar texturas , para así incrementar el realismo de la escena que se
esta construyendo.
•
La iluminación, la cual es indispensable para poder distinguir entre un objeto
tridimensional y uno de dos dimensiones, está provista en OpenGL a través de
una serie de comandos que permiten calcular el color y las propiedades de los
materiales, así como las fuentes de luz en la escena.
•
Para lograr crear animaciones que luzcan continuas en el tiempo es necesario
utilizar la técnica del doble <<buffering>>. Esta técnica, la cual es posible
utilizar en OpenGL, consiste en construir cada cuadro de la escena en un
<<buffer>> separado de la memoria. Una vez terminado el cuadro de la escena,
el <<buffer>> con la información nueva remplaza al <<buffer>> que se muestra
actualmente en la pantalla, evitando de esta manera el parpadeo en la animación.
•
La técnica del anti-rizado, la cual permite reducir los bordes escalonados en las
líneas del dibujo, producto de la baja resolución.
•
El sombreado de Gouraud, que se aplica para crear sombras suaves en objetos
tridimensionales y así generar diferencias entre los colores de su superficie.
•
El <<buffer>> Z o <<buffer>> de fondo, el cual mantiene un registro de la
coordenada Z de cada vértice, lo que permite establecer la proximidad entre el
observador y el objeto y eliminar las superficies que se encuentran ocultas para
el observador.
•
Efectos especiales como humo y neblina, los cuales añaden realismo a la escena.
•
La mezcla Alfa (<<alpha blending>>), opción del código de colores RGBA,
que permite controlar las transparencias de los colores, por ejemplo para crear
una ventana o una mesa de vidrio.
•
Transformaciones de objetos, entre ellas la rotación, la traslación y el escalado.
9
2.3.2
La interfaz DIRECTX
Direct3D forma parte del API DirectX, propiedad de la empresa Microsoft.
Direct3D solamente se encuentra disponible para los sistemas Windows, superiores al
sistema Windows 95 y para las consolas de videojuegos XBox, también propiedad de
Microsoft. Direct3D se utiliza para crear gráficos tridimensionales. La interfaz soporta
aceleración de hardware, si la opción se encuentra disponible en la tarjeta de video del
equipo. El API contienen muchos comandos para crear escenas tridimensionales, pero
contiene pocos comandos para crear y controlar escenas en dos dimensiones.
Direct3D no es multiplataforma, pero sí logra esconder algunas de las diferencias
que existen entre los diferentes dispositivos de aceleración 3D, utilizando un proceso de
emulación de hardware.
Al igual que OpenGL, Direct3D puede operar tanto en modo inmediato como en
modo de retención. En el modo de retención Direct3D no utiliza listas de definición, sino
que ofrece una interfaz de alto nivel orientada a objetos. Después de haber cargado un
objeto, éste se puede rotar, escalar y trasladar utilizando diferentes funciones del API. En
el modo de retención, el API ofrece funciones con las cuales se puede leer y escribir un
formato de archivo que guarda datos de objetos tridimensionales, tales como objetos
predeterminados, texturas y conjuntos de animaciones.
2.3.3
Comparación general entre OpenGL y Direct3D1
Direct3D es un API propiedad de la corporación Microsoft, que provee aceleración
3D de hardware para las plataformas Windows.
OpenGL es un estándar abierto de un API que provee un número de funciones para
la producción de gráficos 2D y 3D. Una implementación de estos estándares se encuentra
disponible en la mayoría de los sistemas operativos modernos.
Portabilidad:
Por el momento Direct3D solo se ha implementado en sistemas operativos de la
familia Microsoft, incluyendo las versiones que se han utilizado en las consolas de
videojuegos XBox. Algunas funciones del API Direct3D han sido implementadas en el
proyecto Wine, el cual intenta trasladar APIs comúnmente utilizadas en Windows a Linux,
pero el trabajo ha sido difícil debido a la dependencia entre DirectX y otros componentes
del sistema Windows.
1
Fuentes: Artículo: “Comparison of Direct3D and OpenGL”, http://www.wikipedia.org
Artículo: “Direct3D vs. OpenGL”, http://www.gamedev.net
10
OpenGL posee implementaciones disponibles para una gran variedad de sistemas
operativos incluyendo Windows de Microsoft, Linux, sistemas basados en UNIX, Mac OS
X y las consolas de videojuegos de Nintendo y Sony, por ejemplo PlayStation 3. A
excepción de Windows, todos los sistemas operativos que permiten gráficos 3D con
aceleración de hardware han determinado OpenGL como la interfaz para programación
gráfica primaria. En términos de portabilidad, Direct3D posee más limitaciones que
OpenGL, sin embargo este encierro solo presenta un problema para algunas aplicaciones.
Facilidad de manejo:
Antes de la versión 8, Direct3D era conocido por ser un tanto difícil de manejar, por
ejemplo para realizar un cambio de estado se requería llevar a cabo un numero de
operaciones complicadas. Por ejemplo para habilitar la combinación de colores conocida
como <<alpha blending>>, se tenia que crear un <<buffer>> o almacenador intermedio
llamado <<buffer>> de ejecución, amarrarlo, llenarlo con los códigos de operación
correctos, junto con un encabezado estructural señalando cuántos códigos de operación
contenía el <<buffer>> y un código de operación especial de salida, liberarlo y finalmente
enviarlo al controlador de la tarjeta de video para su ejecución. Tal vez la queja mas famosa
fue entablada por el famoso desarrollador de videojuegos John Carmack en el archivo
“.plan”; él recomendaba a Microsoft abandonar Direct3D y utilizar OpenGL.
Sin embargo se produjeron muchos cambios en la versión Direct3D 8, los cuales
ayudaron a mejorar de manera notoria la imagen de Direct3D. Aun así Direct3D y OpenGL
son guiados por paradigmas distintos.
Direct3D está construido sobre el modelo de objetos COM de Microsoft, lo cual
indica que el uso de codigo C++ es un tanto inusual. Las funciones para adquirir valores no
devuelven el valor en el argumento de retorno, ya que todas las funciones COM devuelven
un HRESULT que dice si la función se ejecutó correctamente o no. El lado positivo de usar
el modelo COM, es que se puede utilizar la misma interfaz en cualquier lenguaje con
arquitectura COM, como por ejemplo Visual Basic y Visual Basic Script, entre otros.
OpenGL es una especificación basada en el lenguaje de programación C. Fue
desarrollada utilizando el concepto de una máquina de estados finitos, aunque las últimas
versiones de OpenGL lo han transformado más en un sistema basado en objetos. Aunque la
especificación esta construida en C, también se puede implementar en otros lenguajes.
En general Direct3D esta diseñada para ser una interfaz de hardware 3D. Su
desarrollo depende del desarrollo del hardware y lo que el hardware puede proveer. Por
otro lado OpenGL esta diseñado para ser un sistema de interpretación 3D que puede poseer
aceleración de hardware, como tal, su desarrollo se deriva de todo aquello que es
considerado como útil por el usuario. Direct3D espera que la aplicación maneje los
recursos del hardware, mientras que OpenGL crea la implementación para hacerlo. Esto
facilita al usuario el hecho de escribir una aplicación válida, pero lo deja más susceptible a
implementar errores. Al mismo tiempo, como OpenGL esconde los detalles del hardware,
11
incluyendo el hecho de si el hardware esta siendo utilizado o no, el usuario debe confiar en
que la implementación esta utilizando los mejores recursos del hardware.
Desempeño:
Después de que ambos APIs se establecieron como librerías gráficas viables,
Microsoft y SGI comenzaron lo que se ha llamado la guerra de las APIs. La mayor parte de
la disputa giraba en torno a cual interfaz ofrecía un mejor desempeño.
En general se ha logrado establecer que ninguna de las dos APIs es superior a la
otra en cuanto a velocidad. El desempeño de una aplicación depende de la habilidad del
programador, la calidad de los controladores (<<drivers>>) y del hardware de gráficos.
2.4
Teoría de modelado tridimensional aplicada a computadoras
El modelado 3D es la representación de objetos tridimensionales en un plano
bidimensional, por ejemplo la pantalla de una computadora. Las técnicas de modelado
tridimensional y la creación de motores tridimensionales se basan en axiomas matemáticos
complejos. Aunque existen librerías gráficas que evitan que el programador tenga que
lidiar directamente con todos los pasos algebraicos que implica crear una escena
tridimensional en un computador, es importante comprender las bases de álgebra lineal que
hay detrás de las escenas tridimensionales.
Figura 2.1 Plano 2D y espacio 3D
Un punto en el espacio se describe generalmente utilizando las coordenadas
cartesianas (x,y,z), las cuales describen la posición del punto en un espacio tridimensional.
En la programación de aplicaciones gráficas se utilizan como base puntos y vectores para
describir las figuras geométricas que se desea dibujar. Los vectores se pueden definir como
elementos de determinada magnitud y dirección en el espacio, pero también se pueden ver
como resta entre dos puntos, por ejemplo un vector se puede definir como:
V = Punto 2 − Punto1 = ( x 2, y 2, y3) − ( x1, y1, z1) = (a, b, c)
12
Para una computadora e incluso para el programador, las coordenadas cartesianas
son confusas, ya que no existe ningún elemento que diferencie entre un vector y un punto,
por ejemplo al leer el vector antes definido, es imposible para la computador determinar
si los elementos (a,b,c) representan un vector o si son las coordenadas de un punto en el
espacio.
2.4.1
Las coordenadas homogéneas
Las coordenadas homogéneas son similares a las coordenadas cartesianas, la única
diferencia es la existencia de un cuarto elemento en el vector coordenada, el elemento w.
Por ejemplo si P en coordenadas cartesianas está descrito por los elementos (x,y,z), en
coordenadas homogéneas P está representado por los elementos (x,y,z,w), en donde w es un
cero si P es un vector y un uno si P es un punto.
Por ejemplo:
Punto1 P = (x1,y1,z1)
→ coordenadas cartesianas.
Punto1 P = (x1,y1,z1,1)
→ coordenadas homogéneas.
Vector1 V = (x1,y1,z1)
→ coordenadas cartesianas.
Vector1 V = (x1,y1,z1,0)
→ coordenadas homogéneas.
Generalmente se desea transformar los vértices de los objetos que se van a dibujar,
por ejemplo trasladarlos, rotarlos con respecto a un eje e incluso escalarlos para cambiar el
tamaño del objeto. Estos procesos de transformación, conocidos como transformaciones
afines, se llevan a cabo utilizando matrices homogéneas. Para poder utilizar estas matrices
de transformación, los puntos y vectores deben estar descritos utilizando coordenadas
homogéneas en lugar de coordenadas cartesianas. Las coordenadas homogéneas impiden
que una transformación actúe de igual manera sobre un punto que sobre un vector, lo cual,
si llegara a pasar, sería un error que alteraría de forma inesperada el objeto dibujado.
Por tales razones las coordenadas homogéneas se utilizan para crear aplicaciones
gráficas.
2.4.2
Los sistemas de coordenadas Cámara, Global y Objeto
En el espacio virtual de las aplicaciones tridimensionales existen varios sistemas de
coordenadas, entre ellos se encuentra el sistema de coordenadas propio de la pantalla, la
cual a partir de este punto será llamada Cámara y el sistema de coordenadas Global o
Worldspace, que representa el sistema del mundo en el cual se encuentran todos los objetos
geométricos que se desea dibujar, es decir es el sistema base del espacio virtual.
13
Figura 2.2 La cámara y el mundo
La posición del punto A en la figura 2.2 está definida por las coordenadas
homogéneas A(x,y,z,1), estas coordenadas son con respecto al sistema de coordenadas
globales o sistema Worldspace. Para poder graficar el punto A en la pantalla es necesario
determinar sus coordenadas respecto al sistema de coordenadas de la cámara, ya que este es
el sistema físico en el cual se dibujará el punto.
Figura 2.3 La cámara, el mundo y el objeto
Para simplificar el proceso de crear mundos tridimensionales se utiliza un tercer
sistema de coordenadas llamado sistema de coordenadas locales o sistema Objeto. Si se
tiene un objeto que se encuentra en constante movimiento en el sistema global , se
convierte en una tarea difícil y tediosa tener que calcular a cada instante las coordenadas de
cada punto del objeto respecto al sistema global. Teniendo este nuevo sistema de
coordenadas, cuando un objeto se mueve, las coordenadas de sus puntos respecto al sistema
Objeto no varían, lo que varía es la posición y dirección del sistema de coordenadas Objeto
respecto al sistema global y el sistema Cámara.
14
Teniendo estos tres sistemas de coordenadas en cuenta, el proceso que se debe
llevar a cabo para dibujar un objeto tridimensional en la pantalla es el siguiente:
Primero se deben pasar las coordenadas de cada punto del objeto del sistema de
coordenadas objeto al sistema de coordenadas global. Luego las coordenadas resultantes
deben trasladarse al sistema Cámara, obteniendo de esta manera las coordenadas físicas de
cada punto del objeto en la pantalla.
Para trasladar las coordenadas de un punto de un sistema a otro se utilizan matrices
homogéneas llamadas matrices de transformación.
En el espacio virtual, los sistemas de coordenadas se pueden representar mediante
matrices de cuatro filas y cuatro columnas (matrices 4 x 4).
 a a' a' ' x 


 b b' b' ' y 
A=
c c' c' ' z 


0 0 0 1


Los primeros tres vectores columna de la matriz A definen la base del sistema de
coordenadas, es decir la dirección y magnitud de los vectores x, y, z base. La cuarta
columna define el desplazamiento del sistema de coordenadas, en otras palabras, la
ubicación del punto de origen del sistema de coordenadas. Al igual que se explicó antes
para las coordenadas homogéneas, el cuarto valor de cada columna define si se está
describiendo un punto o un vector. Se utiliza un cero para indicar que los valores de la
columna pertenecen a un vector y un uno para indicar que los valores pertenecen a un
punto.
Por ejemplo la base canónica estaría representada por la matriz B, la cual a su vez es
la matriz identidad.
1

0
B=
0

0

0 0 0

1 0 0
0 1 0

0 0 1 
En la figura 2.4 se observan tres sistemas de coordenadas definidos como Cámara,
Worldspace y Objeto. Los vectores base son los mismos para los tres sistemas, lo único que
diferencia un sistema del otro es la ubicación de los puntos de origen.
15
Figura 2.4 Sistemas de coordenadas Cámara, Global y Objeto.
Los sistemas Cámara, Worldspace (Global) y Objeto de la figura 2.4 están
representados por la matrices C, W y O respectivamente.
1

0
C =
0

0

0 0 − 3

1 0 4 
0 1 0 

0 0 1 
1

0
W =
0

0

0 0 0

1 0 0
0 1 0

0 0 1 
1

0
O=
0

0

0 0 3

1 0 4
0 1 0

0 0 1 
Para la figura 2.4, si se multiplica el vector de coordenadas (x,y,z,0) del punto P, las
cuales son con respecto al sistema Objeto, por la matriz O, la asociada al sistema Objeto, se
obtienen las coordenadas del punto P respecto al sistema Worldspace. Es importante
recordar que se utilizan coordenadas homogéneas y no cartesianas, por lo que se le debe
agregar un uno como cuarto elemento al vector de coordenadas , para indicar que lo que
se está definiendo es un punto y no un vector . El vector P’ representa las coordenadas del
punto P respecto al sistema Worldspace.
1

0
P'= O × P = 
0

0

0 0 3  2  5
    
1 0 4  4  8
×
=
0 1 0 0 0
    
0 0 1   1   1 
16
La matriz asociada al sistema Objeto se puede denominar como T1(O→W), ya que
pasa las coordenadas del sistema Objeto al sistema Worldspace. De forma análoga la matriz
asociada al sistema Cámara, es decir la matriz C, pasa las coordenadas del sistema Cámara
al sistema Worldspace, por lo que se puede denominar como T2(C→W). Cuando se crea
una escena tridimensional, es necesario poseer las coordenadas de todos los objetos de la
escena respecto al sistema Cámara, para así saber en qué posición de la pantalla se deben
dibujar. La matriz inversa de la asociada al sistema Cámara, C’=C-1, pasa las coordenadas
del sistema Worldspace al sistema Cámara. La matriz C’ se puede denominar como
T3(W→C), entonces para obtener las coordenadas del punto P respecto al sistema cámara,
se debe llevar a cabo la siguiente operación:
P ' ' = C '×P ' = C −1 × (O × P )
en donde el vector ’’ representa el vector de coordenadas del punto P respecto al sistema
Cámara.
Es importante recordar que cada vez que la cámara cambia su posición , se debe
cambiar la matriz C. De igual manera cada vez que el objeto al cual pertenece el punto P se
mueve en el espacio virtual, se debe modificar la matriz O, para que represente de manera
adecuada la nueva posición y rotación del sistema de coordenadas propio al objeto.
2.4.3
Trasladar, rotar y escalar objetos
Muchas veces los objetos en una escena se deben trasladar, rotar e incluso escalar,
estas transformaciones, llamadas transformaciones afines, se logran multiplicando los
vértices de los objetos geométricos por matrices homogéneas de transformación. Estas
matrices se pueden concatenar una tras otra y de esta manera hacer múltiples
transformaciones a un mismo objeto geométrico.
Las transformaciones afines están representadas por cinco matrices genéricas. Una
matriz genérica para traslaciones, tres matrices genéricas para rotaciones y una matriz
genérica para escalamientos.
1

0
Matriz de traslación genérica → T = 
0

0

0 0 tx 

1 0 ty 
0 1 tz 

0 0 1 
17
En la matriz genérica de traslación, el cuarto vector columna (tx, ty, tz) representa
los valores de traslación sobre el eje X, el eje Y y el eje Z respectivamente. Nótese que la
matriz T se puede denominar como la matriz asociada a un sistema de coordenadas con
origen en el punto (tx, ty, tz).
0
0
1

 0 cos(α ) − sin(α )
Matriz genérica de rotación respecto al eje X→ R X = 
0 sin(α ) cos(α )

0
0
0

 cos(α )

 0
Matriz genérica de rotación respecto al eje Y→ RY = 
− sin(α )

 0

0

0
0

1 
0 sin(α )
0

1
0
0
0 cos(α ) 0 

0
0
1 
 cos(α ) − sin(α )

 sin(α ) cos(α )
Matriz genérica de rotación respecto al eje Z→ RZ = 
0
0

 0
0

0 0

0 0
1 0

0 1 
En las matrices genéricas de rotación, el ángulo α representa el ángulo de rotación
respecto al eje de rotación determinado.
 sX

 0
Matriz genérica de escalamiento→ S = 
0

 0

0
0
sY
0
0
sZ
0
0
0

0
0

1 
18
En la matriz genérica de escalamiento, los argumentos sX, sY y sZ, determinan el
factor al que se escalarán las coordenadas X, Y y Z de cada vértice, respectivamente.
Es importante recordar que la multiplicación de matrices no es conmutativa, es decir
para la multiplicación de las matrices A y B se tiene que A ⋅ B ≠ B ⋅ A . Esto es importante,
ya que el orden de multiplicación de las matrices afecta la posición final del objeto en la
escena.
Figura 2.5 Rotación y Traslación1
En la figura 2.5 se observa cómo el resultado de rotar la maceta y luego trasladarla,
es distinto al del proceso inverso. Aun cuando las transformaciones sean las mismas, si el
orden en que éstas se aplican a un objeto se cambia, el resultado final será distinto.
Entonces por ejemplo para el primer caso de la figura 2.5, en el que primero se rota
el objeto y luego se traslada, cada vértice del objeto se tendría que transformar de la
siguiente manera:
V f = T ⋅ R ⋅ Vi
en donde i y f son vectores columna que representan las coordenadas homogéneas
iniciales y finales del vértice respectivamente, mientras que R es la matriz homogénea de
rotación aplicada y T la matriz homogénea de traslación aplicada. Por otro lado para el
segundo caso, en el que primero se traslada la figura y luego se rota, la multiplicación entre
las matrices y cada vértice del objeto tendría el siguiente orden:
V f = R ⋅ T ⋅ Vi
Nótese que la dirección de concatenación de las matrices va de derecha a izquierda,
siendo la matriz que está más a la derecha el primer proceso de transformación que sufre el
objeto. Otro punto que se debe tener en cuenta es que las matrices se post-multiplican, es
decir que los vértices de cada objeto se representan como vectores columna que se
multiplican al conjunto de matrices de transformación por la derecha.
1
Figura tomada de la guía libre de programación OpenGL “Red Book”, http://www.glprogramming.com/red
19
Generalmente los programadores de aplicaciones gráficas no tienen que preocuparse
por tener que estar introduciendo matrices de transformación al código, ya que al utilizar
APIs como OpenGL y Direct3D, ellas mismas se encargan de hacer todos los cálculos
matriciales necesarios, mientras que el programador solo tiene que llamar la función
respectiva de rotación, traslación o escalamiento. Aun así es importante conocer cómo
funcionan los procesos de transformación, ya que para algunos casos las funciones básicas
de rotación, traslación y escalamiento provistas por las APIs no son suficientes o pueden
llegar a ser un tanto rígidas, por ejemplo cuando se desea implementar un método de
detección de colisiones.
2.4.4
Sistemas gráficos
Los sistemas gráficos generalmente están compuestos por los siguientes elementos:
Figura 2.6 Elementos típicos de un sistema gráfico
Las entradas definen todo lo que se ha calculado en la aplicación gráfica y se desea
dibujar, es decir el nuevo estado de la escena y los cambios que esto conlleva.
El procesador o CPU se encarga de la comunicación entre los módulos, realiza
operaciones aritméticas con ayuda de las unidades aritméticas (<<ALUs>>) y consulta la
memoria cuando sea necesario. Los sistemas especializados en gráficos poseen varios
procesadores que trabajan en paralelo, por lo que pueden calcular y dibujar al mismo
tiempo, lo que mejora el rendimiento en tiempo real.
20
El <<frame buffer>> es la zona de la memoria en la cual se almacena todo lo que va
a ser dibujado. Las API’s gráficas escriben en esta zona lo que se desea dibujar y luego
envían esta información a la pantalla.
La LUT (por sus siglas en inglés: <<Look Up Table>>), también conocida como
paleta, es la tabla que contiene todos los colores disponibles en el sistema.
El conversor D/A convierte la información del <<buffer>> de digital a analógica,
para que esta pueda ser proyectada en la pantalla.
El <<frame buffer>> está caracterizado por su resolución y su profundidad. La
resolución es la multiplicación del ancho por el alto del conjunto de píxeles, es decir el
numero de filas de píxeles por el numero de columnas de píxeles. El píxel es la unidad
mínima de la pantalla. Éstos se encuentran aglomerados en filas y la combinación de los
colores de cada píxel en conjunto da forma a la imagen que se ve en la pantalla.
La profundidad del <<frame buffer>> determina el numero de bits que se utiliza
para guardar la información de cada píxel. Este número depende de la cantidad de colores
que se desea mostrar en la aplicación.
2.4.5
Arquitectura Gráfica (PIPELINE)
El proceso por etapas o <<pipeline>> gráfico define los pasos necesarios para editar
la escena que se desea dibujar en la pantalla, por lo tanto indica los pasos de actuación que
debe seguir el API gráfico que se esté utilizando. La entrada del <<pipeline>> gráfico es la
geometría u objeto geométrico que se desea dibujar y la salida es la imagen que se obtiene
en la pantalla.
Figura 2.7 Arquitectura Gráfica (pipeline)
21
A la entrada está el objeto geométrico que se desea dibujar, el cual está compuesto
por primitivas geométricas como puntos y líneas. Estos objetos poseen atributos
previamente establecidos, los cuales pueden ser móviles o fijos, por lo que debe existir la
posibilidad de trasladarlos, rotarlos y escalarlos si es necesario, antes de que sean dibujados
en la pantalla.
Durante el proceso de transformación del modelo, se rotan, trasladan y escalan los
objetos geométricos , de manera que se dibujen en la pantalla igual que se dispuso en el
mundo que se desea crear. Para lograr esto se multiplican los vértices de los objetos por las
matrices de rotación , traslación y escalamiento que sean necesarias.
Una vez realizadas las transformaciones de los vértices, se obtienen las coordenadas
de los objetos con respecto al mundo que se está creando, estas coordenadas son las
coordenadas del mundo o coordenadas globales.
El siguiente paso en el pipeline es el de iluminar los objetos para que sean visibles a
la cámara (pantalla) y determinar las coordenadas de los objetos respecto a la cámara. Este
proceso se llama transformación del visionado. Luego de haberlo realizado se sabe cuales
son las coordenadas de todos los objetos con respecto a la cámara.
El próximo paso es recortar todo aquello que existe pero no es visible para la
cámara. Este proceso se conoce como <<clipping>>.
Durante el proceso de proyección se pasan las coordenadas tridimensionales del
mundo que se desea dibujar a coordenadas bidimensionales del plano de proyección.
Una vez proyectado el gráfico se tienen las coordenadas de pantalla independientes
del dispositivo o DISC por sus siglas en ingles (<<Device Independent Screen
Coordinates>>). A estas coordenadas se les llama independientes del dispositivo, ya que
aun no se encuentran ligadas a ningún tipo de monitor, es decir todavía no se sabe la
resolución de la pantalla ni el tamaño del monitor.
Finalmente se lleva acabo el proceso de rasterización el cual consiste en asociar
todos los puntos de la imagen proyectada a píxeles en la pantalla, obteniendo de esta
manera la imagen final en el monitor.
El pipeline gráfico puede ser implementado a través de hardware o software. La
implementación a través de software produce que todo el proceso sea más lento a menos de
que se cuente con tarjetas de aceleración gráfica.
22
2.5
Introducción a OpenGL, GLUT y GTK
2.5.1
La ventana de despliegue utilizando GLUT
Lo primero que se debe hacer a la hora de desarrollar una aplicación gráfica es crear
una ventana donde se puedan desplegar los gráficos. Si se utiliza la biblioteca GLUT, la
función gluInit() es la primera que se debe llamar, ésta se encarga de inicializar la
biblioteca GLUT y negocia una sesión con el sistema de ventanas. Si GLUT no logra ser
inicializado, el programa se cerrará y le dará un mensaje de error al usuario. Algunas causas
de error de inicialización pueden ser fallos al conectarse con el sistema de ventanas, falta de
soporte para OpenGL por parte del sistema de ventanas y opciones invalidas en la línea de
comandos. Los parámetros de la función glutInit() deben ser los mismos que aquellos de la
función main(), específicamente main(int argc, char**argv) y glutInit(&argc, argv).
El próximo paso es escoger el modo en el que se desplegarán los modelos de
colores en la ventana, utilizando la función glutDisplayMode(). Por ejemplo
glutDisplayMode(GLUT_RGBA || GLUT_DOUBLE || GLUT_DEPTH ) indica que se
utilizará el modelo de colores RGB con índice alfa, que se utilizarán dos <<buffers>> para
desplegar los gráficos y un <<buffer>> para implementar pruebas de profundidad para cada
píxel.
Lo siguiente es proporcionar las características de la ventana a crear. La función
glutInitWindowSize(ancho, alto) determina el tamaño de la ventana a crear, en donde el
parámetro “ancho” es el ancho de la ventana en píxeles y “alto” es el alto de la ventana,
también en píxeles. De manera similar glutInitWindowPosition(X,Y) determina la posición
inicial de la esquina superior izquierda de la ventana en la pantalla, donde X y Y son
coordenadas en la pantalla del monitor, medidas en píxeles.
Para crear la ventana asociada a las características anteriormente determinadas se
debe llamar la función glutCreateWindow(nombre), en donde la variable nombre determina
el título que tendrá la ventana.
La librería GLUT posee varias funciones de respuesta, en estas funciones se pueden
almacenar rutinas especificadas por el programador, que son llamadas en respuesta a algún
tipo de evento. Por ejemplo, la función de respuesta más importante en cualquier programa
GLUT es la función glutDisplayFunc(), el argumento de esta función es llamado cada vez
que GLUT determina que el contenido de la ventana debe desplegarse nuevamente. Todas
la rutinas requeridas para dibujar una escena deberían estar en la función
glutDisplayFunc().
El argumento de la función glutReshapeFunc() es llamado cuando se varía el
tamaño de la ventana o ésta se mueve. Esto impide que se dé distorsión en el dibujo al
cambiar el tamaño de la ventana.
23
Existen otras funciones de respuesta, como por ejemplo las funciones
glutMouseFunc() y glutKeyboardFunc(), las cuales llaman las rutinas especificadas en sus
argumentos, cuando se da algún evento en el ratón o el teclado del computador.
La función de respuesta final que debe ser llamada en cualquier programa GLUT es
la función glutMainLoop(). Esta función se encarga de enseñar todas la ventanas que han
sido creadas. El comando glutMainLoop() representa un lazo, del cual nunca se sale, que
se encarga de monitorear todos lo eventos, por ejemplo pulsaciones de teclas, movimientos
del ratón y en caso que sea necesario, llamar a la función de respuesta asociada a cada
evento.
Una vez creada la ventana para desplegar los gráficos, es posible comenzar a
introducir comandos y funciones de OpenGL y así crear los objetos geométricos deseados.
2.5.2
La ventana de despliegue utilizando GTK
GTK (por sus siglas en inglés: <<GIMP ToolKit>>), es una librería que se utiliza
para desarrollar interfaces gráficas de usuario. Posee una licencia LGPL, por lo que puede
ser empleada en el desarrollo de software libre o abierto, e incluso en proyecto comerciales,
de manera gratuita.
GTK está construida sobre la biblioteca GDK (por sus siglas en inglés: <<GIMP
Drawing Kit>>), la cual es básicamente un envoltorio alrededor de funciones de bajo nivel,
que se utilizan para acceder las funciones del sistema de ventanas. Esencialmente, GTK es
un API orientada a objetos, aún cuando todo su código se encuentra escrito en C. Las
funciones de biblioteca se implementan utilizando clases y punteros.
Los pasos a seguir para crear una ventana GTK son muy similares a los pasos para
crear una ventana GLUT.
/* Ejemplo: Como crear una ventana GTK */
#include <gtk/gtk.h>
int main( int argc,
char *argv[] )
{
GtkWidget *ventana;
gtk_init (&argc, &argv);
ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_show (venatna);
gtk_main ();
return 0;
}
24
Todos los programas que utilicen GTK deben incluir el archivo “gtk.h”, en el cual
se declaran las variables, funciones, estructuras, etc, que serán utilizadas por la aplicación
GTK. La función gtk_init(gint *argc, gchar ***argv) es la encargada de inicializar GTK en
la aplicación. Las interfaces gráficas están compuestas por objetos llamados <<widgets>>.
Un <<widget>>, también conocido como artilugio o control, es un componente gráfico con
el cual el usuario interactúa, como por ejemplo, una ventana, una barra de tareas, un botón
o una caja de texto. En la aplicaciones GTK los <<widgets>> son definidos como
GtkWidget, por ejemplo GtkWidget *ventana.
En la línea ventana = gtk_window_new (GTK_WINDOW_TOPLEVEL) del ejemplo
anterior, se define el <<widget>> “ventana”, como un nueva ventana GTK. El argumento
que fue pasado a la función indica que se desea que la ventana acepte las características de
decoración y posición dadas por el controlador de ventanas del sistema operativo. En vez
de crearse una ventana de tamaño 0 x 0, se establece una ventana de tamaño 200 x 200. Por
supuesto estas dimensiones son argumentos predeterminados que pueden ser variados
posteriormente a gusto del desarrollador.
La función gtk_widget_show (GtkWidget *) le indica a Gtk que ya se establecieron
todos los atributos que se deseaban para el <<widget>> y que éste ya puede ser desplegado.
Al igual que las aplicaciones GLUT, todos los programas GTK poseen una estructura de
lazo infinito, en el cual se está a la espera de que suceda algún evento. En GTK el lazo
principal es representado por la función gtk_main(). En el código anterior no se estableció
ninguna función de respuesta a eventos, por lo que cualquier evento será ignorado.
En aplicaciones GTK las funciones de respuesta se conectan a los eventos de la
siguiente manera: g_signal_connect (G_OBJECT (GtkObj), "evento", G_CALLBACK
(func_respuesta), argumento), en donde GtkObj es el objeto del tipo GTK, por ejemplo una
ventana o un botón, en el cual se dió el evento, func_respuesta es la función que debe
llamarse como respuesta al evento y argumento representa posibles argumentos que se le
pueden pasar a la función de respuesta.
2.5.3
Primitivas y vértices
Los objetos geométricos se pueden describir por medio de conjuntos de vértices
conectados entre sí por algún tipo de primitiva geométrica. Para determinar la posición de
los vértices se utiliza el comando glVertex*(), en donde el símbolo * indica que existen
variaciones para el comando base glVertex(). Algunos nombres de funciones poseen de
uno a tres caracteres al final, los cuales determinan el numero y tipo de parámetros que se
pasarán al comando. Por ejemplo la función glVertex3f() indica que se trata de un vector,
donde tres representa el numero de dimensiones o elementos del vector y la letra f indica
que los elementos del vector son números del tipo flotante.
La posición de los vértices debe encontrarse entre las funciones glBegin() y glEnd(),
las cuales señalan el principio y fin de un objeto geométrico. El argumento de la función
25
glBegin() indica el tipo de primitiva que se utilizará para unir los vectores. Algunos tipos
soportados por la librería OpenGL son: GL_LINES, dibuja una línea cada dos vértices,
GL_POINTS, dibuja un punto en cada vértice, GL_POLYGON, dibuja un polígono. Los
polígonos deben ser convexos. La función glColor*(), especifica el color que OpenGL
utiliza para dibujar las figuras, el color no cambia hasta que se vuelva a llamar la función
glColor*().
Para el código que se presenta a continuación , dependiendo de si la variable
PRIMITIVA se sustituye por el argumento GL_POLYGON o GL_POINTS se obtendrá
uno de los objetos presentados en la figura 2.8.
/* Ejemplo del uso de diferentes primitivas */
...
glBegin(PRIMITIVA);
glVertex2f(0.0, 0.0);
glVertex2f(0.0, 3.0);
glVertex2f(4.0, 3.0);
glVertex2f(6.0, 1.5);
glVertex2f(4.0, 0.0);
glEnd();
...
/*Determina el inicio de un conjunto de vértices y el tipo de
primitiva que los une*/
/*Posición del vértice 1*/
/*Posición del vértice 2*/
/*Determina el fin de un conjunto de vértices*/
Figura 2.8 Polígono y puntos.
La librería GLUT posee una serie de rutinas que pueden ser utilizadas para crear
objetos tridimensionales específicos. Por ejemplo glutWireSphere() dibujar una esfera
compuesta por líneas, glutSolidCube(), dibuja un cubo, entre otros. Las dimensiones de
estos objetos se especifican en los argumentos de la función.
2.5.4
La matriz CTM
La librería OpenGL posee funciones que se encargan de las transformaciones de
posición y tamaño que se desean llevar a cabo en la escena. OpenGL transforma el modelo
llevando a cabo multiplicaciones matriciales. En la aplicaciones gráficas como OpenGL, la
geometría se ve afectada por la CTM (por sus siglas en inglés: <<Current Transformation
Matriz>>) o matriz de transformación actual. Aquí se guarda la información de todas las
26
matrices que se han ido acumulando. Los vértices que son procesados se multiplican por
esta matriz y por lo tanto son transformados. En OpenGL la CTM está compuesta por la
multiplicación de dos matrices, la matriz “Model-View” o matriz de transformación del
modelo y la matriz de proyección. La primera se encarga de las transformaciones que se
llevarán a cabo y la segunda representa la proyección sobre el plano bidimensional, es decir
el paso del mundo 3D al mundo 2D.
2.5.5
La matriz de transformación del modelo
Lo primero que se debe hacer para llamar matrices de transformación es cargar la
matriz “Model-View” y luego reiniciarla, lo cual se logra acumulando la matriz identidad.
/* Ejemplo de como llamar la matriz de transformación del modelo e inicializarla */
...
glMatrixMode(GL_MODELVIEW);
glLoadIndentity();
/*Se carga la matriz “Model-View”*/
/*Se carga la matriz identidad*/
...
Después de haber activado la matriz “Model-View” se pueden comenzar a acumular
matrices de transformación para obtener el resultado deseado. Las funciones de
transformación implementadas en OpenGL son glScale*(), glRotate*(), glTranslate*().
El comando glScalef(GLfloat sx, GLfloat sy, GLfloat sz) escala los valores de las
coordenadas X, Y y Z de la geometría, de acuerdo a los valores de sx, sy y sz. Tanto la letra
f al final del nombre de la función como el argumento GLfloat indican que los valores de
sx, sy y sz son del tipo flotante.
La función glTranslatef(GLfloat tx, GLfloat ty, GLfloat tz) traslada la geometría,
según los factores tx, ty y tz.
La función glRotatef(GLfloat ángulo, GLfloat vx, GLfloat vy, GLfloat vz) rota la
geometría en un ángulo determinado. La rotación es en sentido anti-horario, respecto al
vector definido por el conjunto de valores (vx, vy, vz).
A medida que se van introduciendo transformaciones éstas se van postmultiplicando en la matriz de transformación, es decir que la última transformación
introducida será la primera que se le aplicará a la geometría, esto se debe a que la matriz
“Model-View” funciona como una pila LIFO (por sus siglas en inglés: <<Last In First
Out>>), en donde la última transformación en entrar es la primera en salir. Utilizando la
función glPushMatrix() se puede salvar el estado actual de la pila en cualquier momento y
luego puede ser recuperado utilizando la función glPopMatrix(). Esto es muy útil por
ejemplo en los casos que se deseen hacer transformaciones a solo algunas partes de la
geometría. Se puede salvar el estado actual de la pila, transformar solo las partes de la
27
geometría que se desean transformar y luego recuperar el estado de la pila, sin que el resto
de la geometría se vea alterada por las transformaciones que se llevaron a cabo.
/* Ejemplo que muestra como se pueden usar las funciones de la pila (Model-View) para transformar solo
algunas partes de la geometría */
...
dibujo_parte1(){...}
/*se define alguna forma geométrica*/
dibujo_parte2(){...}
/*se define alguna forma geométrica*/
...
glTranslatef...
/*afecta a toda la geometría que se dibuje a partir de ahora*/
glRotatef...
/*afecta a toda la geometría que se dibuje a partir de ahora*/
glPushMatrix();
/*salva el estado actual de la matriz, es decir las dos transformaciones anteriores*/
glTranslatef...
/*afecta solo la geometría que se dibuje antes del próximo glPopMatrix()*/
glRotatef...
/*afecta solo la geometría que se dibuje antes del próximo glPopMatrix()*/
dibujo_parte1(); /*geometría que se ve afectada por 4 transformaciones*/
glPopMatrix();
/*se recupera el estado anterior de la matriz*/
dibujo_parte2();
/*geometría que se ve afectada por solo 4 transformaciones*/
...
2.5.6
Transformación de la visión y proyección de la escena.
Transformar la visión equivale a cambiar la posición del observador. Lo primero
que se debe hacer es activar la matriz de transformación e inicializarla, es decir cargar la
matriz identidad. Luego se determina la posición del observador, para esto se puede utilizar
la función gluLookAt(px, py, px, ox, oy, oz, ax, ay, az), en donde el vector (px, py, pz)
determina la posición del observador, el vector (ox, oy, oz) determina hacia donde ve el
observador y el vector (ax, ay, az) define el vector que apunta hacia arriba, por así decirlo,
hacia el cielo. La función gluLookAt() es parte de la librería GLU.
Una vez que se ha creado la escena tridimensional y se han aplicado todas las
transformaciones a los objetos y al observador, el siguiente paso es pasar la escena del
mundo tridimensional al plano de proyección bidimensional de la pantalla.
La matriz de proyección determina el tipo de proyección y la escogencia de los
atributos de ésta, es análogo a elegir el lente de una cámara, el cual determina el ámbito de
visión y el acercamiento. La matriz de proyección se activa utilizando el comando
glMatrixMode(GL_PROJECTION). Una vez inicializada la matriz de proyección se puede
llamar la función glOrtho() para determinar las dimensiones del volumen de visión. El
volumen de visión determina el rango visual del observador. Utilizando los comandos
gluPerspective() o glFrustum() se define este volumen. El proceso de transformación de la
visión y la matriz de proyección se puede comparar con el de tomar una fotografía, tal y
como se muestra en la figura 2.9.
28
Figura 2.9 Analogía entre cámara y computadora1
A continuación se presenta un ejemplo de cómo configurar la visión y proyección de una
escena.
/* Ejemplo de configuración de las matrices de visión y proyección */
void reshape (int w, int h)
/*función que se llama cuando la ventana se crea o cambia de tamaño*/
{
glViewport (0, 0, (GLsizei) w, (GLsizei) h);
/*tamaño del cuadro de visión*/
glMatrixMode (GL_PROJECTION);
/*activa la matriz de proyección*/
glLoadIdentity ();
/*la reinicia*/
gluPerspective(60.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0);
/*determina la perspectiva*/
glMatrixMode(GL_MODELVIEW);
/*activa la matriz de transformación*/
glLoadIdentity();
/*la reinicia*/
gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
/*indica la posición del observador*/
}
1
Figura tomada de la guía libre de programación OpenGL “Red Book”
29
En el ejemplo anterior, la función glViewport() define el área de visión, en otras
palabras el área de la ventana que se utiliza para dibujar la escena. En este caso se utilizó
toda la ventana. Tras haber cargado e inicializado la matriz de proyección, se define el
volumen de visión utilizando la función gluPerspective(), cuyos argumentos son: el ángulo
del campo de visión (en el ejemplo es 60.0), la razón de aspecto, la cual se define como la
división del ancho del área de visión entre el alto del área de visión, la posición del plano
más cercano del volumen de visión, respecto al observador (en el ejemplo es 1.0) y la
posición del plano más lejano del volumen de visión, respecto al observador (en el ejemplo
es 2.0). La última función del ejemplo determina la posición del observador y el punto
hacia el cual éste se encuentra mirando.
La figura 2.10 es una representación de un volumen de visión especificado
utilizando la función gluPerspective().
Figura 2.10 Volumen de visión especificado por gluPerspective()1
1
Figura tomada de la guía libre de programación OpenGL “Red Book”
CAPÍTULO 3: La robótica
Desde un punto de vista muy básico y técnico, los seres humanos están compuestos
por cinco componentes principales:
•
Una estructura corporal.
•
Un sistema muscular que mueve la estructura corporal.
•
Un sistema sensorial que recibe información del cuerpo y el ambiente que lo
rodea.
•
Una fuente de poder para activar los músculos y sensores.
•
Un cerebro que procesa la información sensorial y le indica a los músculos qué
hacer.
Por supuesto que los humanos también poseen atributos intangibles tales como la
inteligencia y la moral, entre muchos otros. Pero si se analiza desde un nivel simplemente
físico y técnico, los cinco atributos antes mencionados bastan para describir de manera
general el cuerpo humano. Un robot está compuesto por los mismos componentes. Los
robots típicos poseen una estructura física móvil, un motor de algún tipo, un sistema
sensorial, una fuente de poder y una computadora que actúa como el cerebro y que
controla todos los componentes anteriores.
3.1
Las bases de los robots
La mayoría de los robots poseen ciertas cualidades comunes. En primer lugar todos
tienen un cuerpo móvil. Algunos simplemente poseen ruedas motorizadas, mientras que
otros poseen segmentos móviles con configuraciones muy complejas. Al igual que los
huesos en el cuerpo humano, los segmentos de un robot se encuentra conectados entre sí
por medio de articulaciones.
Los actuadores son las unidades controladas que se encargan de mover las partes
móviles de los robots. Los actuadores hidráulicos son aquellos que utilizan líquido, los
neumáticos utilizan aire comprimido para generar el movimiento, los actuadores eléctricos,
los cuales están compuestos por motores y solenoides son los más populares.
En un robot, cada actuador se encuentra conectado a un circuito eléctrico. El
circuito le brinda corriente a los motores y solenoides de forma directa, mientras que en el
caso de los actuadores hidráulicos, el circuito se encarga de activar el sistema hidráulico
por medio de la manipulación de válvulas eléctricas. Las válvulas determinan el camino del
líquido presurizado, a través de la máquina.
30
31
La computadora del robot se encarga de controlar todo lo que esté conectado al
circuito. Para mover el robot, la computadora enciende los motores y las válvulas que sean
necesarias.
No todos los robots poseen sistemas sensoriales, por lo que en algunos casos es
necesario el desarrollo de programas de predicción que trabajen de forma paralela al
sistema de control, por ejemplo programas de predicción de choques.
3.2
El brazo robot
El término robot proviene de la palabra checa “robota”, que significa trabajo
forzado, lo cual describe muy bien las tareas que cumplen muchos robots hoy en día. La
mayoría de los robots son diseñados para llevar a cabo procesos industriales pesados y
repetitivos. Se encargan de tareas que pueden ser difíciles, peligrosas e incluso aburridas
para el ser humano. El robot industrial más popular es el brazo robot.
El desarrollo de brazos robot comenzó en Europa hace más de treinta años, donde se
idearon estos brazos para la industria automotriz. Hoy en día sus aplicaciones van desde la
industria del entretenimiento hasta las ciencias de la salud.
Normalmente los brazos robot se clasifican según su configuración, su tipo de
actuador, el método de programación, la metodología de control y las tareas que lleva a
acabo.
La configuración de robot se refiera a la geometría de este, sus grados de libertad y
sus ejes de movimiento. Una de la configuraciones más utilizadas es la de los sistemas
antropomorfos, los cuales son sistemas de brazos robot que imitan la configuración del
brazo humano. Entre este tipo de configuración el brazo más común es el de seis
articulaciones, con seis grados de libertad. El brazo humano tiene siete grados de libertad.
Los grados de libertad se refiere al numero de ángulos de giro que posee la extremidad, en
otras palabras es la suma del número de ejes de giro de todas las articulaciones de la
extremidad. Por ejemplo, un brazo mecánico de dos articulaciones, en el que la primera
articulación gira respecto al eje X y la segunda respecto al eje Z, es un brazo con dos
grados de libertad, mientras que otro con dos articulaciones en el que la primera gira
respecto a X y la segunda respecto a X, Y y Z, posee cuatro grados de libertad.
Los sistemas antropomorfos poseen el equivalente a un hombro, un brazo, un codo,
un antebrazo y una muñeca. El hombro normalmente está montado sobre una base giratoria,
en lugar de un cuerpo móvil.
32
Figura 3.1 Ejemplo de brazo robot con cuatro grados de libertad
Entre los métodos de programación de robots se encuentran, la instrucción por
dirección humana, los controles remotos y la programación fuera de línea.
Los sistemas de control almacenan la ubicación del brazo en forma de puntos
discretos, además proveen la especificación de cómo se debe mover el brazo.
El trabajo del brazo humano es básicamente mover la mano de un lugar a otro, de
manera similar el brazo robot tiene el trabajo de mover el mecanismo de punta, llamado
efector, de un lugar a otro.
3.2.1
El brazo robot Stäubli RX90
El brazo robot Stäubli RX90 L, es un brazo antropomorfo con seis grados de
libertad. Está conformado por siete partes principales, las cuales se encuentran unidas entre
sí por medio de articulaciones. Los movimientos del brazo son generados por motores
eléctricos. La velocidad de giro de cada motor puede variarse de manera independiente. Los
principales elementos del brazo robot Stäubli son: la base (A), el hombro (B), el brazo (C),
el codo (D), el antebrazo (E), la muñeca (F) y la mano (G) (ver figura 3.2).
33
Figura 3.2 Partes del brazo robot Stäubli RX901
3.2.2
Geometría de brazo robot Stäubli RX90
La geometría del objeto es la base de cualquier modelo tridimensional. El brazo
Stäubli esta compuesto, desde un punto de vista geométrico, por diferentes tipos de figuras,
tales como cilindros, cubos, pirámides, entre muchas otras, las cuales se junta para dar
forma al brazo. En la figura 3.3 se observan las principales medidas del brazo, las cuales
están dadas en milímetros.
1
Figura tomada de las hojas del fabricante, http://www.staubli.com/web/robot/division.nsf
34
Figura 3.3. Medidas geométricas del brazo robot Stäubli RX901
Las articulaciones del brazo robot poseen amplitudes de giro máximas, además de
valores máximos y nominales para las velocidades angulares, los cuales se muestran en el
cuadro 3.1. La numeración de las articulaciones en el cuadro 3.1, está basada en la
numeración que recibieron en la figura 3.2.
1
Figura tomada de las hojas del fabricante, http://www.staubli.com/web/robot/division.nsf
35
Cuadro 3.1 Amplitudes de giro y velocidades nominales y máximas 1
Articulación
1
2
3
4
5
6
Amplitud (°)
320
275
285
540
225
540
Distribución
de la amplitud
de giro (°)
A
±160
B
±137.5
C
±142.5
D
±270
E
+120
-105
F
±270
Velocidad
Nominal (°/s)
236
200
286
401
320
580
Velocidad
Máxima (°/s)
356
356
296
409
800
1125
En la figura 3.4 se observa la distribución de trabajo de las amplitudes de giro, de
las diferentes articulaciones.
Figura 3.4 Distribución de trabajo de las amplitudes de giro1
1
Datos y figuras tomados de las hojas del fabricante, http://www.staubli.com/web/robot/division.nsf
CAPÍTULO 4: Desarrollo de la aplicación de simulación
La aplicación de simulación presenta la siguiente estructura de funcionamiento:
•
Se crea la ventana donde se desplegarán todos los objetos de la escena y las
ventanas que componen la interfaz gráfica de usuario. En este punto también
se establece la configuración inicial de las matrices de proyección y del campo
visual del observador.
•
Se crean todos los objetos geométricos que se utilizarán en la escena y se
inicializan los valores de posición de los puntos de control, los objetos
geométricos y la cámara. Además se inicializa la velocidad de giro de cada
articulación del brazo virtual y de la cámara.
•
Se da comienzo al monitoreo de eventos, el cual está compuesto por un lazo
infinito, del cual no se sale a menos que se de la orden de terminar la
aplicación. En este lazo se vigila el estado de las ventanas y de los periféricos
como el teclado y el ratón. Si se da una orden por parte del usuario de
modificar el estado de la escena, se llama a las rutinas necesarias para llevar a
cabo el cambio.
•
Se ejecuta el movimiento de la cámara o del brazo robot solicitado por el
usuario. Si durante la traslación de los elementos del brazo virtual se da una
colisión de éste consigo mismo o con el piso, se despliega un mensaje de
advertencia y se le da un valor de uno a la variable de choque. Esta variable se
utiliza para comunicarle a otras aplicaciones que se ha dado una colisión.
•
Se regresa al inicio del lazo de monitoreo y se espera por un nuevo evento.
36
37
Figura 4.1 Diagrama de flujo de la aplicación de simulación virtual
38
El desarrollo de la aplicación de simulación del brazo robot Stäubli se dividió en
cuatro segmentos principales:
•
La creación de un motor de ejecución de un espacio virtual de tres
dimensiones.
•
El desarrollo de un modelo tridimensional del brazo robot, basado en las
características geométricas dadas por el fabricante.
•
El desarrollo e implementación de un método de detección de colisiones.
•
El desarrollo de una interfaz gráfica de usuario.
Para el desarrollo del motor de ejecución del modelo virtual, se utilizaron las
bibliotecas OpenGL y GLU, luego se creo una interfaz gráfica, dándole la posibilidad al
usuario de interactuar con los objetos y la configuración del espacio virtual. En un principio
se empleó la librería GLUT, para el desarrollo de la interfaz, pero dada la necesidad de una
interfaz más compleja, GLUT tuvo que ser sustituida por la biblioteca GTK-2.0. En general
el programa final se puede dividir en dos secciones principales: la sección desarrollada
utilizando OpenGL, la cual se encarga de graficar y manejar el espacio virtual y la sección
desarrollada con GTK, en la cual se encuentra la interfaz gráfica de usuario y se llevan a
cabo los procesos de comunicación con el sistema de ventanas, que sean necesarios. Para
establecer una unión entre esta dos secciones y que fuera posible la comunicación entre
ellas, se empleó la librería GtkGLext, la cuál es una extensión de GTK, que permite
desplegar y manejar aplicaciones gráficas desarrolladas con OpenGL, desde interfaces
GTK. El simulador fue desarrollado en el lenguaje de programación C, esta elección se
realizó con base en las características positivas propias del lenguaje y además con el fin de
simplificar el acople entre la aplicación aquí implementada y el programa en [4].
4.1
Simulador del brazo robot Stäubli RX90 L
Normalmente, la primera fase en el proceso de creación de gráficos
tridimensionales, es el modelado de los objetos que se desean utilizar en la escena.
Generalmente los desarrolladores de aplicaciones gráficas tridimensionales, no crean sus
modelos directamente desde primitivas geométricas, como las ofrecidas en OpenGL, sino
que utilizan editores gráficos tales como Lightwave 3D, 3D Studio Max, Maya, Blender,
entre otros, los cuales facilitan la creación de las figuras tridimensionales y permiten crear
modelos más exactos desde el punto de vista estético. Posteriormente estos modelos son
cargados o traducidos a primitivas geométricas que la interfaz de programación gráfica, por
ejemplo OpenGL, pueda interpretar. Esta traducción es llevada a cabo por programas o
partes de código que son capaces de leer e interpretar los formatos de archivo creados por
los programas de edición gráfica. Por ejemplo, existen muchos códigos que fueron
39
desarrollados para interpretar y traducir a primitivas geométricas OpenGL, la información
de los archivos con formato .3ds , el cual es el formato de archivos que utiliza el editor 3D
Studio Max. El problema es que la mayoría de estos códigos o programas de traducción de
formatos no son libres. Además la mayoría solo interpretan formatos de archivos de
editores gráficos propietarios, los cuales tienden a tener un costo monetario elevado. Una
de las ideas que se mantuvo al desarrollar el simulador del brazo robot, fue la tratar de
mantener el programa y el desarrollo de éste libre de programas propietarios. Por esta razón
el modelado tridimensional del brazo se desarrolló utilizando directamente las primitivas
geométricas que provee el API OpenGL.
4.1.1
El brazo robot Stäubli como un modelo jerárquico
El principal aspecto que se debe tomar en cuenta al modelar un brazo robot, es que
éste es un objeto articulado, está compuesto por partes rígidas la cuales se encuentran
conectadas entre sí mediante articulaciones. Esta característica conlleva a la existencia de
diferentes niveles de jerarquía para cada componente de la extremidad robótica. Existen dos
diferentes niveles, el de los padres y el de los hijos. A continuación se explican los
diferentes niveles de jerarquía utilizando el modelo de un brazo robot de tres grados de
libertad.
Figura 4.2 Modelo de brazo robot de tres grados de libertad
En la figura 4.2 se observa un modelo simple de un brazo robot con tres grados de
libertad, el cual está compuesto por tres piezas rígidas llamadas base, brazo1 y antebrazo.
La base puede girar alrededor del eje Y y su ángulo de giro es θ, brazo1 gira alrededor del
eje Z y su ángulo de giro es φ y el antebrazo gira alrededor del eje Z con un ángulo de giro
llamado ψ. En la figura 4.2 se puede observar que cada parte del robot posee su propio
sistema de coordenadas en los cuales se encuentran definidos los ángulos de rotación de
cada una de las partes. Además es claro que la base está en el nivel de jerarquía más alto. Si
la base rota, también rota el brazo1 y el antebrazo, en otras palabras la base es el padre del
brazo1 y el antebrazo, mientras que brazo1 y el antebrazo por su parte son hijos de la base.
Brazo1 es el padre del antebrazo, ya que si éste rota, el anterazo también sufre una rotación.
40
Todas las transformaciones de traslación o rotación que sufren los padres, también se dan
en los hijos, pero las transformaciones ocurridas en los hijos no tienen porque ocurrir en los
padres necesariamente. Por ejemplo si brazo1 rota 30 grados, el antebrazo se ve obligado a
rotar 30 grados con respecto al origen de brazo1, pero si el antebrazo por su parte gira 50
grados, brazo1 no se ve obligado a sufrir ninguna rotación. A esto se le conoce como un
modelo jerárquico. Una de las ventajas de definir un sistema de coordenadas para cada una
de las partes del robot es la siguiente, por ejemplo, si brazo1 de la figura 4.2 es rotado 20°,
la posición de antebrazo debe ser transformada para mantener la jerarquía del brazo robot,
de manera que las partes se encuentren unidas correctamente a la hora de desplegar el
modelo. Ahora bien, sería complicado tener que calcular las nuevas coordenadas que debe
tener el antebrazo en su propio sistema de coordenadas debido a la rotación de brazo1. En
lugar lo que se hace es que se mantienen las mismas coordenadas que el antebrazo posee
con respecto a su propio sistema de coordenadas y se gira el sistema de coordenadas del
antebrazo 20° con respecto al origen del sistema de brazo1. Teniendo lo anterior en cuenta
y sabiendo que las transformaciones de rotación y traslación se llevan a cabo mediante la
multiplicación de matrices, para graficar el modelo del robot de la figura 4.2 en donde la
base tiene una altura h1 y una rotación de θ, brazo1 tiene una altura h2 y una rotación de φ
y el antebrazo tiene una altura de h3 y una rotación de ψ, se llevaría acabo el siguiente
proceso:
Matriz M_model = matriz_Identidad;
//Se inicia la Matriz de transformación actual
...
dibujar_robot()
{
M_model = RotarY(θ);
base();
/*Se multiplica a la matriz de Transf.. actual la rotación de la base*/
/*Se dibuja la base*/
/*Se multiplica a la matriz de transformación actual la rotación de la base, la traslación de la altura
h1 y la rotación del brazo1*/
M_model = RotarY(θ)*Trasladar(0,h1,0)*RotarZ(φ);
brazo1();
/*Se dibuja brazo1*/
/* Se multiplica a la matriz de transformación actual la rotación de la base, la traslación de la altura
h1, la rotación del brazo1, la traslación de h2 y la rotación del antebrazo*/
M_model = RotarY(θ)*Trasladar(0,h1,0)*RotarZ(φ)*Trasladar(0,h2,0)*RotarZ(ψ);
antebrazo1();
}
….
/*Se dibujar el antebrazo/
41
En el proceso anterior se nota que es poco práctico tener que estar calculando la
matriz de transformación actual cada vez que se produce una transformación. En lugar de
recalcular la matriz global, ésta se puede actualizar concatenando matrices a su derecha de
la siguiente manera:
Matriz M_model = matriz_Identidad;
/*Se inicializa la Matriz de transformación actual*/
...
dibujar_robot()
{
M_model = RotarY(θ);
base();
/*Se multiplica a la matriz de transf. actual la rotación de la base*/
/*Se dibuja la base*/
/*Se concatena la traslación de la altura h1 y la rotación del brazo1*/
M_model *= Trasladar(0,h1,0)*RotarZ(φ);
brazo1();
/*Se dibuja brazo1*/
/* Se concatenan la traslación de h2 y la rotación del antebrazo*/
M_model *= Trasladar(0,h2,0)*RotarZ(ψ);
antebrazo1();
/*Se dibujar el antebrazo/
}
Como se explico en el capítulo dos, OpenGL mantiene una matriz global de estado
llamada matriz Model-View, la cual es actualizada concatenando matrices a su derecha cada
vez que se da una transformación en el modelo. Utilizando funciones OpenGL el proceso
anterior se puede presentar de la siguiente manera:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity();
...
dibujar_robot()
{
glRotatef( theta, 0.0, 1.0, 0.0 );
base();
glTranslatef( 0.0, h1, 0.0 );
glRotatef( phi, 0.0, 0.0, 1.0 );
brazo1();
glTranslatef( 0.0, h2, 0.0 );
glRotatef( psi, 0.0, 0.0, 1.0 );
antebrazo();
}
…
/*Se carga la Matriz de transformación actual*/
/*Se inicializa la matriz de transf. actual*/
42
4.1.2
El modelado del brazo robot Stäubli RX90 L
Para el desarrollo del modelo del brazo robot se utilizó la técnica de modelado de
geometría sólida, en la cual se emplean primitivas geométricas básicas, tales como vértices
y líneas para crear la figura tridimensional. La primera fase en el proceso de modelado del
brazo robot Stäubli, la comprendió el estudio del nivel de jerarquía de cada uno de los
componentes del brazo. Una vez que se establecieron los diferentes niveles de cada parte,
se experimentó creando modelos de brazo más simples, con el fin de entender
adecuadamente las características de las diferentes funciones de transformación que brinda
OpenGL.
Figura 4.3 Primer modelo creado del brazo robot.
En la figura 4.3 se muestra el primer modelo de un brazo robot, que se creó en este
proyecto. Con este modelo se llegó a comprender el funcionamiento de las figuras
tridimensionales con diferentes niveles de jerarquía y como desplegaralas y controlarlas
utilizando las funciones del API OpenGL. Este primer modelo de un brazo con tres grados
de libertad, fue la base y punto de partida para el proceso de creación del modelo final del
brazo Stäubli RX90 L.
El brazo robot Stäubli RX90 L, posee seis grados de libertad y esta compuesto por
siete partes principales: la base, el hombro, el brazo (o brazo1), el codo, el antebrazo (o
brazo2), la muñeca y la mano (ver capítulo tres).
43
Figura 4.4 Principales componentes del brazo Stäubli RX901.
Para cada uno de los componentes del brazo se estudió y determinó cuales de sus
propiedades geométricas son las más influyentes durante los procesos de colisión del robot,
ya sea con el piso o consigo mismo. Luego se modelaron los componentes utilizando
primitivas geométricas, tomando como guía las medidas dadas por el fabricante del brazo
robot. Se utilizaron listas de despliegue, las cuales almacenan la información gráfica de las
diferentes partes del robot. En total se crearon siete listas de despliegue, una por cada
componente principal del brazo robot.
/* Ejemplo: creación de la muñeca del brazo robot*/
...
void crear_munieca(int slices, int stacks){
/* Se define la variables quadObj1, la cual es un puntero a un objeto cuadrático (lib. GLU) */
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
/* Se señala el inicio de la lista de despliegue MUNIECA*/
glNewList(MUNIECA, GL_COMPILE);
1
Figura (modificada) tomada de las hojas del fabricante, http://www.staubli.com/web/robot/division.nsf
44
/*Se apíla el estado de la matriz actual transformación, de manera que las
transformaciones que se den a partir de este punto, no afecten ningún objeto que haya sido
llamado(dibujado) antes de este punto*/
glPushMatrix();
glRotatef(-90, 0.0, 1.0, 0.0);
/*Se crea un cilindro con un radio de 5 unidades en ambos extremos y un largos
de 11 unidades, “slices” y “stacks” define el numero de cortes horizontales y
verticales que forman el cilindró */
gluCylinder(quadObj1, 5, 5, 11, slices, stacks);
glPushMatrix();
glRotatef(180, 1.0, 0.0, 0.0);
/*Se crea un disco con un radio de 5 unidades*/
gluDisk(quadObj1, 0, 5, slices, stacks);
glPopMatrix();
glTranslatef(0, 0, 11);
/*Se crea un disco con un radio de 5 unidades*/
gluDisk(quadObj1, 0, 5, slices, stacks);
/*Se desapila el estado anterior de la matriz de transformación*/
glPopMatrix();
/*Se indica el fin de la lista de despliegue*/
glEndList();
}
...
En el ejemplo anterior se muestra el código de la función que crea y define la lista
de despliegue encargada de guardar la información gráfica referente a la muñeca del brazo
robot. Se observa que los objetos que describen la forma geométrica de la muñeca se
encuentran definidos entre dos funciones, glPushMatrix() y glPopMatrix(), estas funciones
apilan (salvan) y desapilan (cargan) el estado de la matriz de transformación actual (matriz
Model-View) respectivamente. Esto impide que las transformaciones necesarias para crear
la muñeca, surtan efecto en el resto de los componentes del brazo y de la escena virtual en
general. En el código también es posible observar, que el sistema de coordenadas es rotado
–90° con respecto al eje Y antes de crear el cilindro que conforma la muñeca, mediante la
función glRotatef(90,0.0,1.0,0.0). Esto se debe llevar a cabo, debido a que la función
gluCylinder() posiciona los cilindros que crea a lo largo del eje Z del sistema de
coordenadas propio del objeto y se desea que el cilindro de la muñeca se encuentre
posicionado a lo largo del eje X de su sistema de coordenadas propio. El sistema de
coordenadas propio de cada disco también se rotó por la misma razón, además fueron
trasladados, ya que se espera que los discos se encuentren en los extremos del cilindro. Se
puede observar que con el uso correcto de las funciones glPushMatrix() y glPopMatrix() es
posible llevar a cabo transformaciones sobre una parte especifica del modelo, sin afectar al
resto de este.
Como se señaló anteriormente, se crearon siete funciones encargadas de definir
siete diferentes listas de despliegue, una para cada parte principal del robot. Se estableció
un sistema de coordenadas propio para cada componente principal del brazo robot. Cada
45
sistema de coordenadas es hijo del sistema que le precede. El orden de jerarquía de los
sistemas de coordenadas propios de los objetos es el siguiente: sistema Base, sistema
Hombro, sistema Brazo1, sistema Codo, sistema Brazo2, sistema Muñeca, sistema Mano,
en donde el sistema Base es quien posee el mayor nivel de jerarquía entre todos ellos, en
otras palabras, el sistema Base es padre del resto de los sistemas de coordenadas propios de
los componentes del robot. El punto de origen del sistema Base coincide con el punto de
origen del sistema de coordenadas global de la escena o espacio virtual, de hecho como la
base del brazo no sufre ninguna transformación, es decir no gira ni se traslada, entonces
ambos sistemas serán realmente el mismo sistema de coordenadas en todo momento.
A continuación se presenta el código de la función dibujar(), en la cuál se da el
proceso de posicionamiento y despliegue de los diferentes objetos que componen la escena
virtual. En otras palabras, aquí es donde se giran y trasladan los sistemas de coordenadas
propios de cada componente y donde son llamadas las listas de despliegue, cada vez que
alguna de las partes principales del brazo robot recibe una orden de movimiento por parte
del usuario.
/*Función: dibujar().Se encarga de dibujar todo lo que desea desplegar en la escena. Archivo: dibujo.c. */
gboolean dibujar(void){
GdkGLContext *glcontext = gtk_widget_get_gl_context (glarea1);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (glarea1);
/*** OpenGL INICIO ***/
if (!gdk_gl_drawable_make_current(gldrawable, glcontext)){
printf("Error al buscar area OpenGL");
return FALSE;
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
if(checkmax==1){ /*limita los angulos de giro a sus amplitudes máximas*/
check_maximo();
}
if(grid==1){
/* GRID. Dibujar el piso en configuración de rejilla */
glBegin(GL_LINES);
glColor3f(0.0, 0.1, 1.0);
int i;
for(i=-300;i<=300;i+=30) {
glVertex3f(i,altpiso,-300);
glVertex3f(i,altpiso,300);
glVertex3f(300,altpiso,i);
glVertex3f(-300,altpiso,i);
}
glEnd();
}else {
46
/*PISO. Dibujar el piso en configuración de polígono sólido */
glBegin(GL_QUADS);
glColor3f(0.9, 0.9, 0.9);
glVertex3f(-500.0f, altpiso, -500.0f);
glVertex3f(-500.0f, altpiso, 500.0f);
glVertex3f(500.0f, altpiso, 500.0f);
glVertex3f(500.0f, altpiso, -500.0f);
glEnd();
}
...
La primera etapa de la función dibujar(), consiste en buscar la ventana y el area de
la ventana, destinada a desplegar los gráficos OpenGL, es decir el área en donde se debe
llevar a cabo la graficación. En la línea que contiene la declaración
if(!gdk_gl_drawable_make_current(gldrawable, glcontext)) es en donde se realiza esta
búsqueda. Si el área no se encuentra, la función dibujar() termina, retornando el argumento
FALSE. Si el área, por otro lado, es encontrada, se continua, primero reiniciando o
limpiando el contenido de los almacenadores intermediarios (<<buffers>>) de color y
profundidad mediante la función glClear(). Luego se inicializa la matriz actual de
transformación utilizando la función glLoadIdentity() y se determina si se desea limitar los
angulos de giro de las articulaciones del brazo robot a sus amplitudes máximas (ver cuadro
3.1), posteriormente se dibuja el piso de la escena, ya sea en forma de rejilla o de polígono
sólido.
/* Continuación de la función dibujar()*/
...
/*Base externa sobre la cual se monta el brazo (opcional)*/
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glPushMatrix();
glColor3f(0.1, 0.1, 1.0);
glRotatef(90, 1.0, 0.0, 0.0);
gluCylinder(quadObj1, 12, 12, -1*altpiso, 30, 30);
glPopMatrix();
glColor3f(0.7, 0.8, 0.7);
//BASE (propia del brazo)
glPushMatrix();
glTranslatef(0.0, 0.0, 0.0);
glCallList(BASE);
glPopMatrix();
//HOMBRO
glPushMatrix();
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
47
glCallList(HOMBRO);
glTranslatef(-13.0, 0.0, 0.0);
//BRAZO1
glPushMatrix();
glRotatef(angulo2, 1.0, 0.0, 0.0);
glCallList(BRAZO1);
glTranslatef(13.0, 45.0, 0.0);
//CODO
glPushMatrix();
glRotatef(angulo3, 1.0, 0.0, 0.0);
glCallList(CODO);
glTranslatef(0.0, 10.0, 0.0);
//BRAZO2
glPushMatrix();
glRotatef(angulo4, 0.0, 1.0, 0.0);
glCallList(BRAZO2);
glTranslatef(0.0, 55.0, 0.0);
//MUNIECA
glPushMatrix();
glRotatef(angulo5, 1.0, 0.0, 0.0);
glTranslatef(5.5, 0.0, 0.0);
glCallList(MUNIECA);
glTranslatef(-5.5, 0.0, 0.0);
//MANO
glPushMatrix();
glRotatef(angulo6, 0.0, 1.0, 0.0);
glCallList(MANO);
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
...
Como se observa en el extracto de código anterior, posterior a la creación del piso
de la escena, se comienza con el posicionamiento de los sistemas de coordenadas propios
de cada componente principal del robot y se grafican los diferentes componentes. El primer
componente que se dibuja es la base externa sobre la cuál va montado el brazo robot. Esta
base es opcional y su tamaño puede ser variado por el usuario tal y como se explica más
adelante. La siguiente parte del robot que se dibuja es la base propia del brazo, cuyo
48
sistema de coordenadas se encuentra en el punto de origen de la escena. En la línea
glCallList(BASE) se llama a la lista de despliegue que contiene la información gráfica de la
base. Se puede observar en el código que para cada componente del brazo robot, primero se
llama la función glPushMatrix() y luego se llevan a cabo las transformaciones necesarias y
se llama la lista de despliegue propia del componente. Tal y como se explicó anteriormente,
la función glPushMatrix() salva en una pila el estado actual de la matriz de transformación
global, de manera que cada componente principal posee un sistema de coordenadas propio,
hijo del sistema de coordenadas del componente que le antecede. Por ejemplo, en la línea
glRotatef(angulo1,0.0,1.0 0.0), la cual se encuentra anterior al llamado de la lista de
despliegue del hombro (glCallList(HOMBRO)), se establece una rotación con respecto al
eje Y de magnitud igual al valor de la variable angulo1. Esta rotación afecta a todos los
componentes que sean dibujados después de la función glPushMatrix() precedente más
cercana, es decir que afecta al hombro, brazo1, codo, brazo2, muñeca y mano, pero a la
base no. Esta es la idea principal que se sigue para posicionar y dibujar los componentes
del brazo en la escena, de manera que se respete la jerarquía de cada uno de ellos. Se
observa que cada una de las partes principales del robot está ligada a una variable de giro,
excepto la base, ya que ésta no rota. Estas variables definen el giro de cada uno de los
componentes, respecto a un eje determinado del sistema de coordenadas propio a cada
componente y por lo tanto estás variables son quienes, en conjunto, determinan la posición
o configuración final del brazo. La variable angulo1 especifica el giro del hombro, angulo2
el giro de brazo1, angulo3 el giro del codo, angulo4 el giro de brazo2, angulo5 el giro de la
muñeca y angulo6 el giro de la mano.
/* Continuación de la función dibujar()*/
...
if(lim==1){
dibujar_limites();
}
probar_colision(marg);
semaforo();
glFlush ();
gdk_gl_drawable_swap_buffers (gldrawable);
return TRUE;
}
...
En ultimo fragmento del código de la función dibujar() se llevan a cabo los últimos
pasos necesarios para desplegar la escena en la ventana. Primero se determina si se desea
dibujar los límites geométricos del brazo. Estos límites fueron creados durante el modelado
del brazo para llevar a cabo pruebas sobre la geometría del modelo con respecto a la del
brazo real, son una herramienta para el programador o modelador más que para el usuario.
En la siguiente línea de código se llama a la función probar_colisión(), a la cual se le pasa
el argumento marg. Esta función es la encargada de realizar las diferentes pruebas de
49
colisión que posee el simulador. El argumento marg determina la magnitud del margen de
precaución con el cual se llevan a cabo las pruebas. Una vez terminadas las pruebas de
colisión se llama a la función glFlush (), la cuál exige la ejecución de todos los comandos
OpenGL
que
han
sido
llamados
hasta
este
punto.
La
función
gdk_gl_drawable_swap_buffers (gldrawable) es quien se encarga de intercambiar los
almacenadores intermedios de cuadro o <<frame buffers>>, en los cuales se recolecta todo
lo que se desea dibujar en la pantalla. Esto se debe llevar a cabo en los casos que se utiliza
el método de doble <<buffer>>, el cuál consiste en guardar toda la información gráfica que
se desea desplegar en la pantalla en un <<buffer>> de despliegue secundario, el cúal se
intercambia con el <<buffer>> de despliegue primario, es decir se convierte en el primario,
una vez que toda la información necesaria haya sido recopilada, de tal manera que nunca se
manipula la información del <<buffer>> primario durante el despliegue, ya que esto podría
causar que la animación se parezca cortada y poco fluida.
La iluminación es un aspecto sumamente importante en una escena y puede llegar a
ser difícil de dominar. Sin iluminación, no se podría distinguir entre un objeto
bidimensional y uno tridimensional. Por ejemplo en la figura 4.5 se observa la misma
esfera en dos cuadros diferentes. En el primero hay iluminación en la escena y en el
segundo no.
Figura 4.5 Esfera con iluminación y sin iluminación.
A continuación se muestra parte del código de la función inicio() la cuál se
encuentra implementada en el archivo fuente ventanagl.c. Esta función es la que se encarga
de crear las luces y establecer las propiedades de éstas, así como las propiedades de los
materiales de los objetos en la escena.
/* Fragmentos del código de la función inicio(). Archivo fuente: ventanagl.c */
50
gboolean inicio(void){
GLfloat ambient [] = { 0.1, 0.1, 0.1, 0.0 };
GLfloat specular []= { 1.0, 1.0, 1.0, 1.0 };
GLfloat shininess [] = { 100.0 };
GLfloat position0 [] = { 400.0, 400.0, 400.0, 1.0 };
…
/* Determina la propiedad de reflejo del material*/
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
/* Determina la posición de las luces y el tipo de luz*/
glLightfv(GL_LIGHT0, GL_POSITION, position0);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
…
}
En la figura 4.6 se observa el modelo final del brazo robot Stäubli RX90 L, que se
desarrolló en el proyecto.
Figura 4.6 Modelo del brazo robot Stäubli RX90 L.
Algunas características geométricas del brazo real, que no presentaban relevancia
para la detección precisa de colisiones, no fueron implementadas en el modelo.
51
4.1.3
El método de detección de colisiones
Para la implementación de pruebas de detección de colisiones en el simulador, se
establecieron nueve puntos de control sobre el brazo virtual, cada punto se encuentra ligado
a una figura de control y se localiza en el centro de ésta en todo momento. Se utilizaron dos
tipos de figuras para llevar a cabo el control de colisiones, círculos y cuadros. Las figuras
están compuestas por un número determinado de vértices los cuales se encuentran unidos
entre si mediante una línea continua. Los cuadros constan de cuatro vértices, mientras que
los círculos están compuestos por treinta vértices cada uno. Las figuras de control poseen
un radio característico a cada una, en el caso de los círculos el radio de control es el valor
del radio del círculo propio, mientras que en el caso de los cuadros de control, el radio es la
distancia desde el punto medio hasta el primer vértice del cuadro (ver figura 4.7).
La figura 4.7 es una representación esquemática de la composición de las figuras de
control a partir de vértices. En la gráfica también se observa la ubicación del punto de
control central, así como el radio de cada figura.
Figura 4.7 Tipos de figuras de control
La figura 4.8 muestra la posición de las diferentes figuras de control en el modelo
tridimensional del brazo robot.
52
Figura 4.8 Figuras de control en el modelo del brazo robot
La figura 4.9 es un esquema que señala la posición de las diferentes figuras de
control en el modelo del brazo robot. Los cuatro componentes principales del brazo, mano,
muñeca, brazo2 y codo, fueron graficados separados uno del otro, con el fin de comprender
mejor la ubicación de las figuras de control. En el esquema de la figura 4.9 se tienen dos
perspectivas de visión, una frontal al brazo y una lateral.
Figura 4.9 Figuras de control (esquemático)
53
El cuadro 4.1 enumera los diferentes puntos de control existentes y las figuras de
control que se encuentran ligadas a cada punto.
Cuadro 4.1 Puntos y figuras de control
Punto de control
Figura(s) de control
ligada(s) al punto de
control
CBA (Control Brazo A)
cuadBA
CBB
cuadBB
CBC
cuadBC, circBC
CBD
circBD
CWA (Control Wrist A)
circWA
CWB
circWB
CWC
circWC
CHA (Control Hand A)
circHA
CHB
circHA
En los gráficos anteriores se observa que los puntos y figuras de control se
encuentran ubicados en diferentes componentes del brazo robot. Es decir que no todas las
figuras y puntos de control se ven afectados por el mismo nivel de jerarquía, en otras
palabras, no todos se encuentran dentro del mismo sistema local de coordenadas. Nótese
que por ejemplo, un movimiento de rotación en la articulación cuatro, es decir un cambio
en valor del ángulo angulo4, no afectaría la posición de las figuras cuadBA, cuaBB,
cuadBC, pero si afectaría la posición del resto de las figuras de control, mientras que un
cambio en el ángulo angulo3 afectaría la posición de todas las figuras de control. El nivel
de jerarquía que afecta las figuras y puntos de control es un factor sumamente importante a
la hora de trasladar los puntos de cada figura de control de sus coordenadas locales a
coordenadas globales, ya que es el nivel de jerarquía, quien determina cuales matrices de
traslación y rotación deben ser utilizadas para llevar a cabo el cambio de coordenadas. El
cuadro 4.2 indica el nivel de jerarquía que afecta a cada punto y figura de control.
54
Cuadro 4.2 Niveles de jerarquía de las figuras de control
Nivel de
jerarquía
Ángulos
influyentes
Puntos de
control
Figuras de
control
3
angulo1, angulo2,
angulo3
CBA, CBB,
CBC, CBD
cuadBA, cuadBB,
cuadBC
4
5
angulo1, angulo2,
angulo3, angulo4
angulo1, angulo2,
angulo3, angulo4,
angulo5
CWA, CWB,
CWC
CHA, CHB
circBC, circBD,
circWA, circWB,
circWC
circHA, cirHB
El traslado de las coordenadas de un punto de su sistema local o propio al sistema
de coordenadas Global o Worldspace, se hace mediante la multiplicación de matrices
homogéneas de traslación y rotación (ver capítulo 2). Si rotZ(ang) representa la matriz de
rotación homogénea en el eje Z, rotY(ang) la matriz de rotación respecto al eje Y,
rotX(ang) la matriz de rotación respecto al eje X, ang el ángulo de rotación específico a
cada matriz y tras(vect) la matriz de traslación con un vector de traslación vect, entonces el
proceso de traslación de las coordenadas de un punto, perteneciente al modelo del brazo
robot, desde su sistema local hasta el sistema Global estaría dado por las siguientes
ecuaciones:
Nivel de jerarquía: 3
→ Pcoord. Global = tras(br1)*rotX(angulo2)*tras(cod)*rotX(angulo3)*Pcoord. Local
Nivel de jerarquía: 4
→ Pcoord. Global = tras(br1)*rotX(angulo2)*tras(cod)*rotX(angulo3)*tras(br2)
*rotY(angulo4)*Pcoord. Local
Nivel de jerarquía: 5
→ Pcoord. Global = tras(br1)*rotX(angulo2)*tras(cod)*rotX(angulo3)*tras(br2)
*rotY(angulo4)*tras(man)*Pcoord. Local
55
en donde br1 es un vector que representa la posición en la que se encuentra el sistema de
coodernadas propio de brazo1 (sistema Brazo1) en el sistema Global, cod representa la
posición del sistema de coodernadas propio del codo (sistema Codo) en el sistema Brazo1,
br2 representa la posición del sistema de coodernadas propio de brazo2 (sistema Brazo2)
en el sistema Codo y man representa la posición del sistema de coodernadas propio de la
mano (sistema Mano) en el sistema Brazo2. Pcoord Global es el vector de coordenadas del
punto en el sistema Global, mientras que Pcoord Local es el vector de coordenadas del punto en
el sistema Local.
El procedimiento general de las pruebas de control de colisiones es el siguiente:
•
Se pasan las coordenadas locales de los puntos de control a coordenadas
globales.
•
Si alguno de los puntos de control se encuentra dentro de alguna de las
llamadas zonas de precaución, entonces se realiza la prueba de colisión
necesaria.
Este proceso se lleva acabo cada vez que el programa llama a la función dibujar().
En la aplicación se definieron dos zonas de precaución, una sobre el piso de la
escena virtual y otra que encierra la base del brazo robot. El volumen de estás zonas de
precaución es variable y su valor depende del radio de la figura de control ligada al punto
de control que se encuentra en condición de prueba.
Un punto de control se encuentra dentro de la zona de precaución del piso cuando el
valor de su coordenada Y en el sistema Global es menor que la suma del radio de la figura
de control ligada a él y el margen de precaución definido por el usuario, cuyo valor
predeterminado es cero.
Por otro lado, un punto de control se encuentra dentro de la zona de precaución de
la base cuando su distancia ortogonal al eje Y es menor que la suma del radio de la base del
brazo, más el radio de la figura de control ligada al punto y el margen de precaución
determinado por el usuario.
La siguiente figura permite un mejor entendimiento de las zonas de precaución y la
variación de sus tamaños.
56
Figura 4.10 Zonas de precaución
Por ejemplo, si se deseara saber si el punto de control CBB se encuentra dentro de
alguna de las zonas de precaución, el procedimiento sería el siguiente:
•
Se pasan las coordenadas del punto de control CBB al sistema de coordenadas
Global, mediante la multiplicación apropiada de matrices.
•
Se determina la altura de la zona de precaución del piso. Como el punto de
control CBB se encuentra ligado a la figura de control cuadBB, entonces la
altura de la zona de precaución sería la suma del radio de la figura cuadBB
(ver figura 4.7) más el valor del margen de precaución, cuyo valor
predeterminado es cero.
•
Si el valor de la coordenada Y del punto de control CBB en el sistema de
coordenadas globales es menor que la altura de la zona de precaución del piso,
calculada anteriormente, entonces el punto se encuentra dentro de la zona de
precaución, por lo tanto se debe llevar a cabo la prueba de colisión de la figura
cuadBB contra el piso.
•
Si el valor de la coordenada Y del punto de control CBB en el sistema de
coordenadas globales es menor que la altura de la base del brazo robot virtual,
entonces existe la posibilidad de que el punto de control se encuentre dentro
de la zona de precaución de la base, por lo que se debe calcular el radio de tal
zona de precaución para poder determinar de manera segura, si el punto se
encuentra dentro de la zona o no.
57
•
Como el punto de control CBB se encuentra ligado a la figura de control
cuadBB, entonces el radio de la zona de precaución para este caso sería la
suma del radio de la base del modelo del brazo, más el radio de la figura
cuadBB, más el margen de precaución.
•
Si la raíz cuadrada de la suma de las coordenadas globales X y Z al cuadrado
del punto de control, es decir X 2 + Y 2 , es menor que el radio de la zona de
precaución de la base, entonces el punto de control CBB se encuentra dentro
de la zona de precaución de la base, por lo tanto existe peligro de colisión y se
debe realizar una prueba de colisión de la figura cuadBB contra la base.
Para el punto de control CBC el cual se encuentra ligado a dos figuras de control, se
utiliza la figura cuadBC para llevar a cabo estas pruebas de invasión de zona de precaución,
ya que cuadBC posee un radio mayor que circBC, la cual es la otra figura ligada al punto.
En el simulador se implementaron tres tipos de pruebas de detección de colisión: la
prueba de detección de colisión contra el piso, la prueba de detección de colisión contra la
base, las cuales ya fueron mencionadas anteriormente y la prueba de detección de colisión
contra las orillas del hombro.
Anterior a la realización de cada prueba se lleva a cabo una inicialización de ésta,
fase en la que se trasladan las coordenadas de cada uno de los puntos que componen la
figura de control que va a ser examinada, de su sistema de coordenadas propio o local al
sistema de coordenadas Global.
La prueba de detección de colisión contra el piso consiste en comparar el valor de la
coordenada global Y de cada uno de los vértices que componen la figura de control en
evaluación. Si alguno de estos puntos posee un valor de Y menor o igual a la suma de la
altura del piso más el margen de precaución, entonces se declara que hubo un choque del
brazo robot contra el suelo.
En la prueba de detección de colisión contra la base se monitorea la distancia
ortogonal de cada uno de los puntos de la figura de control examinada hasta el eje Y del
sistema de coordenadas Global, es decir el valor resultante de la ecuación X 2 + Y 2 para
cada uno de los puntos. Si las coordenadas X y Y de alguno de los puntos conlleva a un
resultado menor o igual al radio de la base del brazo virtual más el margen de precaución
dado por el usuario, entonces se determina que hubo un choque del brazo contra la base.
Nótese que las dos pruebas explicadas anteriormente son una derivación de la
popular prueba de colisión de la caja limítrofe (por su nombre en inglés: <<Bounding-Box
Collision Test>>), la cual es prueba de detección de colisión más utilizada en el desarrollo
de juegos de videos y simuladores virtuales. Esta consiste en encerrar un objeto en una caja
no visible y determinar si algún otro objeto a invadido la caja.
58
Para el proyecto se tuvo que desarrollar un tercer tipo de prueba de detección de
colisiones, de manera que se pudieran detectar todos los choque posibles. Debido a las
características geométricas del robot Stäubli RX90 L, el único componente propio del brazo
capaz de chocar contra el hombro es el antebrazo, también llamado brazo2. La colisiones
del antebrazo contra el hombro suceden en las orillas del hombro, por lo que la prueba
desarrollada para detectar estos choques fue denominada como “la prueba de colisión de
orillas” o también “prueba de colisión de pendientes” como se explica más adelante.
Figura 4.11 Orillas del hombro del brazo robot
Ninguna de las figuras de control especificadas en el simulador es capaz de ingresar
en el volumen definido por el componente Hombro, sin que antes ocurra una colisión entre
una de las orillas del hombro y el antebrazo, por esta razón las pruebas de detección de
colisiones basadas en áreas limítrofes son inservibles a la hora de detectar un choque en el
hombro. Se observa en la figura 4.11 que el hombro posee cuatro orillas en las cuales se
pueden dar colisiones del brazo consigo mismo.
La prueba de colisión de orillas consiste en crear una línea recta, llamada línea de
control, entre el punto de la figura de control circBC más cercano a la orilla del hombro y el
punto de la figura de control circBD análogo, tal y como se observa en la figura 4.12.
Posteriormente se lleva a cabo una comparación de pendientes para determinar si se ha
dado una colisión del brazo robot contra su propio hombro.
59
Figura 4.12 Prueba de colisión de orillas (1)
Nótese que si se observa el modelo del brazo robot desde una perspectiva lateral, las
cuatro orillas de colisión del hombro, definidas anteriormente, aparecen como puntos en un
plano bidimensional de coordenadas Z, Y, tal y como se observa en la figura 4.13. Esta es
la idea principal detrás de la prueba de colisión de orillas. Las cuatro orillas del hombro se
convierten en puntos de dos dimensiones y la línea de control entre las figuras de control
circBC y circBD se transforma en una línea recta en el plano ZY, que puede ser descrita
por la ecuación y = m × z + b , en donde m es la pendiente de la línea y b el valor de y
cuando la línea interseca el eje Z.
Figura 4.13 Prueba de colisión de orillas (2)
60
En la figura 4.13 es posible percibir una segunda línea recta, entre el punto de
origen inferior de la línea de control y la orilla superior izquierda del hombro, esta línea se
denomina “línea al hombro”. El nivel Y1 indica la altura a la cual se encuentran las orillas
inferiores del hombro, mientras que el nivel Y2 es la altura de las orillas superiores. Si se
calculan las pendientes de ambas líneas y se comparan entre sí es posible determinar si se
dio una colisión entre el antebrazo y la orilla del hombro.
Nótese en la figura 4.14 que si el origen de la línea de control y de la línea al
hombro posee un valor de Z negativo y un valor Y menor o igual al valor Y de las orillas
superiores del hombro, entonces se puede afirmar que se da una colisión en la esquina
superior izquierda si y solo si la pendiente de la línea de control es menor o igual que la
pendiente de la línea al hombro. Mientras que si el origen de las línea de control y la línea
al hombro tiene un valor de Z positivo y un valor de Y menor o igual al valor Y de las
orillas superiores del hombro, entonces se da una colisión en la esquina superior derecha si
y solo si la pendiente de la línea de control es mayor que la pendiente de la línea al hombro.
Figura 4.14 Prueba de colisión de orillas (3)
Las características anteriores de la prueba de colisión de orillas son la razón por la
que también fue nombrada como la prueba de colisión de pendientes.
En el cuadro 4.3 se muestran las condiciones que se deben cumplir para que se lleve
a cabo una prueba de colisión de pendientes y como determinar si hubo colisión según las
condiciones de cada prueba. En el cuadro las coordenadas que se indican se encuentra en
referencia al sistema Global (WorldSpace) y nivel Y1 y nivel Y2, son los valores de altura
de las orillas inferiores y las orillas superiores del hombro, respectivamente (ver figura
4.13). MControl es la pendiente de la línea de control mientras que MHombro la pendiente de la
línea al hombro.
61
Cuadro 4.3 Condiciones y resultados de la prueba de colisión de orillas
Condiciones para que se lleva a cabo
la prueba de colisión de orillas
Orilla del hombro con la
cual se lleva a cabo la
prueba
(ver figura 4.13)
coord. Y de CBD < nivel Y1
&&
Si coord. Z de CBD ≤ 0
→ orilla inferior izquierda
coord. Y de CBC >nivel Y1-radio circBC
&&
Se da un choque si :
MControl ≥ MHombro
Si coord. Z de CBD > 0
-40-margen ≤ coord. Z de CBD ≤ 40+margen → orilla inferior derecha
coord. Y de CBD < nivel Y2+radio cuadBD
&&
Si coord. Z de CBC ≤ 0
→ orilla superior izquierda
coord. Y de CBC >nivel Y2
&&
MControl ≤ MHombro
Si coord. Z de CBD > 0
-40-margen ≤ coord. Z de CBC ≤ 40+margen → orilla superior derecha
coord. Y de CBC < nivel Y2+radio cuadBC
&&
Si coord. Z de CBD ≤ 0
→ orilla superior izquierda
coord. Y de CBD >nivel Y2
&&
Si coord. Z de CBD > 0
-40-margen ≤ coord. Z de CBD ≤ 40+margen → orilla superior derecha
MControl ≤ MHombro
62
A continuación se muestra el fragmento de código perteneciente a la función
probar_colision(), que se encarga de chequear si se están dando las condiciones necesarias
para llevar a cabo una prueba de colisión de orillas.
/* Fragmento de código. Función: probar_colision(flota margen). Archivo Fuente: dibujo.c.
* Este fragmento de la función es el encargado de verificar si se cumplen las condiciones
* para llevar a cabo las pruebas de colision de orillas. Si se cumplen las condiciones, llama la prueba .
* Nivel Y1 = 28.5 cm , y Nivel Y2 =54.5 cm. */
...
//VORTEX nivel 1 (hombro)
if((CBDreal[1]<28.5) && (CBCreal[1]>18.0) && (acbdz<=40+margen)){
dibujar_circBD();
check_vortex(circBD, circBC, circBDreal, 1);
cuidado=1;
}
//VORTEX nivel 2
if((CBDreal[1]<=65) && (CBCreal[1]>54.5) && (acbcz<=40.0+margen)){
dibujar_circBD();
check_vortex(circBD, circBC, circBDreal, 2);
cuidado=1;
}
if((CBCreal[1]<=65) && (CBDreal[1]>54.5) && (acbdz<=40+margen)){
dibujar_circBC();
check_vortex(circBC, circBD, circBCreal, 2);
cuidado=1;
}
...
Nótese que la función chech_vortex(arg1, arg2, arg3, 2) es la encargada de llevar a
cabo la prueba de colisión de pendientes. El argumento arg1 es la matriz de puntos con
coordenadas locales de la figura de control que se encuentra por debajo de la altura de la
orilla en prueba, arg2 es la matriz de puntos con coordenadas locales de la figura de
control que se encuentra por encima de la altura de la orilla en prueba, arg3 la matriz de
puntos con coordenadas globales de la figura de control pasada en el argumento numero
uno y arg4 es el nivel de altura de la orillas en prueba (Y1 o Y2).
Con cada llamada a la prueba de colisión de pendientes se da el siguiente proceso:
•
Se trasladan las coordenadas de los puntos de la figura de control señalada
en arg1, de su sistema de coordenada local al sistema de coordenadas
Global y se guardan tales coordenadas en la matriz señalada en arg3.
63
•
Se busca el punto de la figura de control señalada en arg1, que se
encuentra más cercano a la orilla en prueba. Se busca el punto análogo en
la figura de control señalada en arg2 y se pasa este punto a coordenadas
globales.
•
Se crea la línea de control entre los dos puntos anteriormente
determinados.
•
Se compara la pendiente entre la línea de control y la línea al hombro.
•
Se determina si existe una colisión o no.
La prueba de colisión de pendientes también fue nombrada como prueba VORTEX
(por sus siglas en inglés: <<Virtual Object Rapid Touching Edges Examination>>) o
“prueba rápida de las orillas en contacto del objeto virtual”.
CAPÍTULO 5: La interfaz gráfica de usuario de la aplicación
Para acceder al programa de simulación desarrollado en este proyecto, éste debe ser
ejecutado desde la línea de comandos, dentro de un entorno gráfico que tenga soporte para
OpenGL, GLU, GTK-2.0 y GtkGLext. Una vez que el programa se esté corriendo, el
simulador puede ser controlado directamente desde el emulador de terminal (la línea de
comandos) o mediante la interfaz gráfica de usuario GTK. Se recomienda utilizar siempre
la interfaz gráfica en lugar de la línea de comandos, ya que ésta facilita el uso de la
aplicación.
Desde la interfaz de usuario se pueden dar órdenes de movimiento, de creación y
ejecución de rutinas y de configuración al simulador. La interfaz está compuesta por tres
ventanas principales. En la figura 5.1 se observan las tres ventanas de la aplicación, además
de la ventana de la terminal desde la cual se encuentra corriendo el programa. Se
recomienda mantener las cuatro ventanas visibles, de manera similar a la figura 5.1. A
pesar de que algunos mensajes son desplegados en el <<textview>> de la aplicación GTK,
muchos mensajes de importancias también son transmitidos a través de la terminal.
Figura 5.1 La aplicación gráfica de usuario del simulador
En la ventana superior derecha de la figura 5.1, la cual es la ventana de despliegue
de la escena virtual, se muestra el modelo virtual del brazo robot Stäubli R90X L. En el
área superior derecha de esta ventana se puede observar una esfera. Esta esfera cambia de
lugar y de color dependiendo del riesgo que representa el movimiento actual del brazo. La
64
65
esfera de color verde indica que el movimiento es seguro y que el brazo no se encuentra
colisionado, la esfera amarilla señala que aunque no se está dando una colisión, al menos
un punto de control se localiza dentro de una de las áreas de precaución, por lo que el
choque puede estar cercano a ocurrir, finalmente la esfera roja indica que se está dando un
choque en al menos uno de los componentes del brazo.
Las ventanas gráficas fueron programadas utilizando funciones de la librería gtk2.0. Se utilizaron objetos como botones, entradas de texto, etiquetas, etc, para formar la
interfaz de usuario la cual consta de tres ventanas, dos ventanas en donde se ubican los
botones y demás objetos para controlar el simular virtual y una tercer ventana en donde se
despliega la escena virtual, es decir el mundo virtual en el cual se encuentra el modelo de
brazo robot.
Las tres ventanas gráficas son creadas en el momento en el que se inicia la
aplicación. Las funciones encargadas de llamar y desplegar las ventanas se encuentran en el
archivo fuente de código main.c.
/* Creación y despliegue de las ventanas de interfaz gráfica. Archivo fuente: main.c*/
...
gtk_init (&argc, &argv);
ventana1 = create_window1 ();
gtk_widget_show (ventana1);
ventana3 = create_window3 ();
gtk_widget_show (ventana3);
ventana2 = create_window2 ();
gtk_widget_show (ventana2);
…
gtk_main ();
En el segmento de código mostrado anteriormente se presentan las funciones
encargadas de crear y desplegar las ventanas gráficas de la aplicación. Las funciones
create_window1(), create_window2() y create_window3() crean las ventanas específicas,
con los objetos gráficos que las componen. La información es pasada a ventana1, ventana2
y ventana3, punteros a objetos GTK del tipo GtkWindow. Nótese como después de la
creación de cada ventana se llama a la función gtk_widget_show() y se le pasa como
argumento el puntero a la ventana que acaba de ser creada. Esta función produce que se
despliegue la ventana en la pantalla. La última función mostrada en el segmento de código
es gtk_main(), ésta señala la entrada al lazo principal Gtk. Este es un lazo infinito, en el
cuál la aplicación se encuentra en espera de algún evento.
Básicamente las funciones encargadas de crear una ventana gráfica, tienen como
tarea llamar a todos los objetos gráficos que sean necesarios y colocarlos dentro de un
marco de manera que se obtenga la configuración de la ventana, además se debe ligar a
66
cada objeto gráfico que lo requiera, las función de respuesta a diferentes eventos, por
ejemplo, los botones de una ventana gráfica deben tener funciones de respuesta que sean
llamadas cada vez que éstos son presionados.
A continuación se muestran algunos segmentos de código que componen la función
encargada de crear la ventana gráficas superior izquierda de la figura 5.1.
/*Ejemplo: Función encargada de crear la ventana 1. Archivo fuente: interface.c*/
…
GtkWidget* create_window1 (void){
/* Se inicializan los objetos que configuran la ventana*/
GtkWidget *window1;
GtkWidget *bsimular;
…
/*Se crea la ventana sobre la cual se colocaran los botones y demás objetos gráficos*/
window1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window1, "window1");
gtk_container_set_border_width (GTK_CONTAINER (window1), 3);
gtk_window_set_title (GTK_WINDOW (window1), _("Controles del Simulador"));
…
/* Se agregan todos los objetos que configuran la ventana. Por ejemplo, a continuación
se despliega la función que agregar el botón SIMULAR */
bsimular = gtk_button_new ();
gtk_widget_set_name (bsimular, "bsimular");
gtk_widget_show (bsimular);
gtk_box_pack_start (GTK_BOX (vbox3), bsimular, FALSE, FALSE, 0);
…
/*Se conectan los objetos gráficos y sus funciones de respuesta correspondientes*/
g_signal_connect ((gpointer) bsimular, "clicked",
G_CALLBACK (on_bsimular_clicked), NULL);
…
/*Se devuelve la ventana, compuesta por los objetos gráficos respectivos*/
return window1;
}
Las tres funciones que crean las ventanas de la interfaz se encuentran desarrolladas
en el archivo fuente interface.c. Nótese en el código anterior que la función
create_window1 devuelve un puntero a un objeto del tipo GTK. Dentro de la función se
inicializan todos los objetos gráficos que componen la ventana, en el ejemplo se muestran
solo dos de los objetos de la ventana, la ventana principal window1 y el objeto del tipo
botón bsimular (botón SIMULAR de la aplicación). En total, esta ventana se encuentra
compuesta por cincuenta y un objetos, incluyendo botones, entradas de texto, marcos,
etiquetas, etc. En el código se puede verificar la existencia de diferentes funciones que
ofrece la librería Gtk, que se pueden utilizar dependiendo del objeto que se desea crear, por
67
ejemplo para crear una ventana vacía se utiliza la función gtk_window_new(), mientras que
para crear un botón se utiliza la función gtk_button_new(). Algunos objetos gráficos
requieren funciones de respuesta para eventos determinados, por ejemplo en el código
anterior se conecta el objeto bsimular, es decir el botón SIMULAR de la aplicación, con la
función on_simular_clicked, ésta función va a ser llamada cada vez que el botón
SIMULAR sea presionado. La función utilizada para ligar el proceso de respuesta al botón
fue la siguiente: g_signal_connect(arg1, arg2, arg3, arg4), a la función se le deben pasar
cuatro argumentos, el primero indica el objeto gráfico en el que se da el evento, en el
ejemplo anterior es bsimular, el segundo argumento señala el tipo de evento para el cual se
debe llamar la función de respuesta, en el ejemplo el evento es del tipo “clicked”, es decir
cuando se presione el botón utilizando el puntero del ratón, el tercer argumento de la
función de conexión indica la función de respuesta que se desea ligar al evento y al objeto,
mientras que el cuarto argumento se utiliza para pasar información a la función de
respuesta.
Todas la funciones de respuesta se encuentran desarrolladas en el archivo fuente
callbacks.c. A continuación se muestra parte del código de la función on_bsimular_clicked,
la cual es llamada cada vez que el botón SIMULAR es presionado.
/*Función de respuesta del botón: Simular. Archivo fuente: callbacks.c*/
void on_bsimular_clicked (GtkButton *button, gpointer user_data){
/*Simula el movimiento del robot utilizando los valores de los botones 'spinner'*/
GtkSpinButton *spin1, *spin2, *spin3;
int velocporciento;
spin1=GTK_SPIN_BUTTON(spinbutton1);
spin2=GTK_SPIN_BUTTON(spinbutton2);
spin3=GTK_SPIN_BUTTON(spinbutton3);
hubochoque=0;
//Toma los valores de los botones 'spinners'
joint = gtk_spin_button_get_value_as_int(spin1);
angulodelta = gtk_spin_button_get_value_as_int(spin2);
velocporciento = gtk_spin_button_get_value_as_int(spin3);
set_velocidad(velocporciento, joint);
selec_mover_joint();
if(hubochoque==1){
printtv("\n\nCUIDADO!!!-->Se produjo un choque durante la simulación del movimiento");
}
}
La función on_bsimular_clicked utiliza los valores de articulación, ángulo y
velocidad, definidos por el usuario por medio de los botones correspondientes de la
68
aplicación, para simular el movimiento del brazo robot. Una vez tomados los valores se
llama a la función selec_mover_joint(), la cual se encarga de producir el movimiento en el
modelo virtual del robot. Cada botón de la aplicación se encuentra ligado a diferentes
funciones de respuesta, las cuales en conjunto permiten el funcionamiento correcto de la
aplicación.
5.1
Los botones de configuración del simulador
En la figura 5.2 se encuentran algunos botones encargados de cambiar la
configuración del simulador.
Figura 5.2 Los botones de configuración del simulador
El primer cuadro en la ventana contiene los botones encargados de guardar, cargar y
reiniciar las posiciones del brazo robot y de la cámara. Los botones “APILAR” y
“DESAPILAR” apilan y desapilan respectivamente, el estado del brazo virtual, en una pila
capaz de contener sesenta posiciones. Mientras que el botón “Reiniciar Pila” borra todas las
posiciones que hayan sido guardadas en la pila. “Reiniciar Robot” y “Reiniciar Cámara”
pueden ser presionados cuando se desea volver a la posición inicial de estos objetos.
En el segundo cuadro de la ventana se encuentra el botón “GRID”, el cual produce
que el piso de la escena sea desplegado en forma de rejilla.
El tercer marco contiene los botones encargados de configurar el proceso de
simulación en sí. El seleccionar la opción “Lim. Angulos” ocasiona que el simulador envíe
un mensaje al usuario cada vez que alguna articulación alcance su amplitud máxima de
giro. Si esta opción se encuentra seleccionada y se alcanza una amplitud de giro máxima,
ya sea durante la ejecución del comando “MOVER (DRIVE)”, la ejecución del comando
“Simular” o la ejecución de una rutina, la rotación de la articulación se detendrá en su
ángulo máximo. De manera similar, la selección de la opción “1° Colisión” ocasiona que el
brazo virtual detenga su movimiento una vez que éste detecta su primera colisión, ya sea
69
durante la ejecución del comando “MOVER (DRIVE)”, la ejecución del comando
“Simular” o la ejecución de una rutina. Si se da un choque y el botón “1° Colisión” se
encuentra seleccionado”, el modelo no se podrá mover nuevamente hasta que se deshabilite
el botón indicado o se recargue en el brazo su posición inicial o alguna en que no se
encuentre colisionado, ya sea utilizando la opción “Reiniciar Robot” o “DESAPILAR”.
Finalmente el botón “SIM ON” se encarga de habilitar y deshabilitar el simulador
durante la edición y ejecución de rutinas o el uso del comando “MOVER (DRIVE)”.
Existen dos cambios de configuración del simulador que se pueden llevar a cabo por
medio de la entrada de texto del marco “Línea de comandos”: agregar una base externa al
brazo virtual y definir un margen de precaución al proceso de detección de colisiones.
Muchas veces los brazos mecánicos se encuentran motados sobre bases externas,
con el fin de ajustarlos mejor a la zona de trabajo en la cual deben desempañarse. El
simulador posee la opción de agregar una base externa al brazo robot, cuyo radio es igual a
la base propia del modelo. Para añadir la base externa se introduce el comando “base” en la
línea de entrada del marco “Linea de comandos” de la primer ventana GTK, seguido por la
altura que se desea tenga la base, en centímetros, luego se ejecuta utilizando el botón
“EJECUTAR”. Por ejemplo en la figura 5.3 se agregó al brazo virtual una base externa de
30 centímetros.
Figura 5.3 Introducción de una base externa
Para definir un margen de precaución se introduce en la entrada de texto del marco
“Línea de comandos” la palabra “margen” seguida por la magnitud del margen de
precaución que se desea, en centímetros, luego se presiona el botón “EJECUTAR”.
70
Figura 5.4 Introducción del margen de error
En el ejemplo de la figura 5.4 se introdujo un margen de precaución de 20
centímetros, lo cual ocasiona que el simulador indique que se dio una colisión 20
centímetros antes de que ésta realmente ocurra.
5.2
Creación, edición y ejecución de rutinas
Existen tres opciones para crear una rutina de movimientos para el brazo virtual. La
primera es editar de forma manual el archivo de texto que contendrá la rutina, siguiendo el
formato específico de los archivos de rutina de la aplicación. La segunda manera es
seleccionar la ventana de despliegue de la escena virtual y presionar la tecla F5, luego se
deben seguir las órdenes desplegadas en la ventana de la terminal.
Figura 5.5 Ventana 1 de la aplicación
71
La opción más recomendable para crear una rutina es la de utilizar la interfaz
gráfica del programa. Lo primero que debe hacerse es asegurarse de que el simulador se
encuentre habilitado, utilizando el botón “SIM ON” en la ventana inferior izquierda de la
figura 5.1, luego se deben seguir los siguientes pasos:
•
Introducir un nombre para la nueva rutina, en la entrada de texto del cuadro
“Rutinas”, que se encuentra en la ventana de la figura 5.5 y presionar el botón
“Crear nueva Rutina” del mismo cuadro, lo cual dará comienzo a la edición de
la nueva rutina.
•
Para agregar movimientos a la rutina se utiliza el botón del comando
“MOVER (DRIVE)” y los botones de ajuste “ARTICULACIÓN”,
“ANGULO” y “VELOCIDAD(%)”. Cada movimiento agregado a la rutina
será simulado y se indicará si el movimiento causó un choque o si se alcanzó
el valor máximo de amplitud de giro para alguna articulación, siempre y
cuando la opción se encuentre habilitada. Si el movimiento conllevó a una
colisión en la simulación, la aplicación preguntará al usuario si éste debe ser
agregado a la rutina o no. Nunca se recomienda agregar a la rutina
movimientos que causen choques durante la simulación. El botón
“SIMULAR”, puede utilizarse como opción al botón “MOVER (DRIVE)” con
la diferencia de que esté simula el movimiento pero no lo guarda en el archivo
de la rutina. Antes de utilizar el botón “SIMULAR”, se recomienda apilar el
estado actual del brazo virtual, con ayuda del botón “APILAR”, en la ventana
de la figura 5.2, de manera que se pueda volver a esta posición luego de haber
simulado el movimiento y así el brazo virtual se encuentre en la posición
correcta cuando se vuelva a utilizar el comando “MOVER (DRIVE)”.
•
Para finalizar la rutina se debe introducir el comando “e” por medio de la
entrada de texto y el botón “EJECUTAR”, ubicados en el cuadro “Línea de
Comandos”, en la ventana de la figura 5.5.
Una vez finalizada la edición de la rutina, está se encuentra cargada en la memoria,
para ejecutarla se debe presionar el botón “Ejecutar Rutina” del marco “Rutinas”. Si se
desea cargar una rutina diferente, se debe presionar el botón “Cargar Rutina”, el cual
despliega una ventana de selección de archivos, mediante la cual se puede seleccionar el
archivo de rutina que se desea cargar. El simulador guarda las rutinas en la carpeta
“rutinas”, ubicada dentro de la carpeta que contiene los archivos fuente de la aplicación.
El botón de comando “MOVER (DRIVE)” también puede utilizarse para dar
ordenes de movimiento fuera de la edición de una rutina, al igual que el botón
“SIMULAR”.
El proceso de cambiar, borrar o agregar movimientos a un archivo de rutina de
simulación existente, debe realizarse de forma manual, directamente desde el archivo de
texto específico, respetando el formato de los archivos de rutina del simulador. Todos los
archivos de rutinas de movimiento del simulador deben comenzar con la palabra “inicio” y
72
terminar con la palabra “fin”. Los movimientos se especifican utilizando el siguiente
formato de frase: “M(número), J (número), A (número), V (número),” , en donde el
número después de la letra “M” señala el número de movimiento, el número posterior a “J”
señala la articulación que se desea realice el movimiento, siendo (1, 2, 3, 4, 5, 6) los únicos
números posibles, “A” indica que el numero que sigue se refiere al ángulo de giro (en
grados), mientras que el número siguiente a “V” es la velocidad porcentual de giro, la cual
debe ser mayor que cero y menor o igual que cien. Todos los números deben ser enteros
excepto el ángulo de giro, el cual puede tener un dígito decimal (utilizar un punto para
ingresar el decimal). Se deben respetar la comas, las mayúsculas y minúsculas. A
continuación se muestra un ejemplo de un archivo de rutina.
Ejemplo: Formato de un archivo de rutina
inicio
M1, J 1, A 10.0, V 100,
M2, J 3, A 90.0, V 60,
M3, J 2, A 50.0, V 73,
M4, J 6, A 60.0, V 96,
Fin
El ejemplo revela el formato de un archivo de rutina de cuatro movimientos. La
primer línea de texto del archivo señala el inicio de este y por lo tanto el inicio de la rutina.
La segunda línea indica un movimiento en la primer articulación del brazo virtual, es decir
la articulación que une la base y el hombro. El giro es de 10° y debe realizarse a una
velocidad del 100% de la velocidad máxima de giro de tal articulación. La siguiente línea
de texto se refiere a un giro de 90° en la tercer articulación (entre brazo1 y codo) a una
velocidad del 60 % de la velocidad máxima del actuador de la articulación. De igual
manera las siguientes dos líneas indican movimientos en la segunda y sexta articulación
respectivamente. Nótese que las palabras que indican el inicio y el fin del archivo están
escritas en minúscula.
5.3
Envío de ordenes al simulador por medio del teclado y el ratón
Es posible enviar al simulador algunas órdenes por medio del teclado y el ratón. Por
ejemplo, si se mueve el puntero del ratón dentro de la ventana de despliegue de la escena
virtual, manteniendo el botón izquierdo (botón 1) presionado, la cámara en la escena virtual
rota en dirección del movimiento, por otro lado, un desplazamiento del puntero dentro de la
misma ventana pero manteniendo el botón central (botón 2) presionado, traslada la cámara
en la dirección del movimiento, mientras que si el movimiento se hace manteniendo el
botón derecho (botón 3) presionado, varia el acercamiento (<<zoom>>) de la cámara.
73
A continuación se muestra una listado de diferentes teclas y las órdenes ligadas a
éstas. Para poder ejecutar órdenes por medio del teclado, la ventana de despliegue OpenGL,
la cual es la ventana en donde se despliega la escena virtual, debe encontrarse seleccionada.
Cuadro 5.1 Envío de ordenes por medio del teclado
Tecla(s):
r
c
z
x
Función
Selecciona el Robot (obj = robot)
Selecciona la Cámara (obj = cámara)
Modo = Rotar la cámara
Modo = Trasladar de la cámara
if(obj = camara)
keyLeft ←
keyRight→
keyUp↑
keyDown↓
if(modo=rotar)
if(modo=trasladar)
+ rotar(eje Y)
- rotar(eje Y)
+ rotar(eje X)
- rotar(eje X)
+ trasladar (eje Y)
- trasladar (eje Y)
if(obj = robot)
+ articulación 1
- articulación 1
+ articulación 2
- articulación 2
PageUp
PageDown
g
j
Zoom Out
Zoom In
-
+ articulación 3
- articulación 3
+ articulación 4
- articulación 4
y
n
o
p
-
+ articulación 5
- articulación 5
+ articulación 6
- articulación 6
a
d
key Home
key End
F1
F2
F3
F5
F6
F7
F8
F9
F10
F11
F12
+ velocidad cámara
+ velocidad robot
- velocidad cámara
- velocidad robot
reiniciar posición robot
reiniciar posición robot
Desplegar Piso como Rejilla (ON/OFF)
Limitar articulación a su amplitud de giro máxima (ON/OFF)
Detener simulación al darse la primera colisión (ON/OFF)
Abrir y Cargar Rutina (desde terminal)
Crear Rutina (desde terminal)
Salvar Rutina en memoria (desde terminal)
Ejecutar Rutina en memoria
Apilar posición actual del robot
Desapilar Posición
Limpiar Pila (borrar todas la posiciones apiladas)
Desplegar los valores de los ángulos de las articulaciones
CAPITULO 6: Pruebas finales y sus resultados
Una vez finalizada la etapa de desarrollo de la aplicación de simulación, se llevo a
cabo un proceso de pruebas, con el fin de determinar la estabilidad y desempeño de ésta.
Las pruebas fueron realizadas en un computador con procesador Pentium 4 de 1.99GHz
(<<giga hertz>>) de velocidad de procesamiento y 256 MB (<<mega bytes>>) de RAM
(<<random access memory>>).
6.1
Pruebas destructivas
Las primeras pruebas realizadas al programa fueron del tipo destructivas. En las
entradas de texto de la aplicación se pasaron cadenas de caracteres de varios formatos y
tamaños, se crearon rutinas que nunca fueron terminadas, se dieron ordenes de abrir
archivos que no existían o cuyos formatos no concordaban con el formato de los archivos
de rutina del simulador. Por medio de estás pruebas se detectó un fallo en la función
encargada de copiar el texto ingresado por el usuario en las entradas de texto Gtk. Algunas
veces después de copiar la cadena, no se añadía a ésta el caracter de finalización de cadena
‘\0’, lo que ocasionaba que se emitiera la señal de error de segmentación, al pasar la cadena
como argumento de algunas funciones de la biblioteca estándar. Además la función muchas
veces copiaba más caracteres de los que realmente había introducido el usuario, entonces al
tener la cadena de caracteres un tamaño mayor al espacio de memoria destinado para ella,
se daban también errores de segmentación. El problema fue solucionado limitando el
número de caracteres que copia la función desde la entrada de texto y concatenado al final
de todas la cadenas copiadas el carácter de terminación ‘\0’. Después de realizar estos
cambios, se procedió nuevamente con las pruebas. No se volvieron a presentar en ninguna
de ellas errores de segmentación.
6.2
Pruebas de velocidad
También fueron realizadas pruebas de velocidad en diferentes puntos del programa.
Algunas funciones poseen una velocidad que depende de la rapidez de reacción del usuario,
por ejemplo, la velocidad del proceso de creación y edición de una rutina va a estar más
ligada a la rapidez con la que el usuario introduzca los movimientos, que a la velocidad de
procesamiento misma del programa. Las funciones y fragmentos de código que poseen
velocidades de ejecución mayormente dependientes de la rapidez del usuario que del
programa en sí, no fueron incluidos en estas pruebas. Por otro lado, el programa posee
algunos procesos o fragmentos de éstos, sobre los cuales el usuario no tiene influencia. Se
realizaron pruebas de velocidad a éstas segmentos de la aplicación, midiendo el tiempo del
sistema a la entrada de cada segmento en prueba y a la salida de éste, luego se calculó la
diferencia entre ambos valores, obteniendo así el tiempo de ejecución del proceso. Los
tiempos fueron medidos en milisegundos.
74
75
Los procesos cronometrados fueron los siguientes:
•
Proceso que se lleva a cabo desde que se llama al programa, hasta que este entra al
lazo de control principal infinito, en el cual están implicados a su ves los procesos
de creación y despliegue de las ventanas.
•
El proceso de extraer los valores de las entradas de texto y de los botones de ajuste
de la interfaz gráfica Gtk.
•
El proceso de cargar de los movimientos de un archivo de rutina a los espacios de
memoria específicos.
•
El proceso realizado por la función dibujar(), la cuál se encargar de graficar la
escena virtual.
Cada proceso en prueba fue cronometrado treinta veces, luego se calculó el tiempo
promedio de ejecución de cada uno.
En promedio al programa le tomó 443 ms (milisegundos) realizar todas las tareas
que implican la creación y el despliegue de las tres ventanas de la aplicación gráfica. La
velocidad del proceso se considera buena, ya que es apenas perceptible por el usuario.
Las mediciones de velocidad de las funciones de extracción y copia de información
desde los objetos de la aplicación gráfica, mostraron resultados positivos. Todas las
funciones que realizan este tipo de proceso extrajeron los valores, tanto de las entradas de
texto como de los botones de ajuste Gtk, a velocidades realmente altas. De hecho no se
pudo captar ninguna diferencia entre el tiempo de entrada y el de salida para estas
funciones, en ninguna de las pruebas, es decir que el tiempo de ejecución siempre fue
menor a 1 milisegundo. Estos resultados muestran un gran desempeño por parte de las
funciones de extracción de información desde objetos Gtk. Se puede afirmar que la
velocidad de éstas es lo suficientemente alta como para que no afecten de manera negativa,
el desempeño de la aplicación.
Al igual que para el proceso de extracción de información desde los objetos Gtk, la
extracción de información desde archivos de texto, durante la función de carga de rutinas,
también reveló tiempos de ejecución muy bajos. Para este caso tampoco se detectaron
tiempos de ejecución mayores a un milisegundo, aún cuando se cargaron rutinas de hasta
sesenta movimientos.
La función dibujar(), es la encargada de graficar la escena virtual en la pantalla.
Realmente esta función está compuesta por varias etapas. Lo primero que ésta hace es
buscar el área de dibujo destinada al despliegue de objetos OpenGL, luego si el área existe
y es encontrada la función la selecciona como área actual de dibujo. Como paso siguiente
se deben limpiar los almacenadores intermedios de color y de profundidad (<<color
buffer>> y <<depth buffer>>), determinar las opciones de dibujo elegidas por el usuario,
llamar a la matriz de transformación actual, llevar a cabo las multiplicaciones de matrices
de traslación y rotación que sean necesarias, así como el apile y “desapile” de la matriz de
76
transformación actual en los puntos que lo requieran y llamar las listas de despliegue
específicas en los puntos necesarios. Luego se debe convocar a la función encargada de
comparar la amplitud de giro de cada una de las articulaciones del modelo con sus
amplitudes de giro máximas y esperar a que ésta termine de ser ejecutada. Posteriormente
se llama a la función de prueba de colisiones y se aguarda a que esta función emita su
resultado. Como último paso se exige la ejecución de todas las funciones OpenGL llamadas
hasta este punto y se intercambian los almacenadores intermedios gráficos. Se observa que
son varios pasos los que debe realizar la función dibujar(), de hecho esta es la función cuyo
tiempo de ejecución posee mayor influencia en el desempeño del programa, en cuanto a
velocidad de procesamiento se refiere, no solo por la cantidad de procesos que debe llevar a
cabo, sino por que es una de las más utilizadas durante la ejecución de la aplicación. A
pesar de todas las tareas que realiza, dibujar() expuso un tiempo promedio de ejecución de
apenas 29 ms, de hecho el mayor tiempo que se logró cronometrar para esta función fue de
32 ms, el cual se considera como aceptable, si se toma en cuenta todos los pasos que
conlleva el proceso. El tiempo es tan bajo que el usuario percibe la graficación como
inmediata.
Las pruebas de velocidad mostraron resultados muy positivos. En su mayoría los
tiempos de ejecución de los procesos son tan bajos, que no pueden ser percibidos por el
usuario.
A continuación se muestran algunos de los resultados obtenidos durante las pruebas
de velocidad.
/* Ejemplo: Algunos valores de tiempo obtenidos en la pruebas de velocidad */
Tiempo Main: 382 ms
Tiempo Main: 409 ms
Tiempo Main: 190 ms
Tiempo cargar movimiento a Simular: 0 ms
Tiempo cargar movimiento a Simular: 0 ms
Tiempo cargar movimiento a Simular: 0 ms
Tiempo Cargar Rutina: 0 ms. Num. movimientos: 60
Tiempo Cargar Rutina: 0 ms. Num. movimientos: 37
Tiempo extracción valor botones de ajuste Mover (Drive): 0 ms
Tiempo extracción valor botones de ajuste Mover (Drive): 0 ms
Tiempo extracción valor entrada de texto: 0 ms
Tiempo extracción valor entrada de texto: 0 ms
Tiempo de dibujar(): 27 ms
Tiempo de dibujar(): 32 ms
Tiempo de dibujar(): 28 ms
77
6.3
Pruebas de memoria
Las pruebas de memoria y búsqueda de fugas de memoria <<memory leaks>>
también mostraron resultados positivos. Para llevar a cabo las pruebas se llamó a la
aplicación y se examinó la cantidad de memoria utilizada en diferentes puntos de la
ejecución de ésta, por ejemplo se crearon, editaron y cargaron archivos de rutinas, se dieron
ordenes de movimientos al brazo, se cambio la configuración de éste etc, midiendo siempre
la memoria en uso antes y después de cada proceso. Mediante las mediciones no se captó
en ninguno momento un crecimiento elevado en el uso de memoria, lo que indica que no se
produjeron fugas de ésta, por el contrario la memoria de acceso aleatorio utilizada por el
programa se mantuvo prácticamente constante durante todo el tiempo que corrió el
programa, para todas las ejecuciones que se llevaron a cabo. En promedio la aplicación
presentó un consumo de 5 MB (mega bytes) de memoria RAM aproximadamente, el cual
es un valor aceptable considerando las tareas que lleva a cabo el programa.
A continuación se muestra una parte de los resultados de una de las pruebas de
memoria que se llevaron a cabo.
/* Ejemplo del proceso de las pruebas de memoria */
No se ha llamado el programa:
total
utilizada
MemRAM:
256288 247080
libre
9208
Se corre el programa:
total
utilizada
MemRAM:
256288 252864
libre
3424
Se ejecuta carga una rutina de treinta movimientos:
total
utilizada
libre
MemRAM:
256288 252292
3996
... /*Se realizan otros procesos y se mide la memoria antes y después de cada uno */
Se cierra el programa:
MemRAM:
total
256288
utilizada
247432
libre
8856
Se corre el programa nuevamente:
total
utilizada
libre
MemRAM:
256288 252384
3904
…
CAPITULO 7: Conclusiones y Recomendaciones
7.1
Conclusiones
•
El programa final es flexible en cuanto a las opciones de uso, se puede controlar ya
sea desde un emulador de terminal o a través de la interfaz gráfica de usuario.
•
La interfaz gráfica de usuario creada en el proyecto, facilita el uso de la aplicación y
la hace más amigable.
•
El API OpenGL demostró ser una biblioteca de gran robustez y flexibilidad, que
permite el desarrollo de aplicaciones gráficas tanto simples como complejas.
•
El estudio previo y entendimiento de la teoría de modelado tridimensional en
computadoras, facilita el uso de los API gráficos y permite al programador un mejor
desenvolvimiento durante el desarrollo de aplicaciones gráficas.
•
Además de ser robusto, flexible, gratuito y amigable al usuario, el API OpenGL
posee un amplio soporte por parte de los desarrolladores. Existe una gran cantidad
de material de ayuda e información referente a la librería, lo cual facilita e incentiva
el uso de ésta.
•
En general existe gran cantidad de documentación informativa y de ayuda tanto
para el sistema GNU/Linux como para las aplicaciones y librerías de esta
plataforma. El fácil acceso a esta documentación y la adquisición gratuita de
programas libres promueve el desarrollo de nuevas aplicaciones de software y el
mejoramiento de las ya existentes.
•
La aplicación desarrollada en el proyecto posee una flexibilidad que le permite
adaptarse a diferentes configuraciones, lo que incrementa su rango de
funcionalidad.
78
79
•
El lenguaje de programación C presenta las características necesarias para el
desarrollo de aplicaciones gráficas complejas.
•
La simulación virtual de un proceso permite al usuario obtener una idea previa del
resultado a esperar, de manera que se pueden realizar los cambios necesarios para
obtener una mayor eficiencia en el proceso, antes de que éste ocurra realmente.
•
La detección y prevención de colisiones es un tema de suma importancia, no solo en
el área de la robótica, sino también en el área de control de otros tipos de
dispositivos electromecánicos. Poder detectar una posible colisión antes de que ésta
ocurra realmente, ayuda a incrementar tanto la seguridad del dispositivo como la de
los usuarios.
•
Los métodos de detección de colisiones utilizados en el proyecto son los
suficientemente flexibles como para ser adaptados a diferentes tipos de dispositivos
electromecánicos.
•
En especial “la prueba de colisiones de orillas” (VORTEX) o “prueba de colisión de
pendientes” creada en el proyecto, demostró ser no solamente efectiva y flexible,
sino que presenta una estructura sumamente simple, en comparación con otros
métodos de detección de colisiones normalmente utilizados en la industria de
desarrollo de aplicaciones gráficas virtuales.
•
El lenguaje de programación utilizado, la interfaz gráfica de usuario y la estructura
del código de la aplicación desarrollada en el proyecto, permiten una fácil
implementación y adición del código creado en [4].
•
El programa creado en este proyecto, puede ser expandido y aplicado a diferentes
áreas, no solo referentes al desarrollo de escenas y mundos virtuales, sino que
también ligadas al control de dispositivos electromecánicos. El proyecto representa
una buena base, para futuras investigaciones ligadas al tema.
80
7.2
Recomendaciones
•
Es importante realizar una investigación previa a la creación de cualquier
aplicación gráfica, con el fin de establecer cuales librerías gráficas representan la
mejor opción tanto para el desarrollador como para el futuro usuario del programa.
•
Es posible añadir portabilidad a la aplicación desarrollada en el proyecto. El lograr
que la aplicación sea compatible con diferentes sistemas operativos, representaría
un gran beneficio para el usuario, ya que no se vería limitado al sistema GNU/Linux
para poder utilizar el programa.
•
La realización de pruebas, utilizando tanto el simulador como el brazo real,
permitiría, si fuera necesario, un mejor ajuste del margen de precaución del
simulador, de manera que se obtenga el mejor desempeño posible de la aplicación
de simulación.
•
Utilizando un programa de edición y diseño gráfico profesional sería posible crear
un modelo virtual del brazo Stäubli RX90L más exacto, lo cual mejoraría la
aplicación no solo desde el punto de vista estético, sino que podría permitir un
incremento en la precisión del proceso de detección previa de colisiones.
•
La implementación de texturas en la escena virtual, podría conllevar a un
mejoramiento en el aspecto y realismo de ésta. Esto haría la aplicación gráfica más
agradable y mejoría la experiencia del usuario.
•
Es posible incrementar la funcionalidad del programa desarrollado en el proyecto,
creando una función que permita al usuario cargar diferentes modelos virtuales de
brazos robot, de manera que se podrían simular los movimientos en diferentes tipos
de brazos y comparar los resultados.
•
Sería de gran utilidad para el usuario, que se desarrollara un algoritmo que calcule
la combinación de ángulos necesaria, para llevar el extremo del brazo robot a una
posición específica en el espacio. Tal algoritmo podría ser creado utilizando el
método de cinemática inversa.
81
BIBLIOGRAFÍA
[1]
Giambruno, M. “3D Graphics & Animation: From Starting Up to Standing
Out”, primera edición, New Riders Publishing, E.E.U.U, 1999.
[2]
Barrientos, A. “Fundamentos De La Robótica”, primera edición, McGrawHill, España, 1997.
[3]
Neider, J., Davis, T., Woo, M., “The OpenGL Programming Guide – The
Red Book”, segunda edición, Addison-Wesley Publishing, E.E.U.U, 1999.
[4]
Montes Solano, C. “Envío de video hacia un cliente remoto utilizando el
sistema video4linux y desarrollo de una aplicación gráfica GTK sobre una
plataforma GNU/Linux, para el control de brazo robot Stäubli RX90”,
proyecto para el grado de bachillerato en Ingeniería eléctrica, Universidad de
Costa Rica, 2005.
[5]
Zeledón Chaves, E. Y Zeledón Méndez, P. “Creación de una interfase
humano-maquina para el control, monitoreo y supervisión del brazo robot
Stäubli RX90 vía internet”, proyecto para el grado de bachillerato en
Ingeniería eléctrica, Universidad de Costa Rica, 2004.
[6]
OpenGL, “OpenGL Homepage”, http://www.opengl.org/, junio, 2006.
[7]
Wikipedia.<<Linux>>, Wikipedia, http://en.wikipedia.org/wiki/Linux, junio,
2006.
[8]
GTK, “GTK Homepage” , http://www.gtk.org/, junio, 2006.
[9]
Lenguaje C, “C programming”, http://www.cprogramming.com/, junio, 2006.
[10]
Wikipedia.<<DirectX>>, Wikipedia, http://en.wikipedia.org/wiki/Directx,
junio, 2006.
[11]
“GL Programming”, http://www.glprogramming.com/, junio, 2006.
[12]
Stäubli, “Stäubli Robots”, http://www.staubli.com/web/robot/division.
[13]
Video juegos, “Game Development”, http://www.gamedev.net/ , junio 2006
APÉNDICES
Archivos de código fuente de la aplicación desarrollada en el proyecto.
callbacks.c: Funciones de respuesta de la interfaz gráfica de usuario
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* callbacks.c: funciones de respuesta de la interfaz gráfica de usuario
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <glib.h>
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include <gdk/gdk.h>
#include <gdk/gdkgl.h>
#include <gdk/gdkkeysyms.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include "callbacks.h"
#include "interface.h"
#include "dibujo.h"
#include "ventanagl.h"
#include "rutina.h"
int simon=1; //variable de habilitación el simulador
int edicion=0; //variable de habilitación de la edición de rutina
int puntrutina;
char nombrerutina[47];
const gchar *archivo_sel;
FILE *archivo1;
82
83
//Posicion del puntero del mouse
int lastx;
int lasty;
/*Función se encarga de Imprimir en TextView*/
void printtv(const char *texto){
int size_t;
size_t=printf(texto);
textbuffer1 = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview1));
gtk_text_buffer_get_end_iter(textbuffer1, &iter1);
gtk_text_buffer_insert(textbuffer1, &iter1, texto, size_t);
}
/*Funcion del boton: Grid*/
void on_bgrid_toggled(GtkToggleButton *togglebutton, gpointer user_data){
if (GTK_TOGGLE_BUTTON (togglebutton)->active) {
grid=1; //encender la rejilla
}else {
grid=0; //apagar la rejilla
}
dibujar();
}
/*Funcion del boton: Fondo*/
/*Cambia el color de fondo de la ventana del mundo virtual*/
void on_btexturas_toggled(GtkToggleButton *togglebutton, gpointer user_data){
if (GTK_TOGGLE_BUTTON (togglebutton)->active) {
/*Fondo de color Blanco*/
glClearColor(1.0, 1.0, 1.0, 0.0);
}else {
/*Fondo de color Negro*/
glClearColor(0.0, 0.0, 0.0, 0.0);
}
dibujar();
}
/*Funcion del boton: Apilar*/
void on_bposicion1_clicked(GtkButton *button, gpointer user_data){
//Apilar posición actual
push_posicion();
}
/*Funcion del boton: Desapilar*/
void on_bposicion2_clicked(GtkButton *button, gpointer user_data){
//Desapilar posición anterior
pull_posicion();
dibujar();
}
/*Funcion del boton: Reiniciar Pila*/
void on_bposicion3_clicked(GtkButton *button, gpointer user_data){
//Reiniciar Pila de posiciones
reiniciar_pila();
}
/*Funcion del boton: Reiniciar Robot*/
void on_bposicion4_clicked(GtkButton *button, gpointer user_data){
//Reiniciar los valores de posición del Robot
valores_iniciales_robot();
dibujar();
}
/*Funcion del boton: Reiniciar Camara*/
84
void on_bposicion5_clicked (GtkButton *button, gpointer user_data){
//Reiniciar los valores de posición de la Cámara
valores_iniciales_camara();
reshape(ventanaw, ventanah);
dibujar();
}
/*Funcion del boton: Lim. Angulos*/
void on_bcheckmax_toggled(GtkToggleButton *togglebutton, gpointer user_data){
if (GTK_TOGGLE_BUTTON (togglebutton)->active) {
checkmax=1; //habilitar prueba de angulos máximos
}else {
checkmax=0; //deshabilitar prueba de angulos máximos
}
}
/*Funcion del boton: 1° Colision*/
void on_bcolision1_toggled(GtkToggleButton *togglebutton, gpointer user_data){
if (GTK_TOGGLE_BUTTON (togglebutton)->active) {
checkcolision=1; //detener rutina al darse la primera colisión
}else {
checkcolision=0; //continuar rutina aun despues de la primera colision
}
}
/*Funcion del boton: Simulador*/
void on_bsimulador_toggled (GtkToggleButton *togglebutton, gpointer user_data){
//ENCENDER SIMULADOR
if (GTK_TOGGLE_BUTTON (togglebutton)->active) {
simon=1; //simular las ordenes
}else {
simon=0; //no simular las ordenes
}
}
/* Dialogo cuando se da una colision en la edición de la simulación */
gint dialogo_mensaje(void){
GtkWidget *dialog, *label;
gint result;
dialog = gtk_dialog_new_with_buttons ("Advertencia!", GTK_WINDOW(ventana1), GTK_DIALOG_MODAL,
GTK_STOCK_YES, 1, GTK_STOCK_NO, 0, NULL);
label = gtk_label_new ("CUIDADO->Se dio una colisión. \n¿Desea aun asi agregar el movimiento a la rutina?");
gtk_container_add (GTK_CONTAINER (GTK_DIALOG(dialog)->vbox), label);
gtk_widget_show_all (dialog);
/* Detiene la aplicación hasta que se de una respuesta al dialogo*/
result = gtk_dialog_run (GTK_DIALOG (dialog));
gtk_widget_destroy(dialog);
return result;
}
/*Funcion del boton: MOVER (Drive)*/
void on_bdrive_clicked(GtkButton *button, gpointer user_data){
GtkSpinButton *spin1, *spin2, *spin3;
int velocporciento;
float *temp_angulo = malloc(sizeof (float) * 6);
gint guardar;
spin1=GTK_SPIN_BUTTON(spinbutton1);
spin2=GTK_SPIN_BUTTON(spinbutton2);
spin3=GTK_SPIN_BUTTON(spinbutton3);
85
hubochoque=0;
/*Toma los valores de los botones 'spinners'*/
joint=gtk_spin_button_get_value_as_int(spin1);
angulodelta=gtk_spin_button_get_value_as_int(spin2);
velocporciento=gtk_spin_button_get_value_as_int(spin3);
set_velocidad(velocporciento, joint);
/*Simula el movimiento especificado por los valores de los spinners*/
if(simon==1 && edicion==0){
selec_mover_joint();
if(hubochoque==1){
printtv("\nSe produjo una colision-->La señal NO será enviada al robot\n");
}else if(hubochoque==0){
printtv("\nEnviando señal al robot\n");
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
}else if(simon==1 && edicion==1){
/*edición de una rutina*/
/*guardar el movimiento en la matriz de rutinas*/
puntrutina+=1;
rutina[puntrutina][0]=joint;
rutina[puntrutina][1]=angulodelta;
rutina[puntrutina][2]=velocporciento;
rutina[0][0]+=1;
/*Guarda los angulos actuales del robot en una memoria temporal, antes de moverlo*/
temp_angulo[0]=angulo1; temp_angulo[1]=angulo2; temp_angulo[2]=angulo3;
temp_angulo[3]=angulo4; temp_angulo[4]=angulo5; temp_angulo[5]=angulo6;
/*simular el movimiento del robot*/
selec_mover_joint();
/*Si no hubo choque guarda el movimient. en la rutina y envia la señal al robot*/
if(hubochoque!=1){
//guardar el movimiento en la matriz de rutinas
fprintf(archivo1,"M%i, J %d, A %3.1f, V
%d,\n",puntrutina,(int)rutina[puntrutina][0],rutina[puntrutina][1],(int)rutina[puntrutina][2]);
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
/*Si hubo choque pregunta si se debe guardar el movimiento.*/
if(hubochoque==1){
printtv("\n\n!!CUIDADO!!-->se produjo una colision durante la simulación del movimiento");
printtv("\nSe recomienda eliminar el ultimo movimiento de la rutina");
guardar=dialogo_mensaje();
if(guardar==1){
/*guardar el movimiento en la matriz de rutinas y enviar señal*/
printtv("\nSe guardo el movimiento en la rutina");
fprintf(archivo1,"M%i, J %d, A %3.1f, V
%d,\n",puntrutina,(int)rutina[puntrutina][0],rutina[puntrutina][1],(int)rutina[puntrutina][2]);
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}else {
/*Se devuelve el brazo y el puntero de rutina a la posición anterior*/
printtv("\nNo se guardo el movimiento en la rutina");
puntrutina-=1;
angulo1=temp_angulo[0]; angulo2=temp_angulo[1]; angulo3=temp_angulo[2];
angulo4=temp_angulo[3]; angulo5=temp_angulo[4]; angulo6=temp_angulo[5];
dibujar();
/*No se le envÃa ninguna señal al robot*/
86
}
}
/*Si se alcanzan los 60 movimientos-> Se termina la edición de rutina del simulador*/
if(puntrutina==60){
printtv("Movimiento #60 --> Máximo que puede guardar la rutina del simulador");
fprintf(archivo1,"fin");
fclose(archivo1);
edicion=0;
printtv("\n\nFin de la edición de la rutina");
printf("\nFin de la edición de la rutina: %s", nombrerutina);
}
}else if(simon==0){
printtv("\nSimulador Apagado. Enviando la señal directamente al robot");
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
free(temp_angulo);
fflush(stdout);
}
/*Funcion del boton: Simular*/
void on_bsimular_clicked (GtkButton *button, gpointer user_data){
/*Simula el movimiento del robot utilizando los valores de los botones 'spinner'*/
GtkSpinButton *spin1, *spin2, *spin3;
int velocporciento;
spin1=GTK_SPIN_BUTTON(spinbutton1);
spin2=GTK_SPIN_BUTTON(spinbutton2);
spin3=GTK_SPIN_BUTTON(spinbutton3);
hubochoque=0;
//Toma los valores de los botones 'spinners'
joint = gtk_spin_button_get_value_as_int(spin1);
angulodelta = gtk_spin_button_get_value_as_int(spin2);
velocporciento = gtk_spin_button_get_value_as_int(spin3);
set_velocidad(velocporciento, joint);
selec_mover_joint();
if(hubochoque==1){
printtv("\n\n¡¡¡CUIDADO!!!-->Se produjo un choque durante la simulación del movimiento");
}
}
/*Funcion del boton: Ejecutar*/
void on_bejecutar_clicked (GtkButton *button, gpointer user_data){
/*Ejecutar comando introducido el linea de comandos*/
char entrada[30];
char *comandos[7];
char base_fija[20];
char margen_p[20];
int comp[7];
int pc;
char letra_c;
/*lista de comandos */
comandos[0]="e\0"; comandos[1]="edit "; comandos[2]="ex ";
comandos[3]="drive "; comandos[4]="base"; comandos[5]="margen";
/*extrae la cadena de caracteres de la entrade de texto entrycomando*/
strncpy(entrada, gtk_entry_get_text(GTK_ENTRY(entrycomando)), sizeof(entrada));
entrada[sizeof(entrada)-1]='\0';
/*compara el comando recibido con la lista de comandos de arriba*/
comp[0] = strncmp(entrada, comandos[0], 2);
87
comp[1] = strncmp(entrada, comandos[1], 5);
comp[2] = strncmp(entrada, comandos[2], 3);
comp[3] = strncmp(entrada, comandos[3], 6);
comp[4] = strncmp(entrada, comandos[4], 4);
comp[5] = strncmp(entrada, comandos[5], 5);
/*dependiendo del resultado de la comparación realiza alguna de las sigueintes acciones*/
if(comp[0] == 0 && edicion==1){
fprintf(archivo1,"fin");
fclose(archivo1);
/*fin de la edición de la rutina*/
edicion=0;
printtv("\n\nFin de la edición de la rutina");
printf("\nFin de la edición de la rutina: %s", nombrerutina);
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}else if((comp[1] ==0 || comp[2] ==0) && (simon==1)){
printtv("\n\nSi desea utilizar el simulador, los comandos de las Rutinas se deben dar utilizando los botones de la
rutina");
printtv("\nNo se ejecutó la orden. Solucion: Utilizar botónes de Rutina o deshabilitar el simulador");
}else if(comp[3] == 0 && simon==1){
printtv("\n\nSi desea utilizar el simulador, los comandos DRIVE se deben dar utilizando el botón DRIVE");
printtv("\nNo se ejecutó la orden. Solucion: Utilizar botón DRIVE o deshabilitar el simulador");
}else if(comp[4] == 0){
/*Se recibio el comando de adaptar una base externa*/
printtv("\n\nAdaptar base\0");
pc=4;
letra_c='e';
while(letra_c != '\0'){
letra_c=entrada[pc];
base_fija[pc-4]=entrada[pc];
pc++;
}
altpiso=-1*(atof(base_fija));
printf(" de: %.1f centÃmetros de alto", -1*altpiso);
dibujar();
}else if(comp[5] == 0){
/*se recibio el comando de estblecer un margen de precaución*/
printtv("\n\nEstablecer margen de precaucion");
pc=6;
letra_c='n';
while(letra_c != '\0'){
letra_c=entrada[pc];
margen_p[pc-6]=entrada[pc];
pc++;
}
marg=atof(margen_p);
init_vortexfijos(marg);
printf(" de: %.1f centÃmetros", marg);
}else{
printtv("\nEnviando señal al robot");
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
gtk_entry_set_text(GTK_ENTRY(entrycomando),"");
fflush(stdout);
/* previene que el handler predeterminado corra */
gtk_signal_emit_stop_by_name(GTK_OBJECT(button),"clicked");
}
/*Funcion del boton: Crear Rutina*/
88
void on_brutina1_clicked (GtkButton *button, gpointer user_data){
/*Establecer el nombre de edición de una nueva rutina y el inicio de la edición de esta misma*/
edicion=1;
//se habilita la edición de la rutina
puntrutina=0;
//se inicializa el puntero de la matriz de rutinas
rutina[0][0]=0;
//se inicializa el numero de movimientos en la matriz de rutinas
strcpy(nombrerutina, "./rutinas/\0");
strncat(nombrerutina, gtk_entry_get_text(GTK_ENTRY(entryrutina)),(sizeof(nombrerutina)-sizeof("./rutinas/\0")-3));
nombrerutina[sizeof(nombrerutina)-1]='\0';
printtv("\n\nInicio de la edición de la rutina");
printf("\nInicio de la edición de la rutina: %s", nombrerutina);
fflush(stdout);
archivo1=fopen(nombrerutina,"w");
fprintf(archivo1,"inicio\n");
}
/* Guarda el nombre del archivo seleccionado */
void guardar_nombre (GtkWidget *widget, gpointer user_data) {
GtkWidget *file_selector = GTK_WIDGET (user_data);
archivo_sel = gtk_file_selection_get_filename (GTK_FILE_SELECTION (file_selector));
printtv("\n\nCargar Archivo seleccionado");
printf("\nCargar Archivo: %s\n", archivo_sel);
}
/* Crea la ventana de seleccion de archivos */
void crear_selector (void) {
GtkWidget *file_selector;
file_selector = gtk_file_selection_new ("Selector de Archivos");
g_signal_connect (GTK_FILE_SELECTION (file_selector)->ok_button,
"clicked", G_CALLBACK (guardar_nombre), file_selector);
g_signal_connect_swapped (GTK_FILE_SELECTION (file_selector)->ok_button,
"clicked", G_CALLBACK (gtk_widget_destroy), file_selector);
g_signal_connect_swapped (GTK_FILE_SELECTION (file_selector)->cancel_button,
"clicked", G_CALLBACK (gtk_widget_destroy), file_selector);
gtk_widget_show (file_selector);
gtk_dialog_run(GTK_DIALOG(file_selector));
}
/*Funcion del boton: Cargar Rutina*/
void on_brutina2clicked (GtkButton *button, gpointer user_data){
FILE *archivo2;
char letra_ui;
char articult_ui[3];
char angt_ui[7];
char veloct_ui[7];
char linea_ui[15];
int p1, p2, p3;
//punteros
/*llama a la ventana de seleccion de archivos*/
crear_selector();
/*abre el archivo seleccionado*/
archivo2=fopen(archivo_sel,"r");
if (!archivo2){
printtv("\n\n-ERROR-->No se pudo abrir el archivo");
printf("\n-ERROR-->No se pudo abrir el archivo %s", archivo_sel);
} else {
89
/*comienza a extraer la información del archivo*/
p1=0;
rutina[0][0]=0;
do{
/*se trata de un nuevo movimiento*/
letra_ui=fgetc(archivo2);
if(letra_ui=='M'){
if(p1<60){
p1++;
rutina[0][0]++;
}
}else if(letra_ui=='A'){
/*se trata de un angulo*/
p2=0;
while(letra_ui!=','){
letra_ui=fgetc(archivo2);
angt_ui[p2]=letra_ui;
p2++;
}
rutina[p1][1]=atof(angt_ui);
}else if(letra_ui=='J'){
/*se trata de una articulación (joint)*/
p2=0;
while(letra_ui!=','){
letra_ui=fgetc(archivo2);
articult_ui[p2]=letra_ui;
p2++;
}
rutina[p1][0]=atof(articult_ui);
}else if(letra_ui=='V'){
/*se trata de la velocidad del movimiento*/
p2=0;
while(letra_ui!=','){
letra_ui=fgetc(archivo2);
veloct_ui[p2]=letra_ui;
p2++;
}
rutina[p1][2]=atof(veloct_ui);
}
}while (letra_ui!='f');
/*se cierra el archivo*/
fclose(archivo2);
}
}
/*Funcion del boton: Ejecutar Rutina*/
void on_brutina3_clicked(GtkButton *button, gpointer user_data){
//Ejecutar Rutina
hubochoque=0;
if(simon==1){
/*Se simula la rutina*/
ejecutar_rutina();
if(hubochoque==1){
/*se dio una colision*/
printtv("\nSe produjo una colision-->La señal de ejecutar rutina NO será enviada al robot\n");
}else if(hubochoque==0){
/*no se dio ninguna colision*/
printtv("\nRutina Segura. Enviando señal de ejecutar rutina al robot\n");
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
}else{
/*Simulador apagado->No se simula la rutina*/
/*La señal se envÃa de una vez al robot. Sin simular*/
90
printtv("\nSimulador Apagado. Enviando señal de ejecutar rutina al robot\n");
/*Aqui se debe introducir el codigo para enviar la señal respectiva al brazo robot*/
}
}
/***********VENTANA OPENGL************************/
/*Movimiento del Mouse genera movimiento de la camara,
*si se mantiene alguno de los botones presionados*/
gboolean on_glarea_motion_notify_event(GtkWidget *widget, GdkEventMotion *event, gpointer user_data){
int x;
int y;
int difx;
int dify;
GdkModifierType state;
if (event->is_hint) {
gdk_window_get_pointer(event->window, &x, &y, &state);
}
/*calcula la diferencia entre las coordenadas actuales del ratón y las pasadas*/
difx=event->x - lastx;
dify=event->y - lasty;
lastx=event->x;
lasty=event->y;
/*hace un acercamiento de la camara*/
if (event->state & GDK_BUTTON3_MASK) {
zoom_z -= (float) 0.1f * difx;
}else if (event->state & GDK_BUTTON1_MASK) {
/*rota la camara*/
angulo_xr += (float) 0.5f * dify;
angulo_yr += (float) 0.5f * difx;
}else if (event->state & GDK_BUTTON2_MASK) {
/*traslada la camara*/
ancho_x += (float) 0.1f * difx;
altura_y -= (float) 0.1f * dify;
}
reshape(ventanaw, ventanah);
dibujar();
return TRUE;
}
//Boton del Mouse presionado
gboolean on_glarea_button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer user_data){
/*se guarda la posicion del mouse*/
lastx= event->x;
lasty= event->y;
return TRUE;
}
//Tecla presionada
/*Se llama una función por cada tecla presionada dentro de la ventana OpenGL*/
gboolean on_glarea_key_press_event(GtkWidget *widget, GdkEventKey *event, gpointer user_data){
event->keyval;
if(event->keyval==GDK_x){
printtv("\nDesplazar-->CAMARA");
mov_cam=1;
}else if(event->keyval==GDK_z){
printtv("\nRotar-->CAMARA");
mov_cam=2;
}else if(event->keyval==GDK_c){
91
printtv("\nObjeto Seleccionado-->CAMARA");
mover_obj=2;
}else if(event->keyval==GDK_r){
printtv("\nObjeto Seleccionado-->ROBOT");
mover_obj=1;
}else if(event->keyval==GDK_l){
if(lim==0){
lim=1;
}else {
lim=0;
}
}else if(mover_obj == 1){ /*se mueve el modelo del robot*/
if(event->keyval==GDK_y){
angulo5-=aument;
}else if(event->keyval==GDK_n){
angulo5+=aument;
}else if(event->keyval==GDK_j){
angulo4-=aument;
}else if(event->keyval==GDK_g){
angulo4+=aument;
}else if(event->keyval==GDK_o){
angulo6+=aument;
}else if(event->keyval==GDK_p){
angulo6-=aument;
}else if(event->keyval==GDK_a){
printtv("\nAUMENTAR VELOCIDAD ROBOT");
aument+=1;
if (aument > 45){
aument = 45;
}
}else if(event->keyval==GDK_d){
printtv("\nDISMINUIR VELOCIDAD ROBOT");
aument-=1;
if (aument < 1){
aument = 1;
}
}
}else if(mover_obj==2){ //*se mueve la camara en la escena*/
if(event->keyval==GDK_a){
printtv("\nAUMENTAR VELOCIDAD CAMARA");
aument_cam+=1;
if (aument_cam > 45){
aument_cam = 45;
}
}else if(event->keyval==GDK_d){
printtv("\nDISMINUIR VELOCIDAD CAMARA");
aument_cam-=1;
if (aument_cam < 1){
aument_cam = 1;
}
}
}
if(event->keyval==GDK_F1){
if(grid==0){
/*se activa la rejilla del piso*/
grid=1;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bgrid), TRUE);
}else {
/*se desactiva la rejilla*/
grid=0;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bgrid), FALSE);
}
}else if(event->keyval==GDK_F2){
if(checkmax==0){
92
/*se activa la opción de limite de angulos máximos*/
checkmax=1;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcheckmax), TRUE);
}else {
checkmax=0;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcheckmax), FALSE);
}
}else if(event->keyval==GDK_F3){
if(checkcolision==0){
/* se activa la opción de detención de la sim. en el primer choque*/
checkcolision=1;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcolision1), TRUE);
}else {
checkcolision=0;
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcolision1), FALSE);
}
}else if(event->keyval==GDK_F5){
printf("\nCREAR RUTINA");
crear_rutina();
}else if(event->keyval==GDK_F6){
printf("\nABRIR Y CARGAR RUTINA");
abrir_rutina();
}else if(event->keyval==GDK_F7){
printf("\nSALVAR RUTINA");
salvar_rutina();
}else if(event->keyval==GDK_F8){
printtv("\nEJECUTAR RUTINA");
ejecutar_rutina();
}else if(event->keyval==GDK_F9){
printtv("\nAPILAR POSICION");
push_posicion();
dibujar();
}else if(event->keyval==GDK_F10){
printtv("\nDESAPILAR POSICION");
pull_posicion();
dibujar();
}else if(event->keyval==GDK_F11){
printtv("\nREINICIAR PILA");
reiniciar_pila();
}else if(event->keyval==GDK_F12){
printf("\nInformación:");
printf("\nAngulos: A1 %.0f, A2 %.0f, A3 %.0f, A4 %.0f, A5 %.0f, A6 %.0f\n", angulo1,-1*angulo2,1*angulo3,angulo4,-1*angulo5,angulo6);
}
if(mover_obj == 1){/*se mueve el robot*/
if(event->keyval==GDK_Up){
angulo2-=aument;
}else if(event->keyval==GDK_Down){
angulo2+=aument;
}else if(event->keyval==GDK_Right){
angulo1-=aument;
}else if(event->keyval==GDK_Left){
angulo1+=aument;
}else if(event->keyval==GDK_Page_Up){
angulo3-=aument;
}else if(event->keyval==GDK_Page_Down){
angulo3+=aument;
}
}else if(mover_obj== 2){/*se mueve la camara*/
if(event->keyval==GDK_Page_Up){
zoom_z-=1;
}else if(event->keyval==GDK_Page_Down){
zoom_z+=1;
}else if(mov_cam==1){
93
if(event->keyval==GDK_Up){
altura_y+=aument_cam;
}else if(event->keyval==GDK_Down){
altura_y-=aument_cam;
}
}else if(mov_cam==2){
if(event->keyval==GDK_Up){
angulo_xr+=aument_cam;
}else if(event->keyval==GDK_Down){
angulo_xr-=aument_cam;
}else if(event->keyval==GDK_Right){
angulo_yr+=aument_cam;
}else if(event->keyval==GDK_Left){
angulo_yr-=aument_cam;
}
}
reshape(ventanaw, ventanah);
}
if(event->keyval==GDK_Home){
printtv("\nREINICIANDO ROBOT");
mover_obj = 1;
valores_iniciales_robot();//REINICIAR POSICION ROBOT
}else if(event->keyval==GDK_End){
printtv("\nREINICIANDO CAMARA");
valores_iniciales_camara();//REINICIAR POSICION CAMARA
reshape(ventanaw, ventanah);
}
dibujar();
return TRUE;
}
gboolean on_glarea_expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data){
/*se da un evento de exposición , la escena se dibuja de nuevo*/
dibujar();
return TRUE;
}
gboolean on_glarea_configure_event (GtkWidget* widget, GdkEventConfigure* event) {
/*se cambio el tamaño de la ventana, se llama a la función de ajuste de ventana: reshape*/
ventanaw = widget->allocation.width;
ventanah = widget->allocation.height;
reshape(ventanaw, ventanah);
return TRUE;
}
gboolean glarea_init (GtkWidget* widget) {
/*la ventana esta lista para ser iniciada: se llama a la función inicio()*/
inicio();
return TRUE;
}
void on_bsim_clicked(GtkButton *button, gpointer user_data){
/*Simula el movimiento del robot utilizando los valores de los botones 'spinner'*/
GtkSpinButton *spin1, *spin2, *spin3;
int velocporciento;
spin1=GTK_SPIN_BUTTON(spinbutton1);
spin2=GTK_SPIN_BUTTON(spinbutton2);
spin3=GTK_SPIN_BUTTON(spinbutton3);
hubochoque=0;
/*Toma los valores de los botones 'spinners'*/
94
joint = gtk_spin_button_get_value_as_int(spin1);
angulodelta = gtk_spin_button_get_value_as_int(spin2);
velocporciento=gtk_spin_button_get_value_as_int(spin3);
set_velocidad(velocporciento, joint);
/*simula el movimiento*/
selec_mover_joint();
if(hubochoque==1){
printtv("\n\n¡¡¡CUIDADO!!!-->Se produjo un choque durante la simulación del movimiento");
}
}
/* Información del Programa*/
void on_bacerca_clicked(GtkButton *button, gpointer user_data){
GtkWidget *mensaje;
mensaje = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE,
"Programa (Program): SimQNK \nSimulador del brazo Robot Stäubli RX90L (Robotic Arm Simulator)\nAutor
(developer): David Cuenca Alpízar\nJunio 2006 (June 2006)\n(C) Copyright 2006\nE-mail: [email protected]",
NULL);
gtk_widget_show(mensaje);
/* Destruir Ventana de mensaje al presionar el botón */
g_signal_connect_swapped (mensaje, "response",
G_CALLBACK (gtk_widget_destroy),
mensaje);
}
/*************************************************************************************************************/
dibujo.c: Funciones de despliegue de la escena virtual y de pruebas de colisión
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* dibujo.c: funciones de dibujo y pruebas de colision del modelo 3D
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "dibujo.h"
#define BASE 1
#define BRAZO1 2
#define BRAZO2 4
#define CODO 3
#define MUNIECA 5
95
#define MANO 6
#define ESFERA 7
#define CUBO 8
#define HOMBRO 9
#define EMPO 10
//Vectores para prueba de colision
float menorz [4];
float menory [4];
float menorz2 [5];
float menory2 [5];
float vortexm [4];
/*Vectores de las orillas de choque del hombro*/
float vortexfijo1 [3] = {0, 28.5, 13.1};
float vortexfijo2 [3] = {0, 54.5, 13.1};
//Constantes para prueba de colision
float nively1=28.5;
float nively2=54.5;
float zhombro=13.1;
float numpuntos=30;
int choquep;
//control de choque contra el piso
int choqueb;
//control de choque contra la base
int choqueh;
//control de choque contra el hombro
int choquev;
//control de choque contra los vortices del hombro
float radbase=12;
int npuntos=30;
//Punteros de control de colision
int i;
int j;
int n;
int cuidado;
//Vectores para multiplicar matrices
float vtemp [4];
float vectr[4];
//Figuras de control
float circHB [31][4];
float circHA [31][4];
float circWA [31][4];
float circWB [31][4];
float circWC [31][4];
float circBD [31][4];
float circBC [31][4];
float cuadBA [5][4]={0,0,0,0, 6.6,-15,-5.7,1, -5.7,-15,-5.7,1, -5.7,-15,5.7,1, 6.6,-15,5.7,1};
float cuadBB [5][4]={0,0,0,0, 12,-10,-11.1,1, -10.2,-10,-11.1,1, -10.2,-10,11.1,1, 12,-10,11.1,1};
float cuadBC [5][4]={0,0,0,0, 12,10,-11.1,1, -10.2,10,-11.1,1, -10.2,10,11.1,1, 12,10,11.1,1};
float circHBreal [31][4];
float circHAreal [31][4];
float circWAreal [31][4];
float circWBreal [31][4];
float circWCreal [31][4];
float circBDreal [31][4];
float circBCreal [31][4];
float cuadBAreal [5][4];
float cuadBBreal [5][4];
float cuadBCreal [5][4];
//Puntos de control
float CHB [5]={0.0, 8.5, 0.0, 1, 5};
float CHA [5]={0.0, 5.6, 0.0, 1, 5};
float CWA [5]={-5.5, 55.0, 0.0, 1, 4};
float CWB [5]={0.0, 55.0, 0.0, 1, 4};
96
float CWC [5]={5.5, 55.0, 0.0, 1, 4};
float CBA [5]={0.0, -15, 0.0, 1, 3};
float CBB [5]={0.0, -10, 0.0, 1, 3};
float CBC [5]={0.0, 10, 0.0, 1, 3};
float CBD [5]={0.0, 58, 0.0, 1, 3};
float CHBreal [3];
float CHAreal [3];
float CWAreal [3];
float CWBreal [3];
float CWCreal [3];
float CBAreal [3];
float CBBreal [3];
float CBCreal [3];
float CBDreal [3];
//Matrices de rotacion y traslacion
float rotx2 [4][4] = {1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1};//rotacion en x angulo2
float rotx3 [4][4] = {1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1};//rotacion en x angulo3
float rotx5 [4][4] = {1,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1};//rotacion en x angulo5
float roty4 [4][4] = {0,0,0,0, 0,1,0,0, 0,0,0,0, 0,0,0,1};//rotacion en y angulo4
float tras1 [4][4] = {1,0,0,0, 0,1,0,42, 0,0,1,0, 0,0,0,1};//traslacion
float tras2 [4][4] = {1,0,0,0, 0,1,0,45, 0,0,1,0, 0,0,0,1};
float tras3 [4][4] = {1,0,0,0, 0,1,0,10, 0,0,1,0, 0,0,0,1};//traslacion
float tras4 [4][4] = {1,0,0,0, 0,1,0,55, 0,0,1,0, 0,0,0,1};
/*Valores de posicion inicial del robot*/
void valores_iniciales_robot(void){
printf("\nObjeto Seleccionado--> ROBOT");
angulo1 = 0;
angulo2 = 0;
angulo3 = 0;
angulo4 = 0;
angulo5 = 0;
angulo6 = 0;
aument = 1;
}
/*Llenar Matrices de rotacion*/
void llenar_matrices_rot(void){
rotx2[1][1]=cos(angulo2*M_PI/180);
rotx2[1][2]=(-1)*sin(angulo2*M_PI/180);
rotx2[2][1]=sin(angulo2*M_PI/180);
rotx2[2][2]=cos(angulo2*M_PI/180);
rotx3[1][1]=cos(angulo3*M_PI/180);
rotx3[1][2]=(-1)*sin(angulo3*M_PI/180);
rotx3[2][1]=sin(angulo3*M_PI/180);
rotx3[2][2]=cos(angulo3*M_PI/180);
rotx5[1][1]=cos(angulo5*M_PI/180);
rotx5[1][2]=(-1)*sin(angulo5*M_PI/180);
rotx5[2][1]=sin(angulo5*M_PI/180);
rotx5[2][2]=cos(angulo5*M_PI/180);
roty4[0][0]=cos(angulo4*M_PI/180);
roty4[0][2]=sin(angulo4*M_PI/180);
roty4[2][0]=(-1)*sin(angulo4*M_PI/180);
roty4[2][2]=cos(angulo4*M_PI/180);
}
/*Llenar los vectores de posición de la orillas del hombro fijos*/
void init_vortexfijos(float margen){
97
vortexfijo1[1]=28.5-(margen/2);
vortexfijo1[2]=13.1+(margen/2);
vortexfijo2[1]=54.5+(margen/2);
vortexfijo2[2]=13.1+(margen/2);
}
/*DIBUJAR Linea de control de la prueba de colision de orillas*/
void dibujarvortex(float numero1[], float numero2[]){
glPushMatrix();
glRotatef(angulo1, 0, 1, 0);
glBegin(GL_LINES);
glVertex3f(numero1[0], numero1[1], numero1[2]);
glVertex3f(numero2[0], numero2[1], numero2[2]);
glEnd();
glPopMatrix();
}
/*Multiplicacion Matriz(4x4) x Vector(4)*/
void mult_mat_vect(float mat[4][4],float vect[5]){
for(i=0; i<4; i++){
vectr[i]=mat[i][0]*vect[0]+mat[i][1]*vect[1]+mat[i][2]*vect[2]+mat[i][3]*vect[3];
}
}
/*Pasar a Vtemp. Vetor temporal*/
void llenar_vtemp(void){
for(i=0;i<4;i++){
vtemp[i]=vectr[i];
}
}
/*Pasar punto de coordenadas locales a coordenas globales*/
void coord_globales_pta(float numero[5], float numreal[3]){
int s;
s=numero[4];//Nivel de jeraquía del sistema de coordenadas
llenar_matrices_rot();
/*Concatenación de matrices para nivel de jerarquia 5*/
if(s==5){
mult_mat_vect(rotx5, numero);
llenar_vtemp();
mult_mat_vect(tras4, vtemp);
llenar_vtemp();
mult_mat_vect(roty4, vtemp);
llenar_vtemp();
mult_mat_vect(tras3, vtemp);
llenar_vtemp();
mult_mat_vect(rotx3, vtemp);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
numreal[j]=vectr[j];
}
}
/*Concatenación de matrices para nivel de jerarquia 4*/
if(s==4){
mult_mat_vect(roty4, numero);
llenar_vtemp();
mult_mat_vect(tras3, vtemp);
llenar_vtemp();
98
mult_mat_vect(rotx3, vtemp);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
numreal[j]=vectr[j];
}
}
/*Concatenación de matrices para nivel de jerarquia 3*/
if(s==3){
mult_mat_vect(rotx3, numero);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
numreal[j]=vectr[j];
}
}
}
/*Comparar pendientes de la prueba de colision de orillas (VORTEX)*/
void vortex_test(float numero1[4], float numero2[4], float numero3[3], int mayor){
if((numero1[2]!=numero2[2]) && (numero1[2]!=numero3[2])){
/*Calcular la pendiente de la liena de control*/
float pendiente1 = (numero2[1]-numero1[1])/(numero2[2]-numero1[2]);
float pendiente2;
/*Prueba sobre las orillas inferiores*/
if(mayor==1){
if((pendiente1>0) && (numero1[2]>0)){
/*Calcular la pendiente de la linea al hombro (rango z positivo:orilla derecha)*/
pendiente2 = (numero3[1]-numero1[1])/(numero3[2]-numero1[2]);
if((pendiente1 >= pendiente2) && (pendiente2 > 0)){
choquev=1;
}
}
if((pendiente1<0) && (numero1[2]<0)){
/*Calcular la pendiente de la linea al hombro (rango z negativo:orilla izquierda)*/
pendiente2 = (numero3[1]-numero1[1])/((-1)*numero3[2]-numero1[2]);
if((pendiente1 <= pendiente2) && (pendiente2 < 0)){
choquev=1;
}
}
}
/*Prueba sobre las orillas inferiores*/
else if(mayor==2){
if((pendiente1>0) && (numero1[2]<0)){
/*Calcular la pendiente de la linea al hombro (rango z negativo:orilla izquierda)*/
pendiente2 = (numero3[1]-numero1[1])/((-1)*numero3[2]-numero1[2]);
if((pendiente1 <= pendiente2) && (pendiente2 > 0)){
choquev=1;
}
}
if((pendiente1<0) && (numero1[2]>0)){
/*Calcular la pendiente de la linea al hombro (rango z positivo:orilla derecha)*/
pendiente2 = (numero3[1]-numero1[1])/(numero3[2]-numero1[2]);
if((pendiente1 >= pendiente2) && (pendiente2 < 0)){
99
choquev=1;
}
}
if(pendiente1==0){
if(numero1[1] <= numero3[1]){
choquev=1;
}
}
}
}
}
/*Buscar el mismo punto en otra figura de control*/
void mismo_pt_a2(float numero[4], float array[][4], float numsalida[5]){
int n;
n=numero[3],
numsalida[0]=array[n][0];
numsalida[1]=array[n][1];
numsalida[2]=array[n][2];
numsalida[3]=array[n][3];
numsalida[4]=array[0][0];
}
//Buscar el punto con el menor valor de Z
void pila_menorz(float array[][4]){
menorz[0]=0; //valor x
menorz[1]=0; //valor y
menorz[2]=40; //valor z
menorz[3]=0; //# del numero en el array
numpuntos=array[0][1];
for(i=1; i<=numpuntos; i++){
if(fabs(array[i][2])<=fabs(menorz[2])){
menorz[0]=array[i][0];
menorz[1]=array[i][1];
menorz[2]=array[i][2];
menorz[3]=i;
}
}
}
//Buscar el punto con el menor valor de Y
void pila_menory(float array[][4]){
menory[0]=0; //valor x
menory[1]=300; //valor y
menory[2]=0; //valor z
menory[3]=0; //# del numero en el array
numpuntos=array[0][1];
for(i=1; i<=numpuntos; i++){
if(array[i][1]<=menory[1]){
menory[0]=array[i][0];
menory[1]=array[i][1];
menory[2]=array[i][2];
menory[3]=i;
}
}
}
//Prueba de choque contra la base
void lim_radiobase(float array[][4],float radiobase, float margen){
100
float radio;
numpuntos=array[0][1];
for(i=1; (i<=numpuntos)&&(choqueb!=1); i++){
radio=sqrt(pow(array[i][0],2)+pow(array[i][2],2));
if(radio <= radiobase+margen){
choqueb=1;
}
}
}
//Pruebar choque contra el piso
void lim_piso(float array[][4], float margen){
numpuntos=array[0][1];
for(i=1; (i<=numpuntos)&&(choquep!=1); i++){
if(array[i][1] <=(altpiso+margen)){
choquep=1;
}
}
}
//Prueba choque contra el hombro
void lim_box_yz(float array[][4], float margen, float y1, float y2, float z){
numpuntos=array[0][1];
for(i=1; (i<=numpuntos)&&(choqueh!=1); i++){
if((array[i][1]>=y1-margen) && (array[i][1]<=y2+margen) && (abs(array[i][2])<=z+margen)){
choqueh=1;
}
}
}
//Pasar todos puntos del array a coordenadas globales y guardarlo en arrayreal
void coord_globales(float array[][4], float arrayreal[][4]){
int s=array[0][0]; //Nivel de jerarquía de los puntos
numpuntos=array[0][1];
llenar_matrices_rot();
arrayreal[0][0]=array[0][0];
arrayreal[0][1]=array[0][1];
/*Concatenación de matrices para nivel de jerarquía 5*/
if(s==5){
for(n=1; n<=numpuntos; n++){
mult_mat_vect(rotx5, array[n]);
llenar_vtemp();
mult_mat_vect(tras4, vtemp);
llenar_vtemp();
mult_mat_vect(roty4, vtemp);
llenar_vtemp();
mult_mat_vect(tras3, vtemp);
llenar_vtemp();
mult_mat_vect(rotx3, vtemp);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
arrayreal[n][j]=vectr[j];
}
}
}
/*Concatenación de matrices para nivel de jerarquía 4*/
if(s==4){
for(n=1; n<=numpuntos; n++){
101
mult_mat_vect(roty4, array[n]);
llenar_vtemp();
mult_mat_vect(tras3, vtemp);
llenar_vtemp();
mult_mat_vect(rotx3, vtemp);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
arrayreal[n][j]=vectr[j];
}
}
}
/*Concatenación de matrices para nivel de jerarquía 4*/
if(s==3){
for(n=1; n<=numpuntos; n++){
mult_mat_vect(rotx3, array[n]);
llenar_vtemp();
mult_mat_vect(tras2, vtemp);
llenar_vtemp();
mult_mat_vect(rotx2, vtemp);
llenar_vtemp();
mult_mat_vect(tras1, vtemp);
for(j=0;j<4;j++){
arrayreal[n][j]=vectr[j];
}
}
}
}
//Preparar prueba de choque contra el hombro
void check_boundingH(float array[][4], float arrayreal[][4]){
choqueh=0;
coord_globales(array, arrayreal);
lim_box_yz(arrayreal, marg, nively1, nively2, zhombro);
if(choqueh==1){
printf("\nCHOQUE HOMBRO");
}
}
//Preparar prueba de choque contra el vortice
void check_vortex(float array1[][4], float array2[][4], float arrayreal1[][4], int nivel){
choquev=0;
coord_globales(array1,arrayreal1);
if(nivel==1){
pila_menorz(arrayreal1);
mismo_pt_a2(menorz, array2, menorz2);
coord_globales_pta(menorz2, vortexm);
dibujarvortex(menorz,vortexm);
vortex_test(menorz, vortexm, vortexfijo1, 1);
if(choquev==1){
printf("\nCHOQUE Orilla INFERIOR del HOMBRO");
}
}
if(nivel==2){
pila_menory(arrayreal1);
mismo_pt_a2(menory, array2, menory2);
coord_globales_pta(menory2, vortexm);
dibujarvortex(menory,vortexm);
vortex_test(menory, vortexm, vortexfijo2, 2);
102
if(choquev==1){
printf("\nCHOQUE Orilla SUPERIOR del HOMBRO");
}
}
}
//Preparar prueba de choque contra la base
void check_base(float array[][4], float arrayreal[][4]){
choqueb=0;
coord_globales(array, arrayreal);
lim_radiobase(arrayreal, radbase, marg);
if(choqueb==1){
printf("\nCHOQUE BASE");
}
}
//Preparar prueba de choque contra el piso
void check_piso(float array[][4], float arrayreal[][4]){
choquep=0;
coord_globales(array, arrayreal);
lim_piso(arrayreal, marg);
if(choquep==1){
printf("\nCHOQUE PISO");
}
}
//Crear las figuras de control
void crear_controles(void){
//Crear circulos, cuadrados y puntos de control
float dif=2*M_PI/npuntos;
float alpha;
alpha=0;
circHA[0][0]=5;//Nivel del sistema de coordenadas de cada figura de control
circHB[0][0]=5;
circWA[0][0]=4;
circWB[0][0]=4;
circWC[0][0]=4;
circBD[0][0]=4;
circBC[0][0]=4;
cuadBA[0][0]=3;
cuadBB[0][0]=3;
cuadBC[0][0]=3;
circHA[0][1]=npuntos;//Numero de puntos de cada figura de control
circHB[0][1]=npuntos;
circWA[0][1]=npuntos;
circWB[0][1]=npuntos;
circWC[0][1]=npuntos;
circBD[0][1]=npuntos;
circBC[0][1]=npuntos;
cuadBA[0][1]=4;
cuadBB[0][1]=4;
cuadBC[0][1]=4;
//CIRULOS
for(i=1; i<=npuntos; i++){
circHA[i][0]=(3*sin(alpha));//coord. x del punto 1 (local)
circHA[i][1]=5.6;//coord. y del punto 1 (local)
circHA[i][2]=(3*cos(alpha));//coord. z del punto 1 (local)
circHA[i][3]=1;//indica que es un punto
circHB[i][0]=(3*sin(alpha));
circHB[i][1]=8.5;
103
circHB[i][2]=(3*cos(alpha));
circHB[i][3]=1;
circWA[i][0]=-5.5;
circWA[i][1]=(55+5*sin(alpha));
circWA[i][2]=(5*cos(alpha));
circWA[i][3]=1;
circWB[i][0]=0;
circWB[i][1]=(55+5*sin(alpha));
circWB[i][2]=(5*cos(alpha));
circWB[i][3]=1;
circWC[i][0]=5.5;
circWC[i][1]=(55+5*sin(alpha));
circWC[i][2]=(5*cos(alpha));
circWC[i][3]=1;
circBD[i][0]=(8.3*sin(alpha));
circBD[i][1]=48;
circBD[i][2]=(0.7+8.3*cos(alpha));
circBD[i][3]=1;
circBC[i][0]=(9*sin(alpha));
circBC[i][1]=0;
circBC[i][2]=(0.7+9*cos(alpha));
circBC[i][3]=1;
alpha+=dif;
}
}
//Dibujar las figuras de control
void dibujar_circHA(void){
numpuntos=circHA[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glTranslatef(0.0, 55.0, 0.0);
glRotatef(angulo5, 1.0, 0.0, 0.0);
glBegin( GL_LINE_LOOP );
for(i=1; i<=numpuntos; i++){
glVertex3f(circHA[i][0],circHA[i][1],circHA[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circHB(void){
numpuntos=circHB[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
104
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glTranslatef(0.0, 55.0, 0.0);
glRotatef(angulo5, 1.0, 0.0, 0.0);
glBegin( GL_LINE_LOOP );
for(i=1; i<=numpuntos; i++){
glVertex3f(circHB[i][0],circHB[i][1],circHB[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circWA(void){
numpuntos=circWA[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(circWA[i][0],circWA[i][1],circWA[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circWB(void){
numpuntos=circWB[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(circWB[i][0],circWB[i][1],circWB[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circWC(void){
numpuntos=circWC[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
105
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(circWC[i][0],circWC[i][1],circWC[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circBC(void){
numpuntos=circBC[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(circBC[i][0],circBC[i][1],circBC[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_circBD(void){
numpuntos=circBD[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glTranslatef(0.0, 10.0, 0.0);
glRotatef(angulo4, 0.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(circBD[i][0],circBD[i][1],circBD[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_cuadBA(void){
numpuntos=cuadBA[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
106
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(cuadBA[i][0],cuadBA[i][1],cuadBA[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_cuadBB(void){
numpuntos=cuadBB[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(cuadBB[i][0],cuadBB[i][1],cuadBB[i][2]);
}
glEnd();
glPopMatrix();
}
void dibujar_cuadBC(void){
numpuntos=cuadBC[0][1];
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glRotatef(angulo2, 1.0, 0.0, 0.0);
glTranslatef(0.0, 45.0, 0.0);
glRotatef(angulo3, 1.0, 0.0, 0.0);
glBegin(GL_LINE_LOOP);
for(i=1; i<=numpuntos; i++){
glVertex3f(cuadBC[i][0],cuadBC[i][1],cuadBC[i][2]);
}
glEnd();
glPopMatrix();
}
//Pasar los puntos de control a coordenadas globales
void coord_globales_pts_control(void){
/* coordenas globales de solo los puntos de control
* CHB, CHA, CWA, CWC, CWB, CBA, CBB, CBC, CBD, CBE*/
coord_globales_pta(CHB, CHBreal);
coord_globales_pta(CHA, CHAreal);
coord_globales_pta(CWA, CWAreal);
coord_globales_pta(CWB, CWBreal);
coord_globales_pta(CWC, CWCreal);
coord_globales_pta(CBA, CBAreal);
coord_globales_pta(CBB, CBBreal);
coord_globales_pta(CBC, CBCreal);
coord_globales_pta(CBD, CBDreal);
107
}
/*Verificar si se cumplen las condiciones para realizar alguna de las pruebas de colision*/
void probar_colision(float margen){
coord_globales_pts_control(); /*pasar los puntos de control a coord globales*/
float radchb;
float acwaz;
float acwbz;
float acwcz;
float acbez;
float acbdz;
float acbcz;
float acbbz;
float acbaz;
float achbz;
float achaz;
choque=choquep=choqueb=choqueh=choquev=0;
cuidado=0;
/*distancia del punto de control CHB al eje Y*/
radchb=sqrt(pow(CHBreal[2],2)+pow(CHBreal[0],2));
/*Valor absoluto de la coordena Z de los putnos de control*/
acwaz=fabs(CWAreal[2]);
acwbz=fabs(CWBreal[2]);
acwcz=fabs(CWCreal[2]);
acbdz=fabs(CBDreal[2]);
acbcz=fabs(CBCreal[2]);
acbbz=fabs(CBBreal[2]);
acbaz=fabs(CBAreal[2]);
achbz=fabs(CHBreal[2]);
achaz=fabs(CHAreal[2]);
//PISO
/*Verificar algun punto de control entro al area de precaución del piso*/
if(CBAreal[1] <= (6+margen)){
dibujar_cuadBA();
check_piso(cuadBA, cuadBAreal);
cuidado=1;
}
if((CBBreal[1] <= (11.5+margen)) && (CBBreal[1] <= CBCreal[1])){
dibujar_cuadBB();
check_piso(cuadBB, cuadBBreal);
cuidado=1;
}
if((CBCreal[1] <= (11.5+margen)) && (CBCreal[1] < CBBreal[1])){
dibujar_cuadBC();
check_piso(cuadBC, cuadBCreal);
cuidado=1;
}
if((CWAreal[1] <= (5.5+margen)) && (CWAreal[1] <= CWCreal[1])){
dibujar_circWA();
check_piso(circWA, circWAreal);
cuidado=1;
}
if((CWCreal[1] <= (5.5+margen)) && (CWCreal[1] < CWAreal[1])){
dibujar_circWC();
check_piso(circWC, circWCreal);
cuidado=1;
}
if(CHBreal[1] <= (3.5+margen)){
dibujar_circHB();
check_piso(circHB, circHBreal);
108
cuidado=1;
}
//BASE
/*Verificar algun punto de control entro al area de precaución de la base*/
if((CHBreal[1]<25.5) && (radchb<=15.5+margen)){
dibujar_circHB();
check_base(circHB, circHBreal);
cuidado=1;
}
if((CWAreal[1]<23.5) && (acwaz<=17.5+margen)){
dibujar_circWA();
check_base(circWA, circWAreal);
cuidado=1;
}
if((CWBreal[1]<23.5) && (acwbz<=17.5+margen)){
dibujar_circWB();
check_base(circWB, circWBreal);
cuidado=1;
}
if((CWCreal[1]<23.5) && (acwcz<=17.5+margen)){
dibujar_circWC();
check_base(circWC, circWCreal);
cuidado=1;
}
if((CBDreal[1]<28.5) && (acbdz<=21+margen)){
dibujar_circBD();
check_base(circBD, circBDreal);
cuidado=1;
}
if((CBAreal[1]<28.5) && (acbaz<=25+margen)){
dibujar_cuadBA();
cuidado=1;
choqueh=0;
coord_globales(cuadBA, cuadBAreal);
lim_box_yz(cuadBAreal, marg, altpiso, nively1, radbase);
if(choqueh==1){
printf("CHOQUE BASE\n");
}
}
if((CBBreal[1]<28.5) && (acbbz<=25+margen)){
dibujar_cuadBB();
cuidado=1;
choqueh=0;
coord_globales(cuadBB, cuadBBreal);
lim_box_yz(cuadBBreal, marg, altpiso, nively1, radbase);
if(choqueh==1){
printf("CHOQUE BASE\n");
}
}
//HOMBRO
//VORTEX nivel 1 (hombro)
/*Verificar si se cumplen las condiciones para que este cerca una colision en las orillas inferiores*/
if((CBDreal[1]<28.5) && (CBCreal[1]>18.0) && (acbdz<=40+margen)){
dibujar_circBD();
check_vortex(circBD, circBC, circBDreal, 1);
cuidado=1;
}
//VORTEX nivel 2
/*Verificar si se cumplen las condiciones para que se de una posible colision en las orillas superiores*/
if((CBDreal[1]<=65) && (CBCreal[1]>54.5) && (acbcz<=40.0+margen)){
dibujar_circBD();
109
check_vortex(circBD, circBC, circBDreal, 2);
cuidado=1;
}
if((CBCreal[1]<=65) && (CBDreal[1]>54.5) && (acbdz<=40+margen)){
dibujar_circBC();
check_vortex(circBC, circBD, circBCreal, 2);
cuidado=1;
}
//ALGUN CHOQUE?
/*Si se dio algun choque se llena la variable de hubochoque y la de choque*/
if((choquep==1) || (choqueb==1) || (choqueh==1) || (choquev==1)){
choque=1;
hubochoque=1;
}
}
//Verificar angulo máximo de las articulaciónes
void check_maximo(void){
int numart;
angmax=0;
if(angulo1<(-160)){
numart=1;
angmax=1;
angulo1=-160;
}else if(angulo1>160){
numart=1;
angmax=1;
angulo1=160;
}
if(angulo2<(-137.5)){
numart=2;
angmax=1;
angulo2=-137.5;
}else if(angulo2>137.5){
numart=2;
angmax=1;
angulo2=137.5;
}
if(angulo3<(-142.5)){
numart=3;
angmax=1;
angulo3=-142.5;
}else if(angulo3>142.5){
numart=3;
angmax=1;
angulo3=142.5;
}
if(angulo4<(-270)){
numart=4;
angmax=1;
angulo4=-270;
}else if(angulo4>270){
numart=4;
angmax=1;
angulo4=270;
}
if(angulo5<(-120)){
numart=5;
angmax=1;
angulo5=-120;
}else if(angulo5>105){
numart=5;
110
angmax=1;
angulo5=105;
}
if(angulo6<(-270)){
numart=6;
angmax=1;
angulo6=-270;
}else if(angulo6>270){
numart=6;
angmax=1;
angulo6=270;
}
if(angmax==1){
printf("\nArticulación %d alcanzo su máxima amplitud de giro",numart);
}
}
//Limites geométricos del modelo
/*Herramienta para el desarrollador o modelador*/
void dibujar_limites(void){
//Verticales
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINES);
glVertex3f(-26.8, 0, 0);
glVertex3f(-26.8, 180, 0);
glVertex3f(-12.7, 0, 0);
glVertex3f(-12.7, 180, 0);
glVertex3f(15, 0, 0);
glVertex3f(15, 180, 0);
glVertex3f(11.9, 0, 0);
glVertex3f(11.9, 180, 0);
glVertex3f(0, 0, 0);
glVertex3f(0, 200, 0);
glVertex3f(-5.4, 132, 0);
glVertex3f(-5.4, 150, 0);
glVertex3f(5.4, 132, 0);
glVertex3f(5.4, 150, 0);
glVertex3f(0, 0, 13);
glVertex3f(0, 180, 13);
glVertex3f(0, 0, -13);
glVertex3f(0, 180, -13);
glVertex3f(0, 54, 11);
glVertex3f(0, 180, 11);
glVertex3f(0, 54, -11);
glVertex3f(0, 180, -11);
glEnd();
glPopMatrix();
//HORIZONTALES
glPushMatrix();
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_LINES);
glVertex3f(-20, 42.0, 0);
glVertex3f(20, 42.0, 0);
glVertex3f(-20, 54.5, 0);
glVertex3f(20, 54.5, 0);
glVertex3f(-20, 71.7, 0);
glVertex3f(20, 71.7, 0);
glVertex3f(-20, 87, 0);
glVertex3f(20, 87, 0);
glVertex3f(-20, 96.7, 0);
glVertex3f(20, 96.7, 0);
glVertex3f(-20, 111, 0);
glVertex3f(20, 111, 0);
111
glVertex3f(-20, 152, 0);
glVertex3f(20, 152, 0);
glEnd();
glPopMatrix();
}
//SEÑAL DE PRECAUCION
/*Esferas de colores que indican el riesgo del movimiento actual*/
void semaforo(void){
GLUquadricObj *quadObj1;
float semaf_x;
float semaf_y;
float color[3];
semaf_x=140*ventanaw/ventanah;
quadObj1 = gluNewQuadric();
if(choque==1){
color[0]=1.0; color[1]=0.0; color[2]=0.0;
semaf_y=150;
}else if(cuidado==1){
color[0]=1.0; color[1]=0.9; color[2]=0.0;
semaf_y=140;
}else{
color[0]=0.0; color[1]=1.0; color[2]=0.5;
semaf_y=130;
}
glPushMatrix();
glColor3fv(color);
glRotatef(-1*angulo_yr, 0.0, 1.0, 0.0);
glTranslatef(-1*ancho_x, -1*altura_y, 0.0);
glRotatef(-1*angulo_xr, 1.0, 0.0, 0.0);
glTranslatef(semaf_x*zoom_z/160, semaf_y*zoom_z/160, 600.0);
gluSphere(quadObj1, 7*zoom_z/160, 30, 30);
glPopMatrix();
}
//----------------------DIBUJAR ESCENA---------------------------------------gboolean dibujar(void){
/*Buscar area OpenGL*/
GdkGLContext *glcontext = gtk_widget_get_gl_context (glarea1);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (glarea1);
/*** OpenGL INICIO ***/
if (!gdk_gl_drawable_make_current(gldrawable, glcontext)){
printf("\nError al buscar area OpenGL. Se crea la ventana nuevamente");
/* Error al buscar el area GL. Se crea la ventana 2 nuevamente y se vuelve a intentar*/
ventana2 = create_window2 ();
gtk_widget_show (ventana2);
printf("\nCreando area de nuevo");
glcontext = gtk_widget_get_gl_context (glarea1);
gldrawable = gtk_widget_get_gl_drawable (glarea1);
/* Si el error se da nuevamente, se retorna el valor FALSE*/
if (!gdk_gl_drawable_make_current(gldrawable, glcontext)){
printf("\nError al buscar area OpenGL. Segundo Intento");
return FALSE;
}
}
/*Limpiar los buffers de profundidad y color*/
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
112
glLoadIdentity();
if(checkmax==1){
/*limita los angulos de giro a sus amplitudes máximas*/
check_maximo();
}
/*Dibujar piso en forma de rejilla*/
if(grid==1){
// GRID
glBegin(GL_LINES);
glColor3f(0.0, 0.1, 1.0);
glNormal3f(0.0, 1.0, 0.0);
int i;
for(i=-300;i<=300;i+=30) {
glVertex3f(i,altpiso,-300);
glVertex3f(i,altpiso,300);
glVertex3f(300,altpiso,i);
glVertex3f(-300,altpiso,i);
}
glEnd();
}else {
//PISO
/*Dibujar piso en forma poligono solido*/
glBegin(GL_QUADS);
glColor3f(0.9, 0.9, 0.9);
glNormal3f(0.0, 1.0, 0.0);
glVertex3f(-500.0f, altpiso, -500.0f);
glVertex3f(-500.0f, altpiso, 500.0f);
glVertex3f(500.0f, altpiso, 500.0f);
glVertex3f(500.0f, altpiso, -500.0f);
glEnd();
}
/*Base externa sobre la cual se monta el brazo (opcional)*/
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glPushMatrix();
glColor3f(0.1, 0.1, 1.0);
glRotatef(90, 1.0, 0.0, 0.0);
gluCylinder(quadObj1, 12, 12, -1*altpiso, 30, 30);
glPopMatrix();
/*Posicionar y dibujar los componentes del brazo en la escena*/
glColor3f(0.7, 0.8, 0.7);
//BASE
glPushMatrix();
glTranslatef(0.0, 0.0, 0.0);
glCallList(BASE);
glPopMatrix();
//HOMBRO
glPushMatrix();
glTranslatef(0.0, 0.0, 0.0);
glTranslatef(0.0, 42.0, 0.0);
glRotatef(angulo1, 0.0, 1.0, 0.0);
glCallList(HOMBRO);
glTranslatef(-13.0, 0.0, 0.0);
//BRAZO1
glPushMatrix();
glRotatef(angulo2, 1.0, 0.0, 0.0);
glCallList(BRAZO1);
glTranslatef(13.0, 45.0, 0.0);
113
//CODO
glPushMatrix();
glRotatef(angulo3, 1.0, 0.0, 0.0);
glCallList(CODO);
glTranslatef(0.0, 10.0, 0.0);
//BRAZO2
glPushMatrix();
glRotatef(angulo4, 0.0, 1.0, 0.0);
glCallList(BRAZO2);
glTranslatef(0.0, 55.0, 0.0);
//MUNIECA
glPushMatrix();
glRotatef(angulo5, 1.0, 0.0, 0.0);
glTranslatef(5.5, 0.0, 0.0);
glCallList(MUNIECA);
glTranslatef(-5.5, 0.0, 0.0);
//MANO
glPushMatrix();
glRotatef(angulo6, 0.0, 1.0, 0.0);
glCallList(MANO);
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
glPopMatrix();
if(lim==1){
dibujar_limites();
}
/*Realizar las pruebas de colisiones necesarias*/
probar_colision(marg);
/*Actualizar el semaforo de precaución*/
semaforo();
/*Exigir la ejecucuión de todos los comandos OpenGL llamados hasta ahora*/
glFlush ();
/*Intercambiar los frame buffers. Tecnica de double buffering*/
gdk_gl_drawable_swap_buffers (gldrawable);
return TRUE;
}
/*Funciones que crean las listas de despliegue de los componentes principales del robot*/
//BASE del Robot
void crear_base(int slices, int stacks){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(BASE, GL_COMPILE);
glPushMatrix();
glRotatef(-90, 1.0, 0.0, 0.0);
gluCylinder(quadObj1, 12, 12, 30, slices, stacks);
glPopMatrix();
glEndList();
}
//HOMBRO del Robot
void crear_hombro(){
GLUquadricObj *quadObj1;
114
quadObj1 = gluNewQuadric();
glNewList(HOMBRO, GL_COMPILE);
glPushMatrix();
glRotatef(-90, 1.0, 0.0, 0.0);
glRotatef(45, 0.0, 0.0, 1.0);
glTranslatef(0.0, 0.0, -13.5);
gluCylinder(quadObj1, 18.4, 18.4, 26, 4, 16);
gluDisk(quadObj1, 0, 18.4, 4, 16);
glTranslatef(0.0, 0.0, 26);
gluDisk(quadObj1, 0, 18.4, 4, 16);
glPopMatrix();
glPushMatrix();
glRotatef(90, 0.0, 1.0, 0.0);
glTranslatef(0.0, -0.5, 13.0);
glPushMatrix();
glRotatef(45, 0.0, 0.0, 1.0);
gluCylinder(quadObj1, 18.4, 7.0, 2, 4, 16);
glTranslatef(0.0, 0.0, 2.0);
gluDisk(quadObj1, 0, 7, 4, 16);
glPopMatrix();
glPopMatrix();
glEndList();
}
//BRAZO del Robot
void crear_brazo1(int slices, int stacks){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(BRAZO1, GL_COMPILE);
glPushMatrix();
glRotatef(-90, 0.0, 1.0, 0.0);//ROTAR
gluCylinder(quadObj1, 12, 13, 7, slices, stacks);
glTranslatef(0.0, 0.0, 7.0);
gluCylinder(quadObj1, 13, 13, 7, slices, stacks);
glTranslatef(0.0, 0.0, 7.0);
gluPartialDisk(quadObj1, 0, 13, slices, stacks, 90, 180);
glTranslatef(0.0, 0.0, -3.5);
glBegin(GL_QUADS);
glNormal3f(0.0, 0.0, 1.0); //Izquierda
glVertex3f(-11.0, 45.0, 3.5);
glVertex3f(-13.0, 0.0, 3.5);
glVertex3f(13.0, 0.0, 3.5);
glVertex3f(11.0, 45.0, 3.5);
glNormal3f(0.0, 0.0, -1.0); //Derecha
glVertex3f(-11.0, 45.0, -3.5);
glVertex3f(-13.0, 0.0, -3.5);
glVertex3f(13.0, 0.0, -3.5);
glVertex3f(11.0, 45.0, -3.5);
glNormal3f(0.999, 0.044, 0.0); //Frontal
glVertex3f(11.0, 45.0, 3.5);
glVertex3f(13.0, 0.0, 3.5);
glVertex3f(13.0, 0.0, -3.5);
glVertex3f(11.0, 45.0, -3.5);
glNormal3f(-0.999, 0.044, 0.0); //Trasero
glVertex3f(-11.0, 45.0, 3.5);
115
glVertex3f(-13.0, 0.0, 3.5);
glVertex3f(-13.0, 0.0, -3.5);
glVertex3f(-11.0, 45.0, -3.5);
glEnd();
glTranslatef(0.0, 0.0, -3.5);
glBegin(GL_QUADS);
glColor3f(0.1, 0.1, 1.0);
glNormal3f(0.0, 0.0, -1.0); //Derecha COBERTO
glVertex3f(-8.0, 40.0, -3.5);
glVertex3f(8.0, 40.0, -3.5);
glVertex3f(8.0, 7.0, -3.5);
glVertex3f(-8.0, 7.0, -3.5);
glNormal3f(1.0, 0.0, 0.0); //Frontal COBERTOR
glVertex3f(8.0, 40.0, 3.5);
glVertex3f(8.0, 7.0, 3.5);
glVertex3f(8.0, 7.0, -3.5);
glVertex3f(8.0, 40.0, -3.5);
glNormal3f(-1.0, 0.0, 0.0); //Trasero COBERTOR
glVertex3f(-8.0, 40.0, 3.5);
glVertex3f(-8.0, 7.0, 3.5);
glVertex3f(-8.0, 7.0, -3.5);
glVertex3f(-8.0, 40.0, -3.5);
glEnd();
glColor3f(0.7, 0.8, 0.7);
glTranslatef(0.0, 45.0, -7.0);
gluCylinder(quadObj1, 11, 11, 14, slices, stacks);
gluDisk(quadObj1, 0, 11, slices, stacks);
glTranslatef(0.0, 0.0, 14.0);
gluPartialDisk(quadObj1, 0, 11, slices, stacks, 270, 180);
glPopMatrix();
glEndList();
}
//CODO del Robot
void crear_codo(void){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(CODO, GL_COMPILE);
glPushMatrix();
glPushMatrix();
glRotatef(-90, 0.0, 1.0, 0.0);
glTranslatef(0.0, 0.0, 10.1);
gluCylinder(quadObj1, 9.8, 9.8, 3, 12, 16);
glPopMatrix();
glRotatef(-90, 1.0, 0.0, 0.0);
glRotatef(45, 0.0, 0.0, 1.0);
glTranslatef(0.45, -0.45, -10.0);
gluCylinder(quadObj1, 15.556, 15.556, 20, 4, 16);
glTranslatef(0.0, 0.0, -5.0);
gluCylinder(quadObj1, 4.0, 15.556, 5, 4, 16);
glPushMatrix();
glRotatef(180, 1.0, 0.0, 0.0);
gluDisk(quadObj1, 0, 4.0, 4, 16);
glPopMatrix();
glTranslatef(0.0, 0.0, 25.0);
gluDisk(quadObj1, 0, 15.556, 4, 16);
glPopMatrix();
glEndList();
}
116
//ANTERBRAZO del Robot
void crear_brazo2(int slices, int stacks){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(BRAZO2, GL_COMPILE);
glPushMatrix();
glPushMatrix();
glRotatef(-90, 1.0, 0.0, 0.0);
gluCylinder(quadObj1, 9.0, 9.0, 14, slices, stacks);
glTranslatef(0.0, 0.0, 14.0);
gluDisk(quadObj1, 0, 9.0, slices, stacks);
glPopMatrix();
glTranslatef(0.0, 0.0, 0.7);
glPushMatrix();
glRotatef(-90, 1.0, 0.0, 0.0);
glRotatef(45, 0.0, 0.0, 1.0);
gluCylinder(quadObj1, 9.0, 8.3, 48, slices, stacks);
glTranslatef(0.0, 0.0, 48);
gluDisk(quadObj1, 0, 8.3, slices, stacks);
gluCylinder(quadObj1, 8.3, 4.0, 7, 4, 16);
glTranslatef(0.0, 0.0, 7.0);
gluDisk(quadObj1, 0, 4.0, 4, stacks);
glPopMatrix();
glPopMatrix();
glEndList();
}
//MUÑECA del Robot
void crear_munieca(int slices, int stacks){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(MUNIECA, GL_COMPILE);
glPushMatrix();
glRotatef(-90, 0.0, 1.0, 0.0);
gluCylinder(quadObj1, 5, 5, 11, slices, stacks);
glPushMatrix();
glRotatef(180, 1.0, 0.0, 0.0);
gluDisk(quadObj1, 0, 5, slices, stacks);
glPopMatrix();
glTranslatef(0, 0, 11);
gluDisk(quadObj1, 0, 5, slices, stacks);
glPopMatrix();
glEndList();
}
//MANO del Robot
void crear_mano(int slices, int stacks){
GLUquadricObj *quadObj1;
quadObj1 = gluNewQuadric();
glNewList(MANO, GL_COMPILE);
glPushMatrix();
glColor3f(0.1, 0.1, 1.0);
glRotatef(-90, 1.0, 0.0, 0.0);
gluCylinder(quadObj1, 3, 3, 8.5, slices, stacks);
glTranslatef(0, 0, 8);
gluDisk(quadObj1, 0, 3, slices, stacks);
glPopMatrix();
glEndList();
117
}
/*************************************************************************************************************/
interface.c: Interfaz gráfica de usuario
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* interface.c: interfaz gráfica de usuario
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef ENABLE_NLS
# include <libintl.h>
# undef _
# define _(String) dgettext (PACKAGE, String)
# define Q_(String) g_strip_context ((String), gettext (String))
# ifdef gettext_noop
# define N_(String) gettext_noop (String)
# else
# define N_(String) (String)
# endif
#else
# define textdomain(String) (String)
# define gettext(String) (String)
# define dgettext(Domain,Message) (Message)
# define dcgettext(Domain,Message,Type) (Message)
# define bindtextdomain(Domain,Directory) (Domain)
# define _(String) (String)
# define Q_(String) g_strip_context ((String), (String))
# define N_(String) (String)
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <gdk/gdkkeysyms.h>
118
#include <gdk/gdk.h>
#include <gdk/gdkgl.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>
#include <gtk/gtkgl.h>
#include "callbacks.h"
#include "interface.h"
#define GLADE_HOOKUP_OBJECT(component,widget,name) \
g_object_set_data_full (G_OBJECT (component), name, \
gtk_widget_ref (widget), (GDestroyNotify) gtk_widget_unref)
#define GLADE_HOOKUP_OBJECT_NO_REF(component,widget,name) \
g_object_set_data (G_OBJECT (component), name, widget)
/*Función de creación de la ventana 1*/
GtkWidget*
create_window1 (void)
{
/*Objetos de la ventana 1*/
GtkWidget *window1;
GtkWidget *vbox1;
GtkWidget *hbox1;
GtkWidget *frame1;
GtkWidget *scrolledwindow1;
GtkWidget *label1;
GtkWidget *hbox2;
GtkWidget *frame3;
GtkWidget *alignment3;
GtkWidget *vbox2;
GtkWidget *label6;
GtkObject *spinbutton1_adj;
GtkWidget *label7;
GtkObject *spinbutton2_adj;
GtkWidget *label8;
GtkObject *spinbutton3_adj;
GtkWidget *bdrive;
GtkWidget *label3;
GtkWidget *frame4;
GtkWidget *alignment4;
GtkWidget *vbox3;
GtkWidget *bsimular;
GtkWidget *alignment6;
GtkWidget *hbox3;
GtkWidget *image1;
GtkWidget *label9;
GtkWidget *bejecutar;
GtkWidget *alignment7;
GtkWidget *hbox4;
GtkWidget *image2;
GtkWidget *label10;
GtkWidget *label4;
GtkWidget *frame5;
GtkWidget *alignment5;
GtkWidget *vbox4;
GtkWidget *brutina1;
GtkWidget *alignment15;
GtkWidget *hbox11;
GtkWidget *image8;
GtkWidget *label18;
GtkWidget *brutina2;
GtkWidget *alignment16;
GtkWidget *hbox12;
119
GtkWidget *image9;
GtkWidget *label19;
GtkWidget *brutina3;
GtkWidget *alignment17;
GtkWidget *hbox13;
GtkWidget *image10;
GtkWidget *label20;
GtkWidget *label5;
/*Creación y agrupamiento de los objetos de la ventana*/
window1 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window1, "window1");
gtk_container_set_border_width (GTK_CONTAINER (window1), 3);
gtk_window_set_title (GTK_WINDOW (window1), _("Controles del Simulador (Controles Rutinas)"));
vbox1 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox1, "vbox1");
gtk_widget_show (vbox1);
gtk_container_add (GTK_CONTAINER (window1), vbox1);
hbox1 = gtk_hbox_new (FALSE, 0);
gtk_widget_set_name (hbox1, "hbox1");
gtk_widget_show (hbox1);
gtk_box_pack_start (GTK_BOX (vbox1), hbox1, TRUE, TRUE, 0);
frame1 = gtk_frame_new (NULL);
gtk_widget_set_name (frame1, "frame1");
gtk_widget_show (frame1);
gtk_box_pack_start (GTK_BOX (hbox1), frame1, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (frame1), 3);
gtk_frame_set_shadow_type (GTK_FRAME (frame1), GTK_SHADOW_NONE);
scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_name (scrolledwindow1, "scrolledwindow1");
gtk_widget_show (scrolledwindow1);
gtk_container_add (GTK_CONTAINER (frame1), scrolledwindow1);
textview1 = gtk_text_view_new ();
gtk_widget_set_name (textview1, "textview1");
gtk_widget_show (textview1);
gtk_container_add (GTK_CONTAINER (scrolledwindow1), textview1);
gtk_widget_set_size_request (textview1, 250, 100);
label1 = gtk_label_new (_("<b>MENSAJES</b>"));
gtk_widget_set_name (label1, "label1");
gtk_widget_show (label1);
gtk_frame_set_label_widget (GTK_FRAME (frame1), label1);
gtk_label_set_use_markup (GTK_LABEL (label1), TRUE);
hbox2 = gtk_hbox_new (FALSE, 0);
gtk_widget_set_name (hbox2, "hbox2");
gtk_widget_show (hbox2);
gtk_box_pack_start (GTK_BOX (vbox1), hbox2, TRUE, TRUE, 0);
frame3 = gtk_frame_new (NULL);
gtk_widget_set_name (frame3, "frame3");
gtk_widget_show (frame3);
gtk_box_pack_start (GTK_BOX (hbox2), frame3, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (frame3), 3);
gtk_frame_set_shadow_type (GTK_FRAME (frame3), GTK_SHADOW_OUT);
alignment3 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_set_name (alignment3, "alignment3");
gtk_widget_show (alignment3);
120
gtk_container_add (GTK_CONTAINER (frame3), alignment3);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment3), 0, 0, 12, 0);
vbox2 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox2, "vbox2");
gtk_widget_show (vbox2);
gtk_container_add (GTK_CONTAINER (alignment3), vbox2);
label6 = gtk_label_new (_("ARTICULACION"));
gtk_widget_set_name (label6, "label6");
gtk_widget_show (label6);
gtk_box_pack_start (GTK_BOX (vbox2), label6, TRUE, TRUE, 0);
gtk_label_set_justify (GTK_LABEL (label6), GTK_JUSTIFY_CENTER);
spinbutton1_adj = gtk_adjustment_new (1, 1, 6, 1, 10, 10);
spinbutton1 = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton1_adj), 1, 0);
gtk_widget_set_name (spinbutton1, "spinbutton1");
gtk_widget_show (spinbutton1);
gtk_box_pack_start (GTK_BOX (vbox2), spinbutton1, TRUE, TRUE, 0);
label7 = gtk_label_new (_("ANGULO"));
gtk_widget_set_name (label7, "label7");
gtk_widget_show (label7);
gtk_box_pack_start (GTK_BOX (vbox2), label7, TRUE, TRUE, 0);
spinbutton2_adj = gtk_adjustment_new (0, -360, 360, 1, 10, 10);
spinbutton2 = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton2_adj), 1, 0);
gtk_widget_set_name (spinbutton2, "spinbutton2");
gtk_widget_show (spinbutton2);
gtk_box_pack_start (GTK_BOX (vbox2), spinbutton2, TRUE, TRUE, 0);
label8 = gtk_label_new (_("VELOCIDAD (%)"));
gtk_widget_set_name (label8, "label8");
gtk_widget_show (label8);
gtk_box_pack_start (GTK_BOX (vbox2), label8, TRUE, TRUE, 0);
spinbutton3_adj = gtk_adjustment_new (100, 1, 100, 1, 10, 10);
spinbutton3 = gtk_spin_button_new (GTK_ADJUSTMENT (spinbutton3_adj), 1, 0);
gtk_widget_set_name (spinbutton3, "spinbutton3");
gtk_widget_show (spinbutton3);
gtk_box_pack_start (GTK_BOX (vbox2), spinbutton3, TRUE, TRUE, 0);
bdrive = gtk_button_new_with_mnemonic (_("DRIVE"));
gtk_widget_set_name (bdrive, "bdrive");
gtk_widget_show (bdrive);
gtk_box_pack_start (GTK_BOX (vbox2), bdrive, TRUE, TRUE, 0);
label3 = gtk_label_new (_("<b>Comando Drive</b>"));
gtk_widget_set_name (label3, "label3");
gtk_widget_show (label3);
gtk_frame_set_label_widget (GTK_FRAME (frame3), label3);
gtk_label_set_use_markup (GTK_LABEL (label3), TRUE);
frame4 = gtk_frame_new (NULL);
gtk_widget_set_name (frame4, "frame4");
gtk_widget_show (frame4);
gtk_box_pack_start (GTK_BOX (hbox2), frame4, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (frame4), 3);
gtk_frame_set_shadow_type (GTK_FRAME (frame4), GTK_SHADOW_OUT);
alignment4 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_set_name (alignment4, "alignment4");
gtk_widget_show (alignment4);
gtk_container_add (GTK_CONTAINER (frame4), alignment4);
121
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment4), 0, 0, 12, 0);
vbox3 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox3, "vbox3");
gtk_widget_show (vbox3);
gtk_container_add (GTK_CONTAINER (alignment4), vbox3);
entrycomando = gtk_entry_new ();
gtk_widget_set_name (entrycomando, "entrycomando");
gtk_widget_show (entrycomando);
gtk_box_pack_start (GTK_BOX (vbox3), entrycomando, TRUE, TRUE, 48);
gtk_entry_set_text (GTK_ENTRY (entrycomando), _("Comando"));
gtk_entry_set_width_chars (GTK_ENTRY (entrycomando), 0);
bsimular = gtk_button_new ();
gtk_widget_set_name (bsimular, "bsimular");
gtk_widget_show (bsimular);
gtk_box_pack_start (GTK_BOX (vbox3), bsimular, FALSE, FALSE, 0);
alignment6 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment6, "alignment6");
gtk_widget_show (alignment6);
gtk_container_add (GTK_CONTAINER (bsimular), alignment6);
hbox3 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox3, "hbox3");
gtk_widget_show (hbox3);
gtk_container_add (GTK_CONTAINER (alignment6), hbox3);
image1 = gtk_image_new_from_stock ("gtk-network", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image1, "image1");
gtk_widget_show (image1);
gtk_box_pack_start (GTK_BOX (hbox3), image1, FALSE, FALSE, 0);
label9 = gtk_label_new_with_mnemonic (_("SIMULAR"));
gtk_widget_set_name (label9, "label9");
gtk_widget_show (label9);
gtk_box_pack_start (GTK_BOX (hbox3), label9, FALSE, FALSE, 0);
bejecutar = gtk_button_new ();
gtk_widget_set_name (bejecutar, "bejecutar");
gtk_widget_show (bejecutar);
gtk_box_pack_start (GTK_BOX (vbox3), bejecutar, TRUE, FALSE, 0);
alignment7 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment7, "alignment7");
gtk_widget_show (alignment7);
gtk_container_add (GTK_CONTAINER (bejecutar), alignment7);
hbox4 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox4, "hbox4");
gtk_widget_show (hbox4);
gtk_container_add (GTK_CONTAINER (alignment7), hbox4);
image2 = gtk_image_new_from_stock ("gtk-yes", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image2, "image2");
gtk_widget_show (image2);
gtk_box_pack_start (GTK_BOX (hbox4), image2, FALSE, FALSE, 0);
label10 = gtk_label_new_with_mnemonic (_("EJECUTAR"));
gtk_widget_set_name (label10, "label10");
gtk_widget_show (label10);
gtk_box_pack_start (GTK_BOX (hbox4), label10, FALSE, FALSE, 0);
122
label4 = gtk_label_new (_("<b>Linea de Comandos</b>"));
gtk_widget_set_name (label4, "label4");
gtk_widget_show (label4);
gtk_frame_set_label_widget (GTK_FRAME (frame4), label4);
gtk_label_set_use_markup (GTK_LABEL (label4), TRUE);
frame5 = gtk_frame_new (NULL);
gtk_widget_set_name (frame5, "frame5");
gtk_widget_show (frame5);
gtk_box_pack_start (GTK_BOX (hbox2), frame5, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (frame5), 3);
gtk_frame_set_shadow_type (GTK_FRAME (frame5), GTK_SHADOW_OUT);
alignment5 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_set_name (alignment5, "alignment5");
gtk_widget_show (alignment5);
gtk_container_add (GTK_CONTAINER (frame5), alignment5);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment5), 0, 0, 12, 0);
vbox4 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox4, "vbox4");
gtk_widget_show (vbox4);
gtk_container_add (GTK_CONTAINER (alignment5), vbox4);
entryrutina = gtk_entry_new ();
gtk_widget_set_name (entryrutina, "entryrutina");
gtk_entry_set_max_length(GTK_ENTRY (entryrutina), 20);
gtk_widget_show (entryrutina);
gtk_box_pack_start (GTK_BOX (vbox4), entryrutina, TRUE, FALSE, 0);
gtk_entry_set_text (GTK_ENTRY (entryrutina), _("Nombre Rutina"));
brutina1 = gtk_button_new ();
gtk_widget_set_name (brutina1, "brutina1");
gtk_widget_show (brutina1);
gtk_box_pack_start (GTK_BOX (vbox4), brutina1, TRUE, FALSE, 0);
alignment15 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment15, "alignment15");
gtk_widget_show (alignment15);
gtk_container_add (GTK_CONTAINER (brutina1), alignment15);
hbox11 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox11, "hbox11");
gtk_widget_show (hbox11);
gtk_container_add (GTK_CONTAINER (alignment15), hbox11);
image8 = gtk_image_new_from_stock ("gtk-save-as", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image8, "image8");
gtk_widget_show (image8);
gtk_box_pack_start (GTK_BOX (hbox11), image8, FALSE, FALSE, 0);
label18 = gtk_label_new_with_mnemonic (_("Crear Rutina"));
gtk_widget_set_name (label18, "label18");
gtk_widget_show (label18);
gtk_box_pack_start (GTK_BOX (hbox11), label18, FALSE, FALSE, 0);
brutina2 = gtk_button_new ();
gtk_widget_set_name (brutina2, "brutina2");
gtk_widget_show (brutina2);
gtk_box_pack_start (GTK_BOX (vbox4), brutina2, TRUE, FALSE, 0);
alignment16 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment16, "alignment16");
gtk_widget_show (alignment16);
123
gtk_container_add (GTK_CONTAINER (brutina2), alignment16);
hbox12 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox12, "hbox12");
gtk_widget_show (hbox12);
gtk_container_add (GTK_CONTAINER (alignment16), hbox12);
image9 = gtk_image_new_from_stock ("gtk-open", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image9, "image9");
gtk_widget_show (image9);
gtk_box_pack_start (GTK_BOX (hbox12), image9, FALSE, FALSE, 0);
label19 = gtk_label_new_with_mnemonic (_("Cargar Rutina"));
gtk_widget_set_name (label19, "label19");
gtk_widget_show (label19);
gtk_box_pack_start (GTK_BOX (hbox12), label19, FALSE, FALSE, 0);
brutina3 = gtk_button_new ();
gtk_widget_set_name (brutina3, "brutina3");
gtk_widget_show (brutina3);
gtk_box_pack_start (GTK_BOX (vbox4), brutina3, TRUE, FALSE, 0);
alignment17 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment17, "alignment17");
gtk_widget_show (alignment17);
gtk_container_add (GTK_CONTAINER (brutina3), alignment17);
hbox13 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox13, "hbox13");
gtk_widget_show (hbox13);
gtk_container_add (GTK_CONTAINER (alignment17), hbox13);
image10 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image10, "image10");
gtk_widget_show (image10);
gtk_box_pack_start (GTK_BOX (hbox13), image10, FALSE, FALSE, 0);
label20 = gtk_label_new_with_mnemonic (_("Ejecutar Rutina"));
gtk_widget_set_name (label20, "label20");
gtk_widget_show (label20);
gtk_box_pack_start (GTK_BOX (hbox13), label20, FALSE, FALSE, 0);
label5 = gtk_label_new (_("<b>Rutinas</b>"));
gtk_widget_set_name (label5, "label5");
gtk_widget_show (label5);
gtk_frame_set_label_widget (GTK_FRAME (frame5), label5);
gtk_label_set_use_markup (GTK_LABEL (label5), TRUE);
/*Ligar las funciones de respuesta específicas a cada boton de la ventana*/
g_signal_connect ((gpointer) bdrive, "clicked",
G_CALLBACK (on_bdrive_clicked),
NULL);
g_signal_connect ((gpointer) bsimular, "clicked",
G_CALLBACK (on_bsimular_clicked),
NULL);
g_signal_connect ((gpointer) bejecutar, "clicked",
G_CALLBACK (on_bejecutar_clicked),
NULL);
g_signal_connect ((gpointer) bejecutar, "clicked",
G_CALLBACK (on_bejecutar_clicked),
NULL);
g_signal_connect ((gpointer) brutina1, "clicked",
G_CALLBACK (on_brutina1_clicked),
NULL);
124
g_signal_connect ((gpointer) brutina2, "clicked",
G_CALLBACK (on_brutina2clicked),
NULL);
g_signal_connect ((gpointer) brutina3, "clicked",
G_CALLBACK (on_brutina3_clicked),
NULL);
/* destroy - Se desea destruir la ventana, aqui es donde se
*
debe llevar a cabo la limpieza necesaria */
g_signal_connect((gpointer) window1, "destroy",
G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (window1));
return window1;
}
/*Función de creación de ventana 2*/
GtkWidget*
create_window2 (void)
{
/*Objetos de la ventana 2*/
GtkWidget *window2;
GtkWidget *frame6;
GtkWidget *alignment18;
GtkWidget *vbox8;
GtkWidget *hbox14;
GtkWidget *bsim;
GtkWidget *alignment19;
GtkWidget *hbox15;
GtkWidget *image11;
GtkWidget *label24;
GtkWidget *bquit;
GtkWidget *alignment20;
GtkWidget *hbox16;
GtkWidget *image12;
GtkWidget *label25;
GtkWidget *label23;
GtkWidget *bacerca;
GtkWidget *alignment21;
GtkWidget *hbox17;
GtkWidget *image13;
GtkWidget *label26;
/* Atributos del area GTKGL */
GdkGLConfig *glconfig;
glconfig = gdk_gl_config_new_by_mode(GDK_GL_MODE_RGB |
GDK_GL_MODE_DEPTH |
GDK_GL_MODE_DOUBLE);
window2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window2, "window2");
gtk_window_set_title (GTK_WINDOW (window2), _("Mundo Virtual"));
frame6 = gtk_frame_new (NULL);
gtk_widget_set_name (frame6, "frame6");
gtk_widget_show (frame6);
gtk_container_add (GTK_CONTAINER (window2), frame6);
gtk_frame_set_shadow_type (GTK_FRAME (frame6), GTK_SHADOW_NONE);
alignment18 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_set_name (alignment18, "alignment18");
gtk_widget_show (alignment18);
gtk_container_add (GTK_CONTAINER (frame6), alignment18);
125
vbox8 = gtk_vbox_new (FALSE, 2);
gtk_widget_set_name (vbox8, "vbox8");
gtk_widget_show (vbox8);
gtk_container_add (GTK_CONTAINER (alignment18), vbox8);
gtk_container_set_border_width (GTK_CONTAINER (vbox8), 2);
/*Creación del area de despliegue OpenGL*/
glarea1 = gtk_drawing_area_new();
gtk_widget_set_gl_capability(GTK_WIDGET (glarea1),
glconfig,
NULL,
TRUE,
GDK_GL_RGBA_TYPE);
gtk_widget_set_name (glarea1, "glarea1");
/*Eventos habilitados en el area OpenGL*/
gtk_widget_set_events(glarea1,
GDK_EXPOSURE_MASK|
GDK_BUTTON_PRESS_MASK|
GDK_KEY_PRESS_MASK|
GDK_POINTER_MOTION_MASK|
GDK_POINTER_MOTION_HINT_MASK);
gtk_widget_show (glarea1);
gtk_box_pack_start (GTK_BOX (vbox8), glarea1, TRUE, TRUE, 0);
gtk_widget_set_size_request (glarea1, 350, 350);
hbox14 = gtk_hbox_new (FALSE, 0);
gtk_widget_set_name (hbox14, "hbox14");
gtk_widget_show (hbox14);
gtk_box_pack_start (GTK_BOX (vbox8), hbox14, TRUE, TRUE, 0);
bsim = gtk_button_new ();
gtk_widget_set_name (bsim, "bsim");
gtk_widget_show (bsim);
gtk_box_pack_start (GTK_BOX (hbox14), bsim, TRUE, TRUE, 0);
alignment19 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment19, "alignment19");
gtk_widget_show (alignment19);
gtk_container_add (GTK_CONTAINER (bsim), alignment19);
hbox15 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox15, "hbox15");
gtk_widget_show (hbox15);
gtk_container_add (GTK_CONTAINER (alignment19), hbox15);
image11 = gtk_image_new_from_stock ("gtk-media-play", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image11, "image11");
gtk_widget_show (image11);
gtk_box_pack_start (GTK_BOX (hbox15), image11, FALSE, FALSE, 0);
label24 = gtk_label_new_with_mnemonic (_("SIMULAR"));
gtk_widget_set_name (label24, "label24");
gtk_widget_show (label24);
gtk_box_pack_start (GTK_BOX (hbox15), label24, FALSE, FALSE, 0);
gtk_label_set_justify (GTK_LABEL (label24), GTK_JUSTIFY_CENTER);
bquit = gtk_button_new ();
gtk_widget_set_name (bquit, "bquit");
gtk_widget_show (bquit);
gtk_box_pack_start (GTK_BOX (hbox14), bquit, FALSE, FALSE, 0);
gtk_widget_set_size_request (bquit, 133, -1);
alignment20 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment20, "alignment20");
126
gtk_widget_show (alignment20);
gtk_container_add (GTK_CONTAINER (bquit), alignment20);
hbox16 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox16, "hbox16");
gtk_widget_show (hbox16);
gtk_container_add (GTK_CONTAINER (alignment20), hbox16);
image12 = gtk_image_new_from_stock ("gtk-cancel", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image12, "image12");
gtk_widget_show (image12);
gtk_box_pack_start (GTK_BOX (hbox16), image12, FALSE, FALSE, 0);
label25 = gtk_label_new_with_mnemonic (_("QUIT"));
gtk_widget_set_name (label25, "label25");
gtk_widget_show (label25);
gtk_box_pack_start (GTK_BOX (hbox16), label25, FALSE, FALSE, 0);
bacerca = gtk_button_new ();
gtk_widget_set_name (bquit, "bacerca");
gtk_widget_show (bacerca);
gtk_box_pack_start (GTK_BOX (hbox14), bacerca, FALSE, FALSE, 0);
alignment21 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment20, "alignment21");
gtk_widget_show (alignment21);
gtk_container_add (GTK_CONTAINER (bacerca), alignment21);
hbox17 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox16, "hbox17");
gtk_widget_show (hbox17);
gtk_container_add (GTK_CONTAINER (alignment21), hbox17);
image13 = gtk_image_new_from_stock ("gtk-edit", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image13, "image13");
gtk_widget_show (image13);
gtk_box_pack_start (GTK_BOX (hbox17), image13, FALSE, FALSE, 0);
label26 = gtk_label_new_with_mnemonic (_("Acerca de (about)"));
gtk_widget_set_name (label26, "label26");
gtk_widget_show (label26);
gtk_box_pack_start (GTK_BOX (hbox17), label26, FALSE, FALSE, 0);
label23 = gtk_label_new (_("<b>Simulador</b>"));
gtk_widget_set_name (label23, "label23");
gtk_widget_show (label23);
gtk_frame_set_label_widget (GTK_FRAME (frame6), label23);
gtk_label_set_use_markup (GTK_LABEL (label23), TRUE);
g_signal_connect ((gpointer) bsim, "clicked",
G_CALLBACK (on_bsim_clicked), NULL);
g_signal_connect((gpointer) bquit, "clicked",
G_CALLBACK(gtk_main_quit), NULL);
/* Esta función se encarga de monitorear cuando se presiona un botón dentro del
* area GTKGL.*/
g_signal_connect ((gpointer) glarea1, "button_press_event",
G_CALLBACK(on_glarea_button_press_event), NULL);
g_signal_connect((gpointer) bacerca, "clicked",
G_CALLBACK(on_bacerca_clicked), NULL);
/* Esta función se encarga de monitorear cuando se presiona una tecla
127
* y la ventana OpenGL esté en frente*/
g_signal_connect_swapped (G_OBJECT (window2), "key_press_event",
G_CALLBACK (on_glarea_key_press_event), glarea1);
/* motion_notify_event - El ratón se esta moviendo dentro de la ventana */
g_signal_connect ((gpointer) glarea1, "motion_notify_event",
G_CALLBACK(on_glarea_motion_notify_event), NULL);
/* expose_event - La ventana se ha expuesto y el contenido debe de ser redibujado */
g_signal_connect ((gpointer) glarea1, "expose_event",
G_CALLBACK(on_glarea_expose_event), NULL);
/* configure_event - Se ha cambiado el tamaño de la ventana.*/
g_signal_connect ((gpointer) glarea1, "configure_event",
G_CALLBACK(on_glarea_configure_event), NULL);
/* realize - Se ha creado la ventana, aqui se introducen las */
/*
rutinas de inicialización. */
g_signal_connect ((gpointer) glarea1, "realize",
G_CALLBACK(glarea_init), NULL);
/* destroy - Se desea destruir la ventana, aqui es donde se
*
debe llevar a cabo la limpieza necesaria */
g_signal_connect((gpointer) glarea1, "destroy",
G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (window2));
return window2;
}
/*Función de creación de la ventana 3*/
GtkWidget*
create_window3 (void)
{
/*Objetos de la ventana 3*/
GtkWidget *window3;
GtkWidget *hbox16;
GtkWidget *frame2;
GtkWidget *alignment2;
GtkWidget *hbox5;
GtkWidget *vbox5;
GtkWidget *label12;
GtkWidget *bposicion1;
GtkWidget *alignment10;
GtkWidget *hbox6;
GtkWidget *image3;
GtkWidget *label13;
GtkWidget *bposicion2;
GtkWidget *alignment11;
GtkWidget *hbox7;
GtkWidget *image4;
GtkWidget *label14;
GtkWidget *bposicion3;
GtkWidget *alignment12;
GtkWidget *hbox8;
GtkWidget *image5;
GtkWidget *label15;
GtkWidget *bposicion4;
GtkWidget *alignment14;
GtkWidget *hbox10;
GtkWidget *image7;
GtkWidget *label17;
GtkWidget *bposicion5;
GtkWidget *alignment13;
128
GtkWidget *hbox9;
GtkWidget *image6;
GtkWidget *label16;
GtkWidget *vseparator2;
GtkWidget *vbox6;
GtkWidget *label21;
GtkWidget *vseparator1;
GtkWidget *vbox7;
GtkWidget *label22;
GtkWidget *bsimulador;
GtkWidget *label2;
GtkWidget *hbox2;
window3= gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_name (window3, "window3");
gtk_container_set_border_width (GTK_CONTAINER (window3), 3);
gtk_window_set_title (GTK_WINDOW (window3), _("Controles del Simulador (Controles de Configuración)"));
hbox16 = gtk_hbox_new (FALSE, 0);
gtk_widget_set_name (hbox16, "hbox16");
gtk_widget_show (hbox16);
gtk_container_add (GTK_CONTAINER (window3), hbox16);
frame2 = gtk_frame_new (NULL);
gtk_widget_set_name (frame2, "frame2");
gtk_widget_show (frame2);
gtk_box_pack_start (GTK_BOX (hbox16), frame2, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (frame2), 3);
gtk_frame_set_shadow_type (GTK_FRAME (frame2), GTK_SHADOW_OUT);
alignment2 = gtk_alignment_new (0.5, 0.5, 1, 1);
gtk_widget_set_name (alignment2, "alignment2");
gtk_widget_show (alignment2);
gtk_container_add (GTK_CONTAINER (frame2), alignment2);
gtk_alignment_set_padding (GTK_ALIGNMENT (alignment2), 0, 0, 12, 0);
hbox5 = gtk_hbox_new (FALSE, 0);
gtk_widget_set_name (hbox5, "hbox5");
gtk_widget_show (hbox5);
gtk_container_add (GTK_CONTAINER (alignment2), hbox5);
vbox5 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox5, "vbox5");
gtk_widget_show (vbox5);
gtk_box_pack_start (GTK_BOX (hbox5), vbox5, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox5), 13);
label12 = gtk_label_new (_("POSICION"));
gtk_widget_set_name (label12, "label12");
gtk_widget_show (label12);
gtk_box_pack_start (GTK_BOX (vbox5), label12, TRUE, FALSE, 0);
bposicion1 = gtk_button_new ();
gtk_widget_set_name (bposicion1, "bposicion1");
gtk_widget_show (bposicion1);
gtk_box_pack_start (GTK_BOX (vbox5), bposicion1, TRUE, FALSE, 0);
alignment10 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment10, "alignment10");
gtk_widget_show (alignment10);
gtk_container_add (GTK_CONTAINER (bposicion1), alignment10);
hbox6 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox6, "hbox6");
129
gtk_widget_show (hbox6);
gtk_container_add (GTK_CONTAINER (alignment10), hbox6);
image3 = gtk_image_new_from_stock ("gtk-redo", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image3, "image3");
gtk_widget_show (image3);
gtk_box_pack_start (GTK_BOX (hbox6), image3, FALSE, FALSE, 0);
label13 = gtk_label_new_with_mnemonic (_("APILAR"));
gtk_widget_set_name (label13, "label13");
gtk_widget_show (label13);
gtk_box_pack_start (GTK_BOX (hbox6), label13, FALSE, FALSE, 0);
bposicion2 = gtk_button_new ();
gtk_widget_set_name (bposicion2, "bposicion2");
gtk_widget_show (bposicion2);
gtk_box_pack_start (GTK_BOX (vbox5), bposicion2, TRUE, FALSE, 0);
alignment11 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment11, "alignment11");
gtk_widget_show (alignment11);
gtk_container_add (GTK_CONTAINER (bposicion2), alignment11);
hbox7 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox7, "hbox7");
gtk_widget_show (hbox7);
gtk_container_add (GTK_CONTAINER (alignment11), hbox7);
image4 = gtk_image_new_from_stock ("gtk-undo", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image4, "image4");
gtk_widget_show (image4);
gtk_box_pack_start (GTK_BOX (hbox7), image4, FALSE, FALSE, 0);
label14 = gtk_label_new_with_mnemonic (_("DESAPILAR"));
gtk_widget_set_name (label14, "label14");
gtk_widget_show (label14);
gtk_box_pack_start (GTK_BOX (hbox7), label14, FALSE, FALSE, 0);
bposicion3 = gtk_button_new ();
gtk_widget_set_name (bposicion3, "bposicion3");
gtk_widget_show (bposicion3);
gtk_box_pack_start (GTK_BOX (vbox5), bposicion3, TRUE, FALSE, 0);
alignment12 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment12, "alignment12");
gtk_widget_show (alignment12);
gtk_container_add (GTK_CONTAINER (bposicion3), alignment12);
hbox8 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox8, "hbox8");
gtk_widget_show (hbox8);
gtk_container_add (GTK_CONTAINER (alignment12), hbox8);
image5 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image5, "image5");
gtk_widget_show (image5);
gtk_box_pack_start (GTK_BOX (hbox8), image5, FALSE, FALSE, 0);
label15 = gtk_label_new_with_mnemonic (_("Reiniciar Pila"));
gtk_widget_set_name (label15, "label15");
gtk_widget_show (label15);
gtk_box_pack_start (GTK_BOX (hbox8), label15, FALSE, FALSE, 0);
bposicion4 = gtk_button_new ();
130
gtk_widget_set_name (bposicion4, "bposicion4");
gtk_widget_show (bposicion4);
gtk_box_pack_start (GTK_BOX (vbox5), bposicion4, TRUE, FALSE, 0);
alignment14 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment14, "alignment14");
gtk_widget_show (alignment14);
gtk_container_add (GTK_CONTAINER (bposicion4), alignment14);
hbox10 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox10, "hbox10");
gtk_widget_show (hbox10);
gtk_container_add (GTK_CONTAINER (alignment14), hbox10);
image7 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image7, "image7");
gtk_widget_show (image7);
gtk_box_pack_start (GTK_BOX (hbox10), image7, FALSE, FALSE, 0);
label17 = gtk_label_new_with_mnemonic (_("Reiniciar Robot"));
gtk_widget_set_name (label17, "label17");
gtk_widget_show (label17);
gtk_box_pack_start (GTK_BOX (hbox10), label17, FALSE, FALSE, 0);
bposicion5 = gtk_button_new ();
gtk_widget_set_name (bposicion5, "bposicion5");
gtk_widget_show (bposicion5);
gtk_box_pack_start (GTK_BOX (vbox5), bposicion5, TRUE, FALSE, 0);
alignment13 = gtk_alignment_new (0.5, 0.5, 0, 0);
gtk_widget_set_name (alignment13, "alignment13");
gtk_widget_show (alignment13);
gtk_container_add (GTK_CONTAINER (bposicion5), alignment13);
hbox9 = gtk_hbox_new (FALSE, 2);
gtk_widget_set_name (hbox9, "hbox9");
gtk_widget_show (hbox9);
gtk_container_add (GTK_CONTAINER (alignment13), hbox9);
image6 = gtk_image_new_from_stock ("gtk-refresh", GTK_ICON_SIZE_BUTTON);
gtk_widget_set_name (image6, "image6");
gtk_widget_show (image6);
gtk_box_pack_start (GTK_BOX (hbox9), image6, FALSE, FALSE, 0);
label16 = gtk_label_new_with_mnemonic (_("Reiniciar C\303\241mara"));
gtk_widget_set_name (label16, "label16");
gtk_widget_show (label16);
gtk_box_pack_start (GTK_BOX (hbox9), label16, FALSE, FALSE, 0);
vseparator2 = gtk_vseparator_new ();
gtk_widget_set_name (vseparator2, "vseparator2");
gtk_widget_show (vseparator2);
gtk_box_pack_start (GTK_BOX (hbox5), vseparator2, TRUE, TRUE, 0);
vbox6 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox6, "vbox6");
gtk_widget_show (vbox6);
gtk_box_pack_start (GTK_BOX (hbox5), vbox6, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox6), 3);
label21 = gtk_label_new (_("VISTA"));
gtk_widget_set_name (label21, "label21");
gtk_widget_show (label21);
gtk_box_pack_start (GTK_BOX (vbox6), label21, FALSE, FALSE, 18);
131
bgrid = gtk_toggle_button_new_with_mnemonic (_("GRID"));
gtk_widget_set_name (bgrid, "bgrid");
gtk_widget_show (bgrid);
gtk_box_pack_start (GTK_BOX (vbox6), bgrid, TRUE, FALSE, 0);
btexturas = gtk_toggle_button_new_with_mnemonic (_("Fondo Blanco"));
gtk_widget_set_name (btexturas, "btexturas");
gtk_widget_show (btexturas);
gtk_box_pack_start (GTK_BOX (vbox6), btexturas, TRUE, FALSE, 0);
vseparator1 = gtk_vseparator_new ();
gtk_widget_set_name (vseparator1, "vseparator1");
gtk_widget_show (vseparator1);
gtk_box_pack_start (GTK_BOX (hbox5), vseparator1, TRUE, TRUE, 0);
vbox7 = gtk_vbox_new (FALSE, 0);
gtk_widget_set_name (vbox7, "vbox7");
gtk_widget_show (vbox7);
gtk_box_pack_start (GTK_BOX (hbox5), vbox7, TRUE, TRUE, 0);
gtk_container_set_border_width (GTK_CONTAINER (vbox7), 3);
label22 = gtk_label_new (_("SIMULACION"));
gtk_widget_set_name (label22, "label22");
gtk_widget_show (label22);
gtk_box_pack_start (GTK_BOX (vbox7), label22, TRUE, FALSE, 0);
bcheckmax = gtk_toggle_button_new_with_mnemonic (_("Lim. Angulos"));
gtk_widget_set_name (bcheckmax, "bcheckmax");
gtk_widget_show (bcheckmax);
gtk_box_pack_start (GTK_BOX (vbox7), bcheckmax, TRUE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcheckmax), TRUE);
bcolision1 = gtk_toggle_button_new_with_mnemonic (_("1\302\260 Colisi\303\263n"));
gtk_widget_set_name (bcolision1, "bcolision1");
gtk_widget_show (bcolision1);
gtk_box_pack_start (GTK_BOX (vbox7), bcolision1, TRUE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bcolision1), TRUE);
bsimulador = gtk_toggle_button_new_with_mnemonic (_("SIM ON"));
gtk_widget_set_name (bsimulador, "bsimulador");
gtk_widget_show (bsimulador);
gtk_box_pack_start (GTK_BOX (vbox7), bsimulador, TRUE, FALSE, 0);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (bsimulador), TRUE);
label2 = gtk_label_new (_("<b>OPCIONES DEL SIMULADOR</b>"));
gtk_widget_set_name (label2, "label2");
gtk_widget_show (label2);
gtk_frame_set_label_widget (GTK_FRAME (frame2), label2);
gtk_label_set_use_markup (GTK_LABEL (label2), TRUE);
gtk_label_set_justify (GTK_LABEL (label2), GTK_JUSTIFY_CENTER);
/*Conección de la funciones de respuestas a los botones la ventana 3*/
g_signal_connect ((gpointer) bposicion1, "clicked",
G_CALLBACK (on_bposicion1_clicked),
NULL);
g_signal_connect ((gpointer) bposicion2, "clicked",
G_CALLBACK (on_bposicion2_clicked),
NULL);
g_signal_connect ((gpointer) bposicion3, "clicked",
G_CALLBACK (on_bposicion3_clicked),
NULL);
g_signal_connect ((gpointer) bposicion4, "clicked",
G_CALLBACK (on_bposicion4_clicked),
132
NULL);
g_signal_connect ((gpointer) bposicion5, "clicked",
G_CALLBACK (on_bposicion5_clicked),
NULL);
g_signal_connect ((gpointer) bposicion5, "clicked",
G_CALLBACK (on_bposicion5_clicked),
NULL);
g_signal_connect ((gpointer) bgrid, "toggled",
G_CALLBACK (on_bgrid_toggled),
NULL);
g_signal_connect ((gpointer) btexturas, "toggled",
G_CALLBACK (on_btexturas_toggled),
NULL);
g_signal_connect ((gpointer) bcheckmax, "toggled",
G_CALLBACK (on_bcheckmax_toggled),
NULL);
g_signal_connect ((gpointer) bcolision1, "toggled",
G_CALLBACK (on_bcolision1_toggled),
NULL);
g_signal_connect ((gpointer) bsimulador, "toggled",
G_CALLBACK (on_bsimulador_toggled),
NULL);
/* destroy - Se desea destruir la ventana, aqui es donde se
*
debe llevar a cabo la limpieza necesaria */
g_signal_connect((gpointer) window3, "destroy",
G_CALLBACK(gtk_widget_destroy), GTK_OBJECT (window3));
return window3;
}
/*************************************************************************************************************/
rutina.c: Funciones de creación, edición y ejecución de rutinas desde linea de comandos
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* rutina.c: funciones de creación, edición, carga y ejecución de rutinas desde la linea de comandos
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "rutina.h"
int puntpos=0;
133
/*MATRIZ DE PILA POSICIONES*/
/*Espacio de memoria en el que se apilan las posiciones*/
float pilaposiciones[60][6];
/*Velocidades Maximas de giro de cada articulación*/
int velocmaximas[6]={356, 356, 296, 409,800, 1125};
//.............APILAR/DESAPILAR POSICION ACTUAL...................//
void push_posicion(void){
pilaposiciones[puntpos][0]=angulo1;
pilaposiciones[puntpos][1]=angulo2;
pilaposiciones[puntpos][2]=angulo3;
pilaposiciones[puntpos][3]=angulo4;
pilaposiciones[puntpos][4]=angulo5;
pilaposiciones[puntpos][5]=angulo6;
if(puntpos < 60){
puntpos++; //aumenta el puntero de la pila
}
}
void pull_posicion(void){
if(puntpos > 0){ puntpos--;}
angulo1=pilaposiciones[puntpos][0];
angulo2=pilaposiciones[puntpos][1];
angulo3=pilaposiciones[puntpos][2];
angulo4=pilaposiciones[puntpos][3];
angulo5=pilaposiciones[puntpos][4];
angulo6=pilaposiciones[puntpos][5];
}
/*Reiniciar la pila*/
void reiniciar_pila(void){
puntpos=0;
}
//.............VELOCIDADES DE CADA ARTICULACION.........//
/*Se establece la velocidad del movimientos, dependiedo del
* porcentaje pasado y de la velocidad máxima de la articulación*/
void set_velocidad(int porciento, int numart){
velocidad=(velocmaximas[numart-1])*porciento/100;
if(velocidad==0){
velocidad=10;
}
}
//.............CREACION DE UNA RUTINA...................//
/* Utilizando la terminal (linea de comandos)*/
void crear_rutina(void){
int nru;
char basura[10];
/*Se pide el nombre la rutina a crear*/
printf("\n\n----CREACIÓN DE UNA RUTINA (Presione ENTER)---");
fflush(stdin);
scanf("%c",&basura);
printf("\nNumero de movimientos:_");
scanf("%f", &rutina[0][0]);
if(rutina[0][0] > 60){
rutina[0][0]=60;
}
for(nru=1; nru<=rutina[0][0];nru++){
/*Se pide caracterizar los movimientos de la rutina*/
printf("Movimiento #%i ->JOINT:_", nru);
scanf("%f", &rutina[nru][0]);
134
printf("Movimiento #%i ->ANGULO:_", nru);
scanf("%f", &rutina[nru][1]);
printf("Movimiento #%i ->VELOCIDAD:_", nru);
scanf("%f", &rutina[nru][2]);
}
printf("\nUtiliza la tecla >> F8 << para ejecutar la rutina");
fflush(stdout);
}
/*.............CRONOMETRO DE ESPERA.......................*/
/*Regula la velocidad de dibujo de los cuadro de la escena (frames)*/
void esperar(float segundos)
{
clock_t tiempoespera;
tiempoespera = clock () + segundos * CLK_TCK ;
while (clock() < tiempoespera) {}
}
//.............MOVER CADA ARTICULACIÓN.........//
/*mueva cada articulación hasta alcanzar el agnulo deseado*/
void mover_joint(float* ang, float delta, float vel){
float angfinal;
float veldelta=velocidad/50;
int finwhile;
int ya;
ya=0;
finwhile=0;
choque=0;
angmax=0;
push_posicion();
angfinal= (*ang) + delta;
//apila la posición actual;
if(delta > 0){
/*realizar giro positivo*/
while((*ang) < angfinal && finwhile<1){
dibujar();
(*ang)+=veldelta;
if(checkcolision==1 ){
if(choque==1){
/*si se da choque y esta habilitada la opción de terminar al
* primer choque, se manda señal de terminar*/
finwhile=1;
(*ang)-=veldelta;
ya=1;
}
}
if(checkmax==1 ){
if(angmax==1 && ya!=1){
/*si se llega a angulo maximo y esta habilitada la opción de limitar
angulos de giro,
* se manda señal de terminar*/
finwhile=1;
(*ang)-=veldelta;
}
}
//esperar(1);
}
if(finwhile!=1){
(*ang)=angfinal;
}
/*dibujar el modelo en la nueva posición*/
dibujar();
}else if(delta < 0){
135
/*realizar giro negativo*/
while((*ang) > angfinal && finwhile<1){
dibujar();
(*ang)-=veldelta;
if(checkcolision==1 ){
if(choque==1){
finwhile=1;
(*ang)+=veldelta;
ya=1;
}
}
if(checkmax==1 ){
if(angmax==1 && ya!=1){
finwhile=1;
(*ang)+=veldelta;
}
}
//esperar(1);
}
if(finwhile!=1){
(*ang)=angfinal;
}
/*dibujar el modelo en la nueva posición*/
dibujar();
}
}
void selec_mover_joint(void){
/*Seleccionar cual articulación es la que se debe mover, segun la orden del usuario*/
float* puntang;
if(joint==1){
puntang = &angulo1;
mover_joint(puntang, angulodelta, velocidad);
}else if(joint==2){
puntang = &angulo2;
mover_joint(puntang, -1*angulodelta, velocidad);
}else if(joint==3){
puntang = &angulo3;
mover_joint(puntang, -1*angulodelta, velocidad);
}else if(joint==4){
puntang = &angulo4;
mover_joint(puntang, angulodelta, velocidad);
}else if(joint==5){
puntang = &angulo5;
mover_joint(puntang, -1*angulodelta, velocidad);
}else if(joint==6){
puntang = &angulo6;
mover_joint(puntang, angulodelta, velocidad);
}
}
//.............EJECUTAR RUTINA..............//
/*Ejecutar la rutina cargada en memoria*/
void ejecutar_rutina(void){
int nm;
if(rutina[0][0]!=0){
for(nm=1; nm<=rutina[0][0]; nm++){
joint=rutina[nm][0];
136
angulodelta=rutina[nm][1];
set_velocidad((int)rutina[nm][2], (int)joint);
selec_mover_joint();
}
}
}
//..............ABRIR y LEER ARCHIVO DE RUTINA................//
int abrir_rutina(void){
FILE *archivo1;
char nombrefile[15];
char letra;
char articult[3];
char angt[7];
char veloct[7];
char linea[15];
char basura[10];
int p1, p2, p3;
//punteros
/*Pide nombre de archivo que se desea cargar*/
printf("\nLEER ARCHIVO DE RUTINA (Presione ENTER)\n");
fflush(stdin);
scanf("%c", &basura);
printf("\nEscriba el nombre del archivo que desea abrir:_");
scanf("%s", &nombrefile);
/*abre el archivo y lee la información*/
archivo1=fopen(nombrefile,"r");
if (!archivo1){
printf("\n-ERROR-->No se pudo abrir el archivo %s", nombrefile);
fflush(stdout);
return 1;
}
p1=0;
rutina[0][0]=0;
do{
letra=fgetc(archivo1);
if(letra=='M'){
if(p1<60){
p1++;
rutina[0][0]++;
}
}else if(letra=='A'){
p2=0;
while(letra!=','){
letra=fgetc(archivo1);
angt[p2]=letra;
p2++;
}
rutina[p1][1]=atof(angt);
}else if(letra=='J'){
p2=0;
while(letra!=','){
letra=fgetc(archivo1);
articult[p2]=letra;
p2++;
}
rutina[p1][0]=atof(articult);
}else if(letra=='V'){
p2=0;
while(letra!=','){
letra=fgetc(archivo1);
veloct[p2]=letra;
137
p2++;
}
rutina[p1][2]=atof(veloct);
}
}while (letra!='f');
fclose(archivo1);
}
//................SALVAR RUTINA A ARCHIVO...............................//
int salvar_rutina(void){
FILE *archivo1;
char nombrefile[15];
char basura[10];
int p1;
//punteros
/*Pide nombre de archivo en que se desea guardar la rutina*/
printf("\nSALVAR RUTINA (Presione ENTER)\n");
fflush(stdin);
scanf("%c", &basura);
printf("\nEscriba el nombre del archivo que desea crear:_");
scanf("%s", &nombrefile);
archivo1=fopen(nombrefile,"w");
if (!archivo1){
printf("\n-ERROR-->No se pudo abrir el archivo %s", nombrefile);
return 1;
}
p1=0;
fprintf(archivo1,"inicio\n");
for(p1=1;p1<=rutina[0][0];p1++){
/*imprime cada movimiento de la rutina de la memoria, en una linea nueva del archivo de texto*/
fprintf(archivo1,"M%i, J %d, A %3.1f, V %d,\n",p1,(int)rutina[p1][0],rutina[p1][1],(int)rutina[p1][2]);
}
fprintf(archivo1,"fin");
fclose(archivo1);
}
/*************************************************************************************************************/
ventanagl.c: Funciones de configuración de la ventana OpenGL
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* ventanagl.c: funciones de configuración de la ventana de despliegue OpenGL
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
138
#include "ventanagl.h"
#define SLICES 30
#define STACKS 30
/*Posición de las luces*/
GLfloat position0 [] = { 0.0, 0.0, 600.0, 1.0 };
/*Función reinicia los valores de posición y velocidad de la camara*/
void valores_iniciales_camara(void){
printf("\nObjeto Seleccionado--> CAMARA");
camara_x = 0;
camara_y = 0;
camara_z = 300;
ancho_x=0;
altura_y =0;
angulo_yr=0;
angulo_xr=0;
zoom_z = 160;
aument_cam = 1;
mov_cam = 1;
}
/* Inicio de la ventana OpenGL --> (luces, materiales, buffers)*/
gboolean inicio(void){
GdkGLContext *glcontext = gtk_widget_get_gl_context (glarea1);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (glarea1);
/*** OpenGL BEGIN ***/
if (!gdk_gl_drawable_make_current(gldrawable, glcontext)){
printf("\nError al buscar area OpenGL");
return FALSE;
}
/*Vectores que definen el tipo de material*/
GLfloat ambient [] = { 0.1, 0.1, 0.1, 0.0 };
GLfloat specular []= { 1.0, 1.0, 1.0, 1.0 };
GLfloat shininess [] = { 100.0 };
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_SMOOTH);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_DEPTH_TEST);
/*Definición del tipo de material de los objetos a dibujar*/
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, shininess);
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
/*Posición de las luces*/
glLightfv(GL_LIGHT0, GL_POSITION, position0);
glLightfv(GL_LIGHT0, GL_AMBIENT, ambient);
/*Se habilitan las luces*/
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
/*Crear las diferentes partes del robot compilando las listas de despliegue*/
crear_base(SLICES, STACKS);
crear_hombro();
crear_brazo1(SLICES, STACKS);
139
crear_codo();
crear_brazo2(SLICES, STACKS);
crear_munieca(SLICES, STACKS);
crear_mano(SLICES, STACKS);
return TRUE;
}
/* Reajustar el tamaño de la ventana OpenGL*/
gboolean reshape(int w, int h){
/*Buscar area OpenGL*/
GdkGLContext *glcontext = gtk_widget_get_gl_context (glarea1);
GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable (glarea1);
/*** OpenGL INICIO ***/
if (!gdk_gl_drawable_make_current(gldrawable, glcontext)){
printf("\nError al buscar area OpenGL");
return FALSE;
}
/*Prevenir la división por cero*/
if(h==0){h=1;};
/*Define el area de dibujo de la ventana*/
glViewport(0, 0, (GLint)w, (GLint)h);
/*Se carga la Matriz de proyección*/
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
/*Volumen de visión de la camara*/
glOrtho(-1*zoom_z*w/h, zoom_z*w/h, -1*zoom_z, zoom_z, -1200, 1200);
/*Posición de la camara en la escena*/
gluLookAt(camara_x, camara_y, camara_z, 0, 0, 0, 0, 1, 0);
/*Se mueva la camara */
glRotatef(angulo_xr, 1.0, 0.0, 0.0);
glTranslatef(ancho_x, altura_y, 0.0);
glRotatef(angulo_yr, 0.0, 1.0, 0.0);
glMatrixMode(GL_MODELVIEW);
/*Mover la luces. Luces siguen a la camara*/
glPushMatrix();
glRotatef(-1*angulo_yr, 0.0, 1.0, 0.0);
glTranslatef(-1*ancho_x, -1*altura_y, 0.0);
glRotatef(-1*angulo_xr, 1.0, 0.0, 0.0);
glLightfv(GL_LIGHT0, GL_POSITION, position0);
glPopMatrix();
return TRUE;
}
/*************************************************************************************************************/
main.c: Función principal de la aplicación
/***************************************************************************
* SimQNK
* Simulador virtual del brazo robot Stäubli RX90 L
* main.c: función principal del programa
* Copyright(C) 2006 David Cuenca
* Email: [email protected]
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
140
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdio.h>
#include <gtk/gtk.h>
#include "ventanagl.h"
#include "dibujo.h"
#include "rutina.h"
#include "interface.h"
#include "callbacks.h"
int main (int argc, char *argv[])
{
/* Se establecen los parametros de configuración inicial del simulador*/
checkmax=1; //Se verifican los ang. máximos
checkcolision=1; //Se detiene en el primer choque
mover_obj = 1; //El objeto seleccionado es el robot
lim=0;
grid=0;
//Piso solido
altpiso=0.0; //Sin base externa
marg=0.0; //margen de precaución=0
/*Incializar GTK*/
gtk_set_locale ();
gtk_init (&argc, &argv);
/*Creary desplegar las ventanas de la aplicación*/
ventana1 = create_window1 ();
gtk_widget_show (ventana1);
ventana3 = create_window3 ();
gtk_widget_show (ventana3);
ventana2 = create_window2 ();
gtk_widget_show (ventana2);
/*Inicializar los valores de posición de la camara y el robot*/
inicio();
valores_iniciales_camara();
valores_iniciales_robot();
init_vortexfijos(marg);
/*Crear los puntos de control de colisión*/
crear_controles();
/*Lazo infinito del que nunca se sale*/
gtk_main ();
return 0;
}
/*************************************************************************************************************/
141
Descargar