Self-Balancing Robot




LABSI

2019


Autores:


Marcelo Lopes (Nº 1170959)

1170959@isep.ipp.pt


António Leite (Nº 1170760)

1170760@isep.ipp.pt


Introdução

Arquitetura

Hardware

Software

Resultados

Conclusões

Referências





  • Introdução

Início


O trabalho realizado teve como objetivo principal o desenvolvimento de um robô que se autoequilibra. Este trabalho ficou subdividido em vários projetos pequenos de complexidades variadas que foram, posteriormente, montadas em conjunto de forma a conseguir o resultado pretendido. Existem na atualidade diversos projetos envolvendo este tipo de tecnologia tais como a Segway ou a Hoverboard.

Fig.1 - primeira Segway [1]
Fig.2 - conceito de hoverboard [2]

Podem-se encontrar também projetos DIY (Do It Yourself) destinados aos estudantes em que se usam placas Arduino , motores e baterias [3]. No entanto, cada projeto tem um objetivo diferente, alguns são seguidores de linha [4], outros são controlados por uma App, mas em termos de hardware a maior diferença é sem dúvida no microcontrolador a utilizar [5]. Neste projeto o microcontrolador é um Atmel AVR. Procurou-se com este projeto aplicar os conhecimentos adquiridos ao longo do curso, bem como, autonomamente, realizar um trabalho de pesquisa acerca do mesmo.


  • Arquitetura

Início


Fig.3 - Diagrama de blocos arquitetura

Regulador de tensão transforma os 12V fornecidos pela fonte em 5V, tensão utilizada por quase todos os componentes, sendo a exceção os motores que são controlados pela ponte H com os 12V. O microcontrolador comunica por I2C com o acelerómetro e giroscópio obtendo os seus respetivos valores, após o processamento são atualizados os motores pela ponte H e apresentado no display os valores tratados respetivos à inclinação e temperatura.


  • Hardware

Início


Fig.4 - regulador de tensão 7805 [6]

Foi utilizado um regulador de tensão inserido no circuito estudado nas aulas teóricas com o intuito de conseguir transformar a tensão de entrada, que geralmente é superior à necessária, numa tensão apropriada para o microcontrolador, neste caso 12V transformados em 5V. Conseguindo, assim, a tensão de 5V para o controlo e a tensão superior para os motores.   Datasheet


Fig.5 - Motor drive L298 [7]

Este componente teve por objetivo o de controlar os motores para que estes funcionassem da maneira correta de acordo com as instruções fornecidas, através da limitação da corrente de saída do microcontrolador. Sem este componente os motores não iriam funcionar e poderiam, até, danificar o microcontrolador devido às altas correntes.  Datasheet


Fig.6 - Motores DC [8]

Utilizou-se motores DC para o movimento do robô, tendo o objetivo do projeto o equilíbrio, a variação da velocidade dos motores DC é uma abordagem possível e funcional. Neste caso foram necessários dois motores, sendo possível que os motores andem em direções opostas e com velocidades diferentes. Para este trabalho foram utilizados motores 12V.


Fig.7 - Módulo MPU-6050 [9]

O módulo MPU-6050, tem um sensor Acelerómetro de 3 eixos, sensor Giroscópio de 3 eixos e sensor de temperatura, a comunicação com o mesmo foi efetuada por I2C. O MPU 6050 tem 96 registos mapeados desde o endereço 0x0D até 0x75, foi consultada a respetiva datasheet (referente aos registos) de forma a percebermos os endereços onde são guardados os dados do acelerómetro, giroscópio e temperatura.  Datasheet


Fig.8 - LCD 16x2 [10]

Utilizou-se um display 16x2 com a comunicação a 4bits. Colocar display em funcionamento foi prioridade com o intuito de visualizar dados no ecrã e ser possível comprovar o bom funcionamento dos módulos seguintes.


Fig.9 - Atmega88 [11]

Este é um microchip de alto rendimento, possuí 8KB ISP flash memory com a capacidade de leitura enquanto se escreve no mesmo, 512B EEPROM, 1KB SRAM, 23 pinos I/O divididos em 3 portos (B,C,D), 32 registos, 3 timers flexíveis com comparadores, interrupções externas e internas, USART, comunicação 2-wire serial interface orientada ao Bit, SPI serial port, 6 canais de conversão A/D 10-Bit. Tensão de operação entre 2,7 V e 5,5 V. Sabendo o projeto a implementar, foi escolhido este AVR pelo facto de ser suficiente e o mais apropriado às necessidades do trabalho.O atmega88 utilizado encontra-se configurado com uma frequência de clock de 8MHz.


Esquema elétrico

Fig.10 - Esquema elétrico desenhado em Kicad

PCB

Fig.11 -PCB desenhado em Kicad

