Graduado en Ingeniería Informática Universidad Politécnica de Madrid Escuela Técnica Superior de Ingenieros Informáticos TRABAJO FIN DE GRADO


Save this PDF as:
 WORD  PNG  TXT  JPG

Tamaño: px
Comenzar la demostración a partir de la página:

Download "Graduado en Ingeniería Informática Universidad Politécnica de Madrid Escuela Técnica Superior de Ingenieros Informáticos TRABAJO FIN DE GRADO"

Transcripción

1 Graduado en Ingeniería Informática Universidad Politécnica de Madrid Escuela Técnica Superior de Ingenieros Informáticos TRABAJO FIN DE GRADO Interfaz gráfica de usuario para configuración y supervisión de sistemas robóticos aéreos Autor: Jorge Luis Pascual y Mora Director: Martín Molina González MADRID, ENERO 2018

2 1

3 Índice Resumen 4 Abstract 5 Introducción Objetivos Organización de la memoria 7 Trabajos previos Aerostack Conclusiones 10 Diseño Graphical User Interface Resumen de extensiones y mejoras Paquete MainWindow Requisitos Diseño gráfico Diseño de clases Paquete ControlPanel Requisitos Diseño gráfico Diseño de clases Paquete Connection Requisitos Diseño de clases Paquete VehicleDynamics Requisitos Diseño gráfico Diseño de clases Paquete TabManager Requisitos Diseño gráfico Diseño de clases Paquete UserCommander Requisitos 26 2

4 3.6.2 Diseño de clases Paquete ParameterTemporalSeries Requisitos Diseño gráfico Diseño de clases Paquete CameraViewer Requisitos Diseño gráfico Diseño de clases Paquete FirstPersonView Requisitos Diseño gráfico Diseño de clases Paquete ExecutionViewer Requisitos Diseño gráfico Diseño de clases Paquete EnvironmentViewer Requisitos Diseño gráfico Diseño de clases 42 Implementación 47 Validación Pruebas unitarias Integración de los paquetes Pruebas de vuelo simulado Pruebas de vuelo real 53 Conclusiones 55 Referencias 56 Anexo A: Pruebas para validación del sistema. 58 3

5 Resumen Este proyecto se centra en el análisis exhaustivo del interfaz gráfico de usuario perteneciente al proyecto Aerostack y su rediseño, tanto a nivel visual para mejorar la estética y usabilidad como a nivel de estructura de código para mejorar su calidad y aumentar la modularidad y escalabilidad del software. En este documento se tratan, en concreto, el análisis tanto de código como de diseño gráfico, diseño de los cambios propuestos, implementación y validación sobre los distintos módulos o componentes que conforman el interfaz gráfico de usuario. Esta interfaz permite al usuario realizar actividades como: Visualización de un mapa simulado en 2D, valores numéricos de la posición del robot y datos sobre la misión que está siendo ejecutada, entre otros. Creación y edición de mapas de manera visual. Utilización de diversos modos de operación: teleoperación con el teclado, ejecución de misiones guiadas por diferentes lenguajes (Python, TML o behavior trees). 4

6 Abstract This project is focused on the comprehensive analysis of graphical user interface that belongs to the Aerostack project and it s redesign, both at a visual level to get better aesthetics and usability and at a code structure level to improve its quality and enhance the software s modularity and scalability. This very document contains both the code and graphical design analysis, the design of the proposed changes, the implementation of those changes and the validation of every and each component of the graphical user interface. This interface allows the user to perform different tasks: Visualization at a glance of a 2D simulated map, numerical values of the robot s position and data about the mission that s currently being executed, amongst others. Map creation and edition. Utilization of diverse operation modes: keyboard teleoperation, execution of missions guided by different types of languages (Python missions, TML or Behavior Trees). 5

7 Capítulo 1 Introducción Décadas atrás, aún durante el auge de las primeras computadoras y el comienzo de la automatización de procesos industriales, ya se especulaba dentro del ámbito de la ciencia ficción sobre un futuro dominado por vehículos voladores de uso diario, androides artificialmente inteligentes y, en general, una gran sinergia entre el ser humano y sus creaciones. Si bien estas ideas eran extremadamente revolucionarias para su época, no se alejan demasiado de lo que ha deparado, y deparará, el futuro. El amplio espectro de posibilidades en que se podría utilizar la robótica es un muy gran incentivo para que diferentes empresas, patrocinadores y laboratorios de investigación continúen investigando en innovadoras formas de hacer que un robot sea lo más autónomo y eficiente posible en la realización de las tareas para las que esté diseñado. Sin embargo, la humanidad aún no ha conseguido crear una inteligencia artificial pura, que sea imposible de distinguir de un ser humano; y es por este motivo que se necesita el desarrollo de programas que sirvan de una interfaz entre los procesos informáticos del robot y el supervisor humano, mediante la cual dicho operador pueda monitorizar que la actividad que está realizando el robot siga un curso correcto y le permita actuar al instante en caso contrario, de manera que no puedan prevalecer las decisiones del robot sobre las decisiones del supervisor. En este documento se va a tratar precisamente este tema, el diseño, desarrollo y mejora de una interfaz gráfica que nos permite comunicarnos, en este caso, con un vehículo aéreo no tripulado. Esta interfaz gráfica en concreto está diseñada para su integración y uso con el framework Aerostack, que está creado conjuntamente por un equipo perteneciente a la Escuela Técnica Superior de Ingenieros Industriales y un equipo de la Escuela Técnica Superior de Ingenieros Informáticos, del que forma parte el autor del presente informe. El objetivo de este framework es el de dotar a diferentes tipos de vehículos aéreos no tripulados de una navegación autónoma ante entornos complejos y dinámicos que, además de proporcionar directamente una serie de algoritmos y funcionalidades que permiten al robot moverse, situarse y realizar diferentes actividades autónomamente, también le proporciona al usuario o desarrollador una serie de herramientas que le facilitan la creación de sus propios algoritmos o procesos. Una parte muy importante del proyecto es la llamada Graphical User Interface o, abreviando, la GUI. La función de este módulo consiste en representar gráficamente de una manera sencilla y entendible la información más importante sobre las tareas que está llevando a cabo el robot en tiempo real. También facilita la interacción máquina - operador, así como la intervención de dicho operador sobre los procesos del robot en caso de emergencia. 6

8 1.1 Objetivos El objetivo general del presente trabajo es construir una interfaz gráfica de usuario para operar con robots aéreos que opere de forma integrada en el entorno Aerostack. Este trabajo cuenta como antecedente la disponibilidad de un prototipo parcial de dicha interfaz. Por tanto, el presente trabajo se ha centrado en la revisión de dicho prototipo para la realización de extensiones y mejoras que permitan ofrecer una interfaz de usuario de mayor calidad de operación y mantenimiento. En concreto, los objetivos planteados son los siguientes: 1. Análisis del problema de operación con robots aéreos. Se trata de conocer con detalle las necesidades de operación con robots y el entorno Aerostack. 2. Diseño de la interfaz gráfica de usuario. El objetivo es contemplar de forma global el diseño de la interfaz, sobre el que se realizan extensiones y mejoras. 3. Programación de la interfaz gráfica de usuario. Se trata de hacer uso del correspondiente lenguaje de programación para construir las mejoras y extensiones contempladas. 4. Validación. El objetivo es validar la correcta operación de la interfaz gráfica de usuario desarrollada mediante los oportunos conjuntos de pruebas. 1.2 Organización de la memoria En primer lugar se introducirá el estado actual de las soluciones existentes que intentan mejorar la comunicación con vehículos aéreos no tripulados. Seguidamente se explicará el diseño de la Graphical User Interface que se heredó de las primeras iteraciones y apertura al público del proyecto Aerostack, y se justificarán los cambios en dicho diseño que se han realizado a lo largo del tiempo, acompañando dicha justificación con un diagrama de clases de la interfaz y una explicación de la jerarquía de clases que se ha diseñado, así como se justificarán el diseño gráfico de los diferentes elementos de la interfaz, su posicionamiento dentro de la misma, sus funcionalidades, etcétera. Acompañando estas explicaciones se añadirá una sección que tratará la validación de las funciones de la GUI unitariamente, así como la validación de la integración de susodicha interfaz con el resto del proyecto Aerostack. Por último se expresarán unas conclusiones personales fruto del desarrollo de este trabajo y de los resultados obtenidos. 7

