PIC18 Robot - Recorriendo la distancia exacta en línea recta usando problemas de codificadores de dos ruedas

0

(EDITAR: Se agregó TMR0 & TMR1 código de configuración)

Tengo un pequeño vehículo robot controlado por un PIC18f4585. Tiene dos ruedas (rodamiento de bolas en la parte delantera), así como dos codificadores giratorios. Se supone que la siguiente función recorre una distancia específica en pulgadas perfectamente recta. Viaja lo suficientemente recto, el problema es la distancia. La distancia recorrida tiene un error de hasta 2 cm. Me preguntaba si alguien más podría ver un error que no haya visto.

Información adicional: el PIC está sincronizado a 40MHz. Estoy contando los tics del codificador utilizando TMR0 y TMR1 en modo de 8 bits.

Cualquier ayuda / sugerencia sería muy apreciada, gracias.

CONFIGURACIÓN TMR:

TRISAbits.TRISA4 = IO_INPUT; 
    OpenTimer0( TIMER_INT_OFF &
            T0_8BIT &
            T0_SOURCE_EXT &
            T0_EDGE_RISE &
            T0_PS_1_1   );

TRISCbits.TRISC0 = IO_INPUT;
OpenTimer1( TIMER_INT_OFF &
        T1_8BIT_RW &
        T1_SOURCE_EXT &
        T1_PS_1_1 & 
        T1_OSC1EN_OFF &
        T1_SYNC_EXT_ON  );

FUNCIÓN RECTA MOVE:

void moveForwardDistance(double distance, int power)
{
int tickGoal = distance * 5.13348;      //This seems to get ~the correct distance

int masterPower = power;
int slavePower = power;
int error = 0;
int kp = 50;            //error factor
int totalTicks = 0;     //used to keep track of total ticks

resetEncoder();

while(totalTicks < tickGoal)
{
    SetRightMotor(masterPower);
    SetLeftMotor(slavePower);

    error = TMR1L - TMR0L;
    slavePower = power + (error / kp);

    resetEncoder();
    DelayMs(100);
    totalTicks += TMR1L;
}
SetRightMotor(0);
SetLeftMotor(0);

DelayS(1);
}
    
pregunta chib

2 respuestas

1

Parece que faltan tics de codificador cuando la CPU está ocupada con otras tareas.

Su robot está ejecutando el código en un bucle y los tiempos de bucle son aproximadamente constantes, por lo que le falta el mismo número de tics (aproximadamente) en cada bucle. Has calibrado este error fuera de tu sistema.

Sin embargo, ocasionalmente, tienes mala suerte y el bucle se prolonga más y pierdes más tics que el promedio.

Es imposible descodificar / depurar / verificar dos veces su firmware con el fragmento de código proporcionado ya que se omite la configuración de nivel de registro de la unidad de captura de entrada de hardware. Miraría allí.

    
respondido por el DrFriedParts
0

El ejemplo que citó en sus comentarios es incorrecto. Lo siento, pero eso sucede a veces.

Conceptualmente, lo que intenta hacer es transferir el valor del contador de hardware de 8 bits a un contador de software de 16 bits (o más). El problema con esto es que el contador de hardware puede avanzar una marca en cualquier momento, incluso entre cuándo lo lees y cuando lo restableces.

El código de ejemplo tiene muchos problemas con la implementación de esta transferencia, y borrar el contador de hardware antes de agregarlo al contador de software es uno de los más notorios. Leer el contador de hardware varias veces es otro problema, y permitir que transcurra un tiempo arbitrario entre la lectura y el borrado es otro.

Idealmente, le gustaría tener un tiempo cero entre la lectura y la compensación, pero esto no será posible. Lo siguiente mejor sería escribir una función (posiblemente en lenguaje ensamblador) que lea ambos contadores, los restablezca en la menor cantidad de instrucciones posible y luego guarde los valores leídos en el primer paso en variables que el resto del programa pueda usar. .

En C, esta función se vería así:

unsigned char tmr0_value;
unsigned char tmr1_value;

void read_and_reset_timers (void)
{
   register unsigned char tmr0_temp;
   register unsigned char tmr1_temp;

   /* Read the two timers into CPU registers and reset them
    * as quickly as absolutely possible.
    */
   tmr0_temp = TMR0L;
   TMR0L = 0;
   tmr1_temp = TMR1L;
   TMR1L = 0;

   /* Now, write the values to global variables.
    * It is less critical how long this takes.
    */
   tmr0_value = tmr0_temp;
   tmr1_value = tmr1_temp;
}

Luego usarías esto en tu bucle principal de esta manera:

void moveForwardDistance (double distance, int power)
{
  int tickGoal = distance * 5.13348;      //This seems to get ~the correct distance

  int masterPower = power;
  int slavePower = power;
  int error = 0;
  int kp = 50;            //error factor
  int totalTicks = 0;     //used to keep track of total ticks

  read_and_reset_timers();

  while (totalTicks < tickGoal) {
    SetRightMotor (masterPower);
    SetLeftMotor (slavePower);

    read_and_reset_timers();
    error = tmr1_value - tmr0_value;
    totalTicks += tmr1_value;
    slavePower = power + (error / kp);

    DelayMs(100);
  }

  SetRightMotor(0);
  SetLeftMotor(0);

  DelayS(1);
}
    
respondido por el Dave Tweed

Lea otras preguntas en las etiquetas