Neste post, depois do processo de modelagem e síntese do controlador (Post anterior), o objetivo agora é integrar ao Arduino o código gerado pelo Heptagon/BZR. Let’s go! 🙂
1 Integrando o controlador ao Arduino
Após a síntese do controlador, alguns diretórios e arquivos foram criados no diretório em que estavam os arquivos car.ept e o Makefile. Os arquivos que de fato implementam o controlador estão distribuídos nas pastas car_c e controller_controller_c. O arquivo _main.c presente na pasta car_c implementa o programa que recebe as entradas do usuário via terminal e imprime na tela as respostas do controlador (programa que vimos usando o último comando via terminal).
A partir de agora iremos integrar o controlador gerado ao Arduino. Para isso, precisamos copiar os arquivos que se encontram nas pastas car_c e controlloer_controller_c em um mesmo diretório. Importante, o arquivo _main.c da pasta car_c não deve ser copiado. Lembrando que existem dependências na pasta /lib/c na pasta raiz do Heptagon. Desse diretório precisaremos copiar os arquivos math.h, math.c e pervasives.h para o mesmo diretório em que estão os arquivos copiados anteriormente. Feito isso, estamos prontos para iniciar a integração.
Ops! Antes disso, é importante definir os componentes que vão compor o veículo e como eles serão ligados ao microcontrolador. Afinal, queremos fazer com que ele funcione. Nesse caso, a lista abaixo apresenta cada componente utilizado na montagem do veículo.
- Arduino UNO (datasheet)
- Motor Shield L293D (datasheet)
- Sensor Ultrassônico HC-SR04 (datasheet)
- Bateria 5v 10400mAh TL-PB10400 (datasheet)
- Kit Chassi 4WD Robô
Consideramos o uso de um Motor Shield L293D acoplado ao Arduino em um Chassi 4WD, que possui quatro motores para controlar as quatro rodas existentes, conforme o que já foi definido no processo de modelagem. O sensor HC-SR04 servirá para medir a distância entre o veículo e o obstáculo à frente. Utilizamos também uma bateria de 5v para garantir a alimentação do veículo.
Para montagem do veículo, os pinos dos quatro motores e do sensor de distância foram ligados ao shield conforme a imagem abaixo:
Com o Shield Motor acoplado ao Arduino, sensor HC-SR04 e o chassi conectados ao shield, podemos agora apresentar a integração do código.
É importante ressaltar que a implementação da leitura do sensor de distância deve ser feita manualmente no código para o Arduino pois a modelagem considera apenas a detecção do obstáculo. Assim, a síntese do controlador não dispensa a necessidade de implementar códigos específicos, como a leitura do sensor. O código implementado para integração é apresentado a seguir.
#include <AFMotor.h>
#include "controller_controller.c"
#include "car.c"
AF_DCMotor motor1(1);
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);
int obs_sensor;
Car__controller_mem mem;
Car__controller_out _res;
void setup() {
Serial.begin(9600);
Car__controller_reset(&mem);
pinMode(A0, OUTPUT);
pinMode(A1, INPUT);
digitalWrite(A0, LOW);
}
double read_obstacle(){
digitalWrite(A0, HIGH);
delayMicroseconds(15);
digitalWrite(A0, LOW);
uint32_t pulse_time = pulseIn(A1, HIGH);
double distance = 0.01715 * pulse_time;
return distance;
}
void loop() {
obs_sensor = read_obstacle();
Car__controller_step(obs_sensor, &_res, &mem);
motor1.setSpeed(_res.vel1);
motor1.run(_res.motor1);
motor2.setSpeed(_res.vel2);
motor2.run(_res.motor2);
motor3.setSpeed(_res.vel3);
motor3.run(_res.motor3);
motor4.setSpeed(_res.vel4);
motor4.run(_res.motor4);
delay(10);
}
Nesse processo criamos um arquivo car.ino no Sketch e salvamos na mesma pasta em que estão os arquivos que foram copiados anteriormente. No código do arquivo car.ino incluímos a biblioteca AFMotor.h e os arquivos controller_controller.h e car.h que foram gerados pelo Heptagon. Entre as linhas 5 e 13 estão declaradas as variáveis dos motores, do sensor de distância e duas variáveis, mem e _res, que guardam a memória dos estados e as respostas do controlador, respectivamente. Os tipos dessas variáveis são definidos pelo processo de síntese do controlador.
A declaração do procedimento setup() inicializa a variável mem definida anteriormente e define os pinos para a leitura do HC-SR04. A função read_obstacle é implementada a partir da linha 25 para a leitura do sensor de distância que será chamada no loop() para medir a distância até o próximo obstáculo.
Observe que no loop() a função Car__controller_step() é chamada e tem como argumentos a distância lida pelo sensor e a referência para as variáveis _res e mem. Essa função altera os valores da variável _res e atualiza a os estados dos autômatos (memória) na variável mem. A variável _res carrega a resposta para o estado dos motores, logo, entre as linhas 40 e 47 podemos ver que _res modifica o sentido do giro e velocidade dos motores. Na prática, o sensor de distância verifica se há obstáculos a cada 10 milissegundos e a resposta do controlador define o comportamento do veículo. O resultado pode ser conferido no vídeo abaixo:
2 Considerações
Os posts tinham como objetivo apresentar a programação de um veículo autônomo utilizando a linguagem BZR. Dados os resultados, podemos considerar que o objetivo foi alcançado. Entre outras coisas, podemos destacar que o Heptagon/BZR se mostra como uma ferramenta bastante eficiente, atendendo aos requisitos do problema, permitindo a modelagem através de uma linguagem de alto nível e permitindo a verificação de inconsistências nas regras com auxílio do Sigali.
Outra coisa que podemos notar é a facilidade para integrar o código gerado a um microcontrolador como o Arduino, sendo necessário apenas selecionar os arquivos gerados no processo de síntese e incluir ao código do *.ino. Esse processo pode representar uma significativa redução do trabalho de escrita de código e garante um controle eficiente dado que nem sempre é possível escrever um código e verificar todos os possíveis erros lógicos sem o auxílio de uma ferramenta.