9 Capítulo 2 Trabajos previos Este apartado del documento tiene como fin describir entorno y el contexto en el que se ha realizado el proyecto. Se describe el estado actual del framework Aerostack con el fin de establecer un marco que justifique el propósito de este proyecto. 2.1 Aerostack Aerostack [Sanchez-Lopez et al 2016; 2017] es un framework diseñado para ayudar a los desarrolladores a diseñar y crear una arquitectura de control para vehículos aéreos no tripulados, y por este fin está disponible para cualquier persona que desee utilizarlo. Integra varias soluciones computacionales como son algoritmos de visión computacional, controladores de movimiento, autolocalización, sistemas de mapeo y algoritmos de planificación de trayectorias entre otros. La problemática que supone la comunicación entre los procesos que están siendo ejecutados en una máquina y los procesos del propio robot se ha solucionado usando el middleware ROS Kinetic [Camporredondo, 2017]. Este middleware facilita dichas comunicaciones proporcionando una API para los lenguajes C++ y Python que sigue un modelo editor - suscriptor. Un modelo de este tipo permite que diferentes procesos intercambien información de forma desacoplada, es decir, no existe ningún vínculo entre un proceso emisor de datos y otro proceso cualquiera receptor de dichos datos salvo que el primero se declara publisher de un determinado topic y el último se declara subscriber de este mismo topic. Este desacoplamiento a la hora de intercambiar datos importantes para muchos procesos facilita la creación de nuevos procesos o la modificación de procesos existentes. Otra ventaja que nos aporta este sistema editor - suscriptor es la posibilidad de crear con mucha facilidad funciones callback, esto es, funciones que únicamente van a ser ejecutadas en el momento en que se publique un mensaje en un topic en concreto al que estamos suscritos y al que está asociada dicha función callback. Esta posibilidad aumenta mucho la eficiencia individual de diferentes procesos que, en caso de no usar funciones callback, tendrían que implementar un bucle en un thread receptor que estuviese constantemente comprobando si se han recibido nuevos datos, lo que reduciría cuantiosamente la eficiencia del proceso. Si bien se viene dada en el paquete una estructura ejecutiva totalmente funcional (Figura 2.1), Aerostack permite a los desarrolladores crear su propia jerarquía de ejecución, sus propios comportamientos (behaviors), etc. También se le permite al usuario adherir a la presente estructura ejecutiva un nuevo behavior o incluso modificar un behavior existente para que se ajuste a las necesidades del usuario. 8

10 Figura 2.1. Arquitectura de ejecución de Aerostack. Esta arquitectura de ejecución está cimentada, como puede verse en la Figura 2.1 [Molina, 2017], sobre el middleware ROS y los controladores que trabajan a bajo nivel con el robot. Al mismo nivel que estos pilares se encuentran los procesos de supervisión y de administración de otros procesos. Sobre estas tres capas básicas, encontramos los módulos de percepción, planificación, control y comunicación que son utilizados en conjunto por el módulo Execution Engine. El Execution Engine trabaja con dos conceptos básicos, los behaviors y los beliefs. Los behaviors son, en general, acciones sencillas que se ejecutan mediante el uso de servicios utilizando las herramientas de comunicación que nos proporciona ROS. En general todo behavior dispone de varios servicios que permiten realizar un start y un stop. Esto facilita el control o supervisión de la correcta ejecución de cada behavior. Si bien cada behavior constituye una actividad simple, como pueden ser despegar o moverse a un punto determinado, se pueden combinar a la hora de elaborar una misión para llevar a cabo actividades más complejas. Este submódulo del Execution Engine también comprende una serie de procesos que establecen y coordinan la ejecución de los behaviors; se establecen los behaviors disponibles según se hayan descrito en un fichero BehaviorCatalog.yaml y se coordina su ejecución con el proceso BehaviorCoordinator, que controla en todo momento que no se puedan ejecutar behaviors incompatibles entre sí. 9

11 Por otro lado también nos encontramos con el submódulo que comprende los procesos relacionados con los beliefs. Los beliefs son expresiones sencillas que el robot considera como verdaderas, y en su combinación y guardado en memoria reside la base de conocimientos del robot. Es en esta base de conocimientos donde se pueden encontrar datos como el nivel de carga de batería, la posición actual del robot o un estado que describe la actividad que está llevando a cabo actualmente. Si bien estos datos son publicados y actualizados periódicamente por el robot, el submódulo dispone de una serie de servicios que permiten al desarrollador crear y publicar sus propios beliefs, así como la posibilidad de eliminarlos. De esta forma se puede disponer de bases de conocimiento personalizadas que se ajusten a las necesidades del desarrollador, garantizando flexibilidad por parte del Execution Engine. 2.2 Conclusiones La plataforma Aerostack pone a disposición del público general un framework de creación y ejecución de misiones con vehículos aéreos no tripulados pulido, modularizado, escalable y funcional. Permite a los desarrolladores modificar los módulos disponibles para que se ajusten a sus necesidades personales, así como permite integrar nuevos módulos en la existente arquitectura garantizando una correcta ejecución. Incluso permite sustituir por completo módulos existentes por nuevos módulos creados por los desarrolladores siempre y cuando se integren apropiadamente con el resto de la arquitectura. Por estos motivos es una plataforma extremadamente útil a la hora de la investigación y experimentación con vehículos aéreos no tripulados. Para facilitar la operación con el sistema robótico y, como parte del entorno Aerostack, se planteó desde su concepción la necesidad de disponer de una interfaz gráfica de usuario. Para ello, se construyeron diversos prototipos parciales experimentales [De la Hoz, 2015; Gordo, 2016; García, 2016; Sánchez 2017a; Sánchez 2017b; Valencia, 2017] en las versiones iniciales de Aerostack en las que se apoya el presente trabajo. 10

12 Capítulo 3 Diseño En este capítulo se pretende describir y justificar el diseño de la arquitectura que se ha construido para formar el módulo Graphical User Interface. Se describirán los diferentes submódulos existentes y sus funcionalidades, así como la arquitectura interna de dichos submódulos. Toda solución que se ha acabado implementando se ha diseñado teniendo en cuenta que se quiere disponer de la máxima modularidad de software posible entre los submódulos que juntos conforman la GUI. De este mismo modo y pensando en el futuro uso y desarrollo de la GUI, se ha pretendido que también proporcionen una buena oportunidad de escalabilidad, así como se ha pretendido que se otorgue la máxima facilidad posible a la hora de mantener el código implementado. Esto se ha conseguido implementando el código de manera que toda funcionalidad quede explicada mediante comentarios y que funcionen de manera independiente. Así, un desarrollador puede separar un submódulo de la interfaz sin que esta deje de funcionar y mantenerlo o crear nuevas funcionalidades para el mismo pudiendo mantener el correcto funcionamiento del resto de componentes de la GUI. Debido a que la GUI está fuertemente apoyada en las librerías aportadas por el framework de diseño gráfico Qt, el lector se encontrará con referencias a conceptos relacionados con dicho framework, tales como pueden ser eventos, señales, conexiones, o cualquier objeto definido por dichas librerías; que son fácilmente distinguibles porque sus nombres siempre comienzan con una Q (p. ej.: QPushButton, QComboBox, QWidget, etc.). 3.1 Graphical User Interface La GUI (Figura 3.1) o Graphical User Interface es un conjunto de procesos que constituyen un módulo muy importante dentro del proyecto Aerostack. Si bien su ejecución no es necesaria para el correcto funcionamiento del resto de procesos, proporciona al usuario la posibilidad de interactuar visualmente e intuitivamente con el robot y viceversa, proporciona al robot un medio por el cual comunicar diferentes datos críticos para la supervisión del vuelo por parte del usuario. A continuación de una breve explicación de sus funciones generales, se explicará el diseño arquitectural que ha sentado los pilares de este módulo definiendo el diseño y funciones de los diferentes paquetes o submódulos que componen la GUI. 11

13 Figura 3.1. Pantalla principal del interfaz de usuario En general, la Graphical User Interface le permite a su usuario realizar los siguientes tipos de tareas: Especificar qué modo de operación va a seguir el robot. Antes del comienzo de una misión, el usuario puede especificar qué modo de operación quiere que use el robot. Los modos de operación disponibles son teleoperación por teclado, guiado por misiones Python, guiado por el Task Based Mission Planner o TML y guiado por BehaviorTrees. Operar el robot con acciones sencillas, como pueden ser moverse hacia delante o hacia atrás, rotar el robot en cualquier dirección o ejecutar acciones de emergencia en cualquier punto de la ejecución de cualquier modelo de operación, como puede ser un aterrizaje de emergencia. Monitorizar en todo momento las actividades que esté llevando a cabo el robot. Esto es especialmente importante en misiones complejas, en las que puede ser necesario redefinir la misión, pararla o abortarla o incluso reparar cualquier malfuncionamiento que pueda suceder durante la ejecución de la misión. Estas capas o niveles permiten a los diferentes tipos de usuario acceder a la información que desean analizar. Esto es, un usuario interesado en debuggear o mantener el software puede utilizar la capa de software para obtener más detalles de la ejecución de la misión, mientras que aquellos usuarios no familiarizados con arquitecturas software pueden consultar una información más abstracta, sin detalles específicos de la ejecución, en la capa de misión. La GUI es un importante módulo de Aerostack que implementa muchas funcionalidades diferentes e independientes entre sí. Por esto y por conseguir una buena escalabilidad, 12

14 modularidad y mantenibilidad se ha decidido establecer una jerarquía y separar dichas funcionalidades en diferentes submódulos específicos que se especializan cada uno de ellos en una funcionalidad concreta. Se puede enmarcar la arquitectura general de la GUI dentro del concepto MCV (modelo - vista - controlador). Este patrón de arquitectura software separa la lógica de operación de la interfaz de usuario. Esto se justifica porque el paquete GUI hace una primera separación en el sistema de ficheros con una carpeta denominada view, que contiene los ficheros correspondientes a iconos e interfaces, y otra carpeta denominada controller, que contiene todos los paquetes o controladores de los que se hablará en detalle más adelante. En la figura 3.2 podemos observar un esquema en forma de diagrama UML los paquetes o submódulos que conforman la GUI, así como la jerarquía de creación de los mismos. Figura 3.2. Diagrama UML de jerarquía de paquetes. Se ha optado por la jerarquía de la figura anterior (Figura 3.2) porque, de esta manera, se tiene un proceso principal o MainWindow que se encarga de, por un lado, crear la ventana principal en la que visualizamos la GUI y, por otro lado, crear y controlar los objetos correspondientes al resto de paquetes, colocándolos también en su posición correspondiente dentro de la ventana principal consiguiendo de esta manera el diseño gráfico que puede observarse en la Figura