A placa de PCB foi desenhada com vista à fácil adaptação e ligação dos componentes. Foi inserido um socket para a aplicação do módulo MPU-6050, um socket para o simples encaixe do microcontrolador. O conector J1 é para a entrada da alimentação e ainda mais dois J2 e J3 para serem ligados os motores.


Vista 3D

Fig.12 - Vista em 3D da placa PCB


  • Software

Início


Fluxogramas

Fig.13 - Fluxogramas 1
Fig.14- Fluxogramas 2
Fig.15- Fluxogramas 3

Descrição das funções

Função Init()

Aqui são inicializados os ports do microcontrolador que serão utlizados. Neste caso utilizou-se apenas o PORTB, sendo o pino 0 para piscar um LED à frequência de 1Hz, o pino 1 para gerar a onda PWM de controlo dos motores e o pino 6 e 7 para a seleção da direção dos mesmos. Estes pinos foram definidos como saídas. Foram utilizados 3 timers, o timer 0 para a marcação de um tempo de 500ms, o timer 2 para a contagem de um tempo com uma grande precisão, na ordem dos 208us, e o timer 1 para gerar uma onda PWM à frequência de 50Hz. De forma a ocorrerem interrupções nos timers utilizados para a contagem de tempos (timer 0 e 2), é necessário ativar as interrupções colocando a 1 o bit I do SREG, ou utilizando a função sei() da linguagem C.
Timer 0
Utilização em modo CTC, prescaler 1024 para um tempo base de 2ms. É ativa a interrupção a cada “compare match”. No ciclo de interrupção é decrementada uma variável com o valor 250, e sempre que esta é 0 é feito o toogle de um LED.
Timer 1
Utilizado no modo FAST PWM com uma frequência de 50Hz, prescaler 64. Foi escolhido o Input capture como valor de top, tendo uma gama de valores entre 0 e 155. Foi gerada uma onda pwm “on compare match” com o OCR1A. Desta forma controla-se o duty cycle em função do valor de OCR1A.
Timer 2
Utilização em modo CTC, prescaler 64 para um tempo base de 208us. Encontrando-se ativas as interrupções a cada 208us é incrementado um contador de forma a ser feito o controlo de tempo desde a última vez que este foi 0. Esta contagem de tempo é utilizada para o cálculo do ângulo theta do giroscópio, quanto melhor a precisão do tempo maior a precisão do ângulo lido.

Toda esta informação foi consultada pela datasheet do atmega e referente pinout.


Função get_mpu_values()

Leitura dos valores do acelerómetro, giroscópio e de temperatura fornecidos pelo módulo mpu-6050, para tal é chamada a função twi_read passando por referência a respetiva informação a ler. Nesta função é realizada a correção dos valores do acelerómetro após se ter verificado algum offset em cada um dos eixos e ainda a conversão do valor para aceleração gravitacional equivalente em g. O acelerómetro foi utilizado com a precisão de 16384 LSB/g e o giroscópio 131 LSB/°/s, fazendo a divisão dos valores lidos do acelerómetro e do giroscópio por 16384 e 131 respetivamente, obtêm-se o valor em g’s e em °/s.


Cálculo inclinação

Após se obterem os valores do módulo, estes são tratados baseado no seu funcionamento. Focamo-nos na inclinação sobre o eixo X, pois é a orientação para a qual o robô tenderá a cair. Ao ângulo detetado chamou-se theta, e para o determinar utiliza-se como referência a aceleração gravitacional sobre o eixo Z, sendo esta conhecida, 1 g. Foi deduzida a seguinte igualdade para determinar o ângulo theta:

Fig.16- Principios fisicos de um Inertial measurement unit (IMU)

Assim sendo, theta é dado pela tangente inversa da aceleração em X sobre a aceleração em Z. O valor do giroscópio após ser atualizado pela função get_mpu_values(), encontra-se em graus por segundo, desta forma, ao se multiplicar ao valor lido o tempo decorrido em segundos desde a última leitura, descobrimos a variação em graus.


Filtro complementar

Estudando o módulo mpu-6050, descobre-se que o acelerómetro tem muito ruído a curto prazo, mas a longo prazo retorna medições mais precisas, o contrário se verifica no giroscópio, a curto prazo as variações são confiáveis, mas a longo não. Assim sendo e de forma a se obter o melhor dos dois mundos, aplicamos um filtro passa baixo nos valores do acelerómetro, confiando 95% no valor anterior e apenas 5% no valor lido. De seguida fundiu-se a informação do giroscópio confiando 99% nesta e apenas 1% na do acelerómetro. Desta forma, a curto prazo foi eliminado o ruído do acelerómetro confiando-se nas rápidas variações detetadas pelo giroscópio e a longo prazo mantemos os valores do acelerómetro.


Função erro()

