segunda-feira, 16 de agosto de 2010

Robô explorador 4x4 com PROGRAM-ME



Depois de montar dois pequenos robôs com o PROGRAM-ME e executar diversas experiências com sensores, displays e motores, resolvi montar um robô agregando todos esses dispositivos de forma funcional permitindo assim um amplo aprendizado em robótica.

A base deste robô é o PROGRAM-ME (Arduino turbinado da Globalcode) e um chassi 4x4 que comprei na forma de kit pronto para montar.


Como os motores que vêm no kit operam entre 3 e 6 volts e eu pretendia alimentar o robô com células LIPO de 11.1 volts, substitui os 4 por motores de autorama modelo Mabuch Green de 12 volts x 24000 RPM.

Na base do chassi além dos motores instalei uma placa que permite controlar os motores (2 a 2) utilizando apenas uma porta do Program_Me, um transmissor de vídeo de 2.4 GHz para transmitir as imagens captadas pela câmera e um relé para controlar a alimentação do transmissor e da câmera.



Logo acima dos motores, na base inferior do chassi, instalei o Program-Me.
Parece que o chassi foi feito para ele não acham ?



Como a quantidade de dispositivos e sensores instalados neste robô é grande, o consumo da voltagem de 5 volts subiu bastante e portanto o regulador de voltagem 7805 tende a aquecer bastante. Para evitar o aquecimento excessivo desse regulador, tomei a decisão de remanejar ele para um pequeno dissipador na lateral do robô.



Para que seja possível estabelecer comunicação wireless com o robô, acoplei ao Program-Me um módulo XBee utilizando um shield.



O fato do Program-Me ter várias portas de conexão (SERVO 1, SERVO 2, POT 1, POT 2 e os bornes), facilitou bastante o trabalho de conexão dos sensores frontais, do display LCD e do Servo que controla a câmera, porém ainda faltavam pontos de conexão para os sensores traseiros, sensor PING, GPS, e portas I2C (sensores de temperatura/umidade, bússola e RTC).
A solução foi utilizar a área de proto do shield XBee para implementar esses pontos de conexão. (foto abaixo)



A próxima etapa seria dar ao robô a oportunidade de falar, para isso instalei um VOICE shield modificado com o chip TTS256-Text to Speech.
Esse chip em conjunto com o Voice shield, permite ao robô pronunciar palavras em inglês bastando enviar a string de caracteres via serial.



Na base superior instalei o servo que posiciona a câmera, o sensor de temperatura/umidade, a bússola, o GPS, os sensores de distância frontais e traseiros e o display LCD gráfico.

A figura seguinte mostra a parte de baixo da base superior do robô, onde são fixados os sensores, o servo e os cabos de conexão de todos os sensores e dispositivos.



Na foto abaixo temos a visão da base superior já com os sensores, GPS, RTC e o display gráfico instalados.
A antena branca é a responsável pela transmissão do vídeo captado pela câmera do robô.



A função do display gráfico é permitir a visualização das várias informações captadas pelos sensores do robô. A interface dele é serial a 115200 bps e utiliza apenas uma porta do PROGRAM-ME.


As informações indicadas pelo display são as seguintes:

superior esquerdo - Temperatura, umidade e ângulo de deslocamento.
central - "bússola" que indica a direção de deslocamento do robô.
superior direito - 3 barras verticais indicam as leituras dos sensores frontais (IR-esquerdo, PING e IR-direito)
Os dois quadradinhos indicam obstáculos na parte traseira.
A barrinha na horizontal indica o nível de luminosidade captado pelo sensor, sendo que o backlight do display é ligado automaticamente quando o nível de luminosidade é baixo.
esquerdo inferior - latitude e longitude obtidas do GPS.
direito inferior - Hora e Data que são obtidas do RTC.


Na parte superior instalei também dois mini autofalantes desses utilizados em Notebooks que são responsáveis em reproduzir as falas do robô, e também uma micro câmera montada em um servo motor.






