Sistema de semáforos inteligente

Vídeo

Sistema de semáforos inteligente – Grupo 12 SEyTR

Titulación

Grado en Ingeniería Informática

Curso 2024-2025

Autores

Grupo 12:

  • Diego Benavente Prieto
  • José Jesús Durán Quintana
  • Marcos Fernández-Lomana Murcia
  • Pedro Enrique Metes Zevoian

Introducción

Las retenciones de tráfico son uno de los mayores problemas de las ciudades modernas, causando pérdidas económicas, aumento de la contaminación y estrés en los conductores.  Este problema se agrava en intersecciones complejas como la de Colonia Jardín (ubicada en la carretera de Carabanchel a Aravaca, en Madrid), en la que se cruzan cuatro vías con alta densidad de vehículos haciendo que los semáforos tradicionales, con tiempos fijos, no responden a las variaciones del tráfico en tiempo real. Las largas colas en horas punta (especialmente en sentido a Madrid) y la falta de priorización a vías más congestionadas demuestran la necesidad de un sistema más eficiente. Por este motivo, hemos decidido tomar esta intersección como molde para el diseño del proyecto.

El proyecto que hemos diseñado propone una solución innovadora: un prototipo a escala de semáforos inteligentes, que combina:

  • sensores ultrasónicos para detectar vehículos,
  • barreras automatizadas para garantizar la seguridad de vehículos y conductore, así como la gestión dinámica del flujo y
  • algoritmos adaptativos que ajustan los tiempos de los semáforos en función del tráfico real.

Aunque desarrollado en miniatura, este sistema replica los desafíos de Colonia Jardín y prueba que la tecnología puede reducir atascos, optimizar tiempos de espera y disminuir la huella ambiental, siendo escalable a implementaciones reales con componentes industriales.

Objetivos

  • Desarrollar un sistema de semáforos inteligente a escala reducida que optimice el tráfico en intersecciones complejas, utilizando sensores ultrasónicos y automatización.
  • Detectar vehículos de manera efectiva y eficiente para medir el volumen del tráfico en cada vía.
  • Implementar un algoritmo que ajuste los tiempos de los semáforos en función del tráfico detectado.
  • Integrar servomotores que accionen barreras automatizadas.
  • Recrear las características previamente mencionadas de la intersección de Colonia Jardín en un entorno reducido.
  • Elaborar una documentación técnica para facilitar su escalado a un sistema real.

Características del proyecto

  • Maniobras permitidas. El proyecto replica fielmente la estructura de la intersección de Colonia Jardín, incluyendo las maniobras permitidas en cada vía. Mediante el siguiente esquema visual, se nombran las vías y se definen los giros permitidos, los sentidos de circulación y las restricciones específicas, garantizando que el prototipo simule las condiciones reales de tráfico. Nótese que las vías 1 y 2 se consideran de manera conjunta ya que sus semáforos tienen comportamientos idénticos por no interferir entre sí.

Ilustración 1: Maniobras permitidas y nombre de las vías

  • Detección de vehículos. El sistema incorpora sensores ultrasónicos estratégicamente ubicados para detectar la presencia de vehículos en cada vía. Estos sensores proporcionan datos en tiempo real, permitiendo al sistema adaptarse dinámicamente al flujo de tráfico. La detección precisa es fundamental para activar los algoritmos de control y optimizar los tiempos de los semáforos, reduciendo así los cuellos de botella.
  • Conteo de vehículos por vía. Cada vía cuenta con un mecanismo de conteo automático de vehículos, registrando el número de automóviles que acceden a la intersección. Este dato es crucial para determinar la densidad del tráfico y asignar prioridades de manera inteligente. El sistema almacena esta información para ajustar continuamente la gestión de las luces semafóricas y las barreras.
  • Registro de tiempos de espera por vía. Además del conteo de vehículos, el sistema mide los tiempos de espera acumulados en cada vía. Esta métrica permite identificar posibles congestiones y aplicar correcciones inmediatas, mejorando la fluidez del tránsito.
  • Lógica de prioridades. El sistema implementa un algoritmo de priorización que considera tanto el número de vehículos como los tiempos de espera en cada vía, asignándole un mayor peso al primer parámetro. En concreto, la prioridad es un número entero al que se le suma cuatro puntos por vehículo en la vía y 1 punto por segundo de espera. Esta lógica dinámica asegura que las vías con mayor congestión reciban atención preferente, minimizando los retrasos y evitando la saturación del cruce.
  • Tiempos de los semáforos. Los intervalos de tiempo en verde para cada semáforo se calculan en función del número de vehículos detectados, asignando un tiempo base de 4 segundos por automóvil y un mínimo garantizado de 12 segundos. Estos valores son configurables, lo que permite adaptar el sistema a distintas intensidades de tráfico y escenarios específicos sin comprometer su eficacia.
  • Límite de vehículos por vía. Para evitar la saturación en la maqueta, se ha establecido un límite máximo de 3 vehículos por vía, determinado por el espacio disponible. Este parámetro es ajustable según las dimensiones del entorno de implementación, asegurando que el sistema pueda escalarse a intersecciones reales con capacidades variables.
  • Barreras controladas por servomotores. El proyecto incluye barreras (similares a las utilizadas en los aparcamientos privados) controladas por servomotores, que suben y bajan en sincronía con el semáforo que gobierna la correspondiente vía.