15 De esta forma se centralizan internamente los componentes de la GUI en una única clase controladora, lo que facilita mucho las actividades de eliminación o adición de componentes, la mantenibilidad del sistema y la escalabilidad del mismo. A continuación se detallarán las características de los diseños de cada uno de los paquetes, explicando cuál es su función, adjuntando el diagrama de clases que lo componen y argumentando por qué se han diseñado de la manera en que se han diseñado Resumen de extensiones y mejoras A continuación se resume la lista de extensiones y mejoras que se han realizado en la interfaz gráfica como resultado de la realización del presente trabajo: Actualización integral del código de la GUI para su uso en conjunto con la arquitectura de ejecución Execution Engine. Soporte por parte de la GUI para cargar y ejecutar misiones Python. Rediseño gráfico del modelo de operación Behavior Tree. Implementación de un nuevo tipo de nodo remove_belief. Creación de listas de behaviors disponibles de manera dinámica. Rediseño gráfico del Control Panel. Creación de un módulo Execution Viewer que permita ver el estado actual de la ejecución de Aerostack, independientemente del modelo de operación que se esté usando. Esto es, poder observar los behaviors y beliefs activos en tiempo real, así como poder eliminarlos o añadirlos desde esta ventana. Rediseño de la implementación de la teleoperación por teclado. Rediseño de la implementación de las conexiones del framework Qt utilizadas en todo submódulo de la GUI. Documentación y adición de comentarios en el código para explicar el mismo, con el fin de apoyar y facilitar el futuro mantenimiento de la GUI. 3.2 Paquete MainWindow Este paquete constituye el núcleo de ejecución de la GUI. Por un lado, se encarga de inicializar el nodo de ROS con el fin de incluirlo en la lista de nodos existentes y poder trabajar con el modelo editor - suscriptor que nos proporciona este middleware. Por otro lado, se encarga de crear el layout o disposición de widgets general de la GUI, permitiendo añadir widgets dinámicamente al mismo. Este layout es rellenado con un determinado orden con el resto de widgets o paquetes de la GUI. 14

16 3.2.1 Requisitos Los requisitos que debe cumplimentar la implementación de este paquete son los siguientes: Creación e inicialización del nodo de ROS que representa la GUI. Creación de una ventana general y aplicación de un QGridLayout a la misma. Creación dinámica y organización gráfica de paquetes de la GUI. Los paquetes que crea la MainWindow pueden observarse en la Figura 3.2. Detección y procesamiento de órdenes por teclado para el modelo de ejecución Keyboard Teleoperation Diseño gráfico Debido a que este paquete conforma la ventana principal en la que se despliegan los paquetes del módulo GUI las únicas decisiones de diseño relevantes son las colocaciones de los diferentes widgets como se puede observar en la Figura 3.1. Por sí solo el paquete MainWindow constituiría una ventana en blanco Diseño de clases Se ha intentado conseguir para este paquete un diseño compacto debido a que, si bien sus funciones son cruciales, se pueden agrupar fácilmente. Disponemos, por tanto, de dos clases (Figura 3.3): un main y el propio main_window. De esta forma, la clase main se encarga de crear el proceso graphical_user_interface y el nodo ROS correspondiente, así como de crear el objeto main_window. Al crearse este objeto se crean a su vez los objetos correspondientes a los paquetes que se consideren necesarios y se ordenan dentro del QGridLayout para que se muestren por pantalla correctamente. También y al mismo tiempo, se suscribe este objeto a una serie de topics y servicios creados por la Execution Engine para poder manejar el modelo Keyboard Teleoperation. 15

17 Figura 3.3. Diagrama UML del paquete MainWindow 16

18 Los topics y services del ExecutionEngine que utiliza este paquete son: behavior_event: topic por el que se publican los estados de finalización de los behaviors. Cuando se reciba un mensaje por este topic se ejecutará la función callback behaviorcompletedcallback( ) que se utiliza para un manejo especial al rotar el robot con la teleoperación por teclado. initiate_behaviors: servicio que se llama al crear el objeto main_window. Nos sirve para iniciar behaviors básicos en la ExecutionEngine. activate_behaviors: servicio que se llama cada vez que se solicite un comportamiento mediante la teleoperación por teclado. inhibit_behaviors: servicio que se llama cada vez que se deba cancelar un behavior previamente activado mediante la teleoperación por teclado. Para implementar el modelo de ejecución Keyboard Teleoperation se ha decidido sobreescribir los eventos propios de Qt keypressevent y keyreleaseevent que detectan el pulsado y soltado de una tecla, respectivamente. Esto facilita mucho la tarea a la hora de la implementación debido a que la detección de teclas la hacen automáticamente las librerías de Qt, que envían eventos con el código correspondiente a la tecla en forma de un QKeyEvent. 3.3 Paquete ControlPanel El ControlPanel (Figura 3.4) es un pequeño widget situado en la esquina superior izquierda de la MainWindow y se encarga de mostrarnos información crucial con respecto al tipo del robot, el estado de la batería del robot, el tiempo de vuelo y el estado de la conexión WiFi con el robot. También nos permite seleccionar uno de los cuatro modos de operación, así como nos muestra botones o acciones característicos de cada modelo, como pueden ser comenzar una misión Python o abortarla, realizar un aterrizaje de emergencia, etc. Esta ventana se ha diseñado con el fin de ser lo más compacta posible sin restarle usabilidad. Así, el usuario puede obtener de un vistazo una gran cantidad de información y además se reduce a muy poco tiempo el que necesita para hacerse y acostumbrarse a utilizar esta crítica sección de la GUI Requisitos Los requisitos que ha de cumplimentar la implementación de este paquete son los siguientes: Mostrar el tipo del robot, el estado de la conexión con el mismo y el cronómetro de vuelo. 17

19 Permitir al usuario seleccionar uno de los cuatro modos de operación: Keyboard Teleoperation, Guided by Python mission, Guided by TML mission, Guided by behavior tree. Mostrar dinámicamente botones (acciones) directamente relacionados con el modelo de ejecución seleccionado y el estado del mismo Diseño gráfico Este widget es, de cara al usuario, de los más importantes de la GUI debido a las funcionalidades e información que aporta (Figura 3.4). Por esto se ha decidido un diseño gráfico que es a la vez compacto y fácil de comprender y utilizar. La simetría y consistencia gráfica entre las diferentes funcionalidades es clave para este propósito. Figura 3.4. ControlPanel con Keyboard teleoperation. El ControlPanel aporta información que no cambia si el usuario selecciona un modelo de operación diferente (nombre del robot, estado de la conexión, estado de la batería, tiempo de vuelo) mientras que, en la parte inferior, despliega una serie de funcionalidades que sí cambian según qué modelo de operación esté seleccionado, como puede observarse en la Figura 3.4 y

20 Figura 3.5. ControlPanel con Guided by Python mission Diseño de clases Para la implementación de este paquete se ha optado por disponer de una clase contenedora control_panel_view que se encarga de crear el propio objeto ControlPanel y mostrarlo, como se puede observar en el diagrama UML del paquete (Figura 3.6). El ControlPanel dispone de varias funciones conectadas cada una a una de las diferentes acciones que puede realizar el usuario, de manera que dichas acciones queden modularizadas y resulte un proceso simple el modificar o debugear dichas acciones por separado. Los topics y services del ExecutionEngine que utiliza este paquete son: activate_behavior: servicio llamado cada vez que se pulse un botón de despegue o de aterrizaje. list_of_active_behaviors: por este topic se recibe una lista con los behaviors que tiene el ExecutionEngine activos. Se recibe un mensaje por este topic cada vez que dicha lista se vea modificada por cualquier motivo. Cuando se reciben mensajes por este topic se ejecuta la funcion newbehaviorcallback(dronemsgsros::listofbehaviors) que se utilizará para el correcto manejo del cronómetro de vuelo. 19

21 Figura 3.6. Diagrama UML del paquete ControlPanel. 20

