terça-feira, 12 de janeiro de 2016

Robótica: Telemetria & Comunicação Wireless

Atuei em um projeto recentemente em que seria utilizada telemetria analógica para realizar medições de veículos de corrida. Para isto, seria necessário a comunicação entre o dispositivo instalado no veículo com uma central de comunicação, ou seja, comunicação a distância sem-fio.

Utilizando o - sempre sutil - Arduino, consegui desempenhar a tarefa com sucesso, por meio de poucos componentes e com baixo custo, como irei explicitar a seguir.

Componentes
Os componentes utilizados para auxiliar no projeto foram:

  • ARDUINO MEGA 2560
    • foi designado para embarcar os sensores, o display e o transceptor;
    • modelo ideal pela quantidade de portas digitais e analógicas, o suficiente para poder embarcar todos os componentes necessários e poder armazenar memória o suficiente;
    • para alimentação, foi utilizada uma bateria comum de 9V;
Arduino Mega 2560

  • ARDUINO UNO
    • se assemelha ao modelo acima, com a diferença de ser menor, contendo menos portas analógicas, digitais e menos memória;
    • atuou como receptor dos dados da telemetria, conectado ao computador receptor dos dados, via cabo USB;
Arduino Uno

  • NRF24L01 WIRELESS TRANSCEIVER
    • o componente mais importante: o módulo de rede sem fio NRF24L01 é um transceptor fabricado pela Nordic e é uma excelente opção de comunicação wireless entre vários dispositivos, como: Arduino, PIC, Raspberry, BeagleBone entre outros;
    • algumas especificações:
      • alcance de 10 metros (ambientes internos) a 50 metros (campo aberto);
      • possui uma antena embutida que opera na frequência de 2,4GHz com velocidade de 2Mbps;
      • modulação GFSK;
      • habilidade de anti-interferência;
      • verificação de erros por CRC;
      • comunicação multi-ponto de 125 canais;
      • controle de fluxo;
      • tensão de alimentação pode variar entre 1,9 e 3,6V;
    • foram utilizados no total dois módulos: 
      • um NRF24L01 comum, acoplado ao Uno, e
      • outro adaptado com PA (amplificador de potência) e LNA (antena externa), acoplado ao Mega, para alcance de longas distâncias: estima-se 1km!
Módulo NRF24L01 e outro adaptado com PA+LNA

  • DISPLAY LCD
    • este componente realizou a apresentação dos dados lidos pelo Arduino;
    • foi utilizado o modelo 16x2 da fabricante Winstar, com backlight ajustável;
    • foi programado para atuar em tempo real, atualizando os dados de acordo com o interpretado pelos sensores e também com a função de alternar as informações mostradas na tela, por meio de um push-button;
Display LCD Winstar

Montagem

ARDUINO MEGA (TRANSMISSOR)
Do projeto e componentes apresentados na imagem acima, há algumas observações pertinentes a se fazer:
  • para o funcionamento correto do módulo wireless NRF24L01, o ideal é que se use um capacitor de 4.7 μF para o 3.3V do Arduino ou um regulador de tensão, se precisar utilizar alimentação de 5V, convertendo a tensão para 3.3V;
  • no caso de problemas com transmissão utilizando wireless com PA+LNA, uma alternativa é baixar a taxa de transmissão (testar a partir de aprox. 2400bps);
  • a montagem neste Arduino não segue o mesmo padrão dos demais, e deve ser realizado um mapeamento para converter a pinagem dos conectores, e ligar corretamente como mostrado no esquemático a seguir e na tabela de pinagens mais à frente;
  • o led ligado ao pino digital 8 é utilizado para informar visualmente que as mensagens estão sendo trnasmitidas: quando enviado um sinal ao outro transceptor, o led irá piscar;
  • o projeto foi desenvolvido utilizando os RFs com funções distintas - apenas como agente transmissor ou receptor, por sua vez; no entanto é possível utilizá-los como transceptores propriamente: realizar envio e recebimento de mensagens simultaneamente, ficando passível apenas de tratamento para o uso correto;
  • este Arduino possui capacidade para embarcamento de mais sensores; no caso do projeto em questão, os pinos analógicos de A0 a A15 ficaram reservados para embarcar o acelerômetro a ser desenvolvido posteriormente;
