Código: Funcionamiento de Seguidor de Línea PDF

Summary

Este documento contiene el código fuente para un seguidor de línea de robot. El código incluye declaraciones de variables, definiciones de pines, y varias funciones utilizadas para el control del robot.

Full Transcript

//\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*// //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- CÓDIGO: FUNCIONAMIENTO DE SEGUIDOR DE LÍNEA \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-...

//\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*// //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- CÓDIGO: FUNCIONAMIENTO DE SEGUIDOR DE LÍNEA \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\--// //\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*// //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- Librerias\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- \#include \ //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- Declaración de variables. \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //Defino pines de START \#define PIN\_LedStart 46 \#define PIN\_SwStart 47 //Defino pines de DIP Switch/selector de codigos \#define PIN\_Sw\_Dip1 51 \#define PIN\_Sw\_Dip2 50 \#define PIN\_Sw\_Dip3 49 \#define PIN\_Sw\_Dip4 48 //Defino pines Sensor ultrasonido \#define PIN\_TriggerUs 21 \#define PIN\_EchoUs 20 //Defino pines IN del driver L298N (Dan sentido de giro a motores) \#define IN1 43 \#define IN2 42 \#define IN3 41 \#define IN4 40 //Defino pines EN del driver L298N (Dan velocidad a motores) \#define ENA 44 \#define ENB 45 \#define PIN\_Rele 39 //Defino pines de los IR frontales \#define PIN\_IRF0 11 \#define PIN\_IRF1 10 \#define PIN\_IRF2 9 \#define PIN\_IRF3 8 //Defino Variables relacionadas con Sensores IR Frontales \#define NumSensores\_Front 4 bool IR\_F\[NumSensores\_Front\]={}; //Defino variable donde almacenar la corrección a efectuar. float correccion = 0; //Defino el número de lecturas que quiero realizar para cada sensor. A partir de este número luego se realizará el promedio. EJEMPLO: PISO\_NumeroLecturas = 10 =\> Cada valor almacenado en el array «PISO\_arrayPROMEDIOS» habrá sido el resultado de promediar diez lecturas de cada sensor. \#define PISO\_NumeroLecturas 5 //Defino cuántos sensores IR\_P voy a estar utilizando en el robot. Sirve para crear estructuras iterativas en base a ese valor. \#define numeroSensores 7 //Defino matriz, array multidimensional para generar una cola circular. Esta matriz tendrá numeroSensores filas y PISO\_NumeroLecturas columnas. int BufferCircIRP\_Val\[numeroSensores\]\[PISO\_NumeroLecturas\] = {}; //Defino índices para recorrer la cola circular. int BufferCircIRP\_IndiceVal = 0; int BufferCircIRP\_IndiceSens = 0; //Defino el array que almacenará mis X (7) promedios. Las lecturas promediadas de cada sensor se almacenarán en una posición distinta del vector. int IR\_P\[numeroSensores\]={}; //Defino Valores ideales para calculo de SetPoint float ValoresIdeales\_IZQ\[7\] = {0,0,0,0,0,0,1000}; float ValoresIdeales\_DER\[7\] = {1000,0,0,0,0,0,0}; //Defino los valores máximos que se visualizaron manualmente para cada sensor int Val\_IRP\_Max\[numeroSensores\]={730,460,590,670,500,780,460}; //Porcentaje del valor maximo de cada sensor a partir del cual se considera negro pleno para el calculo. Evaluar subir a 0.9 si funciona bien lo del enganche de abajo float Margen\_Negro=0.88; //en porcentaje 100%=1 bool BanderaIRF\_Alguno = false; //Valor a partir del cual se considera negro para enganche de lado. float EngancheNegro = 500; //en porcentaje 100%=1 //Defino arrays de pesos según derecha o izquierda float PesosDerecha\[7\] = {15, 10, 10 ,10, 10, 10, 10}; //{2, 2, 4, 8, 12, 16, 21} float PesosIzquierda\[7\] = {10, 10, 10 ,10, 10, 10, 15}; float SumaPesos\_IZQ = 0; float SumaPesos\_DER = 0; float Velocidad = 255; //Seteo de Velocidad que se usa en el Robot float Veloc = Velocidad; //Variable que se le pasa a la función Adelante, se va modificando durante el programa float Mult\_HayObjeto = 1.2; //En caso de detectar Objeto en el camino incrementa la velocidad usando este factor float Correccion\_Max = 255; //Correccion\_Max es una variable y va a valer 255 o Correccion\_MaxBlanco segun lo determine el codigo luego //Defino variables globales de la funcion ModificacionVeloc char ModoAcel=1; unsigned long RampaAcel\_Inicio=0; unsigned long RampaAcel\_Fin=0; unsigned long Timeout\_RampaAcel = 800; unsigned long Timeout\_RampaAcel\_Bloqueo = 500; bool Bandera\_RampaAcel\_Habilitada=1; const float Correccion\_MaxBlanco = 100; // Ver NOTA abajo // NOTA: Correccion\_MaxBlanco es fijo y tiene q ser menor que Velocidad pero a su vez menor a 125 // para que el robot no gire sobre su propio eje // QUEDO VIEJO, NO SE USA. CHEQUEAR //Defino variables de PID float errorActual = 0; float errorAnterior = 0; float integral = 0; float derivativa = 0; const float S\_kp = 1.3; // ganancia proporcional //kp=2 const float S\_ki = 1.8; // ganancia integral //ki=3 const float S\_kd = 0.15; // ganancia derivativa //kd=0.2 float kp = S\_kp; // ganancia proporcional //kp=2 float ki = S\_ki; // ganancia integral //ki=3 float kd = S\_kd; // ganancia derivativa //kd=0.2 float Mult\_enBlancoPID = 2.55; // Pensar si lo hacemos depender de otras variables unsigned long tiempoAnterior = 0; //Variables que nos permiten arrancar desde todo blanco buscando lentamente el lado seteado en UltimoLado unsigned long Timeout\_SinPegarLado = 1000; //2s bool Bandera\_PegarladoAct = false; //Variables para arranque suave bool Bandera\_ArranqueSuave = false; unsigned long Timeout\_ArranqueSuave = 400; //Intento de giro abrupto en Blanco unsigned long TodoBlanco\_Inicio = 0; unsigned long Timeout\_DerivativoElevado = 200; //100ms, considerar aumentarlo o disminuirlo en función de respuesta de robot. bool Bandera\_DerivativoElevado = 0; float FactorDerivativoE = 0.2; // Debería ser un poco mayor a 0.2, pero no cerca de 0.2 ya //0.3 unsigned long DerivElevado\_Fin = 0; unsigned long Timeout\_DerivElev\_Bloqueo = 200; //200 ms //Defino variables para setpoint en cada lado float SetPoint\_IZQ = 0; float SetPoint\_DER = 0; //Defino variables para determinar el lado. int LadoActual = 2; //IZQ=0 DER=1 TODOBLACO=2 TODONEGRO=3 (sin implementar, no deberiamos llegar a esa situación) int UltimoLado = 0; //Si se posiciona el Robot sobre todo blanco,osea dentro de pista, se debe setear AQUI el Lado al que nos queremos enganchar. IZQ=0 DER=1 bool Bandera\_CambioLado = 0; //Creo variables para trabajar el PWM y sentido de giro de cada motor, que con el negado (!) voy a poder ajustar el IN (en la funcion Motores). int sentidoGiroM1 = HIGH; int sentidoGiroM2 = HIGH; int pwmENA = 0; int pwmENB = 0; //Defino Variables para Timer1 y Sensor ultrasonido \#define TIMER\_US 50 // 50 uS Timer1 duracion \#define CONT\_TIMEOUT\_TRIGGER 2000 // 50\*2000=100 mS Timer1 volatile long echo\_start = 0; // Records start of echo pulse volatile long echo\_end = 0; // Records end of echo pulse volatile long echo\_duration = 0; // Duration - difference between end and start volatile int trigger\_time\_count = 0; // Count down counter to trigger pulse time volatile long distance\_cm = 0; int DistDeteccObjeto = 15; //Defino variables para el Inicio por reglamento bool Bandera\_Arrancamos = 0; bool Bandera\_SwStartYaPres = 0; bool Boton\_Start=0; unsigned long TempoStart = 0; unsigned long TempoStart\_Inicio = 0; unsigned long Bandera\_ArranqueConDemoraReglamentaria=1; //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- Fin Declaración de variables. \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void setup() { //Defino Pines de Start pinMode(PIN\_LedStart, OUTPUT); pinMode(PIN\_SwStart, INPUT); //Defino pines de DIP Switch/selector de codigos pinMode(PIN\_Sw\_Dip1, INPUT); pinMode(PIN\_Sw\_Dip2, INPUT); pinMode(PIN\_Sw\_Dip3, INPUT); pinMode(PIN\_Sw\_Dip4, INPUT); //Defino salidas de los motores pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(ENA, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); pinMode(ENB, OUTPUT); pinMode (PIN\_Rele, OUTPUT); //Defino entradas de los sensores IR Frontales pinMode(PIN\_IRF0, INPUT); pinMode(PIN\_IRF1, INPUT); pinMode(PIN\_IRF2, INPUT); pinMode(PIN\_IRF3, INPUT); //Inicializo Pines y Timer para Sensor ultrasonido pinMode(PIN\_TriggerUs, OUTPUT); pinMode(PIN\_EchoUs, INPUT); Timer1.initialize(TIMER\_US); // Initialise timer 1 Timer1.attachInterrupt( TimerISR ); // Attach interrupt a la rutina de servicio del timer attachInterrupt(digitalPinToInterrupt(PIN\_EchoUs), Echo\_ISR, CHANGE); // Attach interrupt to the sensor echo input //Inicializo serial a 9600 bits por segundo! Serial.begin(9600); //Calculo SumaPesos\_DER/IZQ y los SetPointDER/IZQ de cada lado segun posicion ideal deseada CalcularSumaPesos(); CalcularSetpoint(); } void loop() { delay(10); //Es necesario un valor de compromiso que permita al PID calcular un DT y funcionar bien. InicioPorReglamento(); //Funcion bloqueante por while hasta que se presiona SwStart. Si está Sw\_DIP1 en 1 arranca de una, sino hace el inicio a los 5 segundos //Llamo a la función para leer los valores de los sensores y almacenarlos en un buffer circular. Lectura\_IRP\_Val(); //Llama a la función para calcular el promedio de las lecturas almacenadas en el buffer circular. ActualizarPromedioIRP\_Val(); //Llamo a la función para leer los sensores frontales y almacenarlos en un buffer. Lectura\_IRF(); //Llama a la función que evalua el lado al que esta enganchado o al que tiene que engancharse. EvaluarLado(); //Llama a la función que calcula el error (segun el lado de enganche) como diferencia entre el setpoint y la medición actual. CalcularErrorActual(); //Llama a la funcion que modifica los valores de Kp, Ki, Kd y CorreccionMax, segun circunstancias de la pista ModificacionValoresK (); //Llama a la función que calcula la correción segun los parametros seteados CorreccionPID(); //Llamo a la funcion que modifica el valor de Veloc, segun circunstancias de la pista ModificacionVeloc(); //Llama a la función que calcula los abs(PWM) y los sentidos de giro de cada motor PWMySentido(Veloc, correccion); //Llama a la función que interviene en los valores de PWM ya calculados segun circunstancias de la pista IntervencionPWM (); //Ya le paso PWM final a los motores y su sentido de giro correspondiente Motores(); } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN INICIO POR REGLAMENTO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //Resumen: Funcion bloqueante, por el while, hasta que se presione SwStart. Si está Sw\_DIP1 en 1 arranca de una, sino hace el inicio a los 5 segundos void InicioPorReglamento() { while(!Bandera\_Arrancamos) // Este while se ejecuta ciclicamente hasta que ocurra la largada, luego nunca mas se ejecuta. { Lectura\_IRP\_Val(); ActualizarPromedioIRP\_Val(); Lectura\_IRF(); EvaluarLado(); CalcularErrorActual(); if(Bandera\_SwStartYaPres==0) // Chequeo continuamente si apretan el BotonStart y una vez que lo \... { // detecto, guardo el tiempo en que ocurre y no entro mas a este IF. Boton\_Start = digitalRead(PIN\_SwStart); // El led de Start se prende al bootear y se apaga al presionar el boton e iniciar conteo. digitalWrite(PIN\_LedStart, HIGH ); if(Boton\_Start) { Bandera\_SwStartYaPres=1; TempoStart\_Inicio=millis(); digitalWrite(PIN\_LedStart, LOW ); } } else if(Bandera\_SwStartYaPres==1) // Si ya fue presionado el Boton\_Start, contabilizamos 5 segundos para largar. { // Durante los ultimos 2 segundos de esos 5, prendo el Led\_Start. TempoStart=millis()-TempoStart\_Inicio; if(TempoStart\>=3000 && TempoStart\=5000) { digitalWrite(PIN\_LedStart, LOW ); Bandera\_Arrancamos=1; tiempoAnterior = millis(); } if(digitalRead(PIN\_Sw\_Dip1)) // Si el SW\_Dip1 esta activado no espero los 5 segundos, arranco ni bien presionan SwStart. {Bandera\_Arrancamos=1; tiempoAnterior = millis(); Bandera\_ArranqueConDemoraReglamentaria=0; digitalWrite(PIN\_LedStart, LOW ); } } //Serial.println(\"esperando\"); } //Serial.println(\"Largamos\"); } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN INICIO POR REGLAMENTO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN LECTURA SENSORES DE PISO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //Esta función lee los valores de los sensores y los guarda en un buffer circular. void Lectura\_IRP\_Val(void) { //Creo estructura iterativa que recorre todos los sensores. Recordar que BufferIndiceVal comienza siendo cero. for(int i = 0; i \< numeroSensores; i++) { //Leo el valor analógico de cada sensor y lo guardo en el buffer circular correspondiente. BufferCircIRP\_Val\[i\]\[BufferCircIRP\_IndiceVal\] = analogRead(A0+i); } // Incrementa el índice del buffer circular BufferCircIRP\_IndiceVal++; //Si el índice del buffer circular alcanza el número de lecturas deseadas, lo reseteo. if (BufferCircIRP\_IndiceVal \>= PISO\_NumeroLecturas) { BufferCircIRP\_IndiceVal = 0; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN ACTUALIZAR SENSORES DE PISO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN ACTUALIZAR PROMEDIO SENSORES DE PISO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //Esta función calcula el promedio de cada sensor y lo almacena en el array IR\_P\[i\] void ActualizarPromedioIRP\_Val(void) { //Itero sobre todos los sensores. for(int i = 0 ; i \< numeroSensores; i++) { //Realizo la operación de obtener promedios PISO\_NumeroLecturas veces. IR\_P\[i\]=0; for(int j = 0 ; j \< PISO\_NumeroLecturas; j++) { //Acumulador que va almacenando en variable IR\_P\[\] el valor leído y almacenado en cada posición del buffer circular. IR\_P\[i\]+=BufferCircIRP\_Val\[i\]\[j\]; } //Obtengo el promedio de mis sensores de piso, al dividir el acumulador por el contador. IR\_P\[i\]=IR\_P\[i\]/PISO\_NumeroLecturas; } //Recorro todos los sensores para aplicar un filtro. for(int i = 0; i \< numeroSensores; i++) { //Si el sensor es menor que el umbral blanco, lo considero blanco. if(IR\_P\[i\]\ (Margen\_Negro\*Val\_IRP\_Max\[i\])) { IR\_P\[i\] = Val\_IRP\_Max\[i\]; } //Añado línea de seguridad para limitar las lecturas de los sensores a, como máximo, sus valores MÁX. IR\_P\[i\] = constrain(IR\_P\[i\],0,Val\_IRP\_Max\[i\]); //Mapeo las lecturas para trabajar en una escala común. IR\_P\[i\] = map(IR\_P\[i\],0,Val\_IRP\_Max\[i\],0,1000); } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN ACTUALIZAR SENSORES DE PISO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN ACTUALIZAR SENSORES FRONTALES \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void Lectura\_IRF() { for(int i=0 ; i\= EngancheNegro) \|\| (IR\_P\[5\] \>= EngancheNegro) \|\| (IR\_P\[4\] \>= EngancheNegro) ) && (IR\_P\[1\] == 0) && (IR\_P\[0\] == 0) ) { //Determino lado IZQ. LadoActual = 0; if(LadoActual!=UltimoLado) Bandera\_CambioLado=1; //Se utilizara para resetear valor del PID UltimoLado = 0; //se podría poner 2? Lo mismo en el siguiente if } if((LadoActual == 2) && ((IR\_P\[0\] \>= EngancheNegro) \|\|(IR\_P\[1\] \>= EngancheNegro) \|\| (IR\_P\[2\] \>= EngancheNegro)) && (IR\_P\[5\] == 0) && (IR\_P\[6\] == 0) ) { //Determino lado derecha. LadoActual = 1; if(LadoActual!=UltimoLado) Bandera\_CambioLado=1; //Se utilizara para resetear valor del PID UltimoLado = 1; //se podría poner 2? Lo mismo en el anterior if } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN EVALUAR LADO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN CÁLCULO DE ERROR ACTUAL \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void CalcularErrorActual() { float sumaPonderada = 0; float medicionActual = 0; //Analizo caso según lado izquierdo, con pesos izquierda. if(LadoActual == 0 \|\| UltimoLado== 0) { for(int i = 0; i \< numeroSensores; i++) { sumaPonderada += IR\_P\[i\] \* PesosIzquierda\[i\]; } //Serial.println(\"Estoy en el lado IZQUIERDO\"); medicionActual = sumaPonderada / SumaPesos\_IZQ; errorActual = SetPoint\_IZQ - medicionActual; } if(LadoActual == 1 \|\| UltimoLado== 1) { for(int i = 0; i \< numeroSensores; i++) { sumaPonderada += IR\_P\[i\] \* PesosDerecha\[i\]; } //Serial.println(\"Estoy en el lado DERECHO\"); medicionActual = sumaPonderada / SumaPesos\_DER; errorActual =SetPoint\_DER - medicionActual; errorActual = -errorActual; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN CÁLCULO DE ERROR ACTUAL \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN MODIFICACION VALORES K \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void ModificacionValoresK (void) { //Lógica para estabilizar el arranque; arranque suave. if((millis() - TempoStart\_Inicio) \> (Timeout\_ArranqueSuave + 5000\*Bandera\_ArranqueConDemoraReglamentaria)) { Bandera\_ArranqueSuave = true; } if (LadoActual==0 \|\| LadoActual==1) { //Intento de suavizar inicio 1: avanzar con kp y kd hasta que algún sensor detecte negro, y ahí los desactiva por x ms. if(Bandera\_ArranqueSuave) { kp = S\_kp; ki = S\_ki; kd = S\_kd; } else { kp = 0; kd = 0; ki = S\_ki; //probar multiplicar por constante para aumentar la potencia de la corrección. } } if (LadoActual==2) { //Si pasaron Timeout\_SinPegarLado segundos desde que salí de cronómetro de 5s, inicio el comportamiento de pegarme de manera extrema a un lado. if((millis() - TempoStart\_Inicio) \> (Timeout\_SinPegarLado + 5000\*Bandera\_ArranqueConDemoraReglamentaria)) { Bandera\_PegarladoAct = true; } if(Bandera\_PegarladoAct) { kp = S\_kp\*1.7; //Valor: 80, 3, 1.7 ki = S\_ki\*2.4; //Valor: 0, 3, 4 kd = S\_kd; //Valor: 8, 5, 10 //Dejar siempre igual a condición default; considerar perjuicio en función CambioDerivativoElevado } else //Antes del tiempo de inicio \"PegarLado\_Start\", la corrección debe ser muy baja para no efectuar un giro muy abrupto. { kp = S\_kp\*0.5; ki = 0; kd = 0; } } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN MODIFICACION VALORES K \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN CORRECCIONPID \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void CorreccionPID() { //Funcionamiento PID unsigned long tiempoActual = millis(); float dt = (tiempoActual - tiempoAnterior) / 1000.0; // convierto a segundos (variación de tiempo en un loop). NO ES NECESARIO, pero ayuda a trabajar en una escala acorde. if (dt == 0) dt = 0.001; // Evitar división por cero tiempoAnterior = tiempoActual; // Condición para resetear Parametros Ki y Kd if(Bandera\_CambioLado == 1) { Bandera\_CambioLado=0; //Si hay cambio de lado se resetean ambos integral=0; derivativa=0; } // Si el error es cero o cruza por cero(o sea cambia signo) se resetea el acumulador de integral else if ( (errorActual \> 0 && errorAnterior \< 0) \|\| (errorActual \< 0 && errorAnterior \> 0) \|\| (errorActual==0) ) { integral = 0; derivativa = (errorActual - errorAnterior) / dt; } else { integral += (errorActual \* dt); //Considerar no multiplicarlo por tiempo. derivativa = (errorActual - errorAnterior) / dt; } correccion = kp \* errorActual + ki \* integral + kd \* derivativa; //errorActual podría tener un maximo de -1000 o +1000 pero correccion depende de esta cuenta. errorAnterior = errorActual; float aux = 1000\*PesosDerecha\[0\]/SumaPesos\_DER; aux = (aux/dt)\* FactorDerivativoE; // Este Factor por el cual se multiplica hay q evaluarlo para que entre al siguiente if en la situacion de giro abrupto pasando por todo blanco if(LadoActual == 2 && abs(derivativa) \> aux && ((millis() - DerivElevado\_Fin) \> Timeout\_DerivElev\_Bloqueo)) { Bandera\_DerivativoElevado=1; digitalWrite(PIN\_LedStart, HIGH); } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN CORRECCIONPID \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void ModificacionVeloc(void) { //Acá vamos a evaluar agregar una funcionalidad que si el ABS(correccion) menor a cierto valor haga una rampa de aceleracion durante por ejemplo 500ms y no la repita en esa recta supongamos float AuxRefErrorIRP1 = 1000\*PesosDerecha\[1\]/SumaPesos\_DER; bool Bandera\_YaPasePorCurva=0; bool Bandera\_Timeout\_RampaAcel\_Bloqueo=0; bool Bandera\_SeDesacomodo=0; bool Bandera\_Timeout\_RampaAcel=0; switch (ModoAcel) { case 1: // Velocidad Seteada por Default //ACCIONES // Definir un comportamiento al detectar objeto delante, modificar Mult\_HayObjeto (siempre 1 o mayor), subir o bajar segun si la velocidad Normal tiene o no suficiente fuerza para empujar if(distance\_cm \=255) Veloc = 255; //Me aseguro de no pasar el limite Maximo del PWM } else Veloc = Velocidad; // Vuelvo la velocidad al valor normal seteado //CONDICIONES para cambio de estado // Evaluo sino esta habilitada y pasa el tiempo de bloqueo, levanto bandera Timeout if(Bandera\_RampaAcel\_Habilitada==0 && (millis()-RampaAcel\_Fin)\>Timeout\_RampaAcel\_Bloqueo) Bandera\_Timeout\_RampaAcel\_Bloqueo=1; //Evalúo si hay un cambio fuerte en correccion que pueda ser considerado una curva, y dar aviso de ello. if (abs(correccion) \> (3\*AuxRefErrorIRP1)) Bandera\_YaPasePorCurva=0; //1 Se puso en cero con la intención de deshabilitarlo y que sólo se utilice el timeout. //Si suceden alguna de las dos situaciones anteriores Habilito que pueda entrar en rampa de aceleración. if ( Bandera\_YaPasePorCurva \|\| Bandera\_Timeout\_RampaAcel\_Bloqueo ) { Bandera\_RampaAcel\_Habilitada=1; } //Si está habilitada y se cumple que la correccion es baja, voy a Acelerar if ( (abs(correccion) \< (1\*AuxRefErrorIRP1)) && (Bandera\_RampaAcel\_Habilitada) ) { ModoAcel=2; RampaAcel\_Inicio=millis(); //digitalWrite(PIN\_LedStart, HIGH); } break; case 2: //Rampa de Velocidad, ya en movimiento //ACCIONES Veloc=Veloc+1; //Evaluar cuanto incrementar en cada ciclo de Loop if (Veloc\>=255) Veloc = 255; //CONDICIONES para cambio de estado //Evalúo si ya estuve mucho en rampa como para llegar a la proxima curva mas despacio if((millis()-RampaAcel\_Inicio)\>Timeout\_RampaAcel) Bandera\_Timeout\_RampaAcel=1; //Evalúo si el auto supera un valor de correccion en el que se vuelva inestable por culpa del aumento de Veloc if(correccion \> 1.6\*AuxRefErrorIRP1) Bandera\_SeDesacomodo=1; //Si suceden alguna de las dos situaciones anteriores, quitamos la aceleracion if (Bandera\_Timeout\_RampaAcel \|\| Bandera\_SeDesacomodo ) { ModoAcel=1; Bandera\_RampaAcel\_Habilitada=0; RampaAcel\_Fin=millis(); //digitalWrite(PIN\_LedStart, LOW); } break; default: // if nothing else matches, do the default. Default is optional ModoAcel=1; Bandera\_RampaAcel\_Habilitada=1; break; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN AVANCE \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void PWMySentido(int vel, float correccion) { //Revisar correccion = constrain(correccion, -1000, 1000); //determino algun limite máximo, REVISAR. correccion = map(correccion, -1000, 1000, -Correccion\_Max, Correccion\_Max); //Correccion\_MAX = 255. //Inicializo las variables globales en su estado default, para que sean modificadas segun la logica de esta función. sentidoGiroM1 = HIGH; sentidoGiroM2 = HIGH; pwmENA = 0; pwmENB = 0; // Obtener el excedente que supera el límite de velocidad de 255 int excedente = abs(vel) + abs(correccion) - 255; // Ajustar la velocidad si hay excedente if (excedente \> 0) { if (vel\>=0) vel = vel - excedente; else if (vel\= 0) { pwmENA = vel - correccion; pwmENB = vel + correccion; } else { pwmENA = vel + abs(correccion); pwmENB = vel - abs(correccion); } //Invertir el sentido de giro si la velocidad es negativa if (pwmENA \< 0) { pwmENA = -pwmENA; sentidoGiroM1 = LOW; } if (pwmENB \< 0) { pwmENB = -pwmENB; sentidoGiroM2 = LOW; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN PWMySentido \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN INTERVENCION DE PWM \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void IntervencionPWM (void) { //Timer para giros abruptos\... TodoBlanco\_Inicio comienza en EvaluarLado() if(Bandera\_DerivativoElevado==1 && ((millis() - TodoBlanco\_Inicio) \> Timeout\_DerivativoElevado)) { Bandera\_DerivativoElevado=0; DerivElevado\_Fin = millis(); digitalWrite(PIN\_LedStart, LOW); } if (sentidoGiroM1 == LOW) // Si estoy en blanco y el giro del motor debiera ser negativo y no hay cambio bruzco, limito PWM \"negativo\". Caso contrario no limito nada { if(LadoActual==2 && pwmENA \> 50 && !Bandera\_DerivativoElevado) { pwmENA = 50; } } if (sentidoGiroM2 == LOW) { if(LadoActual==2 && pwmENB \> 50 && !Bandera\_DerivativoElevado) { pwmENB = 50; } } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN INTERVENCION DE PWM \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN MOTORES \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void Motores(void) { //Dar sentido de giro a los motores digitalWrite(IN1, sentidoGiroM1); digitalWrite(IN2, !sentidoGiroM1); digitalWrite(IN3, sentidoGiroM2); digitalWrite(IN4, !sentidoGiroM2); //Dar velocidad a los motores según todas las condiciones evaluadas analogWrite(ENA, pwmENA); analogWrite(ENB, pwmENB); } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN MOTORES \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-- FUNCIÓN CALCULAR SUMA PESOS (EN SETUP - SE EJECUTA UNA SOLA VEZ) \-\-\-\-\-\-\-\-\-\-- void CalcularSumaPesos() { for(int i = 0; i \< numeroSensores; i++) { SumaPesos\_IZQ += PesosIzquierda\[i\]; } for(int i = 0; i \< numeroSensores; i++) { SumaPesos\_DER += PesosDerecha\[i\]; } } //\-\-\-\-\-\-\-\-- FIN FUNCIÓN CALCULAR SUMA PESOS (EN SETUP - SE EJECUTA UNA SOLA VEZ) \-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-- FUNCIÓN CALCULAR SETPOINT (EN SETUP - SE EJECUTA UNA SOLA VEZ) \-\-\-\-\-\-\-\-\-\-- void CalcularSetpoint() { //Calculo la suma ponderada para el lado izquierdo float SumaPonderada = 0; for(int i = 0; i \< numeroSensores; i++) { SumaPonderada += ValoresIdeales\_IZQ\[i\] \* PesosIzquierda\[i\]; } SetPoint\_IZQ = SumaPonderada / SumaPesos\_IZQ; SumaPonderada = 0; //calculo la otra condición, para lado derecho for(int i = 0; i \< numeroSensores; i++) { SumaPonderada += ValoresIdeales\_DER\[i\] \* PesosDerecha\[i\]; } SetPoint\_DER = SumaPonderada / SumaPesos\_DER; } //\-\-\-\-\-\-\-\-\-- FIN FUNCIÓN CALCULAR SETPOINT (EN SETUP - SE EJECUTA UNA SOLA VEZ) \-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- SERVICIO DE INTERRUPCION TIMER1 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- void TimerISR() { trigger\_pulse(); // Manejo del pulso del trigger del sensor ultrasonido //Aqui se puede aprovechar el timer para incrementar algun contador para alguna otro conteo de tiempo } // trigger\_pulse() called every 50 uS to schedule trigger pulses. // Generates a pulse one timer1 tick long. // Minimum trigger pulse width for the HC-SR04 is 10 us. This system // delivers a 50 uS pulse. void trigger\_pulse() { static volatile int state = 0; // State machine variable if (!(\--trigger\_time\_count)) // Count to 100mS { // Time out - Initiate trigger pulse trigger\_time\_count = CONT\_TIMEOUT\_TRIGGER; // Reload state = 1; // Changing to state 1 initiates a pulse } switch(state) // State machine handles delivery of trigger pulse { case 0: // Normal state does nothing break; case 1: // Initiate pulse digitalWrite(PIN\_TriggerUs, HIGH); // Set the trigger output high state = 2; // and set state to 2 break; case 2: // Complete the pulse default: digitalWrite(PIN\_TriggerUs, LOW); // Set the trigger output low state = 0; // and return state to normal 0 break; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN SERVICIO DE INTERRUPCION TIMER1 \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- SERVICIOS DE INTERRUPCION DE ECHO DEL S-ULTRASONIDO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- // Echo\_ISR() External interrupt from HC-SR04 echo signal. // Called every time the echo signal changes state. // Note: this routine does not handle the case where the timer // counter overflows which will result in the occassional error. void Echo\_ISR() { switch (digitalRead(PIN\_EchoUs)) // Test to see if the signal is high or low { case HIGH: // High so must be the start of the echo pulse echo\_end = 0; // Clear the end time echo\_start = micros(); // Save the start time break; case LOW: // Low so must be the end of hte echo pulse echo\_end = micros(); // Save the end time echo\_duration = echo\_end - echo\_start; // Calculate the pulse duration if(echo\_duration\>=23200)echo\_duration=23200; // Max duracion de echo esperada= 400cm\*58=23200 distance\_cm=echo\_duration/58; break; } } //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- FIN SERVICIO DE INTERRUPCION ECHO DEL S-ULTRASONIDO \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- CAJA DE COPY PASTE \-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-- //Leer sensores\... /\* Serial.println(\"\"); for(int i = 0; i \< numeroSensores; i++) { Serial.println(IR\_P\[i\]); } Serial.println(\"\"); \*/ //Para ver los valores medidos en un ciclo en la función Lectura\_IRP\_Val();\... /\*for(int i = 0; i \< numeroSensores; i++) {Serial.print(BufferCircIRP\_Val\[i\]\[BufferCircIRP\_IndiceVal\]); Serial.print(\"\\t\"); } Serial.println(\"\"); \*/

Use Quizgecko on...
Browser
Browser