Na traseira do robô instalei dois sensores IR que apenas detectam obstáculos. Eles não medem distância, mas o ponto de ativação pode ser ajustado entre 3 e 80 cm. Para alimentar o robô utilizei duas células de baterias LIPO de 1300mAh x 11,1 volts, que associadas em paralelo dão uma capacidade de 2600mAh x 11,1 volts



Para a unidade de controle remoto, utilizei o Brasileirino (Arduíno básico da Globalcode), um shield XBee e um Nunchuck do WII da NINTENDO (este emprestado do vídeo game do Leo).



O Nunchuck possui internamente um acelerômetro (que neste projeto controla os movimentos do robô), um mini joystick (controla a posição da câmera) e dois botões que controlam a alimentação do transmissor de vídeo/câmera e a buzina do robô (speaker do Program-Me).

O melhor de tudo é que ele usa interface I2C o que facilita demais a comunicação com o Arduino usando apenas duas portas.

A figura abaixo mostra a identificação dos sinais no conector do Nunchuck e a tabela mostra a formatação dos 6 bytes que devem ser lidos para obter as informações dos botões, do acelerômetro e do joystick.






Segue abaixo o código que usei como referência para tratar os dados do Nunchuck:

/*
* wii_nunchuck_sevo -- Use a Wii Nunchuck to control a servo
*
* Tod E. Kurt, http://todbot.com/blog/
*
* The Wii Nunchuck reading code is taken from Windmeadow Labs
* http://www.windmeadow.com/node/42
*/

#include
// comment out the below for Arduino 0011 and above, uncomment for older
//#include
//#include

uint8_t outbuf[6]; // array to store arduino output
int cnt = 0;
int ledPin = 13;

int servoPin = 7; // Control pin for servo motor
int pulseWidth = 0; // Amount to pulse the servo
long lastPulse = 0; // the time in millisecs of the last pulse
int refreshTime = 20; // the time in millisecs needed in between pulses
int minPulse = 700; // minimum pulse width

#define pwbuffsize 4
int pwbuff[pwbuffsize]; // buffer for smoothing pulseWidths
int pwbuffpos = 0; // position in pwbuff

void setup()
{
Serial.begin(19200);
Wire.begin (); // join i2c bus with address 0x52
nunchuck_init (); // send the initilization handshake
pinMode(servoPin, OUTPUT); // Set servo pin as an output pin
pulseWidth = minPulse; // Set the motor position to the minimum
Serial.print ("Finished setup\n");
}

void nunchuck_init()
{
Wire.beginTransmission (0x52); // transmit to device 0x52
Wire.send (0x40); // sends memory address
Wire.send (0x00); // sends sent a zero.
Wire.endTransmission (); // stop transmitting
}

void send_zero()
{
Wire.beginTransmission (0x52); // transmit to device 0x52
Wire.send (0x00); // sends one byte
Wire.endTransmission (); // stop transmitting
}

