Control de velocidad del motor usando arduino y codificadores de cuadratura

Estoy tratando de obtener un control preciso sobre la velocidad del robot basado en rover 5. Tiene cuatro motores controlados por PWM y 4 codificadores de cuadratura óptica. Estoy usando un controlador de motor de 4 canales con chasis rover 5 . Estoy usando arduino Nano para el control. Puedo leer la salida INT del codificador y cambiar PWM según el ancho de pulso para controlar la velocidad. Pero, como resultado, recibo fuertes oscilaciones en la salida de control. Eso hace que el robot se mueva por pasos, ya que PWM cambia constantemente. Necesito un algoritmo que pueda minimizar este timbre y tener un robot de movimiento suave. Aquí está mi fragmento de código de arduino.

void setup() {
    Serial.begin(9600);
    init_motors();
    init_encoders();        
    req_speed[0] = 20;
    req_speed[1] = 20;
    req_speed[2] = 20;
    req_speed[3] = 20;
}

void loop() {
  update_encoders();
  update_motors();
}

void update_motors() {
  int i, err;
  unsigned long req_width;
  if(micros() - mtime > 2999) {
    mtime = micros();

    for(i=0; i<4; i++) {
      digitalWrite(pins_dir[i], req_speed[i]>0);
      if(mtime - change_time[i] > 50000ul && req_speed[i] != 0) {
        cur_pwm[i] += 5;
      } 
      if(req_speed[i] > 0)
        cur_err[i] = req_speed[i]*10  - cur_speed[i];
      else
        cur_err[i] = (-req_speed[i]*10)  - cur_speed[i];
      if(cur_err[i] > 0 && cur_pwm[i] < 255) {
        cur_pwm[i]++;
      } else if(cur_err[i] < 0 && cur_pwm[i] > 0) {
        cur_pwm[i]--;
      }
      analogWrite(pins_pwm[i], cur_pwm[i]);
    }
  }
}

void update_encoders() {
  int i;
  unsigned long w;
  enc_new = PINC & B00001111;
  unsigned long etime = micros();
  for (i=0; i<4; i++) {
    if((enc_old & (1 << i)) < (enc_new & (1 << i)))
    {
      w = (unsigned long)(((etime - change_time[i])));
      pulse_width[i] = (w + pulse_width_h1[i] + pulse_width_h2[i])/3;
      pulse_width_h2[i] = pulse_width_h1[i];
      pulse_width_h1[i] = pulse_width[i];
      change_time[i]=etime;
      pulse_count[i]++;
      cur_speed[i] = (3200000ul / pulse_width[i]);
    }
  }
  enc_old=enc_new;
}

Aquí req_speed está entre -100 y 100, donde el signo indica la dirección. Considere todas las variables indefinidas como globales. Experimentalmente medí que, cuando el motor está funcionando a toda velocidad, el ancho de pulso es de alrededor de 3200us.

Las salidas INT de los codificadores (XOR de A y B) están conectadas de A0 a A3. El motor PWM está conectado a D3, D5, D6, D9.

¿El arduino no es lo suficientemente rápido como para ponerse al día con 4 codificadores? ¿Es PinChangeInts mejor que sondear?

Permítanme sugerir mejoras a este código y aconsejarme sobre lo que me estoy perdiendo aquí.

Respuestas (3)

Me parece que su ciclo de control es esencialmente:

if speed is too slow:
    increase PWM duty cycle one unit
if speed is too fast:
    descreate PWM duty cycle one unit

Como has observado, esto no funciona tan bien. Su lazo de control sobrepasa cíclicamente la velocidad objetivo.

La solución canónica a este tipo de problema es un controlador PID . El concepto es esencialmente el mismo, medir algo y compararlo con un objetivo para calcular un error. Luego ajuste algo (en su caso, el ciclo de trabajo PWM) según el error.

Sin embargo, un controlador PID tiene tres términos de error:

  • P: el error actual
  • I: la integral del error
  • D: la derivada del error

Para cada uno de estos términos, el controlador ha programado alguna ganancia. Luego multiplica cada término por la ganancia respectiva para calcular cuánto se debe cambiar la entrada de control (ciclo de trabajo PWM). Con el ajuste adecuado, esto le permite obtener un circuito de retroalimentación que al principio acelera el ciclo de trabajo y luego, a medida que su vehículo se acerca a la velocidad objetivo, retrocede suavemente para que obtenga un mínimo de exceso.