22 3.3 Paquete Connection El paquete Connection juega un rol muy importante en la calidad del diseño de la Graphical User Interface en cuanto a la modularidad y a la mantenibilidad de este software. En este paquete se concentran una serie de objetos cuyo objetivo es mantener unas suscripciones a sendos topics del ExecutionEngine por los que se reciben datos sensibles y muy usados del robot, como pueden ser su posición o velocidad, la trayectoria encontrada a la hora de moverse o las imágenes captadas por las cámaras. Otros paquetes que necesiten del uso de este tipo de datos deben obtener una referencia al objeto de este paquete que se corresponda con la recepción de unos datos determinados, ahorrándose tener que realizar él mismo dichas suscripciones. De esta manera se reducen considerablemente el número de suscripciones que realizaría la GUI porque quedan todas concentradas en un único objeto receiver. La clase connection se encargará de crear todos estos objetos receiver y de inicializarlos, quedando así concentrados en un único objeto. Por otro lado, este paquete también se encarga en el objeto general_connection de implementar conexiones de Qt que conectan una señal de un objeto de un paquete con una función de un objeto de otro paquete diferente. Esta decisión de diseño se hizo posteriormente a la finalización del código de la GUI debido a que se tenían conexiones de Qt esparcidas sin ningún tipo de orden aparente por todo el código. De esta forma, quedan todas concentradas en un único objeto y resulta más fácil tanto el proceso de aprendizaje por parte de un desarrollador como el proceso de mantenimiento Requisitos Los requisitos que ha de cumplimentar la implementación de este paquete son: Suscripción correcta de una serie de topics del ExecutionEngine y asociación de la recepción de mensajes por dichos topics a funciones callback. Conectar mediante la función QObject::connect( ) determinadas señales emitidas por objetos de un paquete en concreto a funciones contenidas en objetos de un paquete diferente Diseño de clases La clase principal de este paquete se denomina connection. Es esta la encargada de inicializar los diferentes objetos receiver como puede observarse en el diagrama de la Figura 3.7. Por otro lado, tenemos la clase general_connection inicializada por la MainWindow y utilizada por casi todos los paquetes de la GUI. Cada uno de estos receivers se encargan de suscribirse a diferentes topics guardando localmente los valores relevantes y de asegurar que se mantendrán dichas suscripciones. 21

23 Figura 3.7. Diagrama UML del paquete Connection 22

24 3.4 Paquete VehicleDynamics El propósito de este paquete es el de mostrar gráficamente, de una manera simple, los valores básicos de la posición y la orientación del robot. La posición del robot se divide en tres valores numéricos que se corresponden con los tres ejes de una dimensión 3D: el eje X, Y y Z. Estos valores, medidos en metros, están sujetos a la descripción del mapa virtual, es decir, el punto (0, 0, 0) coincide con la esquina inferior izquierda del mapa. La orientación del robot también se divide en tres valores numéricos que están medidos en grados, el yaw, pitch y roll y que se corresponden con el estándar de ángulos de navegación que nos permiten describir la orientación de un objeto en tres dimensiones Requisitos Los requisitos de este paquete son los siguientes: Captado en tiempo real de los cambios en los valores de la posición en el mapa del robot y de su orientación. Representación visual de dichos valores Diseño gráfico Los seis valores de posición (X, Y, Z) y de orientación (Yaw, Pitch, Roll) quedan representados como se puede ver en la Figura 3.8, en un recuadro con colores vivos. En una columna a la izquierda se pueden observar los valores de la posición del robot, y en una columna a la derecha quedan representados los valores de la orientación del mismo. Este widget se coloca posteriormente justo debajo del ControlPanel. De esta manera se dispone de toda la información necesaria del robot y la misión en un único lateral de la MainWindow. Figura 3.8. Vehicle Dynamics 23

25 3.4.3 Diseño de clases El diseño de este paquete es bastante directo. Se tiene (Figura 3.9) una clase contenedora VehicleDynamicsView que se encarga de crear y colocar correctamente dentro de la MainWindow el propio objeto VehicleDynamics. La única tarea que lleva a cabo este objeto es una iteración constante sobre los valores que detecta la clase odometry_state_receiver del paquete Connection, de manera que actualiza sus variables locales y las muestra en pantalla. Figura 3.9. Diagrama UML de VehicleDynamics. 3.5 Paquete TabManager El objetivo de este paquete es el de hacer de controlador de las pestañas que nos pone a nuestra disposición la GUI. Estas pestañas nos permiten cambiar el widget que está siendo visualizado como pueden ser el Environment, las Camera Views o el Execution Viewer. También nos permite extraer una pestaña en concreto y crear una ventana pop-up con el contenido de dicha pestaña Requisitos Los requisitos a cumplimentar son: Crear nuevas pestañas y mostrar widgets en ellas. Destruir pestañas existentes. Extraer una pestaña y mostrarla en una ventana pop-up. Al cerrar dicha ventana pop-up, reinsertar la pestaña en su lugar original. 24

26 3.5.2 Diseño gráfico Las diferentes pestañas seleccionables por el usuario se muestran, como puede observarse en la parte superior de la Figura 3.10, mediante un rectángulo que contiene el nombre del widget que contiene. Esta similitud a, por ejemplo, las pestañas de un navegador o de un menú de ajustes aporta usabilidad e intuitividad a su uso. A la derecha del nombre se tiene un botón que permite desacoplar la pestaña y crear una ventana pop-up con sus contenidos. Figura Pestañas de la GUI Diseño de clases El controlador de pestañas TabManager dispone de tres clases (Figura 3.11). Por un lado, se tienen dos clases controladores per se, el TabManager y NewTabWindow. El TabManager controla las pestañas manteniendo una supervisión sobre qué pestañas se encuentran en la ventana principal en cada momento, mientras que la clase NewTabWindow controla la petición de pop-up por parte del usuario para extraer una pestaña. Por otro lado se tiene la clase TabManagerConnection, que se ha creado con el fin de centralizar las conexiones de Qt que se necesitan dentro de este paquete. Esto sigue la misma filosofía que la clase GeneralConnection del paquete Connection, pero únicamente contiene las conexiones que se efectúan dentro del paquete TabManager. Gracias a esta centralización un desarrollador o mantenedor del código puede hacerse una idea de qué está conectado con qué sin tener que indagar a fondo en el código. 25

27 3.6 Paquete UserCommander Figura Diagrama UML de TabManager Este paquete es utilizado únicamente por la clase Connection del paquete que lleva ese mismo nombre, y su fin es la recopilación de funciones que realizan órdenes directas al robot, sin ser procesadas por el ExecutionEngine. Debido a que no utiliza el ExecutionEngine uno podría considerar este paquete como obsoleto, pero es justo en esa característica donde reside su utilidad. Al ser independiente del módulo de ejecución de Aerostack permite que la GUI se pueda utilizar sin necesidad de tener el resto del proyecto Aerostack actualizado Requisitos Los requisitos a cumplimentar son los siguientes: Suscripción correcta a topics de ejecución de bajo nivel del robot. 26

28 Mandado de órdenes, como pueden ser despegar o moverse, directas al robot Diseño de clases Como se puede observar en la Figura 3.12, este paquete dispone únicamente de una clase que recoge todas las funciones necesarias para mandar comandos al robot. Se ha decidido hacer así porque este paquete no necesita de una representación gráfica ni de conexiones de Qt con otros paquetes. Figura Diagrama UML de UserCommander 3.7 Paquete ParameterTemporalSeries El paquete ParameterTemporalSeries está diseñado para utilizar la gran cantidad de información útil que capta el robot y mostrarla en forma de gráficas que avanzan temporalmente para que los usuarios puedan observar una curva que representa los cambios progresivos de las variables que haya seleccionado. Gracias a estas curvas son fácilmente detectables dichos cambios, así como se hace más sencilla la tarea de detectar posibles anomalías en la valoración de las variables seleccionadas Requisitos Los requisitos a cumplimentar son: Captado satisfactorio de datos recopilados por parte del robot. Muestreo de dichos datos en forma de una gráfica curva que avanza temporalmente. 27

29 Muestro de únicamente las variables que ha seleccionado el usuario de una lista de variables Diseño gráfico La solución gráfica de este paquete puede observarse en la Figura A la izquierda del widget se despliega una lista de todos y cada uno de los atributos o datos junto con su unidad de medida correspondiente que el usuario puede seleccionar para que sean representados en un plot cuadriculado que avanza a tiempo real. Dicho plot se despliega a la derecha de la lista. En la esquina inferior izquierda se muestran opciones de ajuste del plot, así como botones para guardar la información que se está mostrando en ese instante o para parar la ejecución temporal del plot. Figura Solución gráfica de ParameterTemporalSeries Diseño de clases Se dispone únicamente de dos clases (Figura 3.14) en este paquete, la clase DataPlot y la clase ParameterTemporalSeries. 28

30 La clase DataPlot se encarga de controlar únicamente el comportamiento de las curvas de datos y la manera en que se representan dichas curvas, así como los cambios de color de iconos que desee realizar el usuario. La clase ParameterTemporalSeries se encarga de crear la estructura de datos necesaria y de manejar las suscripciones que permiten tener acceso a los valores de las variables seleccionadas. También se encarga de detectar cuáles son las variables que ha seleccionado el usuario y de ordenar al DataPlot que muestre solo dichas variables marcadas. 29

31 Figura Diagrama UML 30

