quinta-feira, 25 de junho de 2015

Controle de carro remoto através de processamento de imagens e arduino

Trabalho desenvolvido para a disciplina de Sistemas Embarcados.

Participantes: Bruno Raniery e Émerson Bezerra.
Sob orientação de: Prof. Clemilson Santos e Prof. Melo Júnior


Uma das formas de interação sem fio que temos contato a um bom tempo e que a utilizamos com muita frequência é o uso de ondas de rádio, onde possuímos um emissor e um receptor. No caso das ondas de rádio, elas podem variar de 3 KHz à 200 GHz no que diz respeito a ciclos por segundo.
Uma das aplicabilidades mais comuns para as ondas de rádio e que está bem próximo do nosso cotidiano é o uso nos carrinhos de controle remoto os quais encapsulam componentes e microcontroladores capazes de controlar o envio e a recepção das ondas, em uma frequência conhecida, para que seja possível controlar o carrinho a distância.
A proposta a seguir visa controlar um carro de controle remoto de maneira automática através de um software de processamento de imagens. O experimento se propõe a tornar o carro “inteligente” sem que tenha uma intervenção em seu hardware.

1. Materiais Utilizados
1 carro de controle remoto 7 funções atuando na frequência de 40 GHz





1 Controle do carro de controle remoto com o microcontrolador TXM TX-2B ZYTAAAD



1 Arduino Uno



6 fios (macho)



1 Webcam



2. Montagem dos materiais


Para este trabalho será preciso trabalhar apenas no controle do carrinho e para isso é necessário abrir o controle removendo os parafusos com uma chave estrela. Tendo acesso a placa do controle é possível acessar o microcontrolador e seus pinos.
Seguindo o esquema do microcontrolador TXM TX-2B ZYTAAAD é possível identificar quais os pinos são os responsáveis por fazer o carrinho ir para frente, trás, direita e esquerda, além de identificar os pinos referentes ao Vcc e ao terra (Gnd).
Tendo identificado os pinos, para acessá-los é preciso usar os fios e soldar os fios em cada um destes pinos e conecta-los nos pinos do Arduino Uno.





No caso deste projeto conectamos os fios no Arduino da seguinte forma:
fio laranja – porta digital 13

fio azul – porta digital 12

fio verde – porta digital 11

fio amarelo – porta digital 10

fio vermelho – 5V

fio preto – GND

Em relação ao ambiente, este projeto precisa de um certo espaço aberto, com um terreno sem obstáculos e com aderência para o carrinho não derrapar.
A webcam precisa estar localizada a uma altura de aproximadamente 5 metros em relação ao chão e apontada para baixo, além de ser conectada via USB em um computador/notebook.
Este posicionamento da webcam se deve pelo motivo de que a webcam deve conseguir visualizar o carrinho mesmo nos casos em que ele precise fazer uma curva completa.




3. Código utilizado no Arduino


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
//Variável que armazena os valores enviados pelo Processing
unsigned int readChar = 0;

void setup() {
  //Inicialização da Serial
  Serial.begin(9600);
}

void loop() {
  if (Serial.available() > 0) {
    //Armazena valor recebido na Serial
    readChar = Serial.read();
    Serial.println(readChar);
    
    //Bloco que recebe valor 1 e manda sinal LOW para o carro ir para frente
    if(readChar == 1) {
      limpa();
      pinMode(13, OUTPUT);  
      digitalWrite(13, LOW);   
    } 
    
    //Bloco que recebe valor 2 e manda sinal LOW para o carro ir para trás
    if(readChar == 2) {
      limpa();
      pinMode(12, OUTPUT);  
      digitalWrite(12, LOW);   
    }
    
    //Bloco que recebe valor 3 e manda sinal LOW para o carro ir para direita
    if(readChar == 3) {
      limpa();
      pinMode(10, OUTPUT);  
      digitalWrite(10, LOW);   
      pinMode(13, OUTPUT);  
      digitalWrite(13, LOW);
    }
    
    //Bloco que recebe valor 4 e manda sinal LOW para o carro ir para esquerda    
    if(readChar == 4) {
      limpa();
      pinMode(11, OUTPUT);  
      digitalWrite(11, LOW);   
      pinMode(13, OUTPUT);  
      digitalWrite(13, LOW); 
    }
    
    //Bloco que recebe valor 0 deixa os pinos "flutuando" para o carro parar
    if(readChar == 0) {
      limpa();
    }
  }
}

void limpa() {
  pinMode(13, INPUT);
  pinMode(12, INPUT);
  pinMode(10, INPUT);
  pinMode(11, INPUT);
}


4. Código utilizado no Processing


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import processing.video.*;
import processing.serial.*;

PImage source;
PImage destination;
PImage prevFrame;
Capture cam;
Serial port;

//parametrizavéis
int limiar_angulo_inicial = 15;
int limiar_angulo = limiar_angulo_inicial;
float limiar_media = 150;
int limiar_carro = 35;
int index_porta = 0;

boolean clicou = false;
boolean desenha = false;

int par = 0;
int mensagem = -1;
int x1 = 0;
int y1 = 0;