Montagem no Arduino Mega

ARDUINO UNO (RECEPTOR)
A montagem da ponta receptora da telemetria por sua vez é mais simples, como mostrada na imagem a seguir, contendo apenas a conexão com o transceptor e um led que, assim como no outro Arduino, irá piscar, mas desta vez, quando receber a mensagem.

Uma observação cabível no caso do receptor: neste caso foi utilizado o Arduino pois se desejava realizar as leituras no computador. Mas há alternativas de se utilizar o módulo RF sem Arduino, por meio de adaptardores FTDI USB, tais como o PIC18F2450 e o FT2232.

Montagem no Arduino Uno

TABELA DE PINAGENS
O mapeamento utilizado no projeto foi como o descrito na tabela a seguir:



NRF24L01 vs. Outros
Algumas considerações sobre outros módulos pesquisados e o porquê de ter escolhido o NRF24L01 para desempenhar o papel de comunicador wireless:
  • xBee
    • ótima qualidade porém inviável, tanto pelo preço pela quantidade de componentes necessários para o funcionamento completo no projeto em questão;
  • Apc220
    • posui ótimo alcançe e, em teoria, é simples de ser programado, porém o custo também restringe a viabilidade;
  • Módulos bluetooth
    • baratos e de usabilidade simples, porém curto alcance;
  • Módulos WiFi:
    • CC3000
      • dependência de instalação, configuração e componentes de infra-estrutura WiFi, além do preço ser um pouco salgados;
    • ESP8266
      • o mais barato e simplista na configuração de infra;
      • foi utilizado no projeto mas não funcionou 100%;
      • breve detalhamento a seguir;
  • ESP8266
Não cabe aqui uma descrição técnica aprofundada do módulo, mas ele basicamente se utiliza de redes wireless 802.11 b/g/n, enviando e recebendo dados nos modos AP (Access Point) e STA (Station).

Uma grande vantagem deste módulo é que ele possui um processador poderoso em sua placa, sendo capaz de integração com outros sensores e aplicações específicas, e podendo funcionar sozinho, por meio da configuração do firmware e do uso dos pinos GPIO, além de ser possível montar um webserver para monitoramento do módulo e de que não é necessário mais de um módulo para haver comunicação com o PC.

O módulo comprado apresentou muita instabilidade na montagem, pelo fato do módulo funcionar inteiramente em 3.3V (não apenas a alimentação, como também os demais pinos: RX, TX, CH_PD, RST e GPIO), e o único meio de fazê-lo funcionar foi utilizando o buffer não-inversor CD4050. A vantagem de usar este CI se dá por ser barato, fácil de encontrar, e fácil de utilizar: basta alimentar o circuito com 3.3V, e as entradas de 5V que ele receber serão então transformadas em 3.3V nas suas respectivas saídas. Além disso, usando um único buffer é possível trabalhar com 6 entradas passíveis de serem modificadas de 5V para 3,3V.

Dessa forma sua montagem e configuração foram realizadas com sucesso, porém a leitura das mensagens pelo monitor serial do Arduino não puderam ser ajustadas corretamente - foram testadas praticamente todas as combinações de baud possíveis.

  • NRF24L01
Por fim, este módulo representou a melhor alternativa, chegando a uma configuração estável e realizando a comunicação devidamente. Os testes iniciais se deram na forma de transceptor, utilizando o módulo para receber e transmtir sinais e comandos, simultaneamente. 

Foram testadas funções com push-button, servo motores, leds e acionamento de demais componentes, além da questão de distância: em campo aberto, o maior alcance funcional testado foi de 90m. Para funcionamento do módulo como transceptor, foi usado o conceito de mestre-servo. Para configurar um deles como o mestre, na configuração presente, passa-se um resistor entre um pino configurado no programa como input pull-up e o GND: dessa forma o programa procura por essa conexão para saber qual papel o módulo deve assumir. Não há problema em trocar, durante a execução, qual deles será o mestre, mas se ambos estiverem simultaneamente como mestre ou como servo, a comunicação não será bem-sucedida.

O uso do módulo RF na Nordic pode ser variado, por exemplo para acionamento de relés, automação e controle de sistemas via wireless.

Algumas fotos durante os testes iniciais do módulo em casa:

Testes

Algoritmo
Algumas bibliotecas foram utilizadas, como a do Display LCD (LiquidCrystal), a do Serial (SoftwareSerial) para testes do ESP8266, ambas nativas do Arduino, e a do RF, baixada do site do fabricante.

Basicamente, o algoritmo do módulo consiste da inicialização dos componentes e do loop de comunicação entre ambos. Seria necessário complementação de lógica para realizar cálculo de medições caso o microcontrolador tivesse mais sensores embarcados, apenas uma adaptação e uma iteração a mais no vetor de dados a serem transmitidos.

A seguir, o pseudo-código implementado para cada ponta da telemetria:

RF-EMISSOR
    setup
        radio.inicializa()
        radio.abreCanalTransmissao()
        led.inicializa()
        display.inicializa()
    loop
        se (tempoEnvio > x)
            para (i, 1 a 8)
                rpm[i] ← leAnalogico(i)
            /* medicao rpm */
            msg[0] = codigo
            msg[1] = rpm
            radio.envia(msg)
            led.pisca()
        senao 
            tempoEnvio++
        display.atualiza(rpm)
        delay(100)

RF-RECEPTOR
    setup
        radio.inicializa()
        radio.abreCanalRecepcao()
        radio.comecaOuvir()
        led.inicializa()
    loop
        se (radio.disponivel())
            boolean concluido ← falso
            enquanto (!concluido)
                concluido ← radio.leMensagem(msg)
            codigo ← msg[0]
            rpm ← msg[1]
            comuta
                caso (codigo = x)
                    monitor.imprime(‘rpm’, rpm)
                    led.pisca()
                default
                    monitor.imprime(‘codigo desconhecido’)

Obs: caso queira o código-fonte, favor solicite comentando no post ou enviando um e-mail.

quarta-feira, 6 de janeiro de 2016

Programação: Sudoku & I.A.

O famoso jogo Sudoku consiste em preencher uma matriz numérica, geralmente de ordem 9x9, sem que haja repetição de números nas linhas, colunas e quadrantes (subconjuntos de 3x3 da matriz inteira), dados valores iniciais fixados em células aleatórias.

Como objetivo então, o algoritmo implementado deve prover ao usuário um jogo preenchido e válido, sem que haja alteração dos valores fixos iniciais:

Jogo inicial à esq. e resolução à dir.

Meta-Heurística
Para resolução de problemas de grande escala, de otimização e complexos, como este, o uso das meta-heurísticas pode ser ideal, desde que implementada devidamente. Para encontrar a melhor solução do problema, foi utilizada uma combinação de duas meta-heurísticas: Simulated Annealing (SA) e Tabu Search (TS).

O algoritmo SA consiste numa técnica de busca local probabilística, e se fundamenta numa analogia com a termodinâmica. Este algoritmo representa uma evolução do algoritmo Hill Climbing, de mesma natureza e, assim como no HC, podemos trabalhar sua eficiência diretamente com seus coeficientes. Como complemento, o algoritmo TS consiste em guardar as soluções de cada iteração, de acordo com o tamanho do vetor tabu, para evitar redundância nas comparações da otimização, podendo assim aumentar bastante a performance e a velocidade de convergência.

Aspectos da Solução
Sobre a meta-heurística, algumas das definições fundamentais são:
  • Forma da Solução
    • como estrutura de dados foi utilizada uma matriz 9x9 predefinida;
  • Solução Inicial
    • alocar uma matriz “inicial” com uma solução válida qualquer;
    • alocar uma matriz “solução”, que receberá o cruzamento da matriz inicial com a de entrada (denominada “espelho”);
    • assim, teremos dados concretos (quantidade válida dos números para o jogo) para aplicar as trocas necessárias;
  • Função Objetivo
    • como a solução inicial já contempla validade das linhas (sem repetição), o custo por iteração se dará pelas colunas e quadrantes, sendo o custo a quantidade de números repetidos dentro destas estruturas, separadamente;
  • Função Incremento
    • consiste em trocar aleatoriamente valores de colunas diferentes, respeitando a regra da matriz espelho (não pode haver alteração dos índices preenchidos na matriz espelho);
    • à medida que se resolve a questão do custo das colunas, “transforma-se” a matriz para o formato quadrante (quadrantes viram colunas), para se fazer o mesmo: calcular custo dos quadrantes, e o mesmo é feito com a matriz espelho (apenas a fim de verificação), contemplando assim os dois casos da otimização do problema na mesma solução;