Materiales

La siguiente tabla recoge la información sobre los componentes utilizados, así como su coste.

PiezaUnidadesPrecio (€)
Módulo HC-SR04 Sensor de ultrasonidos ELEGOO513
Módulo LED de semáforo 3.3-5 V 8 mm APKLVSR89.5
Miuzei SG 9G Micro Servo Motor12
Cables (Hembra-macho, macho-macho y hembra-hembra)20020
Tabla de madera 85x85x1 cm220
Tabla de madera 85x20x2 cm320
Placa Arduino Uno1P.P.U*
Resistencias10P.P.U*
Protoplaca1P.P.U*
Protoplaca15.4
Palillo de madera para café101
Pintura13.5
Típex de cinta10**
Coche de juguete120**
Señal de tráfico de juguete60**
Diodo LED6P.P.U.
Cable USB-A a USB-B13
Materiales de montaje6
Total103.4

Tabla 1: Materiales

*P.P.U: proporcionado por la universidad.

**Las piezas con precio de 0€ indican que ya contábamos con ellas, por lo que no hizo falta comprarlas.

Planificación

La siguiente tabla indica las fases seguidas para la elaboración del proyecto, así como las tareas realizadas en cada fase y el tiempo dedicado.

FaseTareasPeríodo
Definición del proyecto– Investigación acerca de problemáticas actuales
– Elección de atascos en grandes ciudades como problema a resolver
– Selección de Colonia Jardín como intersección a simular
– Definición de características clave (las indicadas en la sección Características del proyecto)
10/03 – 13/03 (4 días)
Diseño conceptual– Diseño de la maqueta
– Definición y compra de materiales
– Diagrama de flujo del sistema
– Especificación algorítmica en pseudocódigo
12/04 – 15/04 (4 días)
Diseño– Diseño del circuito electrónico de manera incremental
– Realización de pruebas intercaladas utilizando programas sencillos para comprobar el correcto funcionamiento de los componentes (semáforos, sensores, servomotor)
– Enumeración de los componentes para facilitar su manipulación y el desarrollo del software
16/04 – 20/04 (5 días)
Desarrollo– Programación del control de semáforos
– Integración de sensores ultrasónicos (lógica de detección de vehículos)
– Calibrado e integración del servomotor
– Implementación de la lógica de tiempos de espera
– Programación de la lógica de estados y transiciones entre ellos
– Definición de estructuras de datos y constantes para facilitar el mantenimiento
– Refactorización
– Desarrollo de pruebas intercaladas entre los pasos anteriores para comprobar el correcto funcionamiento y detectar errores
21/04 – 30/04 (10 días)
Montaje– Sujeción de las bases con tornillos
– Pintado y decoración de la maqueta
– Colocación de los componentes de la base horizontal superior (semáforos, sensores, servomotor y señales)
– Conexión de los componentes de la base superior con las protoplacas
01/05 – 03/05 (3 días)
Pruebas finales– Desarrollo de pruebas que cubran los distintos casos que puedan producirse
– Detección y corrección de errores
04/05 – 05/05 (2 días)
Grabaciones– Grabación de los vídeos que formarán parte de la documentación05/05 – 06/05 (2 días)
Presentación– Presentación del proyecto en horario lectivo07/05 (1 día)
Documentación– Edición del vídeo final
– Elaboración de la memoria
07/05 – 09/05 (3 días)
Entrega– Entrega de la documentación generada
– Publicación de la memoria en el blog de la asignatura
09/05 (1 día)

Tabla 2: Planificación

Cabe destacar también la compra de materiales durante las fases de diseño, desarrollo y montaje como consecuencia de las necesidades y problemas encontrados.

Implementación