Esta função tem por objetivo escrever no Display a palavra “erro” e consequentemente parar o programa. Esta função foi colocada dentro das funções mpu_init() e twi_read() com o intuito de sempre que houver um erro na comunicação de dados ser possível visualizar que tal aconteceu. Da mesma maneira, caso o programa não funcione da maneira esperada mas a função erro não aparecer, é possível deduzir que o erro será de outro tipo que não o descrito.


Função controlador_PID()

Esta função, tal como a implementação do filtro complementar, tem por objetivo a de minimizar o erro no tratamento de dados do acelerómetro. Para tal, foram caracterizados o ganho proporcional (Kp) e o derivativo (Kd), sendo o Kp direcionado à estabilidade do robô dado o ângulo instantâneo (Kp*theta) e o Kd direcionado á variação de ângulos ocorrida num certo intervalo de tempo (Kd*(theta-last_theta)). O ganho integral não entrou no cálculo, foi deduzido que a sua constante seria 0, pois o robot não apresenta erro acumulativo ao longo do tempo. Isto deve-se à fusão dos dados do acelerómetro e giroscópio que foram dimensionados para a correção deste efeito.
Após o cálculo do valor proporcional (P) e do derivativo (D) é necessário estabelecer um limite para o valor que a soma dos dois (P+D) pode tomar, neste caso 124, visto que o valor de P+D somado com 31 não poderá ultrapassar 155, ou seja, o valor do ICR1b (155 origina 100% PWM). O valor de P+D é, então, retornado para uso posterior.


Função mpu_init()

Esta função trata de inicializar o acelerómetro, a cada instrução é identificado o acknowledgment retornado pelo módulo e em caso de não ser o esperado chama a função erro(). Aqui estabelece-se o bit rate da comunicação para 100 kHz e a referência de clock do módulo. Segundo o register map do MPU-6050 (pág.40) é recomendado o uso de um eixo do giroscópio como referência para aumento da estabilidade, assim sendo, foi selecionada a opção “PLL with X axis gyroscope reference”.


Função twi_read()

Aqui são pedidos os dados ao dispositivo em questão, sendo que é passado como parâmetro o registo a ser lido e retornado valor. Mais uma vez a cada instrução identifica -se o acknowledgment retornado pelo módulo e em caso de não ser o esperado chama a função erro().
Inicia-se a comunicação com um start bit, é informado o módulo de uma escrita no mesmo, essa escrita será o endereço a ler. Reinicia-se a comunicação com um restart bit, informa-se o dispositivo de uma leitura no mesmo, e é lido a parte alta inicialmente e depois a parte baixa do respetivo registo. Por fim é retornada uma variável com o valo lido.


Função main()

É na função main que se fazem as declarações de variáveis a utilizar no tratamento dos dados do acelerómetro, bem como dos valores de ângulos calculados e do valor PID. São também chamadas as funções de inicialização dos ports, do acelerómetro e do LCD. Dentro do ciclo while é, então, chamada a função get_mpu_values(), feito o cálculo da inclinação, aplicado o filtro complementar, atualizados os motores e de 200 em 200ms atualizado o display. Já com o theta e o PID_value calculados passa-se para a condição que irá determinar caso os motores andem no sentido positivo ou negativo dependendo do sinal do ângulo determinado (caso theta = 0 os motores não estão ativos). Theta tem limite de 60º visto que foi determinado que caso o robô se incline mais de 60º já não há hipótese de este conseguir reverter a inércia e voltar á posição de equilíbrio. Tendo o sentido estabelecido é feito o cálculo da velocidade de rotação dos motores e atribuido o resultado ao OCR1A. Esta operação, já descrita anteriormente, utiliza o valor do PID_value e soma a uma constante sendo o resultado no máximo 155.


Função referentes ao LCD

O LCD foi colocado em funcionamento com recurso à datasheet para perceber o funcionamento mais detalhado do mesmo e de seguida com uma biblioteca lcd.h. Foi necessário adaptar a biblioteca ao nosso código tendo discriminado a frequência de oscilação do nosso microcontrolador e os ports onde se encontravam ligados os pinos de dados e controlo do display. Com a biblioteca o autor disponibilizou um conjunto de funções que são utilizadas ao longo do nosso código e se encontram no ficheiro lcd.c [12].


lcd_clrscr(); -> ao chamar esta função, que pode ser interpretada como “lcd clear screen”, ela faz isto mesmo, limpa o display ficando pronto a nova impressão.

lcd_gotoxy(0,0); -> esta função interpreta-se como, “lcd go to x y”. Seleciona-se a posição do lcd onde pretendemos iniciar a escrita, como estamos perante um lcd 16x2, podemos escolher valores entre 0 e 15 como primeiro parâmetro e 0 ou 1 como segundo.