Algoritmo
/* ------------------
 * main.cpp 
 * ------------------ */
matrizEspelho <- leMatrizEntrada();
Sudoku sudoku(matrizEspelho, tamVetorTabu, tempoMaxProcess, temperaturaInicial);
sudoku.executa();
sudoku.imprimeResultado();

/* ------------------
 * Sudoku.cpp
 * ------------------ */
Mat atual, vizinho;
atual <- inicializaMatrizSolucao();
enquanto(!fim) {
  custoAtual <- calculaCusto(atual);
  vizinho <- funcaoIncremento(atual);
  se ( insereTabu(vizinho) &&
    funcaoObjetivo(vizinho) > funcaoObjetivo(atual) &&
    random(1) < calculaSimmAnnealing(vizinho, atual)
  )
  atual <- vizinho;

  objetivo <- funcaoObjetivo(atual);
  se (objetivo < melhor)
    melhor <- objetivo;
  se ( objetivo == 0 ||
    tempo < tempoMaxProcess ||
    temperatura < congelamento
  )
  matrizSolucao <- atual;
  fim <- true;
  tempo++;
  temperatura *= CTE_TEMP;
}

Parâmetros
Foram utilizados nas execuções os seguintes parâmetros:
  • Limite Tabu: 200 (entradas no vetor; inserção cíclica)
  • Limite Tempo: 1.000.000 (iterações)
  • Temperatura: 1.000.000.000 (inicial, até chegar abaixo de “1 / temperatura”)

Observações
A otimização do problema pode variar muito dependendo da forma com que são utilizadas as variáveis de controle do SA e do TS, assim de como é implementada as funções da heurística, em especial a função de incremento. Pode-se ajustar a temperatura inicial da solução, por exemplo, ou o tempo máximo de processamento, a fim de se obter uma solução mais precisa ou uma aproximação mais rápida.

A probabilidade de convergência desta solução foi de aprox. 97,8%, pela média das execuções, ou seja: apesar das grandes chances, nem sempre a solução vai convergir a uma solução ótima, no caso, a um jogo válido. No entanto, soluções utilizando lógica convencional, força bruta ou mesmo outras meta-heurísticas podem chegar a levar minutos - ou muito mais que isso - para prover um resultado, enquanto a solução apresentada levou um tempo médio de execução de 0.534 segundos, se saindo muito a frente das demais.

terça-feira, 5 de janeiro de 2016

Jogos: C# & XNA


O XNA é um framework gratuito da Microsoft direcionado ao desenvolvimento de jogos, para Windows PC, Xbox 360 e Windows Phone 7. Permite criar uma infinidade de jogos, com praticidade e usando uma biblioteca bastante completa e robusta. É ideal para estudantes, pequenos desenvolvedores, criadores independentes e também para quem quer apenas brincar e conhecer um pouco da programação para games.

Para me iniciar neste âmbito, criei um jogo bastante simples, mas um tanto desafiador. O nome do jogo é Touchdown, e representa uma partida de futebol americano, simulando a situação em que o jogador com a posse da bola vai fazer o touchdown (isto explica o nome). Assim, o jogo consiste em fazer o personagem principal cruzar a endzone, passando antes por todos os jogadores do time adversário.

O jogo conta com animações dos personagens e do cenário, tela inicial (menu), efeitos sonoros e música de fundo (AC/DC, por sinal!), além de outros elementos básicos de jogos em geral e adendos inerentes ao tema/jogabilidade.

Ao decorrer do jogo, o personagem pode sofrer danos, de acordo com a aproximação do(s) adversário(s) ao jogador e tempo de jogo. No entanto, o personagem conta com itens auxiliares no campo que atuam como incremento de vida e diminuição da velocidade dos inimigos. Ao chegar ao fim da "vida" o jogo é finalizado, assim como quando o jogador chega ao seu objetivo, ambos os casos são informados através da mensagem na tela.

