En
un robot diferencial los giros se realizan cuando las ruedas de un lado
se mueven a distinta velocidad que las ruedas del otro lado. Para ello
podemos dejar paradas las ruedas de un lado mientras se mueven las del
otro, o se puede hacer que las ruedas de cada lado giren en un sentido,
lo que provocará que el robot gire sobre sí mismo.
Lo más práctico suele
ser que las ruedas de cada lado se pueden mover en el mismo sentido
pero a distinta velocidad, cuanto mayor sea la diferencia entre la
velocidad de las ruedas de cada lado mayor es el ángulo de giro
del robot. Por ejemplo si queremos girar a la izquierda, las ruedas del
lado derecho se deben mover más rápido que las del
izquierdo.
Para controlar la velocidad de un motor se suele utilizar lo que se
llama PWM (modulación por ancho de pulso), que consiste en meter
un tren de pulsos de una determinada frecuencia al driver que controla
el motor, y dependiendo del tiempo que el pulso está a 1 dentro
del período, el motor se mueve más o menos rápido.
Para controlar un motor la Baby Orangutan como driver lleva dos puentes
en h, un puente en h está formado por 4 transistores:
Encendiendo y apagando los transistores podemos controlar la
dirección y velocidad del motor. Por ejemplo cuando estén
encendidos los dos transistores de la imagen de arriba por los que pasa
la flecha, que representa la intensidad, el motor gira en un sentido.
Cuando esos dos transistores estén apagados y se enciendan los
otros dos el motor gira en el sentido inverso al anterior. Encendiendo
sólo los dos transistores de debajo conseguimos que el
motor frene.
El microcontrolador se encarga de proporcionar al circuito integrado
con los puentes en h las señales para el control de la velocidad
y sentido de los motores, según la siguiente tabla:
Si dejamos M1A a cero y metemos un 1 en M1B, el motor gira en un
sentido. Si en lugar de meter un 1 (5V) metemos una señal de
este tipo (señal pwm):
El motor gira a una velocidad que es función del tiempo dentro
del período en el que el pulso está a 1, es decir
variando el ancho de pulso de la señal cuadrada que le
proporcionamos en M1B conseguimos que el motor gire más lento o
más rápido. Cuanto mayor sea la frecuencia de la
señal cuadrada que metemos en M1B más se acerca a un
valor constante la intensidad que atraviesa el motor, teniendo como
límite una frecuencia máxima en la que empezamos a tener
pérdidas.
Si el microcontrolador tuviera que estar poniendo a 1 M1B, contando el
tiempo que lleva a 1 para generar el ancho de pulso y depués
ponerlo a cero y además llevar la cuenta del tiempo transcurrido
antes de entrar en el siguiente período, pues implicaría
consumir mucho tiempo del microcontrolador en realizar esta tarea, lo
que nos podría impedir hacer otras cosas. Para ello el
microcontrolador tiene hasta 6 salidas pwm. Son módulos hardware
que sólo debemos activar, indicar la frecuencia de la
señal cuadrada y su ancho de pulso, y estos módulos se
encargarán de generar de manera automática la
señal pwm por un pin del microcontrolador, liberandole a
éste de tal tarea para que pueda dedicar su tiempo a hacer otras
cosas.
No voy a entrar con los registros del microcontrolador encargados
de la tarea de pwm, ni con los distintos modos de pwm que podemos
generar ya que está fuera del propósito de este robot,
pero si lo haré para la sección deArduino/AVR de la
página, debido a que las señales pwm se usan con bastante
frecuencia en robótica.
Voy a aprovechar el código hecho para la Baby Orangutan en la
página de Pololu, donde encontramos las funciones para
inicializar el hardware del microcontrolados para crear la
señales pwm, y las funciones para cambiar el ancho de pulso de
cada señal pwm generada por los módulos, y por tanto para
poder regular en velocidad los motores. Sólo anotar que el modo
de pwm seleccionado es el denominado Fast PWM entre los disponibles en
el microcontrolador, que no es el más indicado para control de
motores, teniendo otros modos disponibles que corrigen la fase de la
señal cuando cambia el ancho de pulso. Pero es el
utilizado por el robot 3pi y para el que se encuentra el código
disponible, y por tanto el que se va a usar.
En la siguiente página:
http://www.pololu.com/docs/pdf/0J15/motor_driver_application_note.pdf
encontramos el código para controlar los motores que conectemos
a la Baby Orangutan. Las funciones que nos interesan son las siguiente:
#include <avr/io.h>
// Motor Control Functions -- pwm is an 8-bit value
// (i.e. ranges from 0 to 255)
void M1_forward(unsigned char pwm)
{
OCR0A = 0;
OCR0B = pwm;
}
void M1_reverse(unsigned char pwm)
{
OCR0B = 0;
OCR0A = pwm;
}
void M2_forward(unsigned char pwm)
{
OCR2A = 0;
OCR2B = pwm;
}
void M2_reverse(unsigned char pwm)
{
OCR2B = 0;
OCR2A = pwm;
}
Incluyendo
estas funciones en nuestro código podremos controlar en velocidad y
sentido los motores. La funciones Mx_forward y Mx_reverse hacen que el
motor giren en un sentido o en el inverso, a la función le pasamos un
valor en la variable pwm, ese valor está comprendido entre 0 y 255 y se
corresponde con la velocidad de giro del motor.
// Motor Initialization routine -- this function must be called
// before you use any of the above functions
void motors_init()
{
// configure for inverted PWM output on motor control pins:
// set OCxx on compare match, clear on timer overflow
// Timer0 and Timer2 count up from 0 to 255
TCCR0A = TCCR2A = 0xF3;
// use the system clock/8 (=2.5 MHz) as the timer clock
TCCR0B = TCCR2B = 0x02;
// initialize all PWMs to 0% duty cycle (braking)
OCR0A = OCR0B = OCR2A = OCR2B = 0;
// set PWM pins as digital outputs (the PWM signals will not
// appear on the lines if they are digital inputs)
DDRD |= (1 << PORTD3) | (1 << PORTD5) | (1 << PORTD6);
DDRB |= (1 << PORTB3);
}
Motors_init() se encarga de inicializar el hardware del
microcontrolador que va a generar las señales de pwm, la
frecuencia de la señal pwm se establece a 9.8 KHz. El puente en
H de la placa Baby Orangutan admite hasta frecuencias de 80 KHz, por lo
que nos puede interesar cambiar esta configuración, para poner
una frecuencia mayor fuera del rango que escuchamos y que probablemente
mejore el rendimiento de los motores. De momento se va a dejar
así ya que es como viene.
Se escribe un código en el AVR Studio para comprobar el funcionamiento de todo lo anterior:
void reset(void)
{
PORTD |= (1<<LEDP); //Encendemos Led en Baby Orangutan.
_delay_ms(300);
PORTD &= ~(1<<LEDP);//Apagamos Led en Baby Orangutan.
_delay_ms(300);
PORTD |= (1<<LEDP);
_delay_ms(300);
PORTD &= ~(1<<LEDP);
_delay_ms(300);
PORTD |= (1<<LEDP);
_delay_ms(300);
PORTD &= ~(1<<LEDP);
}
//Funciones para controlar la velocidad y dirección de los
//motores. PWM controla la velocidad, valor entre 0-255.
void M1_reverse(unsigned char pwm)
{
OCR0A = 0;
OCR0B = pwm;
}
void M1_forward(unsigned char pwm)
{
OCR0B = 0;
OCR0A = pwm;
}
void M2_reverse(unsigned char pwm)
{
OCR2A = 0;
OCR2B = pwm;
}
void M2_forward(unsigned char pwm)
{
OCR2B = 0;
OCR2A = pwm;
}
//Configuración del hardware del micro que controla los motores.
void motors_init()
{
// configure for inverted PWM output on motor control pins:
// set OCxx on compare match, clear on timer overflow
// Timer0 and Timer2 count up from 0 to 255
TCCR0A = TCCR2A = 0xF3;
// use the system clock/8 (=2.5 MHz) as the timer clock
TCCR0B = TCCR2B = 0x02;
// initialize all PWMs to 0% duty cycle (braking)
OCR0A = OCR0B = OCR2A = OCR2B = 0;
// set PWM pins as digital outputs (the PWM signals will not
// appear on the lines if they are digital inputs)
DDRD |= (1 << PORTD3) | (1 << PORTD5) |
(1 << PORTD6); //Ya inicializados en otra función,
se puede quitar.
DDRB |= (1 << PORTB3); //Ya inicializado en otra función, se puede quitar.
}
Se compila y se graba en el micro igual que en los ejemplos anteriores.
Lo único que hace este código es generar dos
señales pwm que salen por los pines del microcontrolador y
llegan a las entradas del puente en h que controla los motores. En el
motor derecho (M1) una señal con un ancho de pulso del 25% y en
el motor izquierdo (M2) una señal con un ancho de pulso del 75%,
lo que provoca que el motor izquierdo gire bastante más
rápido que el derecho.
M1_forward(64); //Motor derecho.
M2_forward(192); //Motor izquierdo.
La velocidad de cada motor y su sentido de giro se establece en estas
dos llamadas a las funciones, al derecho le asignamos el valor 64 que
es poco más del 25% de 255, y al izquierdo le pasamos 192 que se
corresponde con el 75% del valor máximo a pasar, 255. Si
queremos que nuestro robot vaya a la velocidad máxima
habrá que pasar 255 a cada motor. Si un motor gira en el sentido
inverso al esperado, es decir que retroceda en lugar de avanzar para la
función foward, cambiando los cables de la conexión del
motor uno por otro se soluciona (el motor no tiene polaridad), o
cambiamos los nombres de reverse por forward en las funciones.
Si conectamos un osciloscopio al borne a mayor tensión de cada
motor podemos observar la siguiente señal, amarillo M1, azul M2.
Esta es la tensión a la que están conectados el terminal
positivo de cada motor en el tiempo, se puede ver como los resultados
se corresponden con los valores pasados en las funciones M1_foward y
M2_foward que hemos copiado de pololu, y por tanto su funcionamiento es
correcto. Es decir cuanto mayor sea nuestro ancho de pulso mayor
será la tensión eficaz que le proporcionemos al motor,
por lo que más rápido girará.