Sistema Software Software nodo terminal En el siguiente diagrama de estados se muestra el funcionamiento de los nodos terminales.
La misión de los nodos terminales de alertar de la existencia de un peatón, por eso los dos macro-estados son PEATON y NO PEATON. La acción asociada al macro-estado PEATON es encender el led y la acción asociada al macro-estado NO PEATON es apagar el led. Las variables de las que dependen las transicciones entre estados son:
ESTADO: variable estado que identifica lo que detecta el sistema sensorial, su valor puede ser COCHE,PEATON o NADA
ESTADO_OTHERS: variable ESTADO que resume el estado real del resto de nodos terminales, su valor puede ser PEATON o NADA
TIEMPO_NOPEATON: tiempo que ha pasado desde la última vez que se detectó un peatón, puede haberlo detectado el propio nodo o cualquiera del resto.
A partir de lo anteriormente descrito se concluye que un sistema formado por múltiples nodos terminales actuarán de manera coordinada, de tal forma que cuando al menos un nodo detecte la presencia de un peaton él y el resto de nodos enciendan sus led. Mientras que, en el caso de que ninguno detecte algún peatón, todos permanecerán con los leds apagados. El código completo se encuentra en el siguiente enlace: Código Arduino
Funcionamiento del Sistema
En el siguiente video se explica el comportamiento del sistema con un solo nodo terminal.
Software nodo terminal La comunicacion con el servidor MQTT consistirá en recoger los eventos que son enviados por el broker y en el caso de algun evento corresponda con PEATON, se encenderá el led. Además en caso de que el nodo detecte uun peaton emitirá dicho evento. A continuacion se muestra el código correspondiente al cliente MQTT implementado en los nodos terminales
#include <PubSubClient.h> #include <ESP8266WiFi.h> #define OUT_LED D4 #define NADA 0 #define PEATON 1 #define COCHE 2 //EDIT THESE LINES TO MATCH YOUR SETUP #define MQTT_SERVER "192.168.0.5" //"test.mosquitto.org" const char* ssid = "Vodafone6717"; const char* password = "MMCDQCPIYODWPL"; //D9 en la parte de arduino y GPIO2 por detras char* lightTopic = "PASOPEATONES"; int ESTADO_OTHER=NADA; double instante_ESTADO_OTHER; /////////////////////////////////////////////////////////////////////////// ///////////////////////VARIABLES TIEMPO PEATON//////////////////////////// ////////////////////////////////////////////////////////////////////////// double instante_peaton; double MAX_PEATON = 4000; double instante_evento; double MAX_ENVIO_EVENTO=2000; double MAX_LED_ENCENDIDO=4000; /////////////////////////////////////////////////////////////////////////// ///////////////////////FUNCIONES Y OBJETOS ESP8266///////////////////////// ////////////////////////////////////////////////////////////////////////// //Definicion de la funcion callback del cliente MQTT void callback(char* topic, byte* payload, unsigned int length); //Funcion que convierte una direccion mac a string String macToStr(const uint8_t* mac) { String result; for (int i = 0; i < 6; ++i) { result += String(mac[i], 16); if (i < 5) { result += ':'; } } return result; } //VARAIABLES CONEXIÓN A LA RED Y AL SERVIDOR MQTT WiFiClient wifiClient; PubSubClient client(MQTT_SERVER, 1883, callback, wifiClient); void callback(char* topic, byte* payload, unsigned int length) { /*String topicStr = topic; Serial.println("Callback update."); Serial.print("Topic: "); Serial.println(topicStr);*/ if (payload[0] == 'P') { ESTADO_OTHER=PEATON; instante_ESTADO_OTHER=millis(); Serial.println("Evento peaton recibido"); } } //FUNCION DE CONEXION A LA RED void reconnect() { if (WiFi.status() != WL_CONNECTED) { Serial.print("Connecting to "); Serial.println(ssid); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected"); } if (WiFi.status() == WL_CONNECTED) { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); String clientName; clientName += "esp8266-"; uint8_t mac[6]; WiFi.macAddress(mac); clientName += macToStr(mac); if (client.connect((char*) clientName.c_str())) { Serial.print("\tMTQQ Connected"); client.subscribe(lightTopic); } else { Serial.println("\tFailed."); abort(); } } } } //FUNCION DE INICIALIZACION PRINCIPAL void setup() { Serial.begin(115200); delay(100); WiFi.begin(ssid, password); reconnect(); delay(2000); /////////////////////////////////////////////////////////////////////////// ////////////////////INICIALIZACION EVENTO PEATON/////////////////////////// ////////////////////////////////////////////////////////////////////////// instante_peaton=millis(); pinMode(OUT_LED, OUTPUT); digitalWrite(OUT_LED,LOW); instante_evento=millis(); } int last_state=NADA; //FUNCION BUCLE PRINCIPAL void loop() { if (!client.connected() && WiFi.status() == 3) { reconnect(); } client.loop(); ////////////ACCIONES POST FUZZY//////////// if(ESTADO!=last_state){ last_state=ESTADO; Serial.println("Cambio de estado"); if(ESTADO==NADA){ Serial.println("NADA"); }else if(ESTADO==PEATON){ Serial.println("PEATON"); }else if(ESTADO == COCHE){ Serial.println("COCHE"); } } double tiempo_evento=millis()-instante_evento; if(ESTADO==PEATON && tiempo_evento > MAX_ENVIO_EVENTO){ instante_evento=millis(); Serial.println("ENVIO EVENTO"); client.publish(lightTopic, "P"); //emitir peaton } if((ESTADO_OTHER==PEATON && ((millis()-instante_ESTADO_OTHER)<MAX_LED_ENCENDIDO))|| ESTADO==PEATON){ digitalWrite(OUT_LED, HIGH); //Serial.println("ENCENDER LED"); instante_peaton=millis(); } double tiempo_peaton=millis()-instante_peaton; if(tiempo_peaton>MAX_PEATON){ //Serial.println("APAGAR LED"); digitalWrite(OUT_LED, LOW); } }
La
fusión sensorial complementaria implementada trata de proporcionar una fusión
de la información obtenida por el sensor de distancia, el sensor de luz y el
tiempo. Esta fusión trata de determinar la presencia de un peatón, un coche o
nada. Para implementarla se ha hecho uso de un sistema difuso. Las variables de entrada son:
Luminosidad:
Distancia :
TiempoNoLuz
Solo existirá una variable de salida llamada estado que poseera los valores PEATON, COCHE o NADA La base de reglas es:
A continuacion se muestra el código que implementa la fusion sensorial #define NADA 0 #define PEATON 1 #define COCHE int min_medida_luz = 8; //->valores de ejemplo int max_medida_luz = 913; //->valores de ejemplo int min_real_luz = 0; //->valores de ejemplo int max_real_luz = 934; //->valores de ejemplo int min_medida_distancia = 3; //->valores de ejemplo int max_medida_distancia = 13; //->valores de ejemplo int min_real_distancia = 3; //->valores de ejemplo int max_real_distancia = 13; //->valores de ejemplo int UMBRAL_LUZ=550; double instante_no_luz; double MAX_NO_LUZ = 4000; /////////////////////////////////////////////////////////////////////////// //////////////////////VARIABLES SISTEMA DIFUSO///////////////////////////// ////////////////////////////////////////////////////////////////////////// double x0K1[2]; double x1K1[2]; double x2K1[2]; double x3K1[2]; double x0K2[2]; double x1K2[2]; double x2K2[2]; double x3K2[2]; double x0K3[2]; double x1K3[2]; double x2K3[2]; double x3K3[2]; int K1[8]; int K2[8]; int K3[8]; int C1[8]; double H[8]; //FUNCION QUE MAPEA LA VARIABLE DE ENTRADA CON EL RANGO DE ENTRADA EN EL RANGO DE SALIDA double mapdouble(double x, double in_min, double in_max, double out_min, double out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } //FUNCION QUE EJECUTA EL SISTEMA DIFUSO int fuzzysystem(double E1, double E2, double E3) { double corte; for (int i = 0; i<8; i++) { H[i] = 2147483648; //{IniciKlizo K un m¡nimo muy grKnde} corte = 0; if ((E1 >= x0K1[K1[i]]) && (E1<x1K1[K1[i]])) { corte = (E1 - x0K1[K1[i]]) / (x1K1[K1[i]] - x0K1[K1[i]]); } if ((E1 >= x1K1[K1[i]]) && (E1<x2K1[K1[i]])) { corte = 1; } if ((E1 >= x2K1[K1[i]]) && (E1<=x3K1[K1[i]])) { corte = (E1 - x3K1[K1[i]]) / (x2K1[K1[i]] - x3K1[K1[i]]); } if (corte<H[i]) { H[i] = corte; }// {M¡nimo de los Kntecedentes hKstK KhorK} corte = 0; if ((E2 >= x0K2[K2[i]]) && (E2<x1K2[K2[i]])) { corte = (E2 - x0K2[K2[i]]) / (x1K2[K2[i]] - x0K2[K2[i]]); } if ((E2 >= x1K2[K2[i]]) && (E2<x2K2[K2[i]])) { corte = 1; } if ((E2 >= x2K2[K2[i]]) && (E2<=x3K2[K2[i]])) { corte = (E2 - x3K2[K2[i]]) / (x2K2[K2[i]] - x3K2[K2[i]]); } if (corte<H[i]) { H[i] = corte; } //{M¡nimo de los Kntecedentes hKstK KhorK} corte = 0; if ((E3 >= x0K3[K3[i]]) && (E3<x1K3[K3[i]])) { corte = (E3 - x0K3[K3[i]]) / (x1K3[K3[i]] - x0K3[K3[i]]); } if ((E3 >= x1K3[K3[i]]) && (E3<x2K3[K3[i]])) { corte = 1; } if ((E3 >= x2K3[K3[i]]) && (E3<=x3K3[K3[i]])) { corte = (E3 - x3K3[K3[i]]) / (x2K3[K3[i]] - x3K3[K3[i]]); } if (corte<H[i]) { H[i] = corte; }// {M¡nimo de los Kntecedentes hKstK KhorK} } double sumHM[3]; for (int i = 0; i<3; i++) { sumHM[i] = 0; } for (int i = 0; i<8; i++) { sumHM[C1[i]] = sumHM[C1[i]] + H[i]; } int resultado = NADA; double value = 0; for (int i = 0; i < 3; i++) { if (sumHM[i] > value) { resultado = i; value = sumHM[i]; } } return resultado; } //FUNCION QUE RESTRINGE EL RANGO DE UNA VARIABLE double constraintdouble(double x, double in_min, double in_max){ if(x<in_min){ return in_min; }else if(x>in_max){ return in_max; }else{ return x; } } //FUNCION DE INICIALIZACION PRINCIPAL void setup() { Serial.begin(115200); delay(100); /////////////////////////////////////////////////////////////////////////// ///////////////INICIALIZACION BASICA SISTEMA DIFUSO//////////////////////// ////////////////////////////////////////////////////////////////////////// x0K1[0] = 0; x1K1[0] = 0; x2K1[0] = 0.3333; x3K1[0] = 0.6667; x0K1[1] = 0.3333; x1K1[1] = 0.6667; x2K1[1] = 1; x3K1[1] = 1; x0K2[0] = 0; x1K2[0] = 0; x2K2[0] = 0.3333; x3K2[0] = 0.6667; x0K2[1] = 0.3333; x1K2[1] = 0.6667; x2K2[1] = 1; x3K2[1] = 1; x0K3[0] = 0; x1K3[0] = 0; x2K3[0] = 0.3333; x3K3[0] = 0.6667; x0K3[1] = 0.3333; x1K3[1] = 0.6667; x2K3[1] = 1; x3K3[1] = 1; K1[0] = 0; K1[1] = 1; K1[2] = 1; K1[3] = 0; K1[4] = 0; K1[5] = 0; K1[6] = 1; K1[7] = 1; K2[0] = 0; K2[1] = 0; K2[2] = 1; K2[3] = 1; K2[4] = 1; K2[5] = 0; K2[6] = 0; K2[7] = 1; K3[0] = 0; K3[1] = 0; K3[2] = 0; K3[3] = 0; K3[4] = 1; K3[5] = 1; K3[6] = 1; K3[7] = 1; C1[0] = 2; C1[1] = 2; C1[2] = 2; C1[3] = 2; C1[4] = 2; C1[5] = 2; C1[6] = 1; C1[7] = 0; min_medida_luz = medida_luz[0]; max_medida_luz = medida_luz[n_lookup_luz - 1]; min_real_luz = real_luz[0]; max_real_luz = real_luz[n_lookup_luz - 1]; UMBRAL_LUZ = (real_luz[1]+real_luz[2])/2; instante_no_luz=millis(); //Configuracion etiquetas Fuzzy x2K3[0] = mapdouble(real_luz[1],min_real_luz,max_real_luz,0,1); x3K3[0] = mapdouble(real_luz[2],min_real_luz,max_real_luz,0,1); x0K3[1] = mapdouble(real_luz[1],min_real_luz,max_real_luz,0,1); x1K3[1] = mapdouble(real_luz[2],min_real_luz,max_real_luz,0,1); } //FUNCION BUCLE PRINCIPAL void loop() { ////////////OBTENCION TIEMPO (0,1)//////////// if(out_luz<UMBRAL_LUZ){ instante_no_luz=millis(); } double tiempo_no_luz=millis()-instante_no_luz; if(tiempo_no_luz>MAX_NO_LUZ){ tiempo_no_luz=MAX_NO_LUZ; } //////////////PRE-CALCULOS FUZZY////////////// double E1=mapdouble(tiempo_no_luz,0,MAX_NO_LUZ,0,1); double E2=mapdouble(out_distancia,min_real_distancia,max_real_distancia,0,1); double E3=mapdouble(out_luz,min_real_luz,max_real_luz,0,1); ///////////////SISTEMA FUZZY/////////////// // Serial.println(out_distancia); int ESTADO=fuzzysystem(E1,E2,E3); }