Carrinho com Arduino guiado por infravermelho

Será mostrado neste tutorial como montar um carrinho com um Arduino Uno e um Wiimote que segue 2 leds infravermelhos. A partir da leitura feita pelo controle, as coordenadas dos leds são enviadas para o computador que interpreta e envia instruções para o Arduino, acoplado ao carrinho, movendo-o para frente, para trás ou para os lados.

aquitetura-2

Este projeto foi desenvolvido baseado no sistema operacional Ubuntu 14.04 mas o mesmo deveria funcionar tanto no Windows quanto no Mac OS X, feitas as devidas modificações nas instalações do protocolo de Bluetooth instalado (já que o BlueZ suporta apenas distribuições Linux) .

1 Hardware necessário

  • Computador com sistema operacional Linux e dispositivo bluetooth
  • Wiimote
  • 2 pilhas AA
  • 2 módulos led infravermelho
  • Arduino uno
  • Shield Motor L293D
  • Módulo Bluetooth RS232 HC-05
  • Kit chassi 4 rodas com 4 motores
  • Fonte 5v 2000mA

Para montagem do carrinho, utiliza-se o Shield Motor para Arduino e o Arduino acoplados ao chassi. Os pinos dos 4 motores das rodas e do Módulo Bluetooth no Shield são indicados nas imagens abaixo

Shield apresentação

Shield Motor

modulo bt apresentação

Módulo Bluetooth

A placa com os LEDs é bem simples, somente observando o distanciamento entre os leds que influenciam no valor da distância que o Wiimote captura. Pois a distância do Wiimote pra os LEDs é calculada com base na distância entre os dois leds e quanto mais próximos os leds, mais longe o sensor vai achar que os leds estão.

WP_20160705_003

 

2 Configuração dos softwares necessários

Para a execução deste projeto é necessária a instalação de:

  • BlueZ – Implementação do stack de protocolo Bluetooth para Linux
  • WiiUse – API para conexão com o wiimote
  • arduino-serial – API para comunicação com o arduino via portas seriais

2.1 Instalando o BlueZ

A instalação do BlueZ pode ser feita a partir dos arquivos disponibilizados na sua página ou através dos pacotes disponibilizados pela própria distribuição, no Ubuntu 14.04 isso pode ser feito através do comando

sudo apt-get install bluez python-gobject python-dbus

ou através de algum frontend para o apt como o Synaptic

2.2 Instalando o WiiUse

Tanto os arquivos necessários quanto as instruções para instalação podem ser obtidos do seu repositório no GitHub. Em um sistema Ubuntu a instalação dos arquivos pode ser feita através do terminal desta maneira

git clone https://github.com/rpavlik/wiiuse.git
cd wiiuse
mkdir build
cd build

Para montar os arquivos pode ser utilizado tanto o ccmake quanto o cmake-gui

Utilizando o ccmake

ccmake ..
  • Na próxima tela, pressione ‘c’ para configurar a build
  • Caso desejar, altere as configurações do que ser instalado
  • Para executar a build pressione ‘c’ e em seguida ‘g’ para gerar a build e sair

Para compilar todos os arquivos utilize o comando

make

Feito isso, poderá ser encontrado dentro do diretório build/example um programa de exemplo (wiiuseexample) enquanto no diretório build/src pode ser encontrado o arquivo libwiiuse.so que deve ser incluido/linkado ao projeto

2.3 Instalando o arduino-serial

Para instalar a biblioteca arduino-serial-lib utilize os comandos

git clone https://github.com/todbot/arduino-serial.git
cd arduino-serial
make

Executados estes comandos, no diretório atual poderão ser encontrados os códigos-fonte e compilados dos seguintes arquivos:

  1. arduino-serial — Programa com exemplo de uso da biblioteca
  2. arduino-serial-lib – Código da biblioteca que deve ser incluido no projeto

2.4 Pareando o bluetooth do carrinho com o computador

Durante a execução deste projeto, o sensor bluetooth só pode ser conectado após a remoção do pacote brltty, utilizado para a funcionalidade de leitura de tela (display “braile”), leia este post sobre o assunto

Primeiro verifique a existência de dispositivos pareados procurando por arquivos de dispositivos no diretório /dev utilizando

ls /dev | grep 'rfcomm'

ou apenas digitando ls /dev/rf, pressionando TAB algumas vezes e observar o resultado do auto-complete.

Para fazer o ‘bind’ do device, primeiramente encontre o endereço do dispositivo a partir do terminal fazendo

hcitool scan

Caso encontrado o dispositivo de nome HC-05, copie o endereço e execute o seguinte comando:

sudo rfcomm bind <numero_do_dispositivo> <endereco_do_dispositivo>

Supondo que nenhum arquivo de dispositivo rfcomm* tenha sido encontrado em /dev

sudo rfcomm bind 0 <endereco_do_dispositivo>

Caso este dispositivo tenha sido configurado, ele deverá aparecer ao se executar