32 3.8 Paquete CameraViewer Este paquete es el encargado de controlar la visualización de las imágenes captadas por las cámaras del robot. Se le dan al usuario varias opciones de layout para ordenar en pantalla las imágenes en tiempo real, así como qué cámaras se quiere mostrar. Así mismo, se tiene la opción de superponer sobre las imágenes en tiempo real un HUD (Heads Up Display) que proporciona información básica como puede ser la posición del robot, la orientación, el nivel de carga de la batería, etc. También tiene la funcionalidad de poder guardar en el sistema de ficheros del usuario una imagen en formato.jpg,.jpeg o.png el frame actual que se está visualizando por la cámara. El CameraViewer no se suscribe al topic por el que se reciben las imágenes de las cámaras, si no que, siendo fiel a la arquitectura de la GUI, hace uso de la clase ImagesReceiver del paquete Connection con este fin. El ImagesReceiver controla la recepción de imágenes y el CameraViewer muestra dichas imágenes en su widget Requisitos Los requisitos de este paquete son: Disponer de varios layouts de muestreo de imágenes seleccionables por el usuario. Mostrar las imágenes de la cámara según el layout seleccionado. Uso opcional de un HUD (Heads Up Display) Diseño gráfico En la Figura 3.15 se ha capturado un ejemplo del funcionamiento en tiempo real de este paquete mientras capta las imágenes capturadas por la cámara principal de un robot y las muestra por pantalla. En la parte superior de las imágenes se pueden observar diversas funcionalidades con las que el usuario puede interactuar. A la izquierda se puede seleccionar el layout que el usuario quiere usar y, a la derecha, se dispone de un botón que permite guardar el frame que se esté mostrando en ese momento. 31

33 Figura Camera Viewer Diseño de clases En este paquete se tienen cuatro clases (Figura 3.16). Como es común con el resto de paquetes, se dispone de un viewer denominado CameraViewer, que es el objeto que crea la MainWindow y se encarga de controlar el correcto funcionamiento del paquete. Este objeto tiene la tarea de, al inicializarse, crear el resto de objetos necesarios que se corresponden con las otras tres clases del paquete: CameraMainOption, CameraDisplayOption y CameraGridOption. La clase CameraMainOption es el layout por defecto, es decir, el primero que el usuario observará si abre este widget. Se encarga de implementar la funcionalidad de guardado de frames en el sistema de ficheros. También permite cambiar la cámara que se está visualizando. La clase CameraDisplayOption dispone un layout que permite visualizar las imágenes de hasta 6 cámaras al mismo tiempo, así como da la opción de cambiar el orden en que se visualizan estas imágenes al gusto del usuario. Al igual que la clase CameraMainOption, implementa una funcionalidad de salvaguardado de frames. 32

34 Por último, la clase CameraGridOption dispone un layout en el que se pueden observar las imágenes captadas por hasta cuatro cámaras al mismo tiempo. Sin embargo y a diferencia de la última clase mencionada, no permite personalizar el orden en que estas imágenes se observan, así como tampoco implementa una funcionalidad de salvaguardado de frames. Figura Diagrama UML de CameraViewer. 33

35 3.9 Paquete FirstPersonView Este paquete tiene como objetivo superponer un Heads Up Display o HUD sobre las imágenes que captura la cámara del robot que esté seleccionada. Esto le permite al operario observar las acciones del robot en una vista en primera persona con una gran cantidad de información adicional a su disposición. Esta HUD recopila información sobre la posición ordinal del robot, su velocidad, su orientación, el tiempo de vuelo y el estado de la batería. Así, el usuario puede conocer todos estos datos sin tener que dejar de observar en primera persona lo que está haciendo el robot. El paquete FirstPersonView se puede usar de dos formas: un pequeño HUD sobre las imágenes de la cámara ubicado en una pestaña junto con el paquete VehicleDynamics o un HUD más grande y con algo más de información dentro de la pestaña CameraViewer Requisitos Los requisitos establecidos para este paquete son: Superposición de un HUD sobre las imágenes que captura la cámara seleccionada. Botón de activado o desactivado del HUD. Uso correcto del paquete Connection para obtener la información necesaria para el HUD Diseño gráfico Se puede observar un ejemplo gráfico de este paquete en la Figura El FirstPersonView utiliza las imágenes que la clase ImagesReceiver del paquete Connection recibe de la cámara del robot. Posteriormente utiliza la HUD proporcionada por el módulo CameraOverlay [Sánchez J., 2017] que se superpone sobre las imágenes de la cámara en tiempo real. Figura First Person View. 34

36 3.9.3 Diseño de clases Este paquete se compone únicamente de dos clases, CameraOverlayBig y CameraOverlaySmall, cuyos diagramas UML se pueden observar en la Figura Cada una de estas clases hace uso de la HUD proporcionada por el módulo CameraOverlay y la adapta al tamaño de la ventana que se esté usando. La clase CameraOverlaySmall se encarga de adaptar la HUD a una imagen pequeña situada en una pestaña al lado del paquete VehicleDynamics. Si el usuario abre la pestaña del paquete CameraViewer, la clase CameraOverlayBig se encarga de adaptar y superponer la HUD a la imagen, de tamaño más grande que la anterior, que muestra el CameraViewer Paquete ExecutionViewer Figura Diagrama UML de FirstPersonView. El paquete ExecutionViewer compone una funcionalidad muy útil para los usuarios de Aerostack que utilicen la GUI para controlar la correcta ejecución de sus misiones. La función de este paquete es la de mostrarle al usuario los objetos activos de los dos componentes básicos del ExecutionEngine, esto es, los behaviors y los beliefs, así como la posibilidad de interactuar con estos. Su utilidad reside en las funcionalidades que implementa y en que se ha diseñado para poderse usar paralelamente a cualquier proceso en ejecución sobre la ExecutionEngine. Este widget es fácilmente divisible en dos partes. El Behavior Viewer y el Belief Viewer. Ambas siguen la estructura proporcionada por la clase QTableWidget de Qt, que dispone los elemenos integrantes o QTableWidgetItems en forma de tabla. Cada una de estas partes muestra los behaviors y los beliefs, respectivamente. Además, se permiten acciones por parte del usuario como pueden ser añadir más behaviors y beliefs al ExecutionEngine o eliminar los existentes en tiempo real Requisitos Los requisitos establecidos para este paquete son: Mostrado en tiempo real de los behaviors activos y los beliefs existentes en la base de conocimientos del robot. Activación de cualquier behavior disponible en el catálogo de behaviors. 35

37 Adición de cualquier belief personalizado. Eliminación de behaviors activos. Eliminación de beliefs existentes Diseño gráfico En la Figura 3.19 queda representado un ejemplo del estado gráfico de este paquete en medio de la ejecución de una misión. En la parte superior se tiene el Behavior Viewer, encargado de representar los behaviors junto con los argumentos con que son iniciados, y en la inferior el Belief Viewer que representa los beliefs existentes en la base de conocimientos. La utilidad de este paquete reside en, pese a su simpleza, la cantidad de información que aporta de manera compacta y accesible. Un primer vistazo a la Figura 3.17 aporta información sobre la posición ordinal del robot, su estado de vuelo y la acción que está llevando a cabo. Figura Execution Viewer Diseño de clases En este paquete se tienen dos clases (Figura 3.20), la clase ExecutionViewer y la clase ExecutionViewerDialog. Siguiendo la arquitectura común a los paquetes de la GUI, la clase ExecutionViewer la crea la MainWindow y supone el controlador general del paquete. Susodicha clase se encarga también de crear y colocar los widgets necesarios, como son las etiquetas y las propias tablas en que se representan los behaviors y los beliefs. Además, 36

38 implementa también el menú de contexto que permite al usuario interactuar con los elementos que se muestran en las tablas, así como las funciones callback que se ejecutan siempre que la lista de behaviors o la lista de beliefs sean actualizadas para actualizar a su vez su visualización en las tablas. Por otro lado, la clase ExecutionViewerDialog esusada por la clase anterior siempre que el usuario solicite interactuar con los elementos a través del menú de contexto. Esta clase crea una ventana pop up según qué opción haya seleccionado el usuario: Añadir behavior: se muestra una ventana con un menú desplegable rellenado automáticamente y dinámicamente con los behaviors disponibles en el catálogo de behaviors y con un cuadro de texto para rellenarlo con los argumentos que se quieren asociar al behavior seleccionado. Añadir belief: se muestra una ventana con un cuadro de texto en el que el usuario puede definir el belief que quiere añadir. Las opciones de eliminar tanto behaviors como beliefs no suponen la creación de una ventana por parte del ExecutionViewerDialog, si no que éste se limita a hacer una llamada a los servicios pertinentes del ExecutionEngine. 37

39 Figura Diagrama UML de ExecutionViewer. 38

40 3.11 Paquete EnvironmentViewer Este paquete conforma una de las funcionalidades más importantes de entre las que nos aporta la GUI. Por un lado implementa el modelo de ejecución de Behavior Trees, así como su creación y edición y, por el otro, implementa el muy útil visualizador del mapa virtual usado por Aerostack en su ejecución. Además de poder visualizar el mapa, también le permite al usuario editarlo a su gusto e incluso importar mapas desde ficheros de definición en formato.xml. Es debido a estas complejas funcionalidades que este paquete es el más grande de los que integran el módulo Graphical User Interface. Como estas dos funcionalidades son fácilmente divisibles, se ha decidido separar los diagramas UML de diseño para que queden más comprensibles, uno representa la creación, edición y visualización de mapas (Figura 3.25) y el otro representa la creación, edición, visualización y ejecución de Behavior Trees (Figura 3.26). El único motivo por el que se ha decidido que los Behavior Trees y el Environment Map se encuentren ambos en el mismo paquete es porque se ha considerado que es especialmente útil que los árboles se pudieran visualizar al lado del mapa en que se están ejecutando en lugar de en una pestaña o ventana completamente diferente Requisitos Los requisitos asociados a este paquete son los siguientes: Mostrar una simulación 2D del mapa que está siendo usado. Mostrar en tiempo real el movimiento del robot sobre el mapa, así como la trayectoria planeada para alcanzar los objetivos y la trayectoria usada Crear un mapa y editar el existente. Creación de un Behavior Tree. Comprobación de una estructura del árbol correcta y de una sintaxis correcta en los argumentos de los nodos. Guardado y cargado de Behavior Trees en ficheros externos en formato.yaml. Ejecución de Behavior Trees Diseño gráfico Este paquete ha sido el más complejo a la hora de diseñarlo gráficamente ya que tanto la generación del mapa como la generación de los árboles se han creado desde cero. En primer lugar, el mapa se genera creando los iconos pertinentes sobre un canvas en blanco sobre el que se superponen unas cuadrículas. Se puede observar en la Figura 3.21 el proceso de edición de 39

