Classes+HcSr04+e+HcSr04auto

Home > Robótica > Bibliotecas **Recursos** > RunTimer > Timer2 > CircularBuffer > SerialLog > ActionPlan **Dispositivos** > **Ultrassom** > Array de sensores > NRF24L01 > Wireless log > RF24Control **Movimento** > StepperMotor > StepperMotorTm > DoubleStepper > DoubleStepperTm

Este é um dos mais comuns dispositivos no mundo do arduino. É barato, fácil de encontrar e de usar, mas pode ser um tanto frustrante se acreditar piamente na propaganda dos vendedores. Diz a lenda que ele mede distancias de até 4 metros. Talvez até meça, mas dentro de condições tão ideais que ainda não vi acontecer no mundo real. Na prática o que podemos esperar são medidas de até 2 a 2,5 metros e com muita sorte 3 metros, o que aliás não é pouca coisa, já torna o sensor bem útil para um robô de pequeno porte como o deste projeto. Outra lenda é que a abertura do feixe de ultrassom é de 15 graus, não conte com isso, na prática a abertura é bem maior. Significa que objetos bastante à esquerda ou à direita do sensor acabam refletindo a rajada de ultrassom. Também não dá pra confiar muito na precisão de 0,3 cm propagada pelos fabricantes, mas tem uma precisão que chega perto de 1 cm, portanto não compensa usar //float's// para ler a distância medida, a precisão não vai ter nenhum aumento significativo, mas com certeza o tamanho do código gerado vai. Um outro detalhe é que tem diferenças bem significativas no desempenho de cada unidade. Não funcionam todos exatamente do mesmo jeito, então é sempre bom ter mais de um e testar todos eles. Mas apesar de tudo isso, é uma boa aquisição para instalar em um robô e pelo seu preço, proporciona um excelente custo-benefício.



Funcionamento
Este sensor tem 4 pinos, sendo dois para a alimentação (//VCC// e //GND//) e dois para a operação (//Trigger// e //Echo//). Deve ser alimentado com 5 volts. Talvez funcione com 3.3 volts, mas nesse caso acho que seu alcance diminui bastante. Sua operação é relativamente simples, o pino //Trigger// deve ser colocado em nível alto por 10 microssegundos e depois em nível baixo novamente por 2 microssegundos. Após isso o sensor solta uma rajada de ultrassom e coloca o pino //Echo// em nível alto. Quando o eco da rajada retorna o pino //Echo// é colocado em nível baixo novamente. O que o programa tem que fazer é medir o tempo que o pino //Echo// fica em nível alto e a partir dessa medida calcular a distancia do obstáculo que refletiu a rajada de ultrassom. É um sistema simples, mas na prática várias coisas podem dar errado.



Problemas comuns
Se o obstáculo estiver muito distante, o eco não vai ter força suficiente para sensibilizar o sensor, para esse caso o programa deve prever um //timeout// a fim de evitar que o programa fique esperando por muito tempo. Considerando que na teoria o alcance máximo é de 4 metros, um //timeout// de 23500 microssegundos é um valor até generoso. Mas obstáculos fora de alcance não chegam a ser um problema para um robô. O problema maior é quando o obstáculo está próximo e mesmo assim o eco não retorna. Pode acontecer se a superfície do obstáculo for bem lisa e estiver em um angulo bem aberto em relação ao sensor (figura 3). Nesse caso a rajada é refletida para longe e não retorna. Isso engana o robô fazendo-o acreditar que tem terreno livre pela frente quando na verdade está bem próximo de um obstáculo. Outro problema parecido é o de reflexões múltiplas, que ocorre por exemplo em cantos de paredes. A rajada é refletida por uma parede, atinge a outra parede e depois retorna para o sensor. Nesse caso a distância medida será muito maior do que a distância real. Todos esses problemas podem ser contornados se for não for colocada uma confiança excessiva nas medidas feitas pelo sensor. Por exemplo, fazendo várias medidas de cada vez ou confirmando as medidas realizadas antes de tomar alguma decisão baseado nelas.



Operação com um pino
Um outro problema, que tecnicamente não é um problema, mas na prática acaba sendo, é o fato do sensor precisar de dois pinos para ser operado. Se o seu robô usa apenas um sensor, isso pode não ser realmente um problema, mas um dos robôs deste projeto já conta com 10 sensores //HC-SR04// e talvez venha a ter mais alguns em um futuro próximo. Então faz muita diferença o uso de um ou dois pinos por sensor, já que a quantidade de pinos disponíveis no arduino é bem limitada, mesmo utilizando um Mega como é o caso. Felizmente existe uma maneira de utilizar o mesmo pino tanto para o //Trigger// quanto para o //Echo//, só que exige a instalação de um resistor de //pulldown// e uma pequena alteração no programa.

Resistores de pulldown e pullup
Uma característica importante sobre pinos de entrada de microcontroladores é que eles ficam em flutuação quando não há nenhum sinal chegando até eles. Significa que se o pino está desconectado ou se o dispositivo conectado a ele não está enviando sinal nenhum, uma leitura desse pino poderá retornar um valor aleatório (alto ou baixo) que não tem nenhum significado. Para evitar essa flutuação nos pinos de entrada é comum o uso de resistores de //pulldown// ou de //pullup//. São resistores que conectam o pino ao terra (no caso de //pulldown//) ou ao nível alto do sistema (no caso de //pullup//). Como são resistores de um valor relativamente alto, qualquer sinal que chegue ao pino irá se sobrepor ao sinal gerado pelo resistor, mas é o suficiente para eliminar a flutuação no pino quando não tem nenhum sinal chegando. Um resistor //pulldown// que conecta o pino ao terra, fará com que o pino fique sempre em nível baixo até que um sinal de nível alto chegue ao pino. No caso de resistores de //pullup// é a lógica inversa, o pino fica em nível alto, até que chegue um sinal de nível baixo. O arduino tem resistores de //pullup// internos em seus pinos, eles são ativados usando a opção //INPUT_PULLUP// na função //pinMode//.



Classe HcSr04
Esta é a classe que controla o sensor. Foi feita para funcionar tanto com um pino de controle quanto com dois. Para controlar o sensor com um pino, basta informar o mesmo pino nos parâmetros //triggerPin// e //echoPin// do método //init//. Mas lembre-se que isso só funciona se existir um resistor de //pulldown// conectando o pino em questão ao //GND// do arduino (figura 4). Se for usar o sensor com dois pinos, então informe pinos diferentes nesses parâmetros, nesse caso o resistor não é necessário. O terceiro parâmetro (//offset//) é opcional e informa a distância do sensor em relação à borda do chassi do robô em cms. Lembre-se que o sensor mede a distância do sensor até o obstáculo, se o sensor estiver instalado alguns centímetros para "dentro" do robô, então a distância do sensor até a borda do chassi do robô precisa ser descontada a fim de evitar colisões. Abaixo a declaração pública da classe:

code format="java5" class HcSr04{ public: HcFlags* flags; // ver detalhes em HcsrFlags.h

HcSr04; void init(byte triggerPin, byte echoPin, byte offset=0); int ultraSonicDist; int readNow; };

code

Para usar a classe, crie uma instância, inicialize chamando o método //init// no setup e depois use //readNow// ou //ultraSonicDist// para ler uma medida. A diferença entre os métodos //readNow// e //ultraSonicDist// é que //readNow// desconta o //offset// (se for informado) e preenche os //flags// de leitura. Já o método //ultraSonicDist// apenas retorna a distância medida sem nenhum processamento adicional. Caso a primeira leitura retorne erro, são feitas mais duas tentativas antes de retornar.

Flags de leitura
A classe //HcSr04// conta com um pequeno objeto público em seus atributos que pode facilitar bastante o processamento das leituras realizadas, os **//flags//**. Abaixo a declaração da classe que os define:

code format="java5"
 * 1) define dangerDist  12
 * 2) define warningDist 24