ls /dev | grep 'rfcomm'

3 Implementação do código

Nesta seção serão explicadas as partes importantes da lógica do código do rastreamento e envio de comandos para movimentação, para obter o código-fonte completo incluindo o código do arduino ver seção 4.

Para referência, a documentação da API pode ser encontrada aqui.

3.1 Organização geral do código

O código do programa se divide entre três arquivos

  • main.c — Inicia conexão com wiimotes, bluetooth e escuta eventos do wiimote.
  • wiimote-handler.c — Lida com os eventos do wiimote, i.e., informações sobre o sensor IR e botões.
  • robot-controller.c — Lida com as requisições para movimentação do carrinho.

3.2 Conexão dos dipositivos

A conexão com o wiimote e com o bluetooth do carrinho funciona da seguinte forma:

  • Procure por wiimotes por 5 segundos.
    • caso nenhum tenha sido encontrado, imprimir mensagem de erro e sair.
  • Conecte-se com todos os wiimotes.
    • caso nenhum wiimote tenha se conectado, imprimir mensagem de erro e sair.
  • Configure wiimotes (leds, IR e acelerômetro).
  • Conecte-se a porta serial do bluetooth do carrinho.
    • caso não seja possível encontrar o dispositivo, imprimir mensagem de erro e sair.
  • Enquanto houver wiimotes conectados, escutar eventos.
    • caso não houver wiimotes conectados, imprimir mensagem de encerramento e sair.

Sabendo que

typedef enum {
    MAX_WIIMOTES = 4,
    SEARCH_TIMEOUT_SECONDS = 5
} WiiuseConstants;

No código de main.c podemos ver como funciona a lógica da conexão com os wiimotes:

wiimote** wiimotes;
int found, connected, fd;

/* Inicializar array de wiimotes */
wiimotes = wiiuse_init(MAX_WIIMOTES);
found = wiiuse_find(wiimotes, MAX_WIIMOTES, SEARCH_TIMEOUT_SECONDS);

if (!found) {
    printf("Nenhum Wiimote encontrado!\n");
    return 0;
}

/* Conectar wiimotes */
connected = wiiuse_connect(wiimotes, MAX_WIIMOTES);

if (connected) {
    printf("Conectado a %d Wiimotes de um total de %d encontrados.\n",
           connected, found);

    setup_wiimote(wiimotes);
} else {
    printf("Conexão com Wiimotes falhou!\n");
    return 0;
}

E como funciona a lógica da conexão com o bluetooth do arduino:

/* iniciar conexão com o arduino */
fd = connect_to_robot();
if (fd == -1) {
    printf("--A conexão com o arduino falhou!--\n");
    return 0;
} else {
    printf("--Conectado ao arduino--\n");
}

3.3 Escuta de eventos do wiimote

A escuta de eventos do wiimote funciona dentro de um laço:

  • Verifique se existem wiimotes conectados
    • caso não existam, saia do laço e encerre a conexão com a porta serial.
  • Para cada um dos quatro possíveis wiimotes, observe o atributo ‘event’
    • Caso ‘WIIUSE_EVENT’, execute ‘handle_event()’
    • Caso ‘WIIUSE_STATUS’, execute ‘handle_status()’
    • caso ‘WIIUSE_DISCONNECT’, execute ‘handle_execute()’
  • volte ao primeiro passo

Ainda em main.c podemos ver como funciona este código:

/* enquanto existirem wiimotes conectados escutar eventos */
while (has_wiimote_connections(wiimotes, MAX_WIIMOTES)) {
    if (wiiuse_poll(wiimotes, MAX_WIIMOTES)) {
        int i;

        /* Analisar cada um dos quatro slots */
        for (i = 0; i < MAX_WIIMOTES; i++) {
            switch (wiimotes[i]->event) {

            /* eventos - botões, sensores de movimento ou IR*/
            case WIIUSE_EVENT:
                handle_event(wiimotes[i]);
                break;

            /* status - informações do dispositivo */
            case WIIUSE_STATUS:
                handle_status(wiimotes[i]);
                break;

            /* desconexão de dispositivo */
            case WIIUSE_DISCONNECT:
                handle_disconnection(wiimotes[i]);

            default:
                break;
            }
        }
    }
}

/* fechar conexão bluetooth com o arduino */
disconnect();
return 0;

3.4 Rastreamento

O sistema de rastreamento dos LEDs funciona através de:

  • Caso os LEDs se encontrem à esquerda, envie sinal rodar para a direita.
  • Caso os LEDs se encontrem à direita, envie sinal rodar para a direita.
  • Caso mais de um led estejam visíveis, faça:
    • Se a distância z for menor que um certo valor, envie sinal mover para frente.
    • Se a distância z for maior que um certo valor, envie sinal mover para trás

Em wiimote-handler.c podemos ver a implementação do código nesta lógica:

/**
 * Caso o sensor IR esteja habilitado, interpreta os sinais do sensor e
 * envia requisições para a movimentação do carro.
 */
if (WIIUSE_USING_IR(wm)) {
    int i, visible_dot_count;

    /**
     * Margem de erro para alinhamento do carro, 20% para mais ou para menos
     * do centro da tela, o valor '1023' é baseado na resolução do sensor IR
     * do wiimote que é de 1024 por 768.
     */
    float center_tolerance = 0.2;
    float left_limit = 1023 * (0.5 - center_tolerance);
    float right_limit = 1023 * (0.5 + center_tolerance);

    /**
     * Como a distâancia no eixo z (entre o wiimote e o ponto de referencia
     * entre os leds) varia de acordo com a distância entre os leds, os
     * valores 700 e 800 foram escolhidos arbitrariamente para que o 'zero'
     * do carro se mantivesse a uma distância específica dos leds.
     */
    int lower_distance_limit = 700;
    int upper_distance_limit = 800;

    /**
     * Visita cada um dos 4 possíveis leds e imprime suas coordenadas x e y
     * caso estejam visiveis.
     */
    for (i = 0; i < 4; i++) {
        if (wm->ir.dot[i].visible) {
            printf("\nIR SOURCE %d:  (rx: %d, ry: %d)\n",
                   i, wm->ir.dot[i].rx, wm->ir.dot[i].ry);
        }
    }

    /**
     * Executa correção de posicionamento apenas se existirem um ou mais
     * leds visíveis.
     */
    if (wm->ir.num_dots > 0) {
        printf("IR cursor(a): (ax: %d, ay: %d)\n", wm->ir.ax, wm->ir.ay);
        printf("IR cursor(i): ( x: %d,  y: %d)\n", wm->ir.x, wm->ir.y);
        printf("IR z-distance: %f\n", wm->ir.z);

        /**
         * Correção do alinhamento (eixo x).
         */
        if (wm->ir.ax > right_limit) {
            signal_right();

        } else if (wm->ir.ax < left_limit) {
            signal_left();

        } else {
            printf("MEIO\n");

            /**
             * Correção da distância (eixo z), necessita de pelo menos dois
             * pontos de IR visíveis para o cálculo da distância.
             */
            if (wm->ir.num_dots > 1) {
                if (wm->ir.z < 700) {
                    signal_back();
                } else if (wm->ir.z > 800) {
                    signal_forward();
                }
            }

        }
    } //Captura de um ou mais pontos
} //Interpretação do sinal IR

3.5 Envio de requisições para movimentação

Como a lógica para o rastreamento envia requisições tão rapidamente quanto o código puder ser executado, é necessário um meio para que a quantidade de requisições enviadas ao arduino não ultrapasse a sua taxa de execução, visto que essa é limitada pelo tempo de operação da movimentação dos motores, abaixo vemos como funciona esta lógica:

  • Pegue o tempo de execução atual.
  • Subtraia do tempo atual o tempo quando a última requisição foi enviada.
    • Caso o intervalo de tempo do resultado seja superior ao limite estabelecido faça:
      • Atualizar o tempo da última requisição para o tempo atual.
      • Envie a requisição para movimentação.
      • Imprima uma mensagem com a direção do movimento.
    • Caso o intervalo seja inferior, apenas imprima uma mensagem com a direção do movimento.

Abaixo vemos o código para a verificação do tempo:

int has_enough_time(void)
{
    clock_t now;
    now = clock();

    if ( ((double)(now - last_sent)) > limit ) {
        last_sent = now;
        return 1;
    }

    return 0;
}

E como essa verificação é utilizada no envio da requisição de movimentação para frente:

/**
 * \brief  Envia comando para mover para frente.
 *
 * Para fim de testes, caso não seja possível se utilizar a conexão com o
 * bluetooth é possível imprimir no console a mensagem '<direcao>-E'
 */
void signal_forward()
{
    if (fd == ERROR_CODE)
        printf("FRENTE-E\n");
    else {
        if (has_enough_time()) {
            serialport_write(fd, "w");
            printf("FRENTE\n");
        }
    }
}

3.6 Conexão da porta serial

O código para conexão com a porta serial do bluetooth se encontra na sub-rotina connect_to_robot() em robot-controller.c, note que o nome do arquivo de dispositivo utilizado /dev/rfcomm0 é o do dispositivo configurado em 2.4

/**
 * \brief  Inicia uma conexão com a porta serial.
 * \returns  fd válido ou -1 caso contrário.
 */
int connect_to_robot(void)
{
    return fd = serialport_init("/dev/rfcomm0", 9600);
    last_sent = clock();
}

4 Código-fonte do projeto

O código completo dos arquivos main.c, wiimote-handler.c, robot-controller.c e test_motor.ino podem ser obtidos no repositório https://github.com/daniloBlera/ir-tracking.

 WP_20160705_003  WP_20160705_002

Vídeo

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s