Velocidad de paro con encoder

Saludos,
Expongo aqui mi problema a ver si alguien me puede echar una mano... Estoy haciendo una caja con un motor y quiero que me pare en distintas posiciones mediante un encoder. La duda que tengo es que con un encoder muy preciso el paro del motor siempre me lo hace pasandose de valores de encoder y no siempre en los mismos puntos, por lo que mi pregunta es si esto se puede mejorar de alguna forma para que el arduino en cuanto lea cierto valor pare el motor.
A continuacion os detallo lo que estoy usando:
Arduino uno
Encoder chino tipo OMRON 650 pulsos por vuelta
Shield con cuatro reles (se usan solo dos para inversion de giros)
Motor como n reductor de 500W a 220.
El encoder está conectado a las dos unicas interrupciones del UNO.

Y tu código?

Lee las Normas del foro y verás porque lo pido

Lo primero muchas gracias por contestar, aquí va el código:

#include <Encoder.h>

Encoder myEnc(2,3); Interrupciones de Arduino Uno
int rele_adelante = 5;
int rele_atras = 6;
long posicionCero = 0;
long posicionUno = -20000;
long posicionDos = -57000;
long posicionTres = -110000;

void setup() {
  pinMode(rele_adelante,OUTPUT);
  pinMode(rele_atras,OUTPUT);
  Serial.begin(9600);
   

}

void loop() {
  long nuevaPosicion = myEnc.read();
  Serial.println(nuevaPosicion);
  if (Serial.available()) { //Si está disponible USB
      char c = Serial.read(); //Guardamos la lectura en una variable char
      //////////////////Estos son comandos que le mando desde una rasperri/////////////////
      if (c == 'a') { 
          digitalWrite(rele_atras,HIGH); // Por seguridad le decimos que el otro rele este apagado
            while (nuevaPosicion < posicionTres){
              digitalWrite(rele_adelante,LOW);
              }
           digitalWrite(rele_adelante,HIGH); // Una vez que llegue a destino deje todos lo reles apagados
           }
      if (c == 'b') { 
          digitalWrite(rele_adelante,HIGH); // Ahora para atras
            while (nuevaPosicion < posicionDos){
              digitalWrite(rele_atras,LOW);
              }
           digitalWrite(rele_atras,HIGH); 
           }
       if (c == 'c') { 
          digitalWrite(rele_adelante,HIGH); 
            while (nuevaPosicion < posicionUno){
              digitalWrite(rele_atras,LOW);
              }
           digitalWrite(rele_atras,HIGH); 
           }
       if (c == 'd') { 
          digitalWrite(rele_adelante,HIGH); 
            while (nuevaPosicion < posicionCero){
              digitalWrite(rele_atras,LOW);
              }
           digitalWrite(rele_atras,HIGH); 
           }


    
  }


 
}

Hola.
Tal y como tienes el código apostaría a que en el momento en que recibes un carácter se queda indefinidamente girando, pues la condición para detener el giro nunca se cumplirá si las variable nuevaPosicion no se modifica en ningún punto del while. Deberías agregar dentro del bucle, supongo, un paso tipo
nuevaPosicion=myEnc.read();
Por otro lado, el que el motor tenga que girar en una dirección u otra no depende sólo de la posición que buscas, sino también de la posición previa.
No sé si te compilará el código que te propongo, pero creo que es buen punto de partida.

#include <Encoder.h>

Encoder myEnc(2,3); Interrupciones de Arduino Uno
int rele_adelante = 5;
int rele_atras = 6;
long posicionCero = 0;
long posicionUno = -20000;
long posicionDos = -57000;
long posicionTres = -110000;

void setup() {
  pinMode(rele_adelante,OUTPUT);
  digitalWrite(rele_adelante, HIGH);
  pinMode(rele_atras,OUTPUT);
  digitalWrite(rele_atras, HIGH);
  Serial.begin(9600);
}

void loop() {
  long posActual=myEnc.read();
  Serial.print("Pos: ")
  Serial.println(posActual);
  Serial.flush(); // Para evitar que el buffer se llene y haga que el programa vaya a golpes
  
  if (Serial.available()) { //Si está disponible USB
    char c = Serial.read(); //Guardamos la lectura en una variable char
    //////////////////Estos son comandos que le mando desde una rasperri/////////////////
    long posBuscada;
    switch (c){
      case 'a':
        posBuscada=posicionTres;
        break;
      case 'b':
        posBuscada=posicionDos;
        break;
      case 'c':
        posBuscada=posicionUno;
        break;
      case 'd':
        posBuscada=posicionCero;
        break;
      default:
        return; // si recibimos un carácter no observado terminamos el loop
    }
    if (posActual < posBuscada) {
      digitalWrite(rele_adelante, LOW);
      do {
        posActual=myEnc.read();
      } while(posActual < posBuscada);
      digitalWrite(rele_adelante, HIGH);
    }
    else if (posActual > posBuscada) {
      digitalWrite(rele_atras, LOW);
      do {
        posActual=myEnc.read();
      } while(posActual < posBuscada);
      digitalWrite(rele_atras, HIGH);
    }
  }
}

Bueno, muchas gracias por contestar y por tu tiempo. La verdad es que el código le veo muy elegante y más claro que el mío y funciona perfectamente ,pero sigo teniendo el mismo inconveniente y por más que he probado no he podido conseguir nada, me explicó: a cualquier posición que le envíes todas son erróneas , hay un retraso o adelantó entre el encoder y el paro de los relés. Me explico mejor: cuando monitorizo el serial me marca posición 0 y cuando para me pone una posición diferente a la ya definida(posiciónCero,posicionUno,etc...), cuando para (fuera del lugar) me marca una ligera desviación (-25000, -50000). Es como si las interrupciones parasen de sobre manera el programa y cuando llega al punto fijado no le da tiempo a parar...