Esta sección se divide en dos partes, hardware y software, en las que se describe, por medio de esquemas, tablas y diagramas, los aspectos más importantes del proyecto y que fueron tratados en las fases de Diseño y Desarrollo.

Hardware

Funciones de los componentes

ComponenteFunción
Módulo HC-SR04 Sensor de ultrasonidosDetección de vehículos para su posterior conteo mediante el software
Módulo LED de semáforo 3.3-5 V 8 mmSimulación de los semáforos
ServomotorMovimiento de la barrera. Finalmente, sólo se utilizó servomotor para una de las vías por la falta de pines (ver Posibles mejoras)
CablesConexiones entre componentes
Tabla de madera 85x85x1 cmBases horizontales de la maqueta
Tabla de madera 85x20x2 cmBases verticales de la maqueta
Placa Arduino UnoCerebro del sistema: control de los semáforos, procesamiento de los datos de los sensores ultrasónicos y gestión del servomotor
ResistenciasLimitación de la corriente eléctrica para proteger los LEDs y otros componentes sensibles
ProtoplacasPrototipado del circuito electrónico
Palillos de madera para caféSimulación de la barrera mediante el acoplamiento al servomotor para crear efecto visual de subida/bajada
PinturaColoreado de la maqueta para aumentar el realismo y facilitar las pruebas
Típex de cintaDelimitación de los carriles y pasos peatonales
Coches de jugueteElemento de prueba para simular el tráfico real. Permite validar la detección por sensores y los tiempos de los semáforos
Señales de tráfico de jugueteDecoración y realismo de la maqueta
Diodos LEDImplementación de puertas OR
Cable USB-A a USB-BAlimentación de la placa Arduino y carga del código desde el ordenador

Tabla 3: Funciones de los componentes

Aspectos relevantes

  • Utilizaremos el mismo tipo de semáforos para toda la vía pese a que estos tienen dos categorías; por un lado, están los semáforos más convencionales (de entrada), los cuales transicionarán del verde (prioridad de paso) al ámbar (aviso de detención obligatoria), del ámbar al rojo (detención obligatoria) y del rojo al verde. Además, tendremos los semáforos de salida, que sirven para regular el tráfico una vez ya has transitado la intersección. Transicionarán del verde (prioridad de paso) al ámbar fijo (ceda el paso en caso de que vayan a cruzar peatones) y viceversa.
  • Basándonos en el funcionamiento real de la intersección de Colonia Jardín, la carretera principal, aquella que une la vía 1 y la vía 2, funcionan exactamente igual. Cuando se abre el paso en cualquiera de estas vías, se abre en la otra, y lo mismo sucede al cerrar, luego la carretera principal trabaja de forma paralela con la vía 1 y la vía 2, siendo que las señales entre sus semáforos de entrada deben ser idénticas y lo mismo sucede con los semáforos de salida.
  • El funcionamiento de los semáforos de salida está íntimamente ligado a los semáforos de entrada; cuando el de entrada está en verde, el de salida también lo está (conexión en paralelo) ya que no tendría mucho sentido que los peatones pudiesen cruzar por la mitad del paso de peatones y por la otra mitad del semáforo de entrada no. Ahora bien, cuando el semáforo de entrada está en ámbar o en rojo, los vehículos deben detenerse. Podríamos pensar en detener el paso de vehículos por la mitad del paso de peatones correspondientes al semáforo de salida, pero esto provocaría atascos gigantes en la intersección, por lo que se optó por usar un ámbar fijo que, a la hora de salir de la intersección no simbolice que debe detenerse, sino que debe ceder el paso a los peatones. Es por eso por lo que tanto el rojo como el ámbar en el semáforo de entrada, simbolizan el ámbar en el de salida, de forma que, semSalida.Ambar = semEntrada.Ambar OR semEntrada.Rojo.

Diseño del circuito

El diseño se puede encontrar en el Anexo I del documento.

Software