Alguns dos desafios no desenvolvimento do jogo foram a manutenção do sincronismo do plano de fundo com o movimento dos jogadores, a sintonia de movimento e velocidades, a detecção de colisão aprimorada, cálculos de interação posicional, a manutenção de estados do jogo/personagens, e especialmente, no meu caso, a criação do design do jogo! (um fator importantíssimo, mas raramente uma das habilidades de um programador: sorte a minha que tive algumas - apenas algumas - ajudinhas de uma noiva designer...).

Algumas das imagens do jogo:


      


O algoritmo é extenso o suficiente para não ser postado aqui, caso tenha curiosidade ou dúvidas quanto a programação nesta "modalidade" solicite o código-fonte através de meu e-mail que poderei fornecê-lo. Como dito anteriormente, irei me aprofundar na questão "jogo x programação" em breve em um post específico para isto.

Jogos: Android, C++ & NDK

Recentemente desenvolvi um jogo para Android, utilizando a linguagem C++, em conjunto com a IDE CodeBlocks e plataforma Windows. Como sabemos, os aplicativos para Android são por natureza baseados em Java, por isso, foi preciso uma "conversão" do produto do desenvolvimento para a sua execução correta.

Ferramentas
Os elementos gerais necessários para a adequação da plataforma e ambientação para o desenvolvimento foram:

  • Android SDK
    • é o core do Android, contendo aplicação, máquinas virtuais para emulação, bibliotecas, drivers, arquivos de sistema e demais ferramentas
    • para uso deste, foi baixado o Android Studio do site: https://developer.android.com/sdk/
  • NDK - Native Development Kit
    • ferramenta utilizada para realizar a compilação dos fontes do jogo
    • a versão baixada foi a r10e do site https://developer.android.com/ndk/
  • SDL - Simple Directmedia Layer
    • possui bibliotecas específicas para desenvolvimento do jogo e tratamento de imagens
    • a versão baixada foi a v2.0 do site https://www.libsdl.org/
    • também foi necessário baixar separadamente a biblioteca “SDL_image”, que possui módulos que não vêm junto com o pacote SDL
  • Apache Ant
    • ferramenta utilizada para realizar o deploy da aplicação, ou seja, gerar o apk executável que será instalado ou emulado em um dispositivo com Android
    • a versão baixada foi a v1.9.6 do site https://ant.apache.org/

Sobre o Jogo
O nome do jogo é Kingdom Blizzard, e é um jogo do tipo estratégico, estilo “capture the flag”,
englobando diversos aspectos de jogos neste formato. O tema é baseado nos reinos da era
medieval, e o ambiente é de nevasca, por isso o nome “Kingdom Blizzard”.

O objetivo é andar com o rei (personagem principal), passando pelos obstáculos e inimigos, até chegar do outro lado e capturar a bandeira. No caso de colisão com inimigos, o personagem volta à posição inicial do jogo. Não é possível se sobrepor (passar por cima) dos obstáculos no campo (icebergs). A moeda posicionada em um espaço do campo, se capturada pelo jogador, dará a ele um tempo de fuga, em que os inimigos ficarão congelados. O movimento do personagem se dá por toques na tela, por isso é necessário que o aparelho possua touchscreen.

Nas imagens do jogo a seguir é possível ter uma noção do esquema gráfico. Por ser estática a imagem não será possível ver a animação do jogo: os objetos e o personagem possuem quadros que mudam constantemente, e o personagem principal se vira ao lado que for direcionado no movimento, além dos inimigos que ficam o tempo todo indo de um lado para o outro horizontalmente, com a intenção de colidir com o personagem. As únicas peças estáticas no jogo são o fundo e os obstáculos (icebergs).

Obs: Não se enganem, este jogo não foi desenvolvido com o intuito de bombar no Google Play, mas apenas de conhecer, uma espécie de "hello world" nestas águas do Android.

Início/Término do jogo


Emulação
Inicialmente, o jogo foi testado através de emuladores do Android SDK. Por questões
de performance e adequação de hardware, este emulador teve de ser substituído. O programa
utilizado então para esta finalidade foi o BlueStacks, um emulador que atende bem pelo propósito. O jogo foi compilado oficialmente para rodar em Android versão (até) 5.0.1, e foi testado em alguns dispositivos (tablets e smartphones, com versões 4.x e 5.x de Android), não apresentando problemas.