class HcFlags { public: boolean readOk, // flags warning, danger; int lastDist;   // última distância lida (em cm)

void init; void clear; void setFlags(int value); };

code Os //flags// são completamente controlados pela classe //HcSr04//, então não é preciso nenhuma ação para usá-los, a não ser fazer a leitura das medidas com o método //readNow//. Seus atributos são públicos para que possam ser lidos pelo programa principal. O atributo //lastDist// armazena a última distância lida, ou o código de erro caso a leitura tenha falhado. O //flag readOk// informa se a última leitura teve sucesso ou não. Os outros dois //flags// avisam sobre distâncias potencialmente perigosas para o robô. Abaixo um exemplo de uso:

code format="java5"
 * 1) include 


 * 1) define triggerPin 4
 * 2) define echoPin   5

HcSr04 sensor;

void setup { Serial.begin(115200);

sensor.init(triggerPin, echoPin); }

void loop { sensor.readNow; if(sensor.flags->readOk) {   Serial.print("Distância: "); Serial.println(sensor.flags->lastDist);

if(sensor.flags->danger) Serial.println("Perigo!!! Risco de colisão!!!!"); else if(sensor.flags->warning) Serial.println("Atenção!!! Obstáculo próximo à frente."); } else { if(sensor.flags->lastDist == -1) Serial.println("Erro de hardware. Verificar conexões."); else Serial.println("Erro de leitura ou obstáculo fora de alcance."); }

delay(500); }

code

Classe HcSr04auto
Descendente de //HcSr04//, esta classe acrescenta o recurso de leitura automática em intervalos de tempo pré-determinados. Com o recurso de leituras automáticas, os //flags// ficam mais importantes já que a leitura do sensor será feita em "background". Na verdade foi com esta intenção que o sistema de //flags// foi implementado. Seu uso é bem intuitivo. Deve ser inicializada da mesma forma que a classe //HcSc04// e depois indicar o intervalo de tempo para as leituras automáticas chamando o método //setInterval//. O tempo é indicado em milissegundos. O intervalo é inicializado por padrão com 1000 milissegundos (1 segundo), então se o método //setInterval// não for chamado, é esse intervalo que será utilizado. Para ativar as leituras automáticas use o método //autoReadOn// e para desativar o método //autoReadOff//. É importante que o método //run// seja chamado pelo menos uma vez a cada iteração do loop principal. O retorno do método //run// indica se uma leitura foi feita. Opcionalmente, é possível derivar a classe e reescrever o método virtual //onNewRead// que é chamado sempre que uma leitura é realizada. Abaixo um exemplo de uso com o retorno de //run//.

code format="java5"
 * 1) include 


 * 1) define triggerPin 4
 * 2) define echoPin   5

HcSr04auto auto;

void setup { Serial.begin(115200);

auto.init(triggerPin, echoPin); auto.setInterval(500); auto.autoReadOn; }

void loop { if(auto.run){ if(auto.flags->readOk){ ...   }    else{ ...   }  } }

code E aqui um exemplo derivando a classe HcSr04auto:

code format="java5"
 * 1) include 


 * 1) define triggerPin 4
 * 2) define echoPin   5

class MySensor: public HcSr04auto { public: MySensor:HcSr04auto{} // construtor

void init {   HcSr04auto::init(triggerPin, echoPin); setInterval(500); autoReadOn; }

protected: void onNewRead {   if(flags->readOk){ ...   }    else{ ...   }  } };

MySensor sense;

void setup { Serial.begin(115200); sense.init; }

void loop { sense.run; }

code

.