Aspectos relevantes

  • Los semáforos se verán representados mediante estructuras de datos que representen los distintos estados en los que puede estar la intersección. Es por esto que, tendremos tres objetos de tipo de Semaforo: via1y2 (recordemos que trabajan en paralelo), via3 y via4. Estos tendrán un índice 0, 1 o 2 que simbolicen numéricamente el estado que representan. Además, manejarán los tres colores disponibles de los semáforos de entrada (que a su vez gobiernan el comportamiento de los semáforos de salida). El hecho de ver dos sensores asociados es porque en la via1y2 necesitamos llevar el conteo de dos sensores, guardados en numCoches y numCoches2. Para que el loop sea un código genérico que no depende de si hemos estado en la via1y2 activa o si hemos estado en otra vía activa, hemos generalizado el tipo Semaforo como si tuviese dos sensores y dos contadores, que en caso de no tener un segundo sensor (via3 y via4), no influirán en la decisión de otro número entero, la prioridad, la cual decide a qué estado vamos a ir, qué semáforo(s) vamos a poner en verde. Finalmente, el booleano penúltimo será útil para decidir a qué estado pasar en caso de conflicto de prioridades.
  • Para la realización de este proyecto hemos creído que el mejor modelo abstracto de funcionamiento es un sistema de estados que pelean por que sus semáforos estén en verde; tenemos tres estados distintos, el estado 1 (donde la via1y2 está abierta, mientras via3 y via4 están cerradas para entrar, peleando por ser las siguientes en estar en verde), el estado 2 (via3 abierta y via1y2 y via4 peleando por estar en verde a continuación) y el estado 3 (via4 abierta y via1y2 y via3 peleando por estar en verde). Ya que el semáforo de entrada de la vía1 es el que tiene asociado un servomotor, este bajará cuando el semáforo de entrada de la vía a 1 lo haga, y se levantará cuando se ponga en verde. Así, podemos decir que en el estado 1 el servo está subido, mientras que en el estado 2 y 3 estará bajado (pues via1y2 y, en particular, el semáforo de entrada de la via1 está en rojo).
  • La lógica de prioridades es la que gobierna el comportamiento de la intersección; se parte de un estado inicial (setup()), donde una vía(s) está(n) activa(s) mientras que las otras están inactivas de entrada. Las vías inhabilitadas pelearán por tener más prioridad que la otra para ser la siguiente en ponerse en verde. Esta prioridad aumenta por el mero hecho de permanecer en rojo (posibilitando que ninguna vía se termine quedando bloqueada ad infinitum), no obstante, por cada coche que se detecte que quiere entrar mientras su vía está en rojo, la prioridad aumentará considerablemente, pues la base del semáforo es reducir las colas de espera de los coches. Además, un aspecto clave es el reseteo de la prioridad de la vía que acaba de estar activa; si pasas a estar en rojo, tu prioridad vuelve a ser 0, impidiendo que una vía bloquee a las otras o que varias vías bloqueen a una. Finalmente, se puede dar el caso de que dos vías inactivas tengan la misma prioridad, sin embargo, al sólo haber tres estados, una de estas vías que están compitiendo ha sido la penúltima en estar activa, cediéndole el paso a la otra vía que lleva más tiempo inactiva que ella.
  • El conteo de vehículos solo se realiza o se tiene en cuenta en los sensores asociados a vías en rojo, pues son las que enfrentarán su prioridad para ver cuál se pone en verde a continuación. Este conteo se realizará cuando el sensor de ultrasonidos detecte objetos a menos de 14 centímetros, a coches en el carril de entrada, pues coches que salgan de la intersección por la misma vía (naturalmente) no deben ser tenidos en cuenta. Además, debidos a las limitaciones físicas, siempre cabe la posibilidad de que la cola llegue hasta el propio sensor, detectando constantemente coches, aunque solo sea un coche parado. Es por esto que el conteo de coches siempre está capado teniendo en cuenta la cantidad aproximada de coches que hay entre el semáforo y el sensor.
  • El método setup(), encargado de inicializar el sistema, da como estado inicial el estado 1, teniendo inicialmente la carretera principal libre y las perpendiculares en rojo. También se configuran los pines de entrada y salida en consecuencia al estado escogido, mientras que se conecta al servo para utilizarlo posteriormente.
  • El método loop(), el bucle infinito principal siempre funciona de la misma manera genérica; se activa un estado y se obtiene la(s) vía(s) asociada(s) activas y las inactivas, y mientras se da una cuenta atrás calculada en función de los coches contados por el semáforo que acaba de ponerse en verde, los semáforos de las vías en activas suman por cada segundo pasado y cada coche contado. Finalmente, antes de que se acabe el bucle, se comparan las prioridades y se decide a qué estado nuevo se va a transicionar, reseteando el estado que acababa de estar activo y activando el que tenía más prioridad.

Diagrama de flujo

En base a lo expuesto anteriormente, el comportamiento del sistema se puede representar con el siguiente diagrama:

Ilustración 2: diagrama de flujo del sistema

Código Arduino

El código completo se puede encontrar en el Anexo II del documento

Funcionamiento