41 un mapa de manera gráfica, en concreto se está editando y añadiendo un muro. En la figura 3.21 puede observarse el estado de la GUI teniendo el EnvironmentViewer seleccionado. Figura Edición de un mapa. Por otro lado, este paquete también nos permite crear, editar y utilizar los Behavior Trees. En la Figura 3.22 se ha capturado el estado de la GUI cuando el usuario está editando un Behavior Tree. En la parte derecha del fondo se tiene la representación gráfica del árbol que se está creando junto con las variables que el usuario haya decidido definir. A la izquierda del árbol se ha decidido mantener el mapa por si el usuario quisiera hacer una rápida comprobación de las coordenadas de un punto o del la disposición de los elementos del mapa que está usando. 40

42 Figura Edición de un Behavior Tree. Por último, el lector puede observar cómo se comporta un Behavior Tree durante su ejecución en la Figura Los nodos se colorean en tiempo real en tres colores: azul si se está ejecutando, rojo si su ejecución ha sido fallida y verde si su ejecución ha sido exitosa. Figura 3.23 Behavior Tree en ejecución. En la Figura 3.24 se puede observar un Behavior Tree que representa una misión compleja. 41

43 Figura Behavior Tree complejo Diseño de clases Como se ha explicado anteriormente se ha dividido el paquete en dos, con el fin de una mejor comprensibilidad. La clase principal o controladora se denomina mission_visualizer y de ella parten los dos submódulos mencionados. Esta clase también es la principal encargada de manejar la visualización del mapa, además de colocar cada uno de los submódulos uno al lado del otro. El primer submódulo, encargado de controlar la creación, edición y visualización de mapas, se genera a partir del MissionVisualizer con la clase environment_widget. Esta clase genera un objeto controlador denominado de la clase object_controller que se dedica a mantener el mapa actualizado con los posibles movimientos del robot y las trayectorias que pueda planificar utilizando la clase received_data_processor para procesar los datos que envía el robot. Además, hace uso del resto de clases: drone_widget, pole_widget, wall_widget, landmark_widget y de un struct denominado Objects en el que se recopilan todos los objetos existentes en el mapa. Esta estructura permite disponer de varios objetos creados a partir de ellas, todos ellos con un identificador único y una serie de atributos necesarios para su correcto funcionamiento dentro del mapa, como pueden ser sus dimensiones, su orientación, etc. Todos estos atributos se establecen al crear un nuevo objeto e incluirlo en el mapa, y también se permite editar objetos existentes en el mapa gracias a la clase tool_widget. Esta clase pone a disposición del usuario una serie de herramientas que sirven para seleccionar un objeto 42

44 existente y editarlo, mover un objeto de posición así como hay una herramienta coincidente con cada tipo de objeto que el usuario puede añadir. De esta manera si el usuario selecciona la herramienta robot, puede posteriormente clicar en el mapa y añadir un robot en la posición clicada con los atributos que haya introducido en el menú de creación y edición. También se dispone de una funcionalidad mediante config_file_manager para importar o exportar un mapa a ficheros externos. 43

45 Figura Diagrama UML del controlador de mapas de EnvironmentViewer 44

46 El otro submódulo es el encargado de poner a disposición del usuario el modelo de ejecución por Behavior Trees. Si el usuario selecciona dicho modelo en el Control Panel, aparece a la derecha del mapa la interfaz de los Behavior Trees. Este paquete está controlado por una clase denominada behavior_tree_visualizer, que dispone los diferentes widgets y botones de una manera predeterminada. Cuando el usuario desee editar o crear un nuevo árbol, el Widget correspondiente al paquete EnvironmentViewer, es decir, el mapa y el árbol, se maximizan para una mejor visualización. El BehaviorTree se puede cargar y guardar en ficheros externos formato.yaml mediante la clase tree_file_manager. Los árboles se manejan mediante la clase behavior_tree, que hereda de la clase de Qt QTreeWidget y hace uso de la clase tree_item que hereda de QTreeWidgetItem para representar sus nodos y los atributos de cada uno de ellos. Los árboles también se pueden crear desde cero añadiendo nodos en forma de objetos de la clase tree_item al árbol mediante un menú de edición dinámico definido en la clase behavior_dialog. Podemos tener multitud de nodos, pero esencialmente tenemos dos tipos: Nodos de ejecución: estos nodos indican la lógica que seguirá el árbol al ejecutarse. Pueden indicar una ejecución secuencial (nodo Sequence), paralela, (nodo Parallel), inversión del valor de la ejecución del nodo hijo, etc. Nodos de acción: suponen una acción por parte del ExecutionEngine, como puede ser hacer una Query a la base de conocimientos, añadir un belief, o ejecutar un behavior. La lista de behaviors que se despliega al crear este tipo de nodos es totalmente dinámica y depende de los behaviors definidos en un fichero denominado BehaviorCatalog. La propia ejecución del árbol se implementará en la clase execution_tree, que dispone de una ejecución recursiva de nodos y de una estructura switch-case para detectar qué tipo de nodo es el que está siendo ejecutado y cómo hay que proceder con su ejecución. Debido a que pueden existir nodos de ejecución paralela, se debe introducir un complejo sistema de semáforos para que no exista ningún conflicto entre uso de variables ni a la hora de enviar órdenes al ExecutionEngine. Para evitar posibles errores a la hora de crear el árbol, se tienen una serie de métodos que hacen una comprobación tanto de una buena estructura de nodos del árbol, así como una comprobación de que los argumentos de los nodos behavior o los nodos que hacen uso de la base de conocimientos tienen una sintaxis correcta para evitar errores en tiempo de ejecución debidos a un error sintáctico evitable. Además y siguiendo con la estructura general de la GUI, se dispone de una clase denominada environment_viewer_connection que implementa todas y cada una de las conexiones de Qt que se necesiten entre objetos de este paquete. Al quedar centralizadas resulta más sencillo conocer qué objeto está conectado con cuál, por lo que el proceso de aprendizaje del módulo, la solución de errores o el escalado de estas conexiones resulta más asequible. 45

47 Figura Diagrama UML de Behavior Tree 46

48 Capítulo 4 Implementación El módulo Graphical User Interface de Aerostack ha sido programado íntegramente utilizando el lenguaje C++, utilizando puntualmente las librerías del framework ROS en caso de necesitar la comunicación mediante topics o la llamada a servicios definidos en el ExecutionEngine. Debido a que Aerostack está siendo programado por dos equipos geográficamente separados y a que dentro de los equipos cada integrante está trabajando en diferentes paquetes, se vio necesario el uso de un software que ayudase con el control de versiones. De esta manera se reducen en gran cantidad la introducción de errores en el proyecto por parte de un integrante. Se decidió utilizar el sistema Git, un sistema ampliamente utilizado por equipos informáticos de todo el mundo que nos permite un control exhaustivo sobre las modificaciones realizadas en diferentes ficheros así como el trabajo en diferentes ramas sobre un mismo proyecto, que facilita el trabajo en un mismo proyecto con varias personas. Además, el sistema Git es usado por multitud de plataformas online que proporcionan una interfaz gráfica a sus usuarios para representar los contenidos de su proyecto. En nuestro proyecto hemos utilizado la plataforma BitBucket como controlador de versiones en desarrollo, y la plataforma Github para publicar la versión final, open source y disponible al público del proyecto Aerostack. El framework mencionado anteriormente facilita la integración con el resto de módulos de Aerostack gracias a que el uso de este framework nos proporciona un sistema débilmente acoplado. Esto es debido a que el framework ROS utiliza un sistema de comunicación editor - suscriptor que únicamente fuerza la necesidad por ambas partes, tanto el editor como el suscriptor, a conocer y definir un mismo nombre de topic o de servicio. Este sistema es verdaderamente útil porque, por un lado, proporciona fácilmente la posibilidad de crear funciones callback, esto es, la implementación de funciones que se ejecutan siempre que se reciba un mensaje por el topic en cuestión. Por otro lado, la utilización de un servicio también es muy simple, sólo es necesario conocer el nombre del servicio y qué tipo de mensaje acepta como petición. Además, estos servicios tienen la capacidad de devolver un feedback prácticamente instantáneamente, por lo que se pueden hacer comprobaciones posteriores a la llamada del servicio en caso de que haya que implementar código que trate peticiones fallidas a dicho servicio. Por otro lado, el módulo GUI se ha apoyado fuertemente en las librerías gráficas proporcionadas por el framework Qt. Este framework define un elemento básico u objeto denominado widget. Un widget puede representar cualquier cosa, desde un botón hasta la ventana principal de una aplicación, y es por esto que casi todas las clases del módulo heredan de la clase QWidget. Además y gracias a esta herencia, adoptamos una serie de funciones 47