Ambiente
A preparação do ambiente para o desenvolvimento foi provavelmente a etapa mais difícil (e bota difícil nisto), visto que a adequação e interação entre as variadas ferramentas para o desenvolvimento nem sempre demandam uma instalação e configuração padrão, apresentando muitas vezes mal comportamento como problemas e instabilidade, além de que possivelmente seja preciso baixar arquivos, bibliotecas e releases específicas de ferramentas, de acordo com o problema. No entanto ressalto aqui que a adequação do ambiente é fundamental para a construção do projeto e funcionamento da aplicação.

Basicamente, os passos seguidos foram:
  1. Descompactação e instalação das ferramentas listadas
  2. Extração da pasta principal “android-project”
  3. Criação da pasta assets na pasta principal para armazenar texturas
  4. Alteração do arquivo “Android.mk” da pasta android-project/jni
    • Alterar nome do programa principal e adicionar demais criados
  5. Criação de link simbólico da pasta SDL dentro da android-project/jni
  6. Copiar a pasta do SDL_image para dentro da pasta SDL/include
  7. Copiar as libs jpeg-9, libpng-1.6.2, libwebp-0.3.0, tiff-4.0.3 e zlib-1.2.8 para a android-project/jni e mapeá-las no arquivo SDL2_image/Android.mk
  8. Configurar variáveis de ambiente no Windows para contemplar os diretórios:
    • Variável Path
      • {dir}\android-ndk-r10e;
      • {dir}\apache-ant-1.9.6\bin;
      • C:\Users\{user}\AppData\Local\Android\sdk;
      • C:\Users\{user}\AppData\Local\Android\sdk\tools;
      • C:\Users\{user}\AppData\Local\Android\sdk\build-tools;
      • C:\Users\{user}\AppData\Local\Android\sdk\platform-tools
    • Criar variável ANDROID_SDK_HOME:
      • C:\Users\{user}\AppData\Local\Android\sdk

Depois do ambiente configurado, criar programa principal main.cpp na pasta android-project/jni/src, com declaração das bibliotecas a serem utilizadas (inclusive a SDL_image.h). Algumas configurações corretivas, avançadas e adicionais podem ser feitas, por exemplo, nos arquivos da pasta jni:
  • Application.mk: contém informações do emulador e versão do Android
  • src/Android.mk: contém declarações das pastas da infra (SDL, programas e bibliotecas)
Feito isto, para compilação utilizamos o comando ndk-build no cmd. Após a compilação, o deploy é feito através do comando ant debug também no cmd. Com isto, o executável será gerado na pasta android-project/bin. No BlueStacks, a forma de execução foi apenas clicar no arquivo gerado, e este então é instalado no App e pode ser executado pela interface.

Mídia e Texturas
Dentre os elementos que compõem o jogo, temos as texturas, que compõem a estética do jogo. Demais elementos como audio e efeitos visuais (como o flip dos sprites, por exemplo) não foram aprofundados por questões de suporte à plataforma, que são dificilmente encontrados. No mais, os sprites, textos e imagens que constituem a estética do jogo foram em sua maioria encontrados na internet ou produzidos por programas de manipulação de imagem (PhotoFiltre, Adobe Photoshop, etc). Dentre as texturas encontradas no jogo, temos:
  • Sprite do personagem
  • Sprites dos inimigos
  • Sprite de objetos do jogo (no caso, a moeda)
  • Objeto iceberg (obstáculo)
  • Objetos bandeira e portal (posições inicial e final, respectivamente)
  • Textura do plano de fundo

Algoritmo
O programa do jogo desenvolvido envolve aspectos fundamentais recorrentes de jogos, tais como efeitos de animação (texturas e sprites), sensação de movimento, detector de colisão, jogabilidade (herói, inimigo, obstáculos), níveis de dificuldade distintos durante o jogo (no caso, conforme aproximação do objetivo) e etapas principais: início, reinício e fim de jogo (no caso de vitória).

O algoritmo em si não será postado aqui pois é um pouco extenso. Pretendo em breve explicitar o funcionamento de um jogo básico (lógica/algoritmo) de forma genérica, em um post específico.

Obs: este jogo não foi desenvolvido com pretensões comerciais, assim não será disponibilizado no Google Play