La prueba de funcionamiento realizada en el vídeo sigue los pasos descritos a continuación. La elección de esta responde a la cobertura de los distintos casos que se pueden producir por las combinaciones de los tiempos de espera y el conteo de vehículos. Nótese que nos referimos a que una vía está “abierta” si su correspondiente semáforo está en verde y que un vehículo “avanza” cuando supera el semáforo (porque está en verde) y un vehículo “llega” cuando se detiene ante el semáforo (porque está en rojo).

  1. Inicio del programa
  2. Estado 1 activo (vías 1 y 2 abiertas) por 12 segundos.
  3. Por la vía 1 avanzan 2 vehículos; por la vía 2 avanzan 3 vehículos; por el resto de las vías no llega ningún vehículo.
  4. Transición a estado 2 (vía 3 abierta) por 12 segundos.
  5. Por la vía 3 avanza 1 vehículo; por el resto de las vías no llega ningún vehículo.
  6. Transición a estado 3 (vía 4 abierta).
  7. Por la vía 4 avanzan 2 vehículos; por las vías 1 y 2 llegan 3 vehículos en cada una; por la vía 3 llegan 2 vehículos.
  8. Transición a estado 1 (vía 1 y 2 abiertas).
  9. Por las vías 1 y 2 avanzan los vehículos que llegaron en el paso 7; por la vía 4 llegan 3 vehículos; por la vía 3 no llega ningún vehículo.
  10. Transición a estado 3 (vía 4 abierta).
  11. Por la vía 4 avanzan los vehículos que llegaron en el paso 9; por las demás vías no llega ningún vehículo.
  12. Transición a estado 2 (vía 3 abierta).
  13. Por la vía 3 avanzan 3 vehículos; por las demás vías llega 1 vehículo en cada una.

Problemas encontrados

  • Interferencias entre las señales TRIG de los sensores. En principio, el sistema se diseñó para que los sensores compartieran la misma señal TRIG (pero diferentes señales ECHO). Sin embargo, durante las pruebas, se detectó que las señales TRIG generaban interferencias cuando operaban simultáneamente, provocando lecturas erróneas (distancias al objeto detectado iguales a 0 cm., en contraposición con la realidad).
  • Interferencias entre los servomotores conectados en paralelo. Al activar múltiples servomotores en paralelo (en principio los servomotores de las vías 1 y 2 operarían así al tener comportamientos idénticos) se observaron fluctuaciones en la alimentación eléctrica, causando movimientos irregulares en las barreras. Esto comprometió la sincronización entre los semáforos y los mecanismos de bloqueo.
  • Envío constante de pulsos por parte del servomotor. El servomotor, al recibir señales de control continuas, generaron un ruido eléctrico adicional que interfería con la estabilidad del sistema. Este comportamiento provocó que el pin del servomotor no pudiese ser compartido con el LED de un semáforo (como inicialmente se había previsto).
  • Dimensiones de la maqueta. El tamaño de la maqueta fue elegido de forma tal que permitiera simular, por cada vía, tanto situaciones de tráfico ligero (por medio de 1 coche, por ejemplo) como atascos (a través de 3 coches), permitiendo así cubrir un mayor número de casos. Sin embargo, el tamaño y peso de la maqueta, construida en madera para garantizar la estabilidad durante las pruebas, dificultó su transporte, almacenamiento y manipulación.
  • Componentes defectuosos. Algunos cables y nodos de las protoplacas presentaron fallos intermitentes debido a fabricación deficiente (pues eran productos nuevos). Por otra parte, un sensor ultrasónico fue quemado fruto de un error en el diseño del circuito. Estos problemas requirieron reemplazos y modificaciones en el diseño frecuentes durante las pruebas.

Posibles mejoras

  • Usar una placa Arduino Mega, que cuenta con 54 pines digitales. De esta manera se resuelven varios de los problemas descritos en la sección anterior, que se reducen al limitado número de pines de la placa Arduino Uno. Esta mejora tendría un coste de entre 25€ y 35€ adicionales y el tiempo de adaptación sería de aproximadamente 3 días, para modificar el código incorporando la nueva funcionalidad.
  • Incluir displays de 2 dígitos y 7 segmentos. De esta forma se podrían mostrar, para cada vía, una cuenta regresiva del tiempo de los semáforos. Su implementación supondría un coste de aproximadamente 20€ en el precio (para 8 displays) y una integración relativamente sencilla utilizando librerías estándar, como LiquidCrystal.
  • Integración de zumbadores. De esta manera se añadiría un componente auditivo al sistema, por ejemplo, cuando un semáforo pasa a verde (para avisar a los conductores de que deben avanzar) o cuando pasa a ámbar (para avisar del inminente cambio a rojo). Esta sería viable tanto en lo económico (aproximadamente 1.5€ por zumbador) como en su integración en el software (por su fácil uso).
  • Uso de servomotores con barreras en todas las vías. A través de esta mejora se consigue un aumento en la seguridad vial y un diseño uniforme. Su coste asociado es bajo (2€) y su implementación es conocida, al tener ya un servomotor en el sistema, aunque necesita del aumento en el número de pines.
  • Disminución de las dimensiones. Se podría sustituir la madera por un material más ligero, como la fibra de carbono (aunque con un coste mucho mayor) o bien utilizar como simulación de vehículos objetos más pequeños a los utilizados. De esta última forma se reduciría el espacio necesario para tener 3 vehículos por vía.