Gracias. Busqué el controlador PID y parece que eso es lo que necesito aquí. Estoy considerando la biblioteca PID de arduino. Todavía no estoy seguro de cómo ajustar los parámetros kp, ki y kd. ¿Algún consejo sobre con qué valores debo comenzar?
@PunitSoni hay muchas maneras de ajustar, pero la prueba y el error funcionan bastante bien. Prepararía algo con algunas perillas para que pueda ajustar fácilmente sin volver a flashear el Arduino, al menos hasta que encuentre la configuración correcta. La regla general es que el término kp debe hacer la mayor parte del trabajo, hacer que ki sea lo suficientemente grande como para eliminar cualquier error de estado estable y usar kd con moderación para reducir el sobreimpulso. Aprenderá rápidamente qué configuraciones funcionan y cuáles resultan en una horrible inestabilidad. Si desea explorar enfoques más rigurosos, simplemente busque "Ajuste de PID" en Google. Hay libros enteros sobre el tema.
Probé la biblioteca PID de arduino. A partir de prueba y error para el ajuste, pude obtener algunas mejoras de mi solución original. Pero, todavía las fluctuaciones en la velocidad están por encima de los niveles aceptables alrededor de +/-20%. Los parámetros que usé para esto fueron (kp, ki, kd) = (1, 1.2, 0). También creo que hay algunos problemas con mi parte de medición de velocidad. Ya que, incluso con un valor de pwm fijo, el ancho de pulso del codificador varía mucho. Si cuento el número de pulsos en 100 ms, parecen ser bastante constantes. Pero ese valor es de alrededor de 15 a máxima velocidad, parece dar una resolución pobre para la velocidad.
Su problema con respecto a la mala resolución @ velocidad se debe a que está sondeando. Vea mi respuesta y si necesita más orientación sobre por qué está viendo una resolución deficiente a gran velocidad, hágamelo saber y ampliaré

Realmente creo que debería mirar las interrupciones en lugar de sondear al menos. También estoy haciendo un proyecto de codificador similar (aunque no para un robot) y he decidido gastar unos pocos dólares en un chip separado para contar los pulsos que luego puedo leer en los pines analógicos a través de un DAC.

Esto tiene varios beneficios:

  • libera la MCU para hacer otras cosas
  • significa que no tiene que preocuparse por la longitud de su código en el ciclo de sondeo, lo que resulta en pulsos perdidos
  • puede decidir leer solo el recuento del codificador cuando lo necesite

El chip que me han recomendado está aquí.

Al usar, por ejemplo, un pin analógico Arduino -> multi/demultiplexor -> DAC -> chip de interfaz de codificador dedicado, puede liberar MUCHOS pines de arduino / tener muchos más codificadores;)

Es posible que deba reducir la velocidad de su respuesta PWM cuando haya medido que el motor no está funcionando a la velocidad correcta. Supongo que calcula un ciclo de trabajo PWM objetivo dado el error. Se llama control de velocidad y es a lo que la gente se refiere tradicionalmente como el término diferencial en un controlador de tres términos. Básicamente, una forma simple de lograr esto es decidir hacia dónde debe dirigirse pero limitar la cantidad por segundo que cambia el ciclo de trabajo de PWM.

De todos modos, ¡esa es la opinión de un ingeniero analógico y alguien solo escribe código en emergencias!

En el bucle principal de arduino. Estoy haciendo control de motor cada 3ms. Actualmente, la lógica es muy simple. Si la velocidad es menor, aumente pwm en uno, si es mayor, disminuya en uno. Supuse que el rango en el que fluctuará el pwm será de alrededor de +/-3, eso no causaría mucho timbre, ya que se actualiza muy rápido. pero, el resultado que veo, son oscilaciones de baja frecuencia con alrededor de 100 ms, eso es muy notable y funciona muy mal. Creo que necesito usar algún método de control estándar para este caso de uso en lugar de reinventar la rueda. Quiero saber, donde puedo conseguir algunos trabajos existentes sobre esto.
@PunitSoni: tal vez necesite aumentar la frecuencia de PWM para obtener un control más preciso.
Hmm, todavía no pensé en la frecuencia PWM. Ni siquiera sé cuál es la frecuencia predeterminada que usa arduino con PWM. Gracias, ese es un buen lugar para mirar.
@PunitSoni Otra cosa que debe intentar es hacer que el motor funcione a una velocidad fija e incrementar el PWM; vea cuánto cambia la velocidad del motor; puede encontrar que cambia alrededor del 10% y esto es demasiado. Además, ¿cuánto tiempo lleva calcular su velocidad desde el codificador? Puede ser que tomar un poco más de tiempo le dé una "toma" mucho más precisa de la velocidad real. Tal vez si mantuvieras la velocidad del motor a un pwm constante, de alguna manera podrías medir lo que está produciendo el codificador (y tu algoritmo). Estoy hablando de integrar la señal de su codificador y dividirla por algún valor que dé un promedio más preciso.