int t = 0; // when it gets to 25, read nunchuck
void loop()
{
t++;
if( t == 25 ) {
t = 0;
Wire.requestFrom (0x52, 6); // request data from nunchuck
while (Wire.available ()) {
// receive byte as an integer
outbuf[cnt] = nunchuk_decode_byte (Wire.receive ());
digitalWrite (ledPin, HIGH); // sets the LED on
cnt++;
}
// If we recieved the 6 bytes, then go print them
if (cnt >= 5) {
printNunchuckData(); // uncomment this for debug
// update servo pulseWidth
float tilt = outbuf[4]; // z-axis, in this case ranges from ~75 - ~185
tilt = (tilt - 70) * 1.5; // convert to degrees angle, approximately
pulseWidth = (tilt * 9) + minPulse; // convert angle to microseconds
pwbuff[pwbuffpos] = pulseWidth; // save for averaging
if( ++pwbuffpos == pwbuffsize ) pwbuffpos = 0;

pulseWidth=0; // reset so we can
for( int p=0; p= refreshTime) {
digitalWrite(servoPin, HIGH); // Turn the motor on
delayMicroseconds(pulseWidth); // Length of the pulse sets the motor position
digitalWrite(servoPin, LOW); // Turn the motor off
lastPulse = millis(); // save the time of the last pulse
}
}

// Print the input data we have recieved
// accel data is 10 bits long
// so we read 8 bits, then we have to add
// on the last 2 bits. That is why I
// multiply them by 2 * 2
int i=0;
void printNunchuckData()
{
int joy_x_axis = outbuf[0];
int joy_y_axis = outbuf[1];
int accel_x_axis = outbuf[2]; // * 2 * 2;
int accel_y_axis = outbuf[3]; // * 2 * 2;
int accel_z_axis = outbuf[4]; // * 2 * 2;

int z_button = 0;
int c_button = 0;

// byte outbuf[5] contains bits for z and c buttons
// it also contains the least significant bits for the accelerometer data
// so we have to check each bit of byte outbuf[5]
if ((outbuf[5] >> 0) & 1)
z_button = 1;
if ((outbuf[5] >> 1) & 1)
c_button = 1;

if ((outbuf[5] >> 2) & 1)
accel_x_axis += 2;
if ((outbuf[5] >> 3) & 1)
accel_x_axis += 1;

if ((outbuf[5] >> 4) & 1)
accel_y_axis += 2;
if ((outbuf[5] >> 5) & 1)
accel_y_axis += 1;

if ((outbuf[5] >> 6) & 1)
accel_z_axis += 2;
if ((outbuf[5] >> 7) & 1)
accel_z_axis += 1;

Serial.print ("seq");
Serial.print ("\t");

Serial.print ("joy_x");
Serial.print ("\t");
Serial.print ("joy_y");
Serial.print ("\t");

Serial.print ("X_axis");
Serial.print ("\t");
Serial.print ("Y_axis");
Serial.print ("\t");
Serial.print ("Z_axis");
Serial.print ("\t");

Serial.print ("Z_but");
Serial.print (" ");
Serial.println ("C_but");



Serial.print (i,DEC);
Serial.print ("\t");

Serial.print (joy_x_axis, DEC);
Serial.print ("\t");
Serial.print (joy_y_axis, DEC);
Serial.print ("\t");

Serial.print (accel_x_axis, DEC);
Serial.print ("\t");
Serial.print (accel_y_axis, DEC);
Serial.print ("\t");
Serial.print (accel_z_axis, DEC);
Serial.print ("\t");

Serial.print (z_button, DEC);
Serial.print ("\t");
Serial.print (c_button, DEC);

Serial.print ("\r\n");
i++;
delay (200);
}

// Encode data to format that most wiimote drivers except
// only needed if you use one of the regular wiimote drivers
char nunchuk_decode_byte (char x)
{
x = (x ^ 0x17) + 0x17;
return x;
}




A distribuição de portas do PROGRAM-ME ficou da seguinte forma:




Abaixo temos uma foto do conjunto finalizado, sendo que agora estou trabalhando nos últimos ajustes no código para corrigir pequenos bugs e melhorar o desempenho do robô. Assim que concluir disponibilizarei o código aqui para todos.



Os participantes do TDC2010 poderão ver o robô ao vivo no evento no próximo Domingo.

Segue abaixo uma pequena demo do robô.




Como a voz robótica nem sempre é fácil de ser compreeendida, seguem abaixo as falas do robô pronunciadas no vídeo acima.

"Robot activated"
"Hello, this is an explorer robot from Globalcode"
"I will be waiting for you on The Developers Conference 2010"
"Temperature is about 21 Celsius Degrees"
"Humidity is about 56 %"

[]s

José Luiz
http://twitter.com/jllorenzo