49 implementadas por los desarrolladores de Qt que facilitan mucho las tareas relacionadas con la visualización de ventanas o la disposición de widgets dentro de dichas ventanas. Las clases que heredan de algún objeto de Qt también heredan su intrínseco sistema de comunicación. En lugar de tener que utilizar el framework ROS para comunicar un paquete de la GUI con otro se utilizan las conexiones de Qt definidas por la función QObject::connect(sender, signal, receiver, slot). Esto es muy importante porque el simple hecho de pulsar un botón puede suponer una reacción por parte de un paquete completamente distinto al paquete que contiene dicho botón. Por ejemplo, cambiar el modelo de operación en el Control Panel a Guided by Behavior Tree provoca que el EnvironmentViewer muestre la ventana de creación y edición de árboles. Este sistema de comunicación es relativamente sencillo de utilizar, tan sólo se necesitan referencias de los objetos que uno desea conectar (emisor y receptor), así como la correcta definición de las respectivas señales y slots. Estos son los cuatro elementos que componen una conexión: Sender: objeto que emite la señal definida por el campo signal. Qt permite crear y personalizar señales propias, con el nombre que se quiera y el número de argumentos que se quiera, pero también tiene señales predefinidas para diferentes objetos. Por ejemplo, un botón dispone de las señales clicked() o released() que se emiten cuando se clica el botón o cuando se deja de clicar, respectivamente. Receiver: objeto que contiene la función determinada en el campo slot. Al igual que las señales, se puede utilizar slots predefinidos o crear un slot que se ajuste a las necesidades programáticas del desarrollador. Signal: nombre de la señal junto con los argumentos necesarios. Se definen siguiendo el formato nombre(argumento1, argumento2, ) dentro de la clase del objeto emisor. Para emitirlas sencillamente hay que utilizar la siguiente función proporcionada por Qt: Q_EMIT(signal). Slot: nombre del slot junto con los argumentos pasados por la señal. Se definen como se definiría una función cualquiera, con la excepción de que deben ser marcados como public Q_SLOTS dentro de la clase cabecera.h del objeto receptor. Al ser conectados a una señal en concreto de un objeto en concreto, se ejecutan siempre que dicho objeto emita susodicha señal. Este sistema de conexiones con señales y slots es verdaderamente útil, pero su uso sin ningún tipo de pautas puede dar lugar a un código confuso debido a que llega a ser difícil conocer qué objetos están conectados con cuáles, cuándo se emite una determinada señal, etcétera. Es por esto que se tomó la decisión de crear clases contenedoras de conexiones entre objetos dentro de un mismo paquete, así como una clase contenedora de las conexiones entre diferentes paquetes. 48

50 En relación con la implementación de nuevos paquetes encontramos también la importancia de otras elecciones de diseño que se han hecho. El hecho de tener la clase principal Main Window también facilita mucho esta tarea, debido a que sencillamente habría que incluir unas pocas líneas de código para incluir un nuevo paquete en la GUI. Si bien cada paquete realiza una función completamente diferente de los demás, la implementación de cada uno de ellos es relativamente parecida y se puede resumir en los siguientes pasos: Definición del sistema de ficheros: la compilación del proyecto Aerostack se realiza mediante una serie de ficheros CMakeFile que definen la manera en que se compila cada paquete. Esto requiere que los paquetes sigan una estructura de carpetas estrictamente. Cada paquete de la GUI ha de disponer de dos carpetas, una carpeta include que contiene las cabeceras y una carpeta source que contiene la implementación de dichas cabeceras. Definición de las clases cabecera: contenidas en la carpeta include, se ha de definir las clases o librerías que se desean incluir en el paquete, el nombre de la propia clase, sus argumentos, sus atributos, sus funciones y las señales y slots de Qt siguiendo el diseño fijado previamente. Es recomendable que, al mismo tiempo que se realiza esta tarea, se escriba en forma de comentarios un mínimo de documentación sobre qué hace cada elemento. Definición de la clase de implementación: cuando se tenga la estructura de ficheros y las cabeceras correctamente definidas, se puede proceder a implementar las funcionalidades deseadas. Este paso es más sencillo cuanto más se haya preparado el paquete en la fase de diseño debido a que se tendrán menos errores. A la hora de implementar las funcionalidades en la previamente mencionada clase de implementación se ha seguido la siguiente metodología: Definición del constructor y destructor: en el constructor se instancian los objetos que necesitará el paquete y se establecen las conexiones de Qt que sean necesarias, así como se establecen las conexiones de ROS. En el destructor siempre se eliminan los objetos utilizados únicamente internamente por la clase en cuestión con el fin de aumentar la eficiencia del programa. Esto es muy importante porque debido a la gran cantidad de objetos que puede llegar a crear la GUI se puede dar lugar a memory leaks o fugas de memoria. Implementación del resto de funcionalidades: tras una buena estructuración de todas las bases que necesitará el paquete se puede proceder a implementar las propias funcionalidades que ofrecerá el mismo siguiendo, como siempre, el diseño que se haya establecido. 49

51 Por último, pero no por esto menos importante, se ha trabajado sobre todas las clases de la interfaz gráfica implementada para incluir la mayor cantidad de documentación en forma de comentarios, de manera breve y concisa, para que aquel desarrollador que indague en el código tenga una idea general de cuál es el propósito de toda clase y de toda función, señal o slot. 50

52 Capítulo 5 Validación Este capítulo tiene como fin explicar las diferentes metodologías que se han seguido para validar el correcto funcionamiento de cada paquete, la integración de todos ellos para conformar la GUI, la validación del correcto funcionamiento de la GUI y la integración y validación con el resto del proyecto Aerostack. La validación de código es un paso extremadamente importante que se debe dar antes de sacar a producción el software implementado. Sin una buena y profunda validación se pueden pasar por alto fácilmente errores críticos que pueden causar desde pequeñas inconveniencias hasta un crash del software. Dado que tener este tipo de errores es debe evitarse, se ha mantenido una estricta política de validación a la hora de crear la Graphical User Interface. Siempre que se desarrolla código, se prueban unitariamente cada una de sus funcionalidades, refinando código hasta alcanzar una versión estable que cumpla con los requisitos establecidos. Cuando todo el código de un paquete ha sido completado y validado unitariamente, se valida con una densa batería de pruebas dicho paquete. Este procedimiento se continúa hasta que todos los paquetes han sido validados, posteriormente se integran conformando la GUI para validar todo el conjunto de software. Si se pasan las diversas baterías de pruebas correctamente, se puede proceder a validar la GUI junto con la ejecución del ExecutionEngine mediante vuelos simulados y vuelos reales. Todas estas pruebas serán explicadas con más detalle en las siguientes secciones de este capítulo. 5.1 Pruebas unitarias Estas pruebas tienen como fin comprobar el correcto funcionamiento de las diferentes funcionalidades para las que se ha diseñado un paquete, exceptuando todas aquellas que tengan algo que ver con vuelos simulados o reales. Esto es debido a que en este punto del proceso de desarrollo aun no es necesario que la comunicación con el robot sea funcional, si no que es necesario comprobar que el código que va por debajo de dicha comunicación (que es, en el fondo, el objetivo final de la GUI) funciona correctamente. De esta manera cuando se integren los paquetes el desarrollador ha de preocuparse únicamente de la interacción del interfaz con el ExecutionEngine en vez de tener en cuenta que cualquier error puede haber sido provocado por una falla programática en el código que maneja los widgets de la interfaz. Este tipo de pruebas pueden ser, por ejemplo, la comprobación de que al pinchar en la pestaña Execution Viewer se cambia correctamente a dicha pestaña o que el pulsado del botón Take off se detecta correctamente, independientemente de si hace despegar al robot o no. 51

53 Para sistematizar el proceso de validación, se diseña un conjunto de pruebas que verifican cada uno de los requisitos de operación del sistema. En el anexo A se muestra el diseño de pruebas utilizado en este trabajo. 5.2 Integración de los paquetes Una vez que los paquetes han sido validados y funcionan correctamente se puede proceder a integrarlos para conformar el módulo Graphical User Interface. Debido a elecciones de diseño este proceso es relativamente sencillo gracias a la estructura jerárquica de los diferentes paquetes. El paquete MainWindow es el controlador general y de éste nacen la mayoría de paquetes. A la hora de añadir un nuevo paquete al módulo una vez éste ya ha sido conformado simplemente habría que elegir concienzudamente cuál de los paquetes existentes sería su padre y posteriormente integrarlo correctamente. Después de integrar todos los paquetes en uno solo conformando la GUI es necesario repetir las baterías de pruebas que se pasaron satisfactoriamente cuando se realizaron las pruebas unitarias. De esta forma se comprueba si todo sigue funcionando correctamente o si se han introducido errores en el proceso de integración. Es también necesario crear y pasar nuevas baterías de pruebas que contemplen todas las acciones posibles que suponen interacciones entre diferentes paquetes de la propia GUI debido a que, aun estando ya implementadas, es imposible probar estas interacciones sin haber integrado previamente los paquetes. 5.3 Pruebas de vuelo simulado El entorno Aerostack dispone de una herramienta que simula un vuelo real con robots y entornos virtuales. Este simulador nos permite probar el código implementado simulando un futuro entorno y vuelo real, evitando el riesgo que puede suponer un error de código en dichos vuelos reales. El simulador se encarga de ejecutar todo lo necesario, esto es, los planificadores de trayectorias del robot, los controladores de las hélices, el ExecutionEngine, etc. Durante esta fase de pruebas se han de documentar baterías de pruebas que supongan la ejecución de misiones simuladas sobre los tres modos de operación de misiones (Guiado por misiones Python, guiado por TML y Behavior Trees) así como comprobar que la teleoperación con teclado funciona correctamente. Esta fase es, además, una de las más importantes debido a que es aquí donde verdaderamente se comprueba que los sistemas de comunicación entre la GUI y el resto del proyecto Aerostack se han implementado y funcionan como es esperado. Para validar la interfaz gráfica de usuario lo primero que se ha hecho ha sido utilizar el modo de operación Keyboard Teleoperation debido a que es el único que no utiliza ningún lenguaje 52