Conclusiones

  • La lógica de funcionamiento implementada (basada en sensores ultrasónicos y algoritmos de priorización) permite que el sistema pueda escalarse a intersecciones más complejas con solo añadir más sensores o barreras. Esto lo diferencia de prototipos meramente educativos, ya que su arquitectura está pensada para resolver problemas reales de tráfico, como congestión o tiempos de espera excesivos.
  • Al usar datos en tiempo real (conteo de vehículos y tiempos de espera), el sistema se ajusta dinámicamente a situaciones variables (horas punta, accidentes, etc.), fomentando un flujo continuo y reduciendo atascos. Esto demuestra que el proyecto trasciende el aprendizaje básico de electrónica, ofreciendo un valor aplicable en entornos urbanos.
  • Aunque es un prototipo a escala, su diseño (con componentes estándar como Arduino y servomotores) facilita su transición a un sistema real con hardware industrial, manteniendo la misma lógica de control.
  • La placa Arduino Uno impone restricciones (pines insuficientes para muchos sensores/servomotores), lo que obligó a implementar soluciones creativas como la multiplexación de señales para conectar más componentes con menos pines o el Uso eficiente de interrupciones para gestionar múltiples entradas/salidas. Esto profundizó la comprensión de técnicas avanzadas de programación y electrónica.
  • El proyecto requirió coordinar sensores, actuadores (servomotores) y algoritmos de software, lo que permitió entender cómo evitar interferencias entre señales eléctricas (por ejemplo, entre servomotores y sensores), así como la importancia de la calibración de componentes (por ejemplo, los grados de subida y bajada de las barreras).

Anexo I: diseño del circuito en Tinkercad

Ilustración 3: circuito correspondiente a los sensores y el servomotor (I)

Ilustración 4: circuito correspondiente a los sensores y el servomotor (II)

Ilustración 5: circuito correspondiente a los semáforos (I)

Ilustración 6: circuito correspondiente a los semáforos (II)

Ilustración 7: circuito correspondiente a los semáforos (III)

Anexo II: código Arduino

#include <Servo.h>

#define TIEMPO_MINIMO 12

// Variables y estructuras de datos
struct Sensor {
  const int trig, echo;
};

Sensor sensores[] = {
  { 2, 4 },   // SN0 (Vía 1)
  { 5, 6 },   // SN1 (Vía 2)
  { 7, 8 },   // SN2 (Vía 3)
  { 12, 13 }  // SN3 (Vía 4)
};

// Semáforos -> SM1, SM2, SM3, SM4 (principales), SM5, SM6, SM7, SM8 (auxiliares)
struct Semaforo {
  const int indice;
  const int verde, amarillo, rojo;
  const int sensor1, sensor2;
  int prioridad, numCoches1, numCoches2;
  bool penultimo;  // Variable booleana para saber si la vía ha sido la activada por penúltima vez
};

Semaforo via1y2 = {
  0,   // Índice
  A0,  // SM1, SM2, SM5, SM6 verde
  A1,  // SM1, SM2, SM5, SM6 amarillo
  9,   // SM1 y SM2 rojo, SM5 y SM6 amarillo
  0,   // sensor1
  1,   // sensor2
  0,   // prioridad
  0,   // numCoches1
  0,   // numCoches2
  false
};

Semaforo via3 = {
  1,   // Índice
  A2,  // SM3, SM7 verde
  A3,  // SM3, SM7 amarillo
  10,  // SM3 rojo, SM7 amarillo
  2,   // sensor1
  2,   // sensor2
  0,   // prioridad
  0,   // numCoches1
  0,   // numCoches2
  false
};

