Capítulo 3. Procesamiento de imágenes en Sistemas I2DASW
4.3 Framework I2DASW
4.3.3 Arquitectura
Luego de analizar los requisitos se detectó que los componentes arquitectónicos son: · Sensores
· Interfaz de Usuario
· Información Estática (base de datos) · Motor de decisión
El estilo arquitectónico que se decidió aplicar es la Arquitectura en Capas. Esta arquitectura se enfoca en la distribución de roles y responsabilidades de forma jerárquica, muy efectiva en la separación de responsabilidades. Describe la descomposición de servicios de forma que la mayoría de la interacción ocurre solamente entre capas vecinas. Además, permite distribuir el trabajo de creación de una aplicación por niveles; cada grupo de trabajo está totalmente abstraído del resto de niveles. Y cada capa agrega responsabilidad y abstracción a
Capítulo 4. Desarrollo
la capa directamente sobre ella. Los componentes de cada capa se comunican con otros componentes en otras capas a través de interfaces muy bien definidas.
También se decidió usar este tipo de arquitectura porque abstrae la vista del modelo como un todo mientras que provee suficiente detalle para entender las relaciones entre capas. Tiene un buen encapsulamiento, no hace suposiciones acerca de tipos de datos, métodos, propiedades o implementación. Posee una funcionalidad claramente definida. El diseño claramente define la separación entre la funcionalidad de cada capa. La alta cohesión, hace que cada capa contenga funcionalidad directamente relacionada con la tarea de dicha capa. Puede ser reutilizable, las capas inferiores no tienen ninguna dependencia con las capas superiores, permitiéndoles ser reutilizadas en otros escenarios. La comunicación entre las capas al estar basada en la abstracción, provee un desacople entre las capas.
En la siguiente tabla 4.3.1 se muestra el análisis de los beneficios, como así también las desventajas y problemas de esta arquitectura, que se solucionan comenzando con el nivel más bajo.
Beneficios Desventajas Problemas
· Las capas permiten cambios que se realicen en un nivel abstracto.
· El estilo de arquitectura de capas permite aislar los cambios en tecnologías a ciertas capas para reducir el impacto en el sistema total.
· Distribuir las capas entre múltiples sistemas (físicos) puede incrementar la escalabilidad, la tolerancia a fallos y el rendimiento.
· La capacidad de realizar pruebas se beneficia de tener unas interfaces bien definidas para cada capa así como de la habilidad para cambiar a diferentes implementaciones de las interfaces de cada capa.
· A veces no se logra la contención del cambio y se requiere una cascada de cambios en varias capas.
· Pérdida de eficiencia si son demasiadas capas.
·Trabajo innecesario por parte de capas más internas o redundante entre varias capas.
· Dificultad de diseñar
correctamente la
granularidad de las capas.
· Diferentes niveles de abstracción.
· Es posible definir las interfaces entre las capas estables.
· Es capaz de cambiar o añadir capas a través del tiempo.
· Las peticiones van desde las capas superiores hacia las inferiores y las respuestas desde abajo hacia arriba.
Tabla 4.3.1. Análisis de ventajas y desventajas de la arquitectura.
De esta manera el diseño planteado se realizó tal como lo describe la figura 4.3.2. Comenzando de abajo hacia arriba, en la Capa de Captura de Datos se encuentran los
Sensores que son los encargados de brindar la información al framework. Esta información es tomada en bruto por el Recolector de Datos que se encuentra en la capa siguiente (Capa de
Capítulo 4. Desarrollo
Reconocimiento de Datos), y ésta la envía al Reconocedor de Datos que junto con la Base de Datos y el Perfil del Usuario componen la Capa de Procesamiento de Datos. Esta capa es la encargada de procesar dicha información, detectar carriles y obstáculos, y de comunicarse con el Motor de Decisión para que éste tome la decisión adecuada. La decisión puede ser un aviso visualizado mediante la Interfaz de Usuario o una acción directa sobre el Vehículo. Ambos componentes están ubicados en la Capa de Presentación.
Figura 4.3.2: Diagrama de capas.
Se desestimó aplicar una arquitectura Cliente-Servidor debido a que no se ajusta a este tipo de aplicaciones, donde no hay recursos centralizados ni peticiones del Cliente al Servidor. En esta aplicación los datos circulan en un único sentido (de abajo hacia arriba, desde los sensores hacia la interfaz)
La performance de una arquitectura cliente-servidor está dada por la velocidad de transmisión entre los clientes y el servidor, pero en esta aplicación, eso es vital y un buen planteo de la Arquitectura en Capas hace que la performance sea buena.
4.3.4 Diseño Detallado
Para explicar el Diseño Detallado de forma simple, se seguirá el Diagrama de Capas
que se mencionó anteriormente comenzando por la capa más baja y se continuará subiendo y detallando cada una de ellas mediante clases y diagramas.
La figura siguiente muestra la Capa de Captura de Datos (Figura 4.3.3), siendo esta el punto de entrada de información al framework.
Capítulo 4. Desarrollo
Figura 4.3.3: Diagrama de Capas - Capa de Captura de Datos.
Se encuentran dentro de esta capa la clase Sensor y su jerarquía de clases (SensorImage, SensorInfrared y SensorVehicle) las cuales manejan la entrada de datos desde los sensores físicos incluidos en el vehículo. Cada vez que un sensor obtiene un nuevo dato en bruto, este es enviado a la capa siguiente de Reconocimiento de Datos, más específicamente a la clase que recolecta los datos (DataCollector). Esta clase recibe en su iniciación la lista de sensores que la aplicación tendrá en uso, y se subscribe a los eventos de estos sensores para que los datos recibidos por estos le sean transmitidos para su procesamiento. Estos datos son recibidos por un único método NewDataFromSensor, el cual recibe como parámetro la clase padre Data, que representa la información detectada. Estos envíos de información desde los sensores al recolector son asincrónicos, haciendo uso de un sistema de eventos que nos permite recibir información en paralelo desde diferentes sensores al mismo tiempo y sin generar un cuello de botella para el procesamiento de distintas fuentes de información.
En la figura 4.3.4, se puede ver la jerarquía de clases de Sensores.
Capítulo 4. Desarrollo
Siguiendo el nivel de jerarquías del Diagrama de Capas, como se mencionó recientemente se encuentra la Capa de Reconocimiento de Datos (Figura 4.3.5):
Figura 4.3.5: Diagrama de Capas -Capa de Reconocimiento de Datos.
Los datos que recibirá el recolector pueden ser Imágenes, Imágenes Infrarrojas, Información de GPS, etc, representadas por la clase Data y su herencia de clases, GPS, Image e Infrared las cuales serán diagramadas a continuación. Cada una de estas clases tiene información específica de acuerdo al tipo de dato a tratar, por ejemplo, la clase GPS manejará información como velocidad del vehículo, altitud, etc., mientras que la clase Image tendrá entre sus atributos al ancho, alto y resolución de esta. El framework permite el agregado de nuevos tipos de datos, creando nuevas clases hijas de la clase Data.
En la figura 4.3.6, se puede observar la jerarquía de clases de la información reconocida por los sensores y la clase DataCollector con el método NewDataFromSensor, encargado de recibir información de los sensores.
Capítulo 4. Desarrollo
Figura 4.3.6: Clase Data y su jerarquía. Clase DataCollector. Interfaz Visitor.
Ya que el DataCollector recibe una clase abstracta (Data), y necesita saber qué objeto concreto es, se implementó el patrón Visitor. Este patrón fue desarrollado añadiendo una interfaz IVisitor, la cual tiene un método visit por cada clase hija de Data (Image, Infrared,
GPS). Esta interfaz es implementada por la clase DataCollector, la cual está interesada en conocer la verdadera clase hija.
Por otro lado, la clase abstracta Data tiene un método abstracto accept(IVisitorvisitor) el cual necesita ser sobreescrito por cada uno de sus hijos.
Cuando la clase DataCollector recibe el tipo de dato específico con el uso del patrón
Visitor, mediante el patrón Observer-Observable esta información es enviada a la Capa de Procesamiento de Datos (Figura 4.3.7).
Capítulo 4. Desarrollo
Figura 4.3.7: Diagrama de Capas - Capa de Procesamiento de Datos.
Esta información llega a los reconocedores encargados de interpretar cada tipo de información en particular. Por ejemplo, la clase RecognizerRoad, será el encargado de recibir un objeto tipo Image e interpretar este tipo de dato, reconocer el camino y sus respectivas líneas de carril. Para esto, existen diferentes eventos para subscribirse cuando se capturan nuevos datos, tales como NewImageDataCollected, NewInfraredDataCollected y
NewGPSDataCollected. La decisión de diseño de que el DataCollector tenga un evento por cada tipo de dato, es debido a que un reconocedor puede estar interesado en un determinado tipo de información que llega, o en varios tipos si así lo requiriera.
En la figura 4.3.8 y como Capa de Procesamiento de Datos, se observan los diferentes reconocedores del framework y la implementación concreta del reconocedor de caminos (RecognizerRoadMitov) y reconocedor de obstáculos (RecognizerObstacleMitov). En la clase padre RecognizerAF se pueden ver los diferentes eventos a los cuales se suscribe el motor de decisiones para saber cuándo algún tipo de información es reconocida (NewDynamicModelWasRecognized, NewRoadWasRecognized y
NewObstacleWasRecognized). Esto demuestra la facilidad y flexibilidad del framework para adoptar nuevas implementaciones para los diferentes reconocedores.
Capítulo 4. Desarrollo
Capítulo 4. Desarrollo
Figura 4.3.8-B: Clases Reconocedores, genéricas e implementaciones.
Esta jerarquía de clases está constituida por la clase padre RecognizerAF y cada una de las clases que la heredan pueden subscribirse a los eventos detallados en el párrafo anterior para recibir información de datos específicos con los que cada reconocedor desea trabajar. En cada reconocedor se introducirá la lógica para interpretar cada dato y elevar el nivel de abstracción de la información, extrayendo las características particulares y reduciendo el volumen de información.
La información reconocida por los reconocedores se ve representada en la jerarquía de clases teniendo como padre la clase InformationAP, e hijos las clases Obstacle, Road y DynamicModel. Road por ejemplo, será la clase que represente a un camino que haya sido reconocido en una imagen. Contendrá atributos específicos como la línea izquierda, línea
Capítulo 4. Desarrollo
que el vehículo vaya transitando en un momento dado. A su vez, cada línea de la ruta tendrá determinado su color (blanco o amarillo), atributo que es de gran ayuda para determinar por ejemplo, si es posible un sobrepaso o no. Otra información almacenada en cada línea, determina si ésta es línea simple o doble, que ayudado con el color como se explicó previamente, es vital para conocer acerca del camino que se circula.
Se podrán agregar nuevos tipos de información reconocidos, creando clases que hereden de la clase InformationAP, así como nuevos reconocedores de información, heredando de la clase RecognizerAF. Un nuevo reconocedor, como se detalló en párrafos anteriores, deberá subscribirse a la información obtenida por los sensores que esté interesado en recibir y agregar la lógica necesaria para reconocer el objeto.
En la figura 4.3.9, se puede ver los distintos tipos de información que el sistema puede reconocer.
Figura 4.3.9: Jerarquía de clases de Información.
También se utilizó el patrón Factory donde la clase fábrica abstracta en este caso es el
RecognizerAF y la clase abstracta producto es InformationAP. Cada fábrica concreta será la encargada de reconocer (método de creación del producto) y devolver una determinada información (producto concreto).
En la figura 4.3.10 se puede observar la idea del patrón Factory y las clases que la integran.
Capítulo 4. Desarrollo
Figura 4.3.10: Ejemplo de patrón Factory.
En la figura siguiente (4.3.11) se detallan dos clases importantes, LineCollection y Line. Ambas se relacionan con la clase Road, y determinan las líneas reconocidas del camino y los atributos propios de cada una de ellas como se mencionó anteriormente.
Capítulo 4. Desarrollo
La clase LineCollection almacena las líneas del camino, mientras que la clase Line tiene el color, tipo, ángulo y puntos X/Y de inicio y fín de la línea en cuestión.
Esta capa cuenta también con un Perfil de Usuario representado por la clase
UserProfile y ManagerProfile. El Perfil del Usuario permite manejar ciertos aspectos como por ejemplo, mediante qué medios se le informará de posibles riesgos: alerta sonora, visual o si permite que el framework pueda accionar sobre el vehículo. También, el usuario podrá habilitar/deshabilitar alertas referidas al camino, obstáculos y datos del vehículo de forma independiente.
En la figura 4.3.12 se puede ver la clase DataBase, la cual maneja la conexión y las consultas a la información almacenada en esta.
Figura 4.3.12: Clase de la Base de Datos.
Todas las clases de la figura 4.3.9 representan los objetos que junto con las reglas de tráfico almacenadas en la Base de Datos y con el Perfil del Usuario, interactúan con el Motor de Decisiones para que, de acuerdo a toda la información que le llega, tome la mejor decisión.
En la Capa de Acción (Figura 4.3.13) el motor de decisiones, clase DecisionEngine, se subscribe a los eventos para saber cuándo se reconoce determinado tipo de información. El motor recibe la lista de reconocedores y se subscribe a todos los eventos de información que puede tener un reconocedor (NewRoadWasRecognized, NewObstacleWasRecognized y
NewDynamicModelWasRecognized), ya que no se sabe qué tipo de información reconoce cada uno de ellos. Un reconocedor puede proveer distintos tipos de información, aunque no es lo común. Por ejemplo, un reconocedor puede informar de un camino y de un obstáculo reconocido.
Capítulo 4. Desarrollo
Figura 4.3.13: Capa de Acción.
Una vez que al motor le llega un evento que se ha reconocido un tipo de información (Camino, Obstáculo o Datos del vehículo), se obtiene la lista de condiciones a evaluar cuando se ha reconocido ese tipo de información. Por ejemplo, si se ha reconocido un obstáculo, se buscan todas las condiciones que tengan como objeto detectado a un obstáculo. Toda información obtenida de los eventos es procesada de acuerdo a un conjunto de reglas de tráfico que evalúan situaciones de riesgo o precaución. Estas reglas de tráfico podrán ser insertadas o eliminadas por medio de la clase ManagerRules.
Con respecto al almacenamiento de información en el sistema, se optó por utilizar una
Base de Datos relacional PostgreSql instalada localmente.
Esta base de datos contiene 3 tablas: Condition, Action y Action_Argument, las cuales son utilizadas por el Motor de Decisiones para obtener las reglas de tráfico y otras, para determinar si la conducción es segura o no, y en este último caso, qué acción tomar.
Para mejorar la performance del sistema, se implementó que se lea la información de la Base de Datos al iniciar la aplicación y se mantenga en memoria para su posterior acceso, y evitar el continuo acceso a disco que en comparación es mucho más lento.
En la figura 4.3.14-A, se puede ver el diagrama de clases del manejo de expresiones, operadores y Motor de Decisiones.
Capítulo 4. Desarrollo
Figura 4.3.14 - A: Clases del Motor de Decisiones y manejo de Condiciones.
El sistema de guardado de las condiciones, permite que se pueda agrupar varias expresiones uniendo estas mediante distintos operadores lógicos como AND, OR, XOR, etc.
Esta información almacenada en tablas fue llevada a clases manteniendo la programación orientada a objetos y permitiendo la flexibilidad para poder agregar nuevos operadores.
Condition agrupa la lista de expresiones a evaluar, las cuales se pueden agrupar con operadores lógicos (AND, OR, etc), y cada condición puede contener operadores de comparación como ==, >, <, etc.
Cada condición en la tabla Condition tendrá un padre que será la fila que contenga un
IdParent = 0, que además tendrá seteado el objeto reconocido al cual aplica (columna
objectdetected cuyos valores pueden ser road, obstacle, dynamic_vehicle). Los hijos de esta condición tendrán seteado en la columna IdParent el Id de la fila de su padre, y el operador que lo une a la siguiente condición hija en la columna OperatorLogic. Además, contiene la condición a chequear para comprobar si se cumple, y el objeto al cual debe aplicarse la
Capítulo 4. Desarrollo
condición (obstacle, vehicle, road, lineCentral) en la columna object. Esta lógica permite agrupar varias condiciones al detectar cierto objeto, y validar si todas se cumplen, alguna de ellas o ninguna, para determinar qué acción realizar. En la Tabla 4.2 se puede apreciar la agrupación de diferentes condiciones para que se ejecute determinada acción, o conjunto de acciones, dependiendo del tipo de información reconocida.
Por un lado se tiene la clase abstracta Expresion, la cual tiene el método abstracto
eval() que retornará un booleano para conocer si se cumple la condición o el conjunto de condiciones. Como hijas de esta clase están la clase Condition que puede ser evaluada, y la clase Operator, la cual es la clase padre de las clases de los operadores lógicos AND, OR, XOR, que contiene a dos objetos de la clase Expresion, y que también deben implementar el método eval() para ver si se cumplen las dos condiciones, alguna de ellas, o una condición o la otra, respectivamente.
Tabla 4.3.2 Tabla Condition.
Como se puede ver en la Tabla 4.3.2, cuando se detecta un camino, se tienen 4 condiciones distintas a evaluar, cada una con su conjunto de acciones correspondiente. En este ejemplo, las filas de la tabla con id 1, 3, 9 y 14 son las condiciones padre. Por ejemplo, la condición padre con id 9 se formaría con la siguiente expresión:
Capítulo 4. Desarrollo
(camino.tipo == recta) AND (lineaCentral.color == amarillo) AND (lineaCentral.tipoLinea == doble) AND (obstáculo no existe)
Para mapear esta tabla a objetos, se utiliza el patrón Composite creando las clases
Expresion, Operator, OperatorAnd, etc.
En el caso de que una condición evaluada se cumpla, se obtienen las acciones correspondientes a esa Condition desde la Base de datos con la tabla Action.
Tabla 4.3.3 Tabla Action.
En la Tabla 4.3.3, se puede ver a Action y como se pueden agrupar varias acciones, y darles un orden para ser ejecutadas secuencialmente si una condición se cumple.
Para la condición de id 9 que se describió anteriormente, se corresponde la acción con id = 5. Como se ve en la Tabla 4.3.3, se deben ejecutar 2 acciones en un respectivo orden.
1. Mostrar mensaje. 2. Alarma sonora.
Capítulo 4. Desarrollo
Para el mapeo de estas acciones a objetos, se utilizó el patrón Command (Figura 4.3.14 - B). Donde se tienen acciones simples o acciones que pueden encolarse y ejecutarse de manera secuencial.
Figura 4.3.14 - B: Clases para el manejo de acciones.
En la Tabla 4.3.3 y Tabla 4.3.4, se puede observar la tabla Action y Action_Argument, las cuales se relacionan entre sí.
Capítulo 4. Desarrollo
Capítulo 4. Desarrollo
Action_argument permite personalizar cada acción, agregando parámetros para cada una de estas a la hora de ejecutarse. Siguiendo el ejemplo, para cada una de las 2 acciones que deben ejecutarse, existen sus parámetros de ejecución en la tabla Action_Argument. Entonces, las acciones se ejecutarán con la siguiente información:
1. Mostrar mensaje de aviso con título “Atención” y mensaje “No sobrepasar”. Este mensaje debe ser visualizado con color “#FFFF00” (amarillo) y el carril por donde estemos circulando debe ser pintado de color “#59FFFF00” (amarillo con transparencia).
2. Reproducir sonido que se corresponde al valor “No sobrepasar”.
Se decidió el color amarillo para indicar cuando no se puede sobrepasar, rojo si hay un obstáculo y verde si hay vía libre, tanto para las alertas como para el triángulo de conducción, que a su vez a éste último se le agrega una transparencia para no perjudicar la visión del camino.
La base de datos está preparada para soportar N condiciones según el objeto detectado, y X acciones secuenciales para cada condición en particular.
En la figura 4.3.15 se puede observar la clase ActionArgument.
Figura 4.3.15: Clase Action Argument.
Por último, la Capa de Presentación (Figura 4.3.16) es en donde todas las decisiones