54 de misión especial, simplemente utiliza la interacción por teclado. Esto permite conocer si se están enviando correctamente las peticiones a servicios del ExecutionEngine de manera independiente a un intérprete de estos lenguajes. Cuando esta fracción queda validada, se procede a diseñar misiones sencillas (despegar, moverse y aterrizar) de los tres modos de operación guiados por un lenguaje. Por último se crean misiones algo más complicadas con el propósito de encontrar una falla en el sistema. En la Figura 5.2 se puede observar el estado de la GUI mientras se está ejecutando un Behavior Tree. Las líneas rojas representan la trayectoria planificada para el recorrido mientras que la línea negra representa el recorrido real que ha tomado la simulación del robot. Los resultados que podemos observar en la GUI durante una prueba simulada son, teóricamente, los mismos que se observarían ejecutando la misma misión en un vuelo real. Figura 5.2. GUI durante una prueba simulada. 5.4 Pruebas de vuelo real Las pruebas de vuelo reales se han realizado en un recinto preparado (Figura 5.3) para este propósito que se encuentra en la Escuela Técnica Superior de Ingenieros Industriales (Universidad Politécnica de Madrid). En este recinto se ha de preparar el mapa físico colocando los muros, los postes y demás obstáculos. Seguidamente se ha de editar el mapa virtual con el que trabajará la GUI de manera que coincida con exactitud con el mapa real en el que volará el 53

55 robot. Han de coincidir con exactitud también el posicionamiento de los arucos, que son unas figuras parecidas a códigos QR que utiliza el robot para autolocalizarse dentro del mapa. Si bien el procedimiento de pruebas de simulación de vuelos comprueba que en la capa de operación se envían correctamente las peticiones de servicios al motor de ejecución, esto no se puede tomar como una afirmación de que todo va a funcionar correctamente cuando se haga un vuelo con un robot real. Por tanto, se han de diseñar misiones similares para una validación satisfactoria. Estas pruebas han de realizarse siempre con un operario pendiente en el ordenador en caso de que suceda un error crítico y se tenga que realizar un aterrizaje de emergencia. Como los robots son relativamente delicados es también recomendable, durante las primeras pruebas, que haya un operario cerca del robot por si resulta necesario cogerlo en medio del vuelo. Por tanto, el procedimiento que se ha seguido ha sido prácticamente el mismo. En primer lugar se ha comprobado que las interacciones del operario por teclado provocan una respuesta inmediata y satisfactoria por parte del robot. Posteriormente se le ha ordenado al robot que ejecute las mismas misiones simples que se ejecutaron en los vuelos simulados. Por último y si todas las pruebas anteriores han sido exitosas se procede a ejecutar misiones algo más complejas en cada uno de los modos de operación guiados por lenguaje, esto es, misiones Python, TML y BehaviorTree. Una vez se han ejecutado satisfactoriamente una gran cantidad de diferentes misiones con todo tipo de comportamientos y objetivos, se marca el software como apto para ser publicado. Figura 5.3. Recinto para pruebas de vuelo. 54

56 Capítulo 6 Conclusiones Este proyecto ha consistido en la construcción de un interfaz gráfico de usuario mediante extensión y mejora de prototipos disponibles en el entorno Aerostack. La interfaz gráfica de usuario permite al usuario del proyecto Aerostack manejarse de una manera fácil e intuitiva con el motor de ejecución que aporta Aerostack para crear sus propias misiones e incluso sus propios behaviors. Los objetivos propuestos en la introducción han sido completados. Se ha conseguido disponer de un interfaz gráfico completamente funcional, que le permite al usuario hacer uso de todas las funcionalidades que Aerostack pone a su disposición. El software ha sido testado rigurosamente mediante baterías de pruebas unitarias para cada paquete así como baterías de pruebas de integración cuando se han integrado todos los paquetes para formar el propio módulo Graphical User Interface. Además, se han realizado muchas pruebas de vuelos tanto simulados como reales y se ha comprobado que la GUI es un software muy fiable y estable. Este software ha sido publicado recientemente junto con la versión 2.0 del framework Aerostack y es un módulo muy importante debido a que cambia la manera en que un usuario puede interactuar con el framework independientemente de sus conocimientos como desarrollador. Gracias a que el diseño se ha hecho teniendo en cuenta las capacidades de escalabilidad, las líneas futuras de este proyecto son muy variadas, pero siempre irán ligadas a los avances de Aerostack. Como posibles líneas futuras en el estado actual del framework a las que se podría someter la GUI tenemos: Creación de una vista frontal del mapa virtual y sus obstáculos y capacidad de cambiar la visualización entre dicha vista y la vista que existe actualmente. Mejoras visuales en algunos componentes de la interfaz. Cualquier componente, como el ControlPanel o el mapa en 2D puede ser sujeto a una mejora de diseño frente al actual. Posibles mejoras de rendimiento a la hora de recibir o enviar mensajes mediante ROS. Este servicio editor-suscriptor puede crear una gran cantidad de flujo de mensajes entre la interfaz, el ExecutionEngine y el robot. Implementación de un sistema que simule la misión que va a ejecutar un usuario en un vuelo real y sea capaz de detectar cualquier error que dicho usuario haya podido generar, antes de que comience el vuelo real. 55

57 Referencias J.L. Sanchez-Lopez, M. Molina, H. Bavle, C. Sampedro, R. A. Suarez-Fernandez, P. Campoy. (2017). A Multi-Layered Component-Based Approach for the Development of Aerial Robotic Systems: The Aerostack Framework. Journal of Intelligent & Robotic Systems, J. L. Sanchez-Lopez, R. A. Suarez-Fernandez, H. Bavle, C. Sampedro, M. Molina, J. Pestana, P. Campoy (2016). AEROSTACK: An Architecture and Open-Source Software Framework for Aerial Robotics. ICUAS 2016, Arlington, USA. M. Molina (2017): An execution engine for aerial robot mission plans. Technical report. Technical University of Madrid. De la Hoz, Y. (2015): Herramienta de interacción persona-ordenador para la operación de vehículos aéreos no tripulados. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. Gordo, A. (2016): Interfaz de usuario para vehículos robóticos aéreos: desacoplamiento de componentes en Aerostack. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. García, L. (2016). Interacción hombre-máquina en vehículos aéreos no tripulados: Estudio de mejora de comunicación en Aerostack. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. Sánchez, A. (2017a). Herramienta software de configuración de vehículos robóticos aéreos. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. Sánchez, J. (2017b). Visualizador gráfico para monitorización del comportamiento de vehículos aéreos no tripulados. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. Valencia,C. (2017). Interfaz gráfica de usuario para configuración de misiones de vehículos aéreos no tripulados. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. Camporredondo, A. (2017). Development of a behavior management system for the aerial robot software framework Aerostack. Trabajo Fin de Grado. ETS de Ingenieros Informáticos. Universidad Politécnica de Madrid. 56

58 Sánchez, J. (2017). Visualizador gráfico de monitorización del comportamiento de vehículos aéreos no tripulados. Trabajo de Fin de Grado. ETS de Ingenieros informáticos. Universidad Politécnica de Madrid. 57

59 Anexo A: Pruebas para validación del sistema. La siguiente imagen se corresponde con la batería de pruebas usada para validar las funcionalidades de manera unitaria del modo de operación BehaviorTree perteneciente al paquete EnvironmentViewer. El formato seguido para estas baterías de pruebas consiste en describir la ventana que va a someterse a la prueba, describir la prueba y asociar un resultado esperado. A continuación se documenta el resultado obtenido en la siguiente columna para un posterior refinado de código en caso de haberse producido errores durante la validación. 58

60 Este documento esta firmado por Firmante CN=tfgm.fi.upm.es, OU=CCFI, O=Facultad de Informatica - UPM, C=ES Fecha/Hora Mon Jan 15 12:34:10 CET 2018 Emisor del Certificado CN=CA Facultad de Informatica, O=Facultad de Informatica - UPM, C=ES Numero de Serie 630 Metodo urn:adobe.com:adobe.ppklite:adbe.pkcs7.sha1 (Adobe Signature)

Sitemap