int mediaPY, mediaPX;
int quantidade, somax, mediax, somay, mediay;

void setup() {
  size(640, 240);
  cam = new Capture(this, 320, 240, 30);
  cam.start();
  destination = createImage(320, 240, RGB);
  prevFrame = createImage(320,240,RGB);
  
  // Select port
  println(Serial.list());
  port = new Serial(this, Serial.list()[index_porta], 9600);
}

void draw() {

  if(cam.available()) {
    prevFrame.copy(cam,0,0,320,240,0,0,320,240); // antes de ler, salvar o frame anterior para fazer a comparação
    prevFrame.updatePixels();
    cam.read();//recebe novo frame
  }
  
  destination.loadPixels();
  prevFrame.loadPixels();

  quantidade=0;
  somax=0;
  somay=0;
  
  //calcula a diferença de frames pixel a pixel e calcula a média deles
  for (int x = 0; x < cam.width; x++ ) {
    for (int y = 0; y < cam.height; y++ ) {
      int loc = x + y*cam.width;
      color current = cam.pixels[loc];      // Step 2, what is the current color
      color previous = prevFrame.pixels[loc]; // Step 3, what is the previous color
      // Step 4, compare colors (previous vs. current)
      float r1 = red(current); float g1 = green(current); float b1 = blue(current);
      float r2 = red(previous); float g2 = green(previous); float b2 = blue(previous);
      float diff = dist(r1,g1,b1,r2,g2,b2);
      destination.pixels[loc] = color(diff);
      if(diff>limiar_media){
        quantidade++;
        somax = somax + x;
        somay = somay + y;
      }
    }
  }
    
  if(quantidade!=0){
    mediaPX = mediax;
    mediaPY = mediay;
    //calcula angulo entre o previous x,y e o current x,y
    translate(mediaPX,mediaPY);
      mediax = somax / quantidade;
      mediay = somay / quantidade;
      float a = atan2(mediay, mediax);
      a = degrees(a);
      println("ponto 1:"+a);

      stroke(255, 0, 0);
      strokeWeight(2);
      
    translate(-mediaPX,-mediaPY);
    
    //calcula o angulo entre o current x,y e o x,y do destino
    translate(mediax,mediay);
      float distC, distP;
      distC = dist(x1, y1, mediax, mediay);
      distP = dist(x1, y1, mediaPX, mediaPY);
      float b = atan2(y1, x1);
        b = degrees(b);
        println("ponto 2:"+b);
        //mudar limiar com a distância do destino
        if(distP<=200)
          limiar_angulo = 4*limiar_angulo_inicial;
        if(distP<=100)
          limiar_angulo = 3*limiar_angulo_inicial;
        if(distP<=50)
          limiar_angulo = 2*limiar_angulo_inicial;
        if(distP<=25)
          limiar_angulo = limiar_angulo_inicial;
        
        if(a<=b+limiar_angulo && a>=b-limiar_angulo){
          if(distC<=distP) {
            println("siga em frente");
            mensagem = 1;
          }
          if(distC>distP) {
            println("rode mais");
            mensagem = 3;  
            delay(100);
          }
        }
        else{
          if(a<b){
             println("rode esq");
             mensagem = 4;
          }
          if(a<=b){
            println("rode dir");
            mensagem = 3;
          }
        }
        if(mediax< x1+limiar_carro && mediax>x1-limiar_carro && mediay<y1+limiar_carro && mediay>y1-limiar_carro){
          println("parou!!!!");
          mensagem = 0;
        }
        //enviar mensagem
         port.write(mensagem);
         
    translate(-mediax,-mediay);
    //desenhar o current x,y e o previous x,y
    if(par%2 == 0){
      //rect(0,0,320,240);
      image(cam,0,0);
    }
    //evitar estouro
    if(par == 1000){
     par = 0;
    }
    par++;
    //desenha as linhas e os marcadores do caminho
    line(mediax, mediay, x1, y1);
    line(mediaPX, mediaPY, mediax, mediay);
    //line(mediax, mediay, ladoX, ladoY);
    ellipse(mediax, mediay, 1, 1);
    if(desenha)
      ellipse(x1, y1, 5, 5);
  }
    if(clicou) {
      desenha =true;
      ellipse(mouseX, mouseY, 5, 5);
      println("x:"+mouseX+" y:"+mouseY);
      clicou = false;
    }
  destination.updatePixels();
  image(destination,320,0);
 
  
}

void mouseClicked() {
  x1 = mouseX;
  y1= mouseY;
  clicou = true;  
  port.write(1);
  delay(500);
  port.write(0);
}

void keyPressed() {
  if (key == CODED) {
    if (keyCode == UP) {
      port.write(0);
    }
  }
  }


5. Conclusão

Nosso projeto atingiu o objetivo de demostrar o controle do carrinho através do processamento de imagens em conjunto com o arduino. Ainda necessita de algumas melhorias, como fazer curvas para os dois lados, em casos de incapacidade de movimento utilizar-se da ré e diminuir o tempo de procura do ponto quando ele não estiver no lado “dominante”.