No sé exactamente cómo es el sistema que tienes montado, pero ten en cuenta que es muy probable que aunque arduino corte a tiempo, el motor se pase del punto sencillamente por la energía cinética, o porque el relé tarda unos milisegundos en cortar el contacto.
Si al menos el término del desvío es más o menos fijo aún puedes aplicar alguna corrección, pero la exactitud absoluta la veo muy difícil para el sistema que utilizas. Tal vez utilizando motores controlables (esc, servos o pap) puedes encontrar más precisión.
No obstante, no estaría de más que hagas varias pruebas y pongas los resultados que te salen por Serial a ver cuánto podemos reducir el error.

Imposible con lo que describes que el sistema se detenga en la posición. Lo mejor que puedes hacer es contar cuando es su inercia de frenado y entonces tu punto de disparo sea adelantarte en esas posiciones.

Tampoco sera perfecto porque es un rele mecánico y aunque supongo que su accionamiento será bastante similar habra algun pequeño corrimiento pero al menos estarás mas cerca de tu objetivo.

Veo que el motor dices que tiene un reductor y pones

Motor con reductor de 500W a 220.

No se que significa eso.

Motor con reductor de 500W a 220.

Quería decir que es un motor (de una puerta corredera) de 500 W de potencia a 220 voltios.

He estado probando unas cuantas veces el motor, el encoder y no hay manera de que pare de una manera regular en ninguna de las paradas. Quizás no me explicado bien en el enunciado: no quiero que pare exactamente en el pulso exacto que yo le diga, pero que este dentro de un margen considerable. Es como si el arduino no fuese capaz de leer todos los pulsos (2400 / vuelta) y los perdiese por algun sitio, siendo imposible que pare más o menos en una posicion concreta. Lo he probado poniendo un canal en una interrupcion ,el otro en un pin normal y los dos canales en sendas interrupciones (2 y 3 del Arduino UNO). Tambien he hecho pruebas midiendo de A a B y de B a A (16 metros) no midiendo lo mismo ninguna vez. Tampoco la distancia del recorrido me la da con la longitud de la rueda del encoder: 57000 pulsos todo el recorrido (16 metros) y la longitud de la rueda es de 16cm. La rueda apoya en todo momento el suelo por lo que ese error tambien le he mirado, como tambien entiendo que la velocidad de motor (15cm/s) no es una velocidad excesiva para que el arduino se pase en la cuenta de los pulsos...No se, necesito alguna idea para poder seguir...

Yo tampoco termino de hacerme una idea completa de tu sistema. ¿Sencillamente no para donde debería o pierde pasos y muestra erróneamente la posición del encoder?
Podrías pegar una sesión de lo que envías/recibes por el monitor serie, junto con una explicación de lo que ocurre/debería ocurrir.
Sería interesante si pudieras poner unas fotos, o incluso un vídeo para ilustrarnos.
Ten en cuenta que no vemos lo que tú ves, y que lo que a ti te parece evidente, no lo es para nosotros.

Yo si lo entiendo... siguie siendo problema de inercia, pero lo del enconder es un problema de lo que tu has decidido en ver no lo que ocurre en realidad.

Bien.. y que tal si controlas la velocidad y haces la aproximación de manera mas lenta? Es una solución simple pero eficaz.
A la velocidad de desplazamiento actual no puedes controlar su frenado, pero a menor velocidad conforme se aproxime a tu setpoint lo vas desacelerando hasta frenarlo en la posición o margen deseado.

Asi que ahora estudiemos el tema del motor y si puedes agregarle algo que controle la velocidad.

Sea por control de fase con un triac o con un variador de velocidad que permita el comando remoto.

Yo tengo un problema parecido y lo que me he dado cuenta es que Arduino (en mi caso un ESP32) no cuenta todos los pulsos del encoder. En mi caso tengo un encoder de dos canales y 600 pulsos por canal. En total 1200 pulsos por vuelta. Si doy 10 vueltas hacia adelante y luego doy 10 vueltas hacia atrás, la posición "0" se ha modificado ligeramente. Yo solo estoy moviendo el encoder con la mano, así que en mi caso no hay inercias. Cuanto más rápido muevo el eje más se modifica la posición.
Os dejo aquí el código por si a alguien se le ocurre como se puede solucionar:

 #include <ESP32Encoder.h>

ESP32Encoder encoder;

// No se necesitan ni temporizadores ni flancos
    
    int temporizador=0;
    float temporizador2=0;
    int contadorVueltas=0;
    int grados=0;    
    float velocidad=0;
void setup(){
  
    Serial.begin(115200);
        
    ESP32Encoder::useInternalWeakPullResistors=UP;

    // definimos los pines 19 and 18 para los canales A y B del encoder1 respectivamente:
    encoder.attachHalfQuad(19, 18);
    
    // Definimos el valor inicial del contador del encoder 1:
    encoder.setCount(0);

    Serial.println("Valor inicial = " + String((int32_t)encoder.getCount()));
    // iniciamos los temporizadores
    temporizador= millis();
    temporizador2=(float)millis();
}

void loop(){
 
  // Leemos el contador

  Serial.println((int32_t)encoder.getCount());
  Serial.flush();
 
 
}

Un saludo

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.