capítulo 4 4.arquitectura software del sistema -...
TRANSCRIPT
Capítulo 4
4.Arquitectura software del sistema
Introducción
4.1.IntroducciónEn este capítulo se recogen todos aquellos aspectos software que tienen relación con los
objetivos marcados en este Proyecto Fin de Carrera, intentando reflejar la relación entre cada
subsistema. Entre ellos:
1. Software para la adquisición de imágenes: se detallarán las funciones implementadas para la
inicialización, toma de imágenes y cierre de la digitalizadora, así como su relación con las
funciones de la API de Picasso.
2. Comunicación con la unidad Pan&Tilt.
3. Control de la unidad Pan&Tilt.
4. Seguimiento de características: se hará un breve resumen del algoritmo de seguimiento de
características desarrollado por D. Joaquín Ferruz Melero, y que sirve de base para el
seguimiento de objetos móviles.
Arquitectura software del sistema 31
Adquisición de imágenes
4.2.Adquisición de imágenesEn el proceso de adquisición de imágenes intervienen la cámara, la digitalizadora y el PC de
visión, que se encarga de gestionar la captura.
Para el manejo de la digitalizadora a bajo nivel, existe una librería de funciones de Arvoo, que
permiten realizar tareas como son las de configuración, inicio y fin de adquisición. No obstante, el
uso directo de estas funciones sería bastante tediodo, por lo que se proporcionan una serie de
funciones de bajo nivel que hacen uso de las funciones del SDK de Picasso. Sobre éstas, se han
montado una serie de funciones de nivel intermedio que sirven, fundamentalmente para la
sincronización con los procesos de nivel superior, que son los que proporcionan la interfaz con la
digitalizadora a más alto nivel. La interfaz con el algoritmo de seguimiento se proporciona de
manera independiente del medio físico, y se apoyan directamente en las funciones de alto nivel. Si
se tuvieran otras funciones de alto nivel para otro medio (FireWire, por ejemplo), no haría falta
hacer distinción desde el algoritmo. A continuación se muestra un esquema de la arquitectura
software de la parte dedicada a visión:
En este proyecto se han desarrollado las funciones para la gestión de la adquisición de imágenes
desde las digitalizadoras de la familia Picasso, pero se podrían incluir funciones para el manejo de
distintas interfaces físicas de adquisición como se muestra en la figura. La adquisición de imágenes
de esta forma es gracias al desarrollo de una interfaz independiente del medio físico, cuoys detalles
se comentan en el apartado 2.5 del presente capítulo.
Arquitectura software del sistema 32
Figura 4.1: Arquitectura software para visión con interfaz independiente del medio físico
Adquisición de imágenes
En este apartado se detallarán aspectos como el proceso a seguir para la adquición de imágenes y
las funciones encargadas de ello, así como la interfaz que se proporciona al algoritmo de
seguimiento.
4.2.1.Proceso de adquisición. Picasso SDK
Una vez instalado el SDK de Picasso, ya es posible utilizar la digitalizadora y las funciones de la
librería.
Para comenzar el proceso es necesaria la inicialización de la digitalizadora, posteriormente se
procesa la información y finalmente se cierra. Para ello se efectúan llamadas a las siguientes
funciones:
• LoadPicassoDLL: Carga la dll que contiene las funciones de manejo de la digitalizadora.
• GetNumPcssCards: Obtiene el número de digitalizadoras conectadas al bus.
• GetPcssBoardType: Obtiene una referencia del módelo de digitalizadora en concreto.
• PcssOpen: Se obtiene un manejador de la tarjeta para su uso en funciones posteriores.
• Inicialización: se realizan las siguientes acciones:
➢ Obtención de información de la VGA y configuración de la misma (se debe indicar si las
imágenes se le pasarán directamente a la VGA o no).
➢ Indicación del tipo de imagen a tomar (por ejemplo, PCSS_RGB24_GRAY indica que se
tomará una imagen RGB con 24 bits por pixel (3 por canal) y en tonos de gris (por lo que la
imagen tengrá finalmente 8 bits)).
➢ Indicación del sincronismo: se debe especificar por qué canal se tomará el sincronismo o si se
tomará de forma externa.
➢ Establecimiento del rectángulo fuente: se establece el tamaño de la imagen en bits y el offset
respecto al origen. El resultado de un ajuste incorrecto del offset se puede observar en la
siguiente foto en forma de barras negras alrededor de la imagen:
Arquitectura software del sistema 33
Adquisición de imágenes
➢ Establecimiento del rectángulo destino: igual que el anterior, esta es la forma en la que queda
la imagen finalmente.
➢ Puerto de entrada: indicación del número de puerto al que se tiene conectada la cámara.
➢ Campos a captura: pares y/o impares.
➢ DMA Burst length: tamaño de la ráfaga de información que intenta mover la unidad de DMA
cada vez, cuando es el maestro del bus.
➢ DMA FIFO threshold: define el número de Dwords necesarios para que comience el trasvase
de información al bus.
➢ DMA Latency time: tiempo que la digitalizadora usa el bus PCI. Limita el acceso de otros
dispositivos si el valor es muy elevado.
Estos tres últimos parámetros determinan, en definitiva, la cantidad de ancho de banda del
bus que va a usar la digitalizadora. Valores normales son 32, 32 y en torno a 100 respectivamente.
El incremento de la longitud de ráfaga y el tiempo de latencia, así como la disminución del umbral
de la FIFO implican un uso más intensivo del bus por parte de la tarjeta, con lo que implica un mejor
rendimiento pero también limita el acceso de otros dispositivos al bus, ralentizando otras
operaciones de E/S. No obstante, con los valores normales pueden existir problemas con la calidad
de las imágenes. El efecto se muestra a continuación:
Arquitectura software del sistema 34
Figura 4.2: Imagen con barras negras por mal ajuste del offset
Adquisición de imágenes
Como se observa en la foto, existen zonas rayadas con color constante como resultado de la falta de
tiempo para transferir toda la información necesaria de forma correcta. Así pues, es necesario
modificar estos parámetros, por ejemplo con los valores 128, 4 y mayor de 128, respectivamente.
• PcssFunc: es una función generica que realiza diferentes tareas en función de un parámetro. Por
ejemplo, se puede iniciar la captura, pararla y las funciones de inicialización comentadas.
• PcssClose: libera los recursos reservados para la digitalizadora, entre ellos, el manejador. Es
importante asegurarse de que el proceso de adquisición está detenido antes de efectuar la llamada
a esta función, de lo contrario se incurriría en una violación de acceso.
• FreePicassoDLL: Se cierra la librería.
4.2.1.1. Obtención de capturas
En este apartado se describe de forma más exhaustiva el proceso de obtención de imágenes, una
vez la digitalizadora ha sido inicializada. La secuencia de llamadas a realizar es la siguiente:
Arquitectura software del sistema 35
Figura 4.3: Efecto del mal ajuste de los
parámetros de transmisión
Adquisición de imágenes
Como se puede observar en el esquema, existen dos modos de adquisición: no continua y
continua. En el primer modo, se inicia la adquisición y se espera un tiempo y se ve si la imagen está
lista mediante la función PCSSF_ISCAPT_READY. Una vez se ha realizado la captura es posible
salvarla u obtener información de captura que permita acceso al buffer donde está alojada la imagen
para su procesamiento; en el segundo modo, se mira cual es el último buffer lleno mediante la
función PCSSF_GET_LASTCAPTNUM y, o bien se salva o bien procesa. El proceso de
adquisición finaliza una vez que se le indica la parada.
4.2.1.2. Emplazamiento de los buffers
En el esquema anterior se podía observar como se efectúa una llamada a dos funciones
previamente al inicio de captura: PCSSF_INIT_CAPTURE y PCSSF_GET_INIT_CAPTURE_RES.
La primera de ellas sirve para el emplazamiento de los buffers de memoria, que se realiza en la
memoria RAM del ordenador. Tras haberse colocado los buffers, se debe comprobar que realmente
se ha hecho mediante la segunda de las funciones citadas.
Por ejemplo, si se necesitan 10 buffers de captura, la función PCSSF_INIT_CAPTURE calcula
el tamaño total y aloja los buffers. Pero si no existe espacio suficiente para los 10, se emplazaran
los que se puedan, por ejemplo, 7. La función no da error ya que ha podido hacer la tarea en parte, y
es PCSSF_GET_INIT_CAPTURE_RES la que se encarga de devolver el número final de buffers.
Arquitectura software del sistema 36
Figura 4.4: Diagrama de flujo de la adquisición de imágenes
Adquisición de imágenes
La función PCSSF_GET_CAPTUREINFO (con sus variantes para blanco y negro), devuelve la
dirección de los buffers alojados. Esto es necesario si se pretende efectuar un procesamiento de la
imagen. Existen dos posibilidades a la hora de la obtención de los fotogramas: llamar a esta función
cada vez y tras llamar a PCSSF_GET_LAST_CAPTNUM (lo que implica que se debe obtener toda
la estructura de información de los buffers nuevamente) o llamarla una vez al principio y luego usar
PCSSF_GET_LCAPT para actualizar el puntero al buffer. De esta última forma se puede trabajar en
determinados formatos de imagen y se usan dos buffers internos de la digitalizadora de forma
alternativa. Esta foram de trabajar resulta mucho más rápida, por lo que es la solución adoptada en
este caso con el fin de la adquisición no suponga un cuello de botella en el proceso de seguimiento,
ya que es necesario un frame rate de al menos 5-10 fps para controlar de forma correcta.
4.2.2.Funciones de bajo nivel
El uso directo de las funciones proporcionadas por el SDK de Picasso, puede ser muy tediodo y
complejo si se desea implementar una aplicación de alto nivel. Es por ello que existen una serie de
funciones de bajo nivel proporcionadas por Arvoo para la gestión más fácil de los recursos de la
digitalizadora, y que han sido modificadas en parte para la adaptación a las necesidades de la
aplicación y a las que se han añadido algunas para gestión adecuada de las imágenes y la liberación
de recursos:
• enum PcssRetType MainInitPicasso( short *numcards ): función que comprueba la versión de
la librería Picasso (Picasso DLL) y obtiene el número de digitalizadoras presentes en el sistema.
Se le pasa un puntero al número de tarjetas y devuelve código de error.
Arquitectura software del sistema 37
Figura 4.5: Emplazamiento de los buffers en memoria
Adquisición de imágenes
• enum PcssRetType SelectPicasso( short num_cards, short *sel_card ): función que gestiona la
elección de la digitalizadora a usar en el proceso de adquisición. Se le pasa el número de tarjetas
presentes en el sistema, obtenido de la función anterior, y un puntero a la tarjeta a seleccionar.
Devuelve código de error.
• enum PcssRetType OpenPicassoCard( short cardnum, enum PcssBoardType *btype,
PCSS_HANDLE *ph ): esta función se encarga de abrir la digitalizadora, obteniendose un
manejador (PCSS_HANDLE *ph) que será usado en el resto del proceso para referenciar a la
tarjeta. Se le pasa el número de tarjetas en el sistema, puntero al tipo y puntero al manejador.
Devuelve código de error.
• enum PcssRetType InitPicassoCard( PCSS_HANDLE ph, enum PcssBoardType btype, int
ancho, int alto, int sync): función para la inicialización de la digitalizadora en la que, en función
del tipo, se hacen llamadas a funciones concretas para la inicialización de cada tipo de tarjeta de
la familia Picasso. Se le pasa el manejador, el tipo, las dimensiones de la imagen que se quiere
tomar y el canal por el que se toma el sincronismo.
Mediante las llamadas a las funciones de inicialización, se consiguen establecer los siguientes
parámetros de configuración:
➢ Dimensiones (ancho y alto) de los rectángulos de vídeo origen y destino.
➢ Offsets de los rectángulos de vídeo origen y destino.
➢ Obtención de información de la VGA.
➢ Establecimiento del canal de sincronismo.
➢ Establecimiento del tamaño de ráfaga de la DMA.
➢ Umbral de la FIFO de la DMA.
➢ Tiempo de latencia en el bus PCI.
➢ Modo de vídeo: PAL o NTSC. En este caso, PAL.
➢ Selección del puerto por el que se toman las imágenes.
➢ Formato de salida. En este caso RGB24_GRAY, es decir, niveles de gris con 8 bits por pixel
para cada componente o canal (24 para los tres canales).
➢ Vídeo entrelazado: se elige si se capturan los campos pares e impares consecutivamente o
sólo uno de ellos. En este caso se ha optado por tomar sólo los campos pares, ya que si se
tomaba de forma entrelazada, se daban problemas de falta de nitidez de la imagen cuando el
Arquitectura software del sistema 38
Adquisición de imágenes
objeto se mueve de forma rápida, dados los parámetros de configuración de ancho de banda
establecidos para la DMA.
• enum PcssRetType InitAndCreateCapture( PCSS_HANDLE ph, enum PcssBoardType btype,
int ancho, int alto, int sync): esta función que, en principio sólo inicializaba la captura
entrelazada o progresiva, tomaba imágenes y las guardaba en formato bmp haciendo uso de otra
función, en esta aplicación crea además una variable condición (evento_sec_crit) que se usa
posteriormente como mutex para la exclusión mutua en el acceso al buffer interno donde se
alamcenan las imágenes nuevas, dentro de la función copybuffer, se reserva memoria para el
buffer del almacenamiento interno, y se obtiene información acerca de los buffers alojados en
memoria a los que accede la función FuncionHiloCaptura para efectuar la copia. Se le pasa el
manejador de la tarjeta, el tipo, las dimensiones de la imagen para la reserva de memoria y el
sincronismo para tener una copia local. Devuelve código de error.
• enum PcssRetType InitInterlacedCapture( PCSS_HANDLE ph ): esta función inicializa la
captura entrelazada, especificándose el uso de la función de Callback que se explica
posteriormente, y si se trata de captura continua, circular, etc. Se le pasa el manejador de la
tarjeta y devuelve código de error.
• enum PcssRetType TakeCapture( PCSS_HANDLE ph): función que inicia la captura, haciendo
uso de funciones del SDK de Picasso.
• short InterlacedCaptureCB( void *parm ): una vez lanzada la adquisición, cada vez que un
nuevo buffer se ha llenado salta la función de Callback, que debe devolver 1 para que continue
ejecutándose en sucesivas iteraciones.
Esta función debe ser lo más corta posible, de forma que no se consuma excesivo tiempo y se
impida la correcta ejecución de la toma de imágenes.
En este caso se ha optado por hacer uso de un mutex al principio con el objeto de poder bloquear
el hilo desde la función de finalización (para que no se pueda acceder a recursos ya liberados),
para lo cual se hace una llamada a una función de nivel intermedio (espera()). Tras esto se hace
una llamada a otra función de bajo nivel añadida a las existentes (FuncionHiloCaptura) que se
encarga de la obteción del buffer y su gestión.
Se le pasa un puntero de tipo void a los parámetros (en caso de que sean necesarios) y devuelve 1
para continuar la ejecución en posteriores ocasiones.
• void FuncionHiloCaptura(void): es la función que se encarga de la gestión de las imágenes
nuevas, y se ejecuta cada vez que salta la función de Callback. En esta parte del programa se
hace uso de la información de los buffers de almacenamiento que se obtuvo en el momento del
Arquitectura software del sistema
Adquisición de imágenes
lanzamiento de la adquisición, para actualizar un puntero al último buffer lleno, y haciendo una
llamada a una función de nivel intermedio (copybuffer), se encarga de copia la imagen en un
buffer interno del programa con el objeto de tener una copia local. Además, se obtiene el instante
en el que tomó la imagen, lo cual servirá a posteriori para la extrapolación de la posición del
objeto (ver apartado 3.2.1).
La librería de Picasso cuenta con infinidad de funciones adicionales a las comentadas para la
salvaguarda de imágenes en diferentes formatos (bmp, jpeg, tif) y demás modos de configuración de
las digitalizadora, aunque básicamente son las expuestas de las que se hace uso en la aplicación.
4.2.3.Funciones de nivel intermedio
Con el fin de facilitar el trabajo, se han desarrollado una serie de funciones de nivel intermedio,
que se apoyan en las de bajo nivel, que llevan a cabo la labor de acceso a los buffers, la copia de
ellos en un buffer interno del programa y la gestión de los accesos por parte de las funciones de alto
nivel, a las nuevas imágenes obtenidas. Estas funciones son:
• void copybuffer(unsigned char *pbuff, long *t_cap, long opcion): esta función constituye un
mecanismo para el paso de imágenes a las funciones de nivel superior, sin necesidad del acceso
de éstas a los buffers de memoria.
El acceso a esta función puede ser en lectura o escritura: de esta forma, cada vez que se llena un
nuevo buffer, FuncionHiloCaptura efectúa una llamada en modo escritura para dejar una copia
local al programa de la imagen adquirida, mientras que una función de nivel superior
(CapturaPicasso) accede en modo lectura para proporcionar imágenes al algoritmo cuando éste
las requiere. Se tiene, pues, una zona de memoria compartida en la que se arbitra el acceso
mediante un mutex.
Además de estas dos opciones, existe la de acceder para cambiar el tamaño del buffer interno, de
modo que se puede desde un nivel superior cambiar el tamaño de la imagen a adquirir de forma
fácil.
Se le pasa un puntero al buffer de lectura o escritura según el modo de acceso, un puntero al
instante de captura y la opción (lectura, escritura, o en otro caso, tamaño).
• void activa_actualizacion(void): mediante la llamada a esta función, se activa un evento que
permite notificar a la función de captura del nivel superior que existe una imagen nueva,
desbloqueándola y permitiendo el acceso a ésta. De esta forma se asegura que la imagen que
toma la función de alto nivel es una nueva cada vez, y evitando tener que esperar de forma
forzosa, ya que en el caso de existir ya una imagen no usada, no tendría que hacer la espera.
Arquitectura software del sistema
Adquisición de imágenes
• void espera(void): esta función modifica los parametros de captura, activa el evento que inhibe la
ejecución del hilo de captura e inicia ésta. Es la función a la que se llama desde la función de
Callback, y de esta forma se evita que se accedan a ciertos recursos que se estén liberando por
parte de la función de alto nivel de finalización.
• void libera_memoria_hilo(void): esta función libera los recursos reservados en la adquisición (el
buffer de captura y la estructura de información).
4.2.4.Funciones de alto nivel
Apoyándose en las funciones de nivel intermedio y bajo nivel, se han desarrollado funciones de
nivel superior que se encargan de llevar a cabo la inicialización, captura y finalización del proceso
de forma transparente. De este modo se consigue mediante tres funciones tener una interfaz fácil
para la gestión de la toma de imágenes y acceso a la captura. Además existe otra función para la
modificación de los parámetros de adquisición por parte de las funciones que sirven de interfaz al
algoritmo de seguimiento:
• enum PcssRetType InicioPicasso(int ancho, int alto, int sync): en la parte de inicialización se
crean la variables condición y los mutex necesarios, así como los distintos hilos y se reserva
memoria. Además de esto, se le dan determinados parámetros de funcionamiento a la
digitalizadora, como son el tamaño de la imagen a adquirir, el modo de adquisición (continua o
no), el número de buffers de memoria a emplazar, si la imagen es en color o tonos de gris, se
detecta el tipo de digitalizadora presente en el sistema, etc. Esta función hace uso de las
funciones anteriormente comentadas.
Se puede observar como se consigue de forma sencilla (con sólo tres parámetros) llevar a cabo el
proceso de inicialización por parte de las funciones que sirven de interfaz al algoritmo de
seguimiento, abstrayéndose a los niveles superiores de la complejidad y los detalles del bajo
nivel.
Se le pasa el alto y el ancho de la imagen y el canal desde el que debe tomar el sincronismo.
Devuelve código de error.
• enum PcssRetType CapturaPicasso(unsigned char *imagen_sig, long *t_capt): para la captura
se hace uso de esta función, que espera a que esté activo un evento que indica que existe una
imagen nueva disponible.
Como se comentó anteriormente, cuando se lanza el proceso de adquisición en la función de
inicialización, se crea un hilo que se encarga de acceder a los buffers de memoria en los que se van
guardando las imágenes. Esto se hace mediante una función de Callback que salta cada vez que se
Arquitectura software del sistema
Adquisición de imágenes
activa un evento de imagen nueva en el buffer. De esta forma, desde esta función se puede llamar a
cualquier función que haga uso de las imágenes. En este caso, se llama a una función
(FuncionHiloCaptura) que se encarga de actualizar el puntero al último buffer que se llena, copiar la
imagen y registrar el tiempo en el que se produjo la captura, así como activar el evento de imagen
nueva que desbloque a la función de captura.
Esta función accede en modo lectura a copybuffer, proporcionando así una copia de la imagen
más reciente al algoritmo de seguimiento.
De esta forma, la parte de seguimiento y la de adquisición quedan desligadas mediante esta
interfaz, pudiendo ejecutarse ambas de forma independiente.
La citada implementación es posible sin hacer uso del evento de actualización, pero cada
llamada a la función de captura implicaría una espera que no es siempre necesario hacer, es decir, se
haría de forma secuencial, mientras que con la opción adoptada se consigue un trabajo en paralelo.
Trabajar de esta forma implica que se puedan adquirir imágenes que se sobreescriban si el hilo
de seguimiento no accede al buffer antes de que se obtenga otra imagen. Esta pérdida de imágenes
es absolutamente irrelevante, ya que lo que interesa es tomar la imagen más reciente, puesto que se
está controlando el sistema para el seguimiento de un objeto.
• enum PcssRetType FinalizaPicasso(void): la función que se encarga de terminar el proceso de
adquisición es FinalizaPicasso. En este punto se bloquea a la función de Callback y a la de
captura mediante sendas variables condición para que no se produzcan accesos a recursos
mientras se liberan y se finaliza el hilo.
• void establecer_parametros(int ancho, int alto, int sync): esta función permite modificar los
parámetros de la adquisición por parte de las funciones que sirven de interfaz al algoritmo de
seguimiento sin necesidad de comenzar el proceso, activando el evento inhibe la ejecución del
hilo de captura e iniciando ésta.
4.2.5.Interfaz independiente del medio físico
Las funciones comentadas en los capítulos precedentes llevan a cabo la labor de inicialización,
captura y finalización de la digitalizadora para el caso concreto de ser una digitalizadora de la
familia Picasso.
Arquitectura software del sistema
Adquisición de imágenes
No obstante, podría darse el caso de que se tuvieran en el sistema de visión distintas posibles
fuentes para la toma de imágenes, como podrían ser una digitalizadora de otro fabricante, una
cámara digital conectada a una interfaz FireWire, etc. O bien, podría pensarse en el uso del
programa en un sistema en el que se tuviera otro tipo de fuente. En ese caso, deberían modificarse
las funciones que gestionan los recursos y constituiría una forma estática de resolver el problema.
Por estos motivos, sería interesante disponer de una interfaz independiente del medio físico que,
de manera dinámica, permitiera el acceso a las diferentes fuentes disponibles sin necesidad de
modificar las llamadas por parte del algoritmo de seguimiento.
Con el fin de implementar la interfaz independiente del medio físico, se ha dado lugar a una
tabla de estructuras de punteros a funciones de forma que, teniendo disponibles las funciones para el
manejo de cada recurso, y conociéndose el recurso desde el que se quiere tomar como fuente, se
puedan efectuar las llamadas a cada función de manera transparente.
Con el fin de determinar la fuente desde la que se tomarán las imágenes, se introduce en la
aplicación un nombre que identifique al recurso en concreto. En todo este proceso toma parte la
siguiente función:
• int determina_fuente(char *nom_fuente, int *sincr): esta función mapea la cadena que se le
pasa como argumento en un número que hace referencia al recurso, y que se toma como índice en
la tabla de estructuras de punteros a funciones comentadas previamente.
En este momento sólo se dispone de la Picasso, por lo que únicamente distingue entre la toma de
imágenes en tiempo real usando ésta y un fichero. No obstante, bastaría con asignar un nombre
como referencia a otro recurso y mapearlo a otro número diferente para que funcionara con otra
fuente. Con el objeto de aclarar e ilustrar la interfaz definida, se presenta la siguiente figura:
Arquitectura software del sistema
Adquisición de imágenes
Como se puede observar, la tabla tiene tantos elementos como recursos se quieran contemplar. A
cada recurso se le asigna un identificador número, de forma que con la función determina_fuente se
obtiene a qué recurso se hace referencia. Cada elemento de la tabla es una estructura en la que se
tienen punteros inicializados a las funciones de inicialización, captura y finalización para cada tipo
de fuente, de manera que se puede llamar a estas funciones mediante la invocación de los punteros
del elemento adecuado de la tabla.
Arquitectura software del sistema
Figura 4.6: Interfaz independiente del medio físico
Software para la unidad Pan&Tilt
4.3.Software para la unidad Pan&TiltOtra parte importante del software del sistema es la concerniente a la unidad Pan&Tilt. La
unidad está conectada vía serie al PC, por lo que el software tiene dos parte diferenciadas:
comunicación y control.
4.3.1.Software de comunicaciones
La comunicación con el Pan&Tilt se realiza como se ha comentado mediante una línea serie
(RS-232). Existe una jerarquía de funciones de más alto nivel que hacen uso de las funciones
básicas de manejo del puerto serie, de forma que se hace más fácil trabajar con él desde el programa
de control.
Las funciones comentadas han sido proporcionadas por Directed Perception Inc., para
Windows, Unix y Solaris. Las funciones para Windows estaban bastante obsoletas, pudiéndose
ejecutar únicamente en plataformas anteriores a WIN32, por lo que se ha hecho una adaptación del
código para que puedan funcionar en Windows XP.
Además de las modificaciones, se han añadido mutex, para la exclusión mutua en la parte de las
funciones en las que se hace una lectura o escritura en el puerto serie, de forma que solamente un
proceso puede acceder en un instante de tiempo a la unidad. De esta forma se asegura un correcto
funcionamiento del Pan&Tilt, sin desbordamientos de los buffers de transmisión y recepción, lo que
provocaría la pérdida de control sobre la unidad. El incluir en esta parte del código los mútex
permite usarlo desde la parte de control de forma transparente a cómo las funciones gestionan el
recurso.
A la hora de trabajar con la unidad Pan&Tilt es muy importante hacerlo de forma correcta,
liberando de forma adecuada los recursos, ya que de lo contrario se puede dar el caso de que queden
instrucciones pendientes de ejecución en el buffer y se pierda el control de la unidad, pudiéndose
salir de la zona permitida de trabajo, con el consecuente sobreesfuerzo de los motores, lo cual puede
provocare el deterioro de los mismos.
La configuración del puerto serie debe ser la siguiente:
• Velocidad de transmisión de 9600 baudios.
• 1 bit de arranque.
• 1 bit de parada.
• 8 bits de datos.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
• Sin paridad.
Comentar que se trabaja con el modo de funcionamiento raw, que consiste en que la información
transmitida no se procesa ni a la entrada ni a la salida, pudiendo estar disponibles los datos sin
necesidad de esperar nueva línea para ensamblar la información.
4.3.1.1. Funciones de comunicación
Tal y como se comentaba anteriormente, se crearon una serie de funciones jerárquicamente
ordenadas que facilitaran la comunicación bidireccional con el controlador. Tal estructura puede
observarse en la figura 3.7:
Esta estructura organizada dota al sistema software de la flexibilidad necesaria para una posible
implementación en otras plataformas de manera inmediata y sin una excesiva complejidad.
Funciones de Nivel 0
En la base de esta estructura se encuentra lo que podría denominarse Nivel 0, formado por el
sistema operativo. En este nivel están las llamadas al sistema, que permiten el acceso y
configuración del puerto de comunicación serie a través del cual se “dialoga” con el controlador de
Arquitectura software del sistema
Figura 4.7: Estructura de funciones de comunicación para el
Pan&Tilt
Software para la unidad Pan&Tilt
la unidad Pan&Tilt. Asimismo, pueden encontrarse otras funciones que permiten un control
exhaustivo sobre el tiempo del sistema, como es el caso de nanosleep, que es usada para la
detención momentánea del proceso en espera de la recepción de la conformidad enviada por el
controlador.
Las funciones de niveles superiores se irán apoyando de forma recursiva en aquellas otras de
niveles inferiores, descansando, en última instancia, en estas llamadas al sistema constituyentes del
último nivel
Funciones de Nivel 1
A continuación se comentará brevemente la funcionalidad de cada una de estas órdenes:
• SetSerial: configura el puerto serie según lo visto anteriormente. Básicamente, esta
configuración es:
➢ 9600 baudios
➢ 1 bit de arranque
➢ 1 bit de parada
➢ 8 bits de datos
➢ Sin paridad
➢ Modo de comunicación raw.
De igual forma, esta función salva la configuración anterior antes de instaurar la anteriormente
comentada, a fin de que ésta sea restaurada justo antes de cerrar la conexión con el puerto serie.
• Openserial: en apartados anteriores se ha comentado la filosofía de este sistema operativo. Se vio
igualmente que el tratamiento que de todos los dispositivos hace es similar al que cualquier otro
sistema haría con ficheros normales, es decir, para acceder y manipular al dispositivo deben
seguirse los pasos ya conocidos:
➢ Apertura
➢ Lectura/Escritura
➢ Cierre
Pues en esta función se realiza el primero de estos pasos. Básicamente, se abre igual que
cualquier fichero.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
• CloseSerial: con esta otra orden, se restauran los parámetros anteriores a la modificación
realizada en la apertura, y se cierra el puerto serie como si de un fichero se tratara.
• DserialOut: utilizando las llamadas al sistema del nivel 0, se envía un carácter por el puerto de
trabajo hacia el controlador.
• DgetSerialChar: una vez realizada la llamada a esta función, el sistema espera hasta que recibe
un carácter por el puerto serie, procedente del controlador.
Funciones de Nivel 2
• SerialBytesIn: esta función recibe, entre otros parámetros de entrada, el número de caracteres
que debe leer, así como el tiempo máximo que esperará para la recepción de éstos. Se inicia un
temporizador que, si llega a expirar antes de la recepción del número de caracteres establecido,
hará que se devuelva un código de error.
• SerialBytesOut: recibe un determinado mensaje compuesto por una serie de caracteres. La
función se encargará de descomponer dicho mensaje y enviar cada parte al controlador, haciendo
uso de funciones del nivel inferior.
• do delay: paraliza momentáneamente la ejecución del código tanto tiempo como indique el
parámetro de entrada. Este parámetro será indicado en milisegundos.
• FlushInputBuffer: a través de comandos propios del sistema, se “limpia” el buffer de entrada.
Esta función es utilizada a la hora de inicializar los dispositivos a fin de no interpretar
erróneamente códigos que pudieran existir en el mencionado buffer.
• reset PTU parser: inicializa el controlador de la unidad Pan-Tilt enviándole unos determinados
códigos. Se considerará que está convenientemente inicializado si tras estos códigos, éste
responde un código esperado.
Funciones de Nivel 3
• SerialOut: es la función de salida de alto nivel. Esta es la encargada de enviar los mensajes a las
órdenes de niveles inferiores.
• GetSerialChar: al igual que ocurría con la anterior función, ésta es la de alto nivel de entrada, es
decir, de recepción de los códigos enviados por el controlador.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
Funciones de Nivel 4
• GetUnsignedShort / PutUnsignedShort: estas funciones, al igual que ocurre con las siguientes,
leen o escriben en el puerto serie el dato pasado como parámetro. La diferencia entre dichas
funciones estriba en el tamaño del dato a leer o escribir.
Con anterioridad se hablaba de dotar al sistema software de una cierta flexibilidad que
permitiera una migración simple a otras plataformas. En parte, esta flexibilidad también queda
patente en estas funciones, puesto que éstas tendrán presente el tipo de máquina donde el código
será ejecutado.
Debe hacerse esta consideración puesto que dependiendo de la máquina y del sistema, el tipo de
almacenamiento de datos en memoria puede variar. De esta forma, los valores pueden almacenarse
siguiendo la filosofía big endian o bien, little endian. Mientras en el primero de los casos los bytes
son numerados de izquierda a derecha, en el segundo de ellos se numeran en el orden contrario. Así,
puesto que los mensajes se transfieren byte a byte, debe conocerse cual es el byte que se envía
primero, y por extensión, conocer cual es el que se está enviando en cada momento. De no ser así,
pueden producirse errores de la interpretación de los mensajes recibidos en uno u otro extremo de la
conexión. En este caso concreto, los valores que serán enviados o recibidos serán enteros sin signo
en formato corto, o lo que es lo mismo, 16 bits para representar números positivos exclusivamente
(de 0 a 65535).
• GetSignedShort / PutSignedShort: tanto para estas dos funciones como para las siguientes cabe
hacer las mismas consideraciones realizadas anteriormente. En este caso, el tipo de dato a
transmitir o recibir será entero con signo y en formato corto, es decir, 16 bits para números
positivos y negativos (de -32768 a 32767).
• GetSignedLong / PutSignedLong: en esta ocasión, los datos serán enteros con signo en formato
doble, o sea, 32 bits para números positivos y negativos (de -2147483648 a 2147483647).
• SerialStringOut: esta otra función utiliza otras órdenes inferiores para hacer llegar un mensaje
compuesto por diversos códigos al controlador de la unidad Pan&Tilt. Se comentarán a
continuación de forma más exhaustiva aquellas funciones que directamente manejará el usuario.
• OpenHostPort: como puede intuirse del nombre, esta será la función de más alto nivel que se
utilizará para abrir el puerto de comunicaciones.
Esta recibirá como parámetro una cadena de caracteres indicando el puerto que se desea utilizar
para la comunicación. Una vez abierto, la propia función se encarga de configurarlo acorde a las
necesidades planteadas anteriormente, devolviendo finalmente el manejador de ficheros que será
utilizado para referirse al mencionado puerto de trabajo.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
• CloseHostPort: esta otra simplemente cierra la conexión utilizando funciones vistas
anteriormente.
• Reset ptu: con esta orden se ordena un reset software de la unidad Pan&Tilt, no devolviendo el
estado hasta que no se concluya el mencionado reset.
Justo al conectar la alimentación a la unidad Pan&Tilt, y de forma automática, el controlador
ordena un reset hardware de ésta, calibrándose las posiciones home y pasando a estar operativa. No
obstante, debido a que la unidad parece no utilizar encoders de posición que realimenten y controlen
que la posición actual es ciertamente la indicada, en ciertas ocasiones, como por ejemplo al describir
movimientos bruscos, se producen pérdidas de sincronismo, saltos de posiciones en los motores
paso-a-paso de la unidad. Lo que si es evidente es que este fallo provoca errores en el
posicionamiento, por lo que resulta necesario realizar un reset software para volver a recuperar los
ejes de referencia de posición. Es posible que esto pueda ser provocado por el momento de inercia
de la carga, por lo que es aconsejable utilizar cámaras de reducido tamaño y peso.
• Await_completion: esta instrucción, como ocurre con todas aquellas que tienen un comando
equivalente dentro del conjunto de órdenes en formato ASCII, trata de simular lo que aquellas
hacían.
En este caso concreto, con esta instrucción se insta al sistema para que espere a la conclusión del
último movimiento ordenado antes de proseguir con la ejecución del código.
• Halt: con los distintos valores que puede tomar su parámetro de entrada, que son:
➢ ALL
➢ PAN
➢ TILT
Se realiza una parada inmediata de los dos ejes simultáneamente o bien del indicado
exclusivamente.
• Set_Mode: con esta orden son fijados los modos de operación de la unidad. Es conveniente
recordar que inicialmente, justo al conectar la alimentación, la unidad se configura con unos
valores por defecto, lo que también incluye unos modos determinados. Se utilizará esta orden
para modificarlos si los originales no se adaptan a las necesidades de la aplicación, sean cuales
sean éstas.
La unidad dispone de varios modos de operación no excluyentes:
➢ Modo de ejecución de comandos.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
➢ Modo de respuesta.
➢ Modo eco.
➢ Modo de límites de posición.
➢ Modo “valores por defecto”.
Cada uno de estos modos puede tomar un único valor dentro de un conjunto de opciones
posibles, lo que, en definitiva, fijaría el modo de funcionamiento. Los distintos valores que pueden
tomar cada uno de ellos quedan resumido en:
➢ COMMAND_EXECUTION_MODE:
EXECUTE_IMMEDIATELY. Equivale al Modo de Ejecución Inmediata de la Posición.
Es el modo por defecto.
EXECUTE_UPON_IMMEDIATE_OR_AWAIT. Corresponde al Modo de Ejecución
“Esclavo” de la Posición.
➢ ASCII_VERBOSE_MODE:
VERBOSE. Este modo hace que el controlador responda utilizando numerosos códigos.
TERSE. Especifica que el modo de respuesta del dispositivo sea parco en palabras.
QUERY_MODE. Pregunta al controlador el estado actual de este modo.
➢ ASCII_ECHO_MODE:
ON_MODE. Activa el eco.
OFF_MODE. Desactiva el eco.
QUERY_MODE. Demanda del controlador el modo en el que actualmente está.
➢ POSITION_LIMITS_MODE:
ON_MODE. Activa el modo que imposibilita a los motores posicionarse más allá de los
límites de seguridad.
OFF_MODE. Desactiva el modo.
QUERY_MODE. Pregunta al controlador el estado actual.
➢ DEFAULT:
Arquitectura software del sistema
Software para la unidad Pan&Tilt
SAVE_CURRENT_SETTINGS. Salva los valores actuales de aceleración, velocidad, etc.
como valores por defecto.
RESTORE_SAVED_SETTINGS. Restaura los valores anteriormente almacenados.
RESTORE_FACTORY_SETTINGS. Restaura los valores por defecto de fábrica.
De esta forma, el primero de los dos parámetros de entrada que la orden Set_Mode requiere,
indicará el modo que se pretende modificar, mientras que el segundo de los parámetros hará
referencia al nuevo valor. No debe olvidarse que si bien los modos no son excluyentes, las opciones
si lo son. Es decir, cada modo sólo tomará un único valor.
En este caso, dadas las restricciones, se necesitará que el modo de ejecución de comandos sea
tal, que justo después de recibir el comando, lo ejecute. Para ello se utiliza la orden y los parámetros
set_mode(COMMAND EXECUTION MODE, EXECUTE IMMEDIATELY) para la configuración de
la unidad. El resto de modos, no son relevantes, por lo que se usan los valores por defecto.
• Set_Desired: a través de esta única orden son pasadas las consignas al controlador de la unidad
Pan&Tilt. A fin de que ciertamente sea un sola orden la necesaria para la comunicación, en más
alto nivel, con el controlador, ésta requiere una serie de parámetros de entrada para poder
proporcionar tal flexibilidad. Estos parámetros son los 4 siguientes:
➢ Eje. Pueden ser:
PAN.
TILT.
➢ Propiedad cinemática. Mediante este parámetro de entrada se indica cual es la magnitud
física a modificar:
POSITION, para ordenar nuevos posicionamientos en cualquier de los ejes.
SPEED, para indicar nuevas velocidades con las que la unidad se moverá.
ACCELERATION, con la que se indica la aceleración de alcance de la velocidad
estacionaria así como con la que se desacelerará, en los casos en los que la velocidad
ordenada sea superior a la velocidad base. Esta propiedad marca la pendiente finita de la
recta de subida y de bajada de la figura 2.14.
UPPER. Con esta propiedad se fija el Límite Superior de Velocidad.
LOWER. Con esta propiedad se fija el Límite Inferior de Velocidad. Junto con la anterior,
se establecen el rango de velocidades admisibles.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
HOLDPOWERLEVEL. Tanto esta propiedad como la siguiente hacen referencia al control
de potencia del dispositivo. En concreto, la que ahora se trata fija el nivel de potencia
aplicado a los motores cuando éstos están en el permanente, es decir, no se encuentran en
movimiento.
MOVEPOWERLEVEL. En sentido contrario a la propiedad anterior se encuentra ésta. En
este caso, se fija el nivel de potencia que será aplicado a los motores cuando éstos están en
el transitorio, en movimiento.
➢ Valor. A través de este parámetro de entrada podrá indicarse el nuevo valor de la propiedad
cinématica que se pretende modificar. Así, en el caso de posición, velocidad, aceleración,
velocidad base y límites superior e inferior de velocidad, este parámetro será el nuevo valor
numérico (medido en posiciones, posiciones/seg o posiciones/seg2, en el caso de posición,
velocidad o aceleración respectivamente) que tome tal propiedad.
En el caso de los modos de potencia, el valor pasa a ser cualquiera de los siguientes:
PTUREGPOWER
PTULOWPOWER
PTUOFFPOWER
correspondiéndose con el modo de potencia regular, modo de potencia bajo, en el que el
consumo es bajo, pero también lo es la capacidad de reacción, así como la de sustentación de cargas
pesadas, o bien modo de potencia desconectado, en el que el modo está desconectado, provocándose
frecuentes pérdidas de sincronización en el posicionamiento.
➢ Modo de Movimiento. Cuando sea posible, es decir, en aquellas propiedades donde se
implique movimiento, se hará referencia con este parámetro al tipo de movimiento, pudiendo
ser:
RELATIVE, para indicar movimientos relativos.
ABSOLUTE, indicando movimientos absolutos, referentes al sistema de coordenadas de la
posicion cero.
En el resto de los casos, este parámetro de entrada permanece a NULL.
En resumidas cuentas, los posibles valores que esta función puede tomar quedan recogidos en las
sentencias:
set_desired([PAN|TILT],[POSITION|SPEED|ACCELERATION|BASE|UPPER|LOWER],[<posición>|<velocidad>|<aceleración>],[RELATIVE|ABSOLUTE])
Arquitectura software del sistema
Software para la unidad Pan&Tilt
set_desired([PAN|TILT],[HOLD_POWER_LEVEL,MOVE_POWER_LEVEL],<modo de potencia>,NULL)
• Get_Desired: tanto con esta orden como con la siguiente, puede interrogarse al controlador
acerca de los valores que toman las propiedades cinemáticas. En este caso en concreto, se
demanda que éste informe sobre los valores que anteriormente se han ordenado. Debe tenerse
presente que estos mencionados valores pueden diferir de los que actualmente posee. Para
demandar esta otra información (los actuales valores), se utilizará la siguiente orden.
Es lógico pensar que estos valores pueden no coincidir. Por ejemplo, si se pregunta al
controlador la posición en la que se encuentra en ese mismo momento mientras describe un cierto
movimiento, como puede intuirse la respuesta diferirá de la obtenida en el caso de preguntarle por la
posición deseada, que en este caso sería la posición final del movimiento.
Haciendo, pues, esta salvedad, esta orden que ahora se trata, al igual que ocurría con la anterior,
trata de ser la única (en el nivel superior de la torre jerarquizada de órdenes) con la que acceder al
controlador para demandarle información al respecto de las órdenes recibidas. Por ello, hará uso de
una estructura similar a la anteriormente vista. En este caso, sólo requerirá dos parámetros de
entrada:
➢ Eje. Al igual que antes, debe indicarse el eje en cuya propiedad se está interesado:
PAN.
TILT.
➢ Propiedad Cinemática. El segundo de los campos es la propiedad de la que se demanda su
valor. En este caso, el parámetro puede tomar los valores siguientes. Algunos de ellos son de
sobra conocidos, por lo que no se entrará en más detalles.
POSITION, con la que se pregunta por la posición anteriormente ordenada.
SPEED, por la velocidad.
ACCELERATION, aceleración.
BASE, velocidad base.
UPPERSPEEDLIMIT. Con esta orden y con la siguiente puede preguntarse por los límites
superior e inferior de velocidad.
LOWERSPEEDLIMIT.
MINIMUMPOSITION. Como propiamente indica el nombre, se demandan con este valor
y con el siguiente la mínima y máxima posición alcanzable por la unidad Pan&Tilt.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
MAXIMUMPOSITION.
RESOLUTION, indicando con ella que se está interesado en la relación existente entre
grados y posiciones.
HOLDPOWERLEVEL, con la que se solicita el estado del nivel de potencia en estado
estacionario.
MOVEPOWERLEVEL, y el nivel de potencia en estado transitorio, o sea, en movimiento.
Todo ello queda recogido en la sentencia:
get_desired([PAN|TILT],[POSITION|SPEED|ACCELERATION|BASE|UPPER_SPEED_LIMIT|LOWER_SPEED_LIMIT|MINIMUM_POSITION|MAXIMUM_POSITION|RESOLUTION| HOLD_POWER_LEVEL|MOVE_POWER_LEVEL|])
Como puede intuirse lógicamente, aquellas demandas de valores asociadas a propiedades
cinemáticas donde no haya implícito un retardo desde que se recibe la orden de modificación
hasta que se alcanza el valor (como ocurre en el caso del posicionamiento), darán el mismo
resultado tanto si se demandan con esta orden, como si se hacen con la siguiente.
• Get_Current: como ya se comentó anteriormente, también esta orden demanda información del
controlador. A diferencia de la orden Get_Desired,ésta otra retorna los valores de las propiedades
cinemáticas en el momento de recibir la orden.
En la mayoría de los casos, salvo algún error que pueda producirse en las comunicaciones,
coincidirán los resultados de ambas demandas. También esta orden requiere dos parámetros de
entrada:
➢ Eje.
➢ Propiedad cinemática.
En este caso, el conjunto de valores posibles para el segundo de los parámetros de entrada es
ligeramente más reducido:
➢ POSITION.
➢ SPEED.
➢ ACCELERATION.
➢ BASE.
➢ UPPER.
➢ LOWER.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
➢ HOLDPOWERLEVEL.
➢ MOVEPOWERLEVEL.
➢ RESOLUTION.
Como es habitual, también en este caso se resumirá en una sola sentencia los distintos
parámetros:
get_current([PAN|TILT],POSITION|SPEED|ACCELERATION|BASE|UPPER|LOWER|HOLD_POWER_LEVEL|MOVE_POWER_LEVEL|RESOLUTION])
4.3.1.2. Protocolo de comunicaciones
Con la estructura jerarquizada de órdenes vista en la figura 3.7 se pretende establecer una torre
de protocolos similar a la utilizada en comunicaciones entre ordenadores.
Como en todo protocolo de comunicaicones, los elementos de una determinada capa utilizan los
servicios ofrecidos por la capa inferior para realizar su labor.
Queda por analizar la comunicación desde el punto de vista de los niveles más bajos de la torre,
es decir, el formato de la trama usada para la transferencia de la información. Para poder dialogar
con el controlador debe establecerse un formato de trama que permita un mutuo entendimiento.
Dos hechos fundamentales son los que hacen de esta comunicación con el Pan&Tilt una
transferencia simple y flexible de información:
1. La casi totalidad de los diálogos que se establecen entre el PC y el controlador son promovidos
por el mencionado ordenador, o sea, iniciados por éste.
2. La comunicación está basada en la existencia de unos códigos de operaciones que simplifican
notablemente la información que debe transferirse. Así pues, para indicar un posicionamiento,
por ejemplo, basta con enviar el código del movimiento del eje deseado, seguido por la nueva
posición. Tras esta operación, debe esperarse la confirmación por parte del controlador indicando
que ha sido aceptada la orden.
Partiendo de estos dos puntos puede precisarse la forma en la que se realiza el diálogo entre los
dos dispositivos en cada una de las dos modalidades posibles, que son:
• Modificación de parámetros, donde se le indica al controlador el nuevo valor de una cierta
propiedad cinemática. Se realiza con la orden de alto nivel Set_Desired.
• Demanda de parámetros, donde se pregunta al controlador por el valor de una determinada
propiedad cinemática. Realizada con las funciones Get_Desired o Get_Current.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
Estos modos quedan recogidos en la figura 3.8, donde el diagrama superior corresponde al caso
descrito en primer lugar, mientras que el inferior pertenece al segundo de ellos. Como puede
observarse, ambas son iniciadas por el PC.
En la primera situación, haciendo uso en primer lugar de la orden SerialOut se envía el código
de la operación que se desea realizar. Seguidamente, es enviado el nuevo valor con alguna de las
funciones siguientes:
• PutSignedShort, para indicar posiciones (de ahí que sean con signo, puesto que las posiciones
pueden ser tanto positivas como negativas), u otros códigos.
• PutUnsignedShort, para indicar velocidades (sólo positivas, lógicamente).
• PutSignedLong, para las aceleraciones.
Arquitectura software del sistema
Figura 4.8: Cronogramas de la modificación de propiedades
cinemáticas y demanda de valores, respectivamente
Software para la unidad Pan&Tilt
Tras estos envíos, debe recibirse la confirmación por parte del controlador. En el caso de
demanda de parámetros, la forma de proceder es similar: se envía el código de la operación y se
espera la llegada de la respuesta, que en esta situación, actúa de confirmación. De forma análoga, la
lectura se realiza con alguna de las funciones:
• GetSignedShort.
• GetUnsignedShort.
• GetSignedLong.
Un aspecto que no debe pasarse por alto es lo que en la figura 3.8 se ha dado en denominar
Ciclo de Acceso. Con este sobrenombre queda definido el intervalo de tiempo transcurrido desde
que se inicia la comunicación, es decir, desde que es enviado en primer código a través de la línea
serie, hasta que es recibida la confirmación y es posible continuar con la ejecución del código.
No debe olvidarse que toda la comunicación entre el PC y el controlador se lleva a cabo a través
de la línea serie. Si bien el flujo de información puede circular en ambas direcciones, no puede
ocurrir esto de forma simultánea. Es, por tanto, una comunicación semiduplex.
Así pues, no se debe acceder al controlador mientras en otro proceso se está esperando una
respuesta. En definitiva, habrá que garantizar la exclusión mutua durante el ciclo de acceso de tantos
procesos como haya ejecutándose. Es por ello que, como se comentó al principio del apartado 3.1,
se han incluido mutex en las funciones que hacen uso de la comunicación por el puerto serie, ya sea
en lectura o en escritura. De esta forma, se garantiza la exclusión de forma transparente desde el
punto de vista de los niveles superiores.
4.3.2.Software de control
Para el control del Pan&Tilt es imprescindible si se quiere realizar de forma adecuada, que las
consignas de control se proporcionen cada cierto tiempo constante, así como el muestreo de la
posición (para efectuar control en bucle cerrado) y la actualización de las variables. Es por esto que
se ha implementado un hilo independiente que se encarga de ello. Este hilo debe permanecer
inactivo durante el intervalo de muestreo y saltar cuando llegue el momento.
Existen diversas opciones para la espera del hilo, de las que se destacan:
• Hacer una llamada a la función wait(ms), que hacer que se produzca una espera de al menos el
valor en milisegundos que se le pasa como argumento. El problema de esta opción es que no se
asegura la ejecución de forma perfectamente periódica ya que como se comenta, la espera es de
al menos un cierto tiempo. En caso de que la carga computacional no sea excesiva y no existan
Arquitectura software del sistema
Software para la unidad Pan&Tilt
otros hilos que interfieran en el funcionamiento, esta opción proporciona resultados más o menos
aceptables, pero no constituye un mecanismo robusto para el control.
• Hacer uso de una función temporizada o timer que salte cada intervalo de muestreo. Existe una
forma fácil de implementar esta opción, y es mediante la utilización de las funciones
proporcionadas por la librería glib.
La librería glib cuenta con una imnumerable cantidad de funciones, entre ellas timers. Lo que
hace realmente interesante esta librería es el hecho de que constituyen una capa software
intermedia entre la aplicación y el sistema operativo, haciendo uso de funciones del SO de forma
transparente para el usuario e independiente de la plataforma. Es por ello que se puede portar el
código sin cambiar absolutamente nada a otro sistema operativo.
4.3.2.1. Implementación del hilo de control
La implementación adoptada es esta última. La solución consiste en crear un hilo de control y
asociarlo a la activación de una señal procedente de un temporizador. Si se hace esto simplemente,
se dan problemas en la ejecución, ya que en la parte gráfica se usa la librería gtk, que está sobre glib,
por lo que se generan conflictos entre los eventos procedentes de la parte gráfica y los relativos al
temporizador, quedando la ejecución del último relegada al final del proceso.
La solución a este problema consiste en crear un bucle de eventos independiente para el control
del Pan&Tilt. Para llevar a cabo este proceso, se hace uso de las siguientes funciones:
• GMainContext *g_main_context_new(void): esta función es la encargada de crear el nuevo
bucle de eventos. Devuelve un puntero al nuevo contexto.
• gboolean g_main_context_acquire(GMainContext *context): esta función sirve para
convertirse en el propietario del contexto especificado como argumento. Se le pasa un puntero al
contexto y devuelve TRUE si se ha ejecutado con éxito y FALSE en caso contrario.
• GMainLoop *g_main_loop_new(GMainContext *context, gboolean is_running): esta función
crea y devuelve una nueva estructura de tipo GMainLoop, que es un tipo de dato opaco que
respresenta el bucle de eventos principal en una aplicación GLib o GTK+. Se le pasa puntero al
contexto al que se quiere asociar y booleano que indica si el bucle se está ejecutando.
• GSource *g_timeout_sorce_new(guint interval): crea una nueva fuente de timeout. La fuente no
estará asociada inicialmente con nigún GMainContext, y debe ser añadido a un contexto
mediante el uso de la función g_source_attach() antes de ser ejecutado. Se le pasa el intervalo de
tiempo entre cada dos ejecuciones en milisegundos y devuelve un puntero al timeout creado.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
• guint g_source_attach(GSource *source, GMainContext *context): añade un timeout a un
contexto, de forma que será ejecutado dentro de ese contexto. Se le pasa un puntero al timeout,
puntero al contexto y devuelve el identificador de la fuente dentro del contexto.
• void g_source_set_callback(GSource *source, GSourceFunc func, gpointer data,
GDestroyNotify notify): establece la función de callback para una fuente. Se le pasa puntero a la
fuente de temporización, nombre de la función de callback, puntero al dato a pasar como
parámetro y una función para ejecutar cuando el parámetro no se va a usar más o NULL. En este
caso, la función de callback se denomina rtempo, y se le pasa un puntero al bucle de eventos.
• void g_main_loop_run(GMainLoop *loop): ejecuta un bucle principal hasta que se llama a
g_main_loop_quit(). Se le pasa puntero al bucle de eventos.
Todas estas funciones son usadas desde una función llamada escritor(), y que sirve para arrancar
el proceso y terminarlo.
Además de esto, está el hilo que se quiere ejecutar cada cierto tiempo fijo y que es el que se
encarga del control del Pan&Tilt. Esta función se denomina rtempo, y su pseudocódigo es el
siguiente:
si (final_escritor){
final_escritor = 0g_main_loop_quit(GMainLoop p)
}bloquea mutexcopia posición objetodesbloquea mutexactualización de variablesextrapolación de la posiciónllamada a función que implementa controlador PID del P&Tdevuelve TRUE
Como se puede observar en el pseudocódigo, existe una variable que controla la finalización del
hilo de control, que es final_escritor. Esta variable está normalmente a 1, hasta que el algoritmo de
seguimiento finaliza su tarea, momento en el cual la pone a 1, terminando así la ejecución la
siguiente vez que salta el timeout.
De esta forma, se comprueba empíricamente que la desviación en el tiempo de ejecución del hilo
de control es de pocos milisegundos en el caso de Windows, lo cual está en el límite de las
capacidades de temporización de este sistema operativo.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
Nótese la importancia de los tiempos en esta aplicación: en el momento en el que se efectúa el
control de la unidad, la posición del objeto no será la misma que cuando se capturó la imagen. Es
por ello que se hace una extrapolación lineal de la posición del móvil usando la imagen actual y la
anterior, y el tiempo transcurrido desde que se tomó la imagen.
Para que el control funcione correctamente, los instantes de la toma de la imagen y de la
consulta de la posición del Pan&Tilt deben ser el mismo. Es por ello que, cuando salta el control del
Pan&Tilt, se muestrea la posición del mismo y se extrapola linealmente la posición del objeto móvil,
desde la que tenía en el instante en el que se tomó la imagen, hasta el instante actual, teníendose
para ello almacenada la posición anterior.
En la figura se observan en negro los puntos de captura y en rojo los de control. Lógicamente
existe un retraso desde que se tomó la imagen hasta que salta el hilo de control, retraso que se
almacena en una variable delay_act. En la extrapolación se aplica la siguiente expresión:
Fórmula 4.1: Expresión para la extrapolación de la posición
Directamente, no se tienen los valores de los tiempos de adquisición por ser más complejo para
el tratamiento, por lo que se ha optado por expresar la diferencia de tiempos de adquisición como:
Fórmula 4.2: diferencia de tiempos de adquisición
Arquitectura software del sistema
Figura 4.9: Tiempos de captura y control
pext1= pn1 pn1− pnt acq1−t acq
⋅n1T−t acq1
t acq1−tacq=n1T−nT−delayactdelayant
Software para la unidad Pan&Tilt
Nótese que se podía haber expresado la diferencia entre los instantes de ejecución del hilo de
control como el tiempo que se configuró para el timeout. No obstante, esto sería una aproximación,
ya que los tiempos entre ejecuciones no son exactamente constantes como sería deseable. La
aproximación no sería del todo mala, pero ya que el hecho de extrapolar es en sí mismo una
aproximación, se ha preferido, al menos, hacer el cálculo de la forma más precisa posible.
4.3.2.2. Funciones para el manejo del Pan&Tilt
Por encima de las funciones que proporcionan los mecanismos de comunicación y control del
Pan&Tilt, se han desarrollado una serie de funciones que se encargan de llevar a cabo todo el
proceso de configuración, control y finalización. Las funciones son las siguientes:
• void ini_escr(char *nombre): esta función inicializa la escritura. Para ello inicializa el Pan&Tilt,
crea un mutex para la exclusión mutua en el momento del acceso a los datos por parte de
manda_datos y el hilo de control y crea el hilo de escritura.
• void fin_escr(void): finaliza la ejecución del hilo de control modificando el valor de
finaliza_escritor y espera a su finalización.
• void manda_datos(void *p, int tam): esta función constituye la interfaz entre la parte de visión y
la de control, proporcionando al algoritmo de control los datos obtenidos del procesamiento de
las imágenes. Se le pasa puntero a la posición del objeto y el tamaño del puntero.
• static void *escritor(void *p): esta función es la encargada de arrancar el proceso de control.
Crea el bucle de eventos independiente y lanza la función temporizada. Al salir del bucle
principal, inhabilita los datos, finaliza el puerto serie y libera el mutex de acceso a datos.
• gboolean rtempo(gpointer *p): como se comentó anteriormente, esta función implementa el hilo
de control temporizado, encargándose de la toma de datos, extrapolación de la posición del
objeto al instante actual y llamada a la fucnión que implementa el controlador.
• void inicio_pan(void): inicia el Pan&Tilt: abre el puerto serie, resetea el Pan&Tilt en caso
necesario y lo configura.
• char config_pt(void): configura el Pan&Tilt, estableciendo la velocidad de base y
posicionándolo en el origen de coordenadas.
• void contr_pt(void): implementa el controlador del Pan&Tilt. Para ello, lee la posición actual del
Pan&Tilt y calcula el error respecto a la posición del objeto, lo cual sirve de entrada al
controlador, aplica las ecuaciones de control, saca las actuaciones y actualiza las variables. Se
implementa asimismo una saturación de la actuación, de forma que no se superen los límites de
velocidad permitidos.
Arquitectura software del sistema
Software para la unidad Pan&Tilt
• int ang_act_ptu(ptu_param *ptu): lee las posiciones y velocidades del Pan&Tilt.
Arquitectura software del sistema
Software de comunicación con el PC de control
4.4.Software de comunicación con el PC de control
4.4.1.Introducción
Una vez tratados los aspectos concernientes a la adquisición de imágenes y a la unidad de
Pan&Tilt, queda comentar la comunicación con el PC de control romeo4b.
Mediante la adquisición de imágenes y control del Pan&Tilt se puede efectuar el seguimiento
del objeto móvil por parte de la cámara. Para el seguimiento por parte del vehículo, es necesario que
el PC de control sepa la posición del objeto a seguir en cada momento. Es por ello que se necesita
tener un mecanismo de comunición entre ambos PC's, de forma que se le pasen al de control los
datos obtenidos por el de visión mediante el algoritmo de seguimiento.
El dato a transmitir es la posición del objeto en la imagen (coordenadas x e y). Por tanto, cada
vez que se ejecute el hilo de control del Pan&Tilt, momento en el cual se realiza la extrapolación de
la posición del móvil, se le pasen de alguna forma los datos al romeo4b.
Así pues, es en la función que implementa el controlador del Pan&Tilt en la que se ha insertado
una llamada a una función de comunicación que se detallará más adelante.
Tanto el romeo4a como el romeo4b tienen conexión a una red Ethernet con protocolos TCP/IP,
el mecanismo de comunicación utilizado son los sockets, que permiten un comunicación
bidireccional, aunque en este caso sólamente se utilizará la transmisión por parte del PC de visión, y
la recepción por parte del de control. Los sockets permiten la comunicación entre procesos incluso
en distintas máquinas como es el caso.
A continuación se describirá la implementación software realizada. Para una descripción más
exhaustiva de los sockets, consultar apéndice A.3: Características de los sockets.
4.4.2.Implementación software
Teniendo en cuenta todo lo planteado anteriormente, parece evidente la necesidad de
implementar la comunicación entre las dos máquinas a través de sockets.
Para ello, debe tenerse presente que en el interior de un proceso, un socket se identificará por un
descriptor de la misma naturaleza que los que identifican los archivos. Esta propiedad esencial
resulta de suma utilidad en determinadas aplicaciones, dado que permite, por ejemplo, redireccionar
archivos de entrada/salida estándar a los propios sockets. Igualmente significa que todo nuevo
proceso hijo creado a partir del padre heredará los manejadores de sockets de éste.
Arquitectura software del sistema
Software de comunicación con el PC de control
4.4.2.1. Creación y enlazamiento del socket
La creación e inicialización de un socket se realiza a través de la primitiva socket, que da
como valor de retorno el manejador sobre el cual es posible realizar operaciones de lectura y
escritura. Mediante esta operación se generan e inicializan las entradas en las diferentes tablas del
sistema de gestión de archivos, almacenándose así las estructuras de datos conteniendo las
características del socket.
En este caso, dado que será utilizado el dominio Internet, AF INET, y el tipo datagrama,
SOCK_DGRAM, serán incluidas precisamente estas etiquetas en la llamada a esta primitiva. Junto
con ellas, se indica que será utilizado el protocolo por defecto con un valor 0 como tercer parámetro.
Todo ello está contenido en la función abre_sock en el fichero de código misocket.c,
desarrollado por el Doctor D. Joaquín Ferruz Melero, y que funciona tanto en Windows como en
Linux. Aquí se encuentra la instrucción:
socket(AF_INET,SOCK_DGRAM,0)
Una vez abierto y creado el socket con esta orden, es necesario fijar la dirección de su dominio
al nuevo objeto creado. Esto se realiza a través de la primitiva bind. De no ser así, el socket no
sería accesible más que por los procesos que conocen su descriptor. Esta primitiva, que sólo utiliza
el proceso servidor, permite dar un nombre a un socket con una única operación.
Lo que sí es necesario tanto en el proceso cliente como en el proceso servidor es configurar
adecuadamente la conexión. Esto se realiza a través de la estructura de datos propia de cada
dominio. En el dominio Internet, las direcciones de los sockets tienen la estructura:
struct in_addr {u_long s_addr;
}struct sockaddr_in {
short sin_family;u_short sin_port;struct in_addr sin_addr;char sin_zero[8];
}
donde los campos referencian:
– sin_family, la familia de la dirección. En este caso, AF INET.
– sin port, el número de puerto.
– sin_addr, la dirección Internet.
– sin_zero, un campo de 8 ceros.
Arquitectura software del sistema
Software de comunicación con el PC de control
Lógicamente, es necesario para ello conocer la dirección de la máquina local, y la elección de un
puerto con el que será asociado el punto terminal de la conexión utilizada, o sea, el socket.
4.4.2.2. Envío y recepción de datos
Una vez inicializado convenientemente el socket, está en disposición de comunicar los dos
extremos.
A fin de simplificar la estructura de las funciones utilizadas para las operaciones de envío y
recepción de información entre un extremo y otro del socket, se crearon unas funciones que mapean
directamente los campos de datos sobre las primitivas de comunicación que soportan la transmisión
en forma de datagramas. Estas primitivas son:
int sendto (SOCKET manejador_socket, const char *mensaje, intlong_mensaje, int opcion, const struct sockaddr*dir_socket, int long_dir);
int recvfrom (SOCKET manejador_socket, char *buffer , intlong_buffer, int opcion, struct sockaddr*dir_socket, int *long_dir);
siendo los distintos parámetros:
• manejador_socket, son los manejadores del socket de emisión y recepción respectivamente.
• mensaje y buffer, son las direcciones del mensaje que será enviado y de la zona donde se
alojará el mensaje recibido.
• long_mensaje y long_buffer, son, respectivamente, las longitudes del mensaje y del
tamaño reservado para la recepción.
• opcion, es una variable lógica que, en el caso de datagramas, es cero obligatoriamente.
• dir_socket, es la dirección del socket destino y la zona para recuperar la dirección de
expedición en uno y otro caso.
• long_dir, representa la longitud de la dirección anterior. Nótese que en el segundo de los
casos es un puntero a tal dato.
Estas órdenes permiten enviar o extraer mensajes de sockets de los cuales posean descriptores,
no obstante, como se comentó anteriormente, se utilizaron unas funciones encargadas de mapear los
datos sobre éstas. Estas otras funciones son, respectivamente, envia sock y recibe sock.
Éstas, como puede verse en el fichero de código misocket.c reducen considerablemente el número
de parámetros necesarios en su llamada, lo que simplifica ligeramente el código.
Arquitectura software del sistema
Software de comunicación con el PC de control
Por último, para concluir con este aspecto software, debe insistirse en una cuestión comentada
previamente: los únicos errores que pueden ser detectados en la situación en la que se trabaja son los
que se producen de manera local en cada nodo de la comunicación, o sea, los que se produzcan en
las llamadas a estas funciones. Al tratarse de una comunicación no orientada a conexión y sin
asentimiento, no habría ninguna forma de saber que un mensaje enviado por uno de los nodos no ha
llegado a su punto de destino. Si el mensaje se pierde ninguno de los nodos recibirá ninguna
notificación. En este sistema, puesto que físicamente consiste en dos máquinas unidas por un
segmento de red, la probabilidad de que esto ocurra se reduce al mínimo. No obstante, en el caso en
que así sucediera, no traería mayores consecuencias (a no ser que se rompiera el enlace y se
perdieran todos los mensajes), puesto que la frecuencia con la que son enviados éstos es suficiente
como para poder prescindir de algunos de ellos.
Arquitectura software del sistema