lcd_puts(string); -> esta função imprime no ecrã a string passada como parâmetro. Como apenas se recorre a strings para efetuar a impressão, os valores inteiros que queiramos imprimir têm de ser convertidos com recurso à função itoa().


Os valores referentes ao ângulo de inclinação utilizados ao longo do código são do tipo float, mas são impressos no LCD como inteiros. Para transformar um valor float em string teríamos de recorrer à função: sprintf(string,"%.3f", theta), e para o bom funcionamento da mesma é necessário realizar uma alteração no compilador, ativando “vprintf library”. Esta simples alteração tem um impacto gigante na memória de programa utilizada, assim sendo, decidiu-se usar apenas a função itoa() para não sobrecarregar o microcontrolador.Nas figuras seguintes é possível identificar as diferenças e a alteração a ser efetuada.


Fig.17- memória do programa com a função itoa()

Fig.18- alteração a efetuar no compilador

Fig.19- memória do programa com a função sprintf() com dados float


  • Resultados

Início


video demonstração


  • Conclusões

Início

Em suma, pode-se afirmar que o projeto global foi um sucesso na medida em que se atingiu o objetivo proposto inicialmente do autoequilíbrio do robô. O protótipo atual consegue equilibrar-se, sem qualquer ação externa ou até suportar ligeiros toques, indefinidamente. No entanto, carece da robustez para se equilibrar caso a força externa seja moderada.

Este protótipo pode, no entanto, ser melhorado através de algumas mudanças nomeadamente na estrutura, ao se ter em conta o centro de massa de todo o robô, contabilizando a massa de cada componente e ajustando a estrutura de acordo. Seria um projeto de maior robustez se tivesse sido utilizada uma placa PCB devido não só ao peso, mas também ao facto de eliminar a possibilidade de os componentes serem deslocados com o balanço do robô. Finalmente, seria uma mais valia ter sido utilizada uma bateria para alimentar o robô, caso que não foi possível neste projeto devido ao consumo de corrente dos motores ser maior do que o previsto o que iria tornar-se dispendioso.

O projeto foi, também, uma oportunidade de desenvolver as capacidades de pesquisa e aplicar os conhecimentos aprendidos ao longo do curso, de maneira autónoma. Concluindo este trabalho foi um êxito não só por ter sido alcançado o objetivo traçado previamente, mas também pela experiência e conhecimento que o acompanharam.


  • Referências

Início

[1] Segway Inc.'s Human Transporter (HT) models, acedido em 20/10/2019, https://msu.edu/~luckie/segway/i167/i167.html

[2] Donnici, Giampiero, et al. "TRIZ method for innovation applied to an hoverboard." Cogent Engineering 5.1 (2018): 1524537.

[3] OSOYOO, smart balancing car announced in amazon, acedido em 20/10/2019, https://www.amazon.com/OSOYOO-Balancing-Educational-Programmable-Bluetooth/dp/B07G491291

[4] Juang, Hau-Shiue, and Kai-Yew Lurrr. "Design and control of a two-wheel self-balancing robot using the arduino microcontroller board." 2013 10th IEEE International Conference on Control and Automation (ICCA). IEEE, 2013.

[5] Ghani, Nor Maniha Abdul, Faradila Naim, and Tan Piow Yon. "Two wheels balancing robot with line following capability." World Academy of Science, Engineering and Technology55 (2011): 634-638.

[6] Eletrofun, regulador de tensão 7805, acedido em 27/12/2019, https://www.electrofun.pt/744-large_default/regulador-de-tensao-7805.jpg

[7] Newegg, L298N Dual Full Bridge Driver Multiwatt15 Integrated Circuit L298 IC, acedido em 27/12/2019 https://c1.neweggimages.com/NeweggImage/ProductImageCompressAll1280/A4W3_130734047698432156w8p3isDEMy.jpg

[8] Eletrofun, Motores DC, acedido em 27/12/2019, https://www.electrofun.pt/7923-large_default/motor-dc-12v-dc-180ma-11500rpm.jpg

[9] Eletrofun, Módulo Acelerómetro E Giroscópio 3 Eixos 6DOF GY-521 MPU-6050, acedido em 18/10/2019, https://www.electrofun.pt/123-large_default/modulo-acelerometro-giroscopio-mpu-6050.jpg

[10] FilipeFlop, Controlando LCD 16x2 com arduino, acedido em 25/10/2019, https://www.filipeflop.com/blog/controlando-um-lcd-16x2-com-arduino/

[11] Mercado Livre, Microcontrolador Atmega88, acedido em 22/11/2019, https://http2.mlstatic.com/microcontrolador-atmega88pa-pu-atmega88-atmel-avr-D_NQ_NP_762553-MLB31136728588_062019-F.webp

[12] Peter Fleury, Avr software, acedido em 10/11/2019, http://homepage.hispeed.ch/peterfleury/avr-software.html




Início