Semaforo via4 = {
  2,   // Índice
  A4,  // SM4, SM8 verde
  A5,  // SM4, SM8 amarillo
  11,  // SM4 rojo, SM8 amarillo
  3,   // sensor1
  3,   // sensor2
  0,   // prioridad
  0,   // numCoches1
  0,   // numCoches2
  false
};

// Servomotor de la vía 1 y 2
Servo servoMotor;

int viaActiva;

// Variables para simular la multitarea en la medida de las distancias
unsigned long ultimaMedida = 0;
const unsigned long intervalo = 63;  // 250 ms / 4 sensores = 62,5 ms
int etapa = 0;

/*
Estado 1 (Vías 1 y 2 abiertas):
- SM1, SM2, SM5, SM6 verde
- SM7, SM8 amarillo
- SM3, SM4 rojo
- SN0, SN1 no cuentan coches
- SN2, SN3 sí cuentan coches
- Servomotor arriba

Estado 2 (Vía 3 abierta):
- SM3 y SM7 verde
- SM5, SM6 y SM8 amarillo
- SM1, SM2 y SM4 rojo
- SN2 no cuenta coches
- SN0, SN1, SN3 sí cuentan coches
- Servomotor abajo

Estado 3 (Vía 4 abierta):
- SM4 y SM8 verde
- SM5, SM6, SM7 amarillo
- SM1, SM2, SM3 rojo
- SN3 no cuenta coches
- SN0, SN1, SN2 sí cuentan coches
- Servomotor abajo
*/
void setEstado(Semaforo semActivo, Semaforo semInactivo1, Semaforo semInactivo2) {
  digitalWrite(semActivo.verde, HIGH);
  digitalWrite(semInactivo1.rojo, HIGH);
  digitalWrite(semInactivo2.rojo, HIGH);

  digitalWrite(semActivo.amarillo, LOW);
  digitalWrite(semActivo.rojo, LOW);
  digitalWrite(semInactivo1.verde, LOW);
  digitalWrite(semInactivo1.amarillo, LOW);
  digitalWrite(semInactivo2.verde, LOW);
  digitalWrite(semInactivo2.amarillo, LOW);

  // Ajustamos servoMotor en la vía principal
  if (semActivo.indice == via1y2.indice)  // Si los índices son iguales (constantes), los semáforos son iguales
    servoMotor.write(90);
}

void finEstado(int via) {
  Semaforo& sem = getVia(via);
  digitalWrite(sem.verde, LOW);
  digitalWrite(sem.amarillo, HIGH);
}

float medirDistancia(int sensor) {
  // Limpiamos el pin trig
  digitalWrite(sensores[sensor].trig, LOW);
  delayMicroseconds(2);

  // Generamos pulso de 10 microsegundos
  digitalWrite(sensores[sensor].trig, HIGH);
  delayMicroseconds(10);
  digitalWrite(sensores[sensor].trig, LOW);

  // Leemos el tiempo que tarda el eco, en microsegundos
  long duracion = pulseIn(sensores[sensor].echo, HIGH);

  // Obtenemos la distancia (cm) calibrada
  // Multiplicamos por la velocidad del sonido (0.0343 cm/microsegundos), y dividimos entre 2, ya que el sonido va y regresa
  float distancia = duracion * 0.0343 / 2;

  Serial.print("Sensor ");
  Serial.print(sensor);
  Serial.print(": ");
  Serial.print(distancia);
  Serial.println(" cm");

  return distancia;
}

void medirYContar(int sensor, int& numCoches) {
  float distancia = medirDistancia(sensor);
  if (distancia < 14 && distancia > 0.05 && numCoches <= 2)
    numCoches++;
}

int calcularActivo(Semaforo a, Semaforo b) {
  if (a.prioridad > b.prioridad)
    return a.indice;
  else if (b.prioridad > a.prioridad)
    return b.indice;
  return a.penultimo ? b.indice : a.indice;
}

Semaforo& getVia(int via) {
  if (via == 0)
    return via1y2;
  else if (via == 1)
    return via3;
  else
    return via4;
}

String getStringVia(int via) {
  switch (via) {
    case 0: return "Vías 1 y 2";
    case 1: return "Vía 3";
    case 2: return "Vía 4";
    default: return "Vía incorrecta";
  }
}

void inicializarSemaforo(const Semaforo& s) {
  pinMode(s.verde, OUTPUT);
  pinMode(s.amarillo, OUTPUT);
  pinMode(s.rojo, OUTPUT);
  digitalWrite(s.verde, LOW);
  digitalWrite(s.amarillo, LOW);
  digitalWrite(s.rojo, LOW);
}

void setup() {
  // Señal Trig de los sensores a OUTPUT, y señal Echo de los sensores a INPUT
  for (int i = 0; i < 4; i++) {
    pinMode(sensores[i].trig, OUTPUT);
    pinMode(sensores[i].echo, INPUT);
  }

  // Señales de semáforos a OUTPUT, y todos apagados
  inicializarSemaforo(via1y2);
  inicializarSemaforo(via3);
  inicializarSemaforo(via4);

  servoMotor.attach(3);

  // Servomotor bajado, en cualquier caso
  servoMotor.write(0);

  Serial.begin(9600);

  // La ejecución se inicia con el estado 1
  setEstado(via1y2, via3, via4);
  viaActiva = 0;
}

void loop() {
  int viaInactiva1 = (viaActiva + 1) % 3;
  int viaInactiva2 = (viaActiva + 2) % 3;
  Semaforo& semActivo = getVia(viaActiva);
  Semaforo& semInactivo1 = getVia(viaInactiva1);
  Semaforo& semInactivo2 = getVia(viaInactiva2);

  // Distinguir vías con 2 sensores a vías con uno solo
  bool inactivo1Iguales = (semInactivo1.sensor1 == semInactivo1.sensor2);
  bool inactivo2Iguales = (semInactivo2.sensor1 == semInactivo2.sensor2);

  int tiempo = TIEMPO_MINIMO + 4 * max(semActivo.numCoches1, semActivo.numCoches2);

  Serial.println();
  Serial.print("Vía activa: ");
  Serial.println(getStringVia(viaActiva));
  Serial.print("Prioridad activa: ");
  Serial.println(semActivo.prioridad);
  Serial.print("Prioridad inactiva 1: ");
  Serial.println(semInactivo1.prioridad);
  Serial.print("Prioridad inactiva 2: ");
  Serial.println(semInactivo2.prioridad);
  Serial.println();

  unsigned long tInicio = millis();
  int segundosTranscurridos = 0;

  while (segundosTranscurridos < tiempo) {
    unsigned long ahora = millis();

    if (ahora - ultimaMedida >= intervalo) {
      ultimaMedida = ahora;
      switch (etapa) {
        case 0: medirYContar(semInactivo1.sensor1, semInactivo1.numCoches1); break;
        case 1:
          if (!inactivo1Iguales)
            medirYContar(semInactivo1.sensor2, semInactivo1.numCoches2);
          break;
        case 2: medirYContar(semInactivo2.sensor1, semInactivo2.numCoches1); break;
        case 3:
          if (!inactivo2Iguales)
            medirYContar(semInactivo2.sensor2, semInactivo2.numCoches2);
          break;
      }
      etapa = (etapa + 1) % 4;
    }

    if ((ahora - tInicio) >= (segundosTranscurridos + 1) * 1000) {
      segundosTranscurridos++;

      semInactivo1.prioridad++;
      semInactivo2.prioridad++;

      Serial.print("Tiempo restante: ");
      Serial.print(tiempo - segundosTranscurridos);
      Serial.println(" s");
      Serial.println();

      if ((tiempo - segundosTranscurridos) == 3)
        finEstado(viaActiva);
      if ((tiempo - segundosTranscurridos) == 2)
        // Bajamos la barrera en todos los casos
        servoMotor.write(0);
    }
  }

  semInactivo1.prioridad += 4 * max(semInactivo1.numCoches1, semInactivo1.numCoches2);
  semInactivo2.prioridad += 4 * max(semInactivo2.numCoches1, semInactivo2.numCoches2);

  int nuevoActivo = calcularActivo(semInactivo1, semInactivo2);

  if (nuevoActivo == semInactivo1.indice)
    setEstado(semInactivo1, semActivo, semInactivo2);
  else
    setEstado(semInactivo2, semActivo, semInactivo1);

  viaActiva = nuevoActivo;

  semActivo.penultimo = true;
  semInactivo1.penultimo = false;
  semInactivo2.penultimo = false;

  semActivo.prioridad = 0;
  semActivo.numCoches1 = 0;
  semActivo.numCoches2 = 0;
}

Anexo III: imágenes del desarrollo y montaje

Ilustración 8: semáforos utilizados

Ilustración 9: puerta OR para la conexión de semáforos

Ilustración 10: semáforos conectados en paralelo

Ilustración 11: hardware del sistema

Ilustración 12: hardware en el interior de la maqueta

Ilustración 13: maqueta montada (I)

Ilustración 14: maqueta montada (II)

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *