A Raspberry PI Zero W é a mais nova placa da série das Raspberry. Seus maiores atrativos são o impressionante tamanho físico (é somente um pouco maior que um Pendrive), sua alta conectividade (possui, já nativo, conectividade WI-FI e Bluetooth) e seu preço reduzido em relação às demais Raspberries, tornando assim uma interessantíssima opção para quem quer desenvolver projetos utilizando Linux Embarcado. Aproveitando o recente lançamento desta placa aqui na loja, será mostrado nesse post um projeto bem bacana aos que gostam de robótica e visão computacional: como construir um robô seguidor de linha utilizando visão computacional (OpenCV) e a Raspberry Pi Zero W.
Material necessário para você montar o seu robô seguidor de linha
Para fazer esse projeto de robô seguidor de linha, você irá precisar de:
- Raspberry PI Zero W ou Raspberry Pi Zero 2 W
- Um Kit Chassi 2WD Robô para Arduino
- Um Protoboard de 400 pontos
- Jumpers Macho-Fêmea
- Jumpers Macho-Macho
- Uma Barra de Pinos 2×40 180 graus
- Diodos retificadores 1N4007
- Dois Resistores de 1k 1/4W
- Dois Transistores BC337
- Um cabo adaptador USB para micro-USB
- Um suporte de 4 pilhas (tipo AA)
- Quatro pilhas tipo AA (recomendável utilizar pilhas recarregáveis). Tais pilhas tem utilidade exclusiva de alimentar os motores e seu circuito de acionamento.
- Um powerpack USB (para alimentar a Raspberry PI Zero W)
- Uma webcam comum (qualquer uma que seja compatível com o Linux que roda na Raspberry PI Zero W)
- Um cartão SD de no mínimo 16GB de capacidade (recomendável ser classe 10)
Considerações
Este tutorial de robô seguidor de linha considera que você já está com a Raspberry PI Zero W operante (inclusive com acesso à Internet), ou seja, com o Raspbian instalado em um catão na Raspberry PI Zero W. O procedimento para isso é idêntico ao feito na Raspberry PI 3, por exemplo. Portanto, basta baixar a imagem do Raspbian mais recente e instalar!
Dica: configure também o acesso VNC Raspberry PI. Aqui, também o processo é idêntico ao feito na Raspberry PI 3.
Compilação e instalação do OpenCV
O OpenCV é um biblioteca Open-Source de visão computacional e machine learning. Além de muito popular, ela é excelente, permitindo o desenvolvimento de soluções realmente muito boas e profissionais. Um dos grandes méritos da Raspberry PI é a fácil integração com o OpenCV, aumentando ainda mais o leque de opções de projetos possíveis de serem feitos nela.
O mais recomendado, por motivos de otimização de desempenho, é compilar o OpenCV na plataforma-alvo (no nosso caso, a Raspberry PI Zero W). Este processo envolve baixar fontes e bibliotecas e compilar de fato o OpenCV , portanto é um processo que exige conexão com a Internet. Além disso, trata-se de um processo que exige todo o processamento da Raspberry PI Zero W, portanto é extremamente recomendável iniciar o processo de compilação e “esquecer” a placa compilando por algumas horas.
Para baixar todas as bibliotecas necessárias, código-fonte do OpenCV e fazer sua compilação, siga o procedimento abaixo:
- No terminal Linux (na Raspberry PI Zero W), utilize o seguinte comando:
nano InstalaOpenCV.sh
- No editor de textos nano, cole o seguinte conteúdo:
#!/bin/bash sudo apt-get install -y build-essential cmake pkg-config sudo apt-get install -y libjpeg-dev libtiff5-dev libjasper-dev libpng12-dev sudo apt-get install -y libavcodec-dev libavformat-dev libswscale-dev libv4l-dev sudo apt-get install -y libxvidcore-dev libx264-dev sudo apt-get install -y libgtk2.0-dev sudo apt-get install -y libatlas-base-dev gfortran sudo apt-get install -y python2.7-dev python3-dev cd ~ wget -O opencv.zip https://github.com/Itseez/opencv/archive/3.1.0.zip unzip opencv.zip wget -O opencv_contrib.zip https://github.com/Itseez/opencv_contrib/archive/3.1.0.zip unzip opencv_contrib.zip pip install numpy cd ~/opencv-3.1.0/ mkdir build cd build cmake -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=/usr/local \ -D INSTALL_PYTHON_EXAMPLES=ON \ -D OPENCV_EXTRA_MODULES_PATH=~/opencv_contrib-3.1.0/modules \ -D BUILD_EXAMPLES=ON .. make sudo make install sudo ldconfig
- Saia do editor nano pressionando Ctrl+X e depois Y
- Digite o seguinte comando:
sudo chmod +x InstalaOpenCV.sh
- Feito isso, inicie o processo com o comando abaixo:
sudo ./InstalaOpenCV.sh
Agora basta esperar. Devido à Raspberry PI Zero W ser single-core (ao contrário da Raspberry PI 3 que é quad-core), o processo de compilação é bem lento. No meu caso, o processo todo demorou entre 6 e 7 horas. Portanto, uma boa ideia é deixar compilando durante a noite.
Importante: pelo fato da compilação do OpenCV ser algo que exige todo o processamento da Raspberry PI (e por muito tempo), a tendência é esta esquentar consideravelmente. Portanto, recomendo fortemente não fazer este processo com a Raspberry PI Zero W em cases fechados (quanto mais ventilado for pra ela, melhor).
Circuito esquemático
O circuito esquemático do projeto robô seguidor de linha pode ser visto na figura abaixo:
Movimentação do robô
A movimentação do robô seguidor de linha é feito conforme mostra a figura abaixo:
Ou seja:
- Para ir para a direita, aciona-se o motor da esquerda e para-se o motor da direita (figura 2.a)
- Para ir para a esquerda, aciona-se o motor da direta e para-se o motor da esquerda (figura 2.b)
- Para ir para frente, aciona-se ambos os motores (figura 2.c)
Funcionamento do projeto
Compilado e instalado o OpenCV, estamos prontos para prosseguir no projeto! Este projeto consiste em um robô seguidor de linha, porém ao invés de utilizar sensores reflexivos infravermelhos ou sensores semelhantes, será utilizada visão computacional (com OpenCV) para detecção da linha. O robô, com base nos resultados obtidos do processamento de imagem, funcionará da seguinte forma:
- Se o processamento de imagens indicou que o robô está a direita da linha, aciona-se o motor da roda da direita (desta forma, levando o robô para a esquerda)
- Se o processamento de imagens indicou que o robô está a esquerda da linha, aciona-se o motor da roda da esquerda (desta forma, levando o robô para a direita)
- Se nenhuma linha for detectada, o robô irá parar (será considerado fim de curso)
Em suma, enquanto alguma linha foi “enxergada” pelo robô, este se movimenta seguindo-a. A partir do momento que nenhuma linha é mais “vista” pelo robô, é considerado fim de curso e este irá parar.
Processamento de imagens – detalhes
O processamento de imagens desse projeto possui as seguintes etapas:
- Aquisição de um quadro/frame da webcam: cada frame obtido é processado individualmente pelas etapas seguintes. É importante ressaltar aqui que, devido à baixa capacidade de processamento da Raspberry PI Zero W (em relação às outras da linha), uma boa resolução de imagem (ou seja, que permite um bom tempo de processamento de imagem) é 320×240 pixels.
- Desenho de uma linha vertical, no centro da imagem: esta linha servirá como referência: se a linha do chão / linha- guia cruzar esta linha, o robô está andando em cima da linha-guia. Observe a figura abaixo:
Caso contrário, precisará ir para a esquerda ou direita,dependendo do posicionamento relativo entre as linhas. Observe a figura abaixo:
- Transformação do frame para escala de cinza: a escala de cinza é normalmente a primeira etapa de um processamento de imagens pois, por conter muito menos informações que uma imagem colorida, torna seu processamento muito mais rápido nas etapas posteriores.
- Gaussian Blur: Este filtro faz com que contornos fiquem mais suaves (efeito smooth), fazendo com que a linha-guia fique mais “uniforme”. isso significa que variações bruscas de cor / reflexos, etc. serão suavizados e ficarão com a cor mais parecida com a da linha-guia.
- Binarização: a binarização consiste em reconfigurar as cores de todos os pixels da imagem, seguindo a lógica: Se a cor do pixel (em escala de cinza) for menor que um dado limiar, esse pixel tem sua cor substituída pela cor branca. Se a cor do pixel (em escala de cinza) for maior que um dado limiar, esse pixel tem sua cor substituída pela cor preta. Portanto, a imagem ficará em preto-e-branco, com mais ou menos detalhes dependendo do limiar de binarização (limiar de cor escolhido).
Importante: a determinação deste limiar é empírica, ou seja, varia de caso para caso e deve ser determinado na prática (até a qualidade da imagem obtida da câmera pode influenciar). Portanto o limiar de binarização aqui utilizado pode ser diferente no seu caso. - Dilatação: após a binarização, a linha (objeto que desejamos realçar / detectar) pode conter “buracos” dentro de si, o que nas etapas posteriores podem ser considerados objetos/contornos à parte. Estes “buracos” são péssimos para o processamento de imagens pois, além e uma falsa detecção de um contorno/objeto, estes farão com que o processamento de imagem fique mais pesado (pois serão processados contornos que tem relevância nenhuma para este projeto). Para eliminar estes “buracos”, é feito um processo chamado de dilatação. Ao fim deste, o objeto (no caso, a linha-guia) virará uma “massa de pixels” única, sem “buracos”.
- Inversão de cores: neste ponto, a linha detectada deve estar na cor preta e o restante da imagem na cor branca. Como a etapa seguinte (busca e processamento de contornos) considera uma massa de pixels branca como contorno fechado, é realizada aqui a inversão de cores da imagem (desta forma, a linha passará a ser branca e, portanto, detectável como contorno).
- Busca e processamento de contornos: esta é a etapa definitiva do processamento de imagens, pois aqui determina-se onde a linha-guia está. Espera-se que o único contorno encontrado (de área maior que um determinado limite) seja a linha no chão. Em seguida, obtêm-se o centro de uma área retangular capaz de “englobar” todo o contorno da linha que foi detectado(este centro recebe o nome de centróide).
Feito isso, as decisões abaixo são tomadas, conforme explicado no tópico “Funcionamento deste projeto” deste artigo:
- Se o centróide do contorno da linha-guia estiver à esquerda da linha azul desenhada no frame (conforme passo 2 do processamento de imagens), o robô deve se mover para a esquerda.
- Se o centróide do contorno da linha-guia estiver à direita da linha azul desenhada no frame (conforme passo 2 do processamento de imagens), o robô deve se mover para a direita.
- Se nenhuma linha for “vista”, considera-se que é fim de curso e o robô para de se mover.
Código-fonte do projeto
O código-fonte do projeto robô seguidor de linha pode ser visto abaixo:
import cv2 import numpy as np import RPi.GPIO as GPIO LimiarBinarizacao = 125 #este valor eh empirico. Ajuste-o conforme sua necessidade AreaContornoLimiteMin = 5000 #este valor eh empirico. Ajuste-o conforme sua necessidade #GPIOs utilizados: GPIOMotor1 = 18 #Broadcom pi2067n 18 (P1 pin 12) GPIOMotor2 = 17 #Broadcom pin 17 (P1 pin 11) #Funcao: trata imagem e retorna se o robo seguidor de linha deve ir para a esqueda ou direita #Parametros: frame capturado da webcam e primeiro frame capturado #Retorno: < 0: robo deve ir para a direita # > 0: robo deve ir para a esquerda # 0: nada deve ser feito def TrataImagem(img): #obtencao das dimensoes da imagem height = np.size(img,0) width= np.size(img,1) QtdeContornos = 0 DirecaoASerTomada = 0 #tratamento da imagem gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (21, 21), 0) FrameBinarizado = cv2.threshold(gray,LimiarBinarizacao,255,cv2.THRESH_BINARY)[1] FrameBinarizado = cv2.dilate(FrameBinarizado,None,iterations=2) FrameBinarizado = cv2.bitwise_not(FrameBinarizado) #descomente as linhas abaixo se quiser ver o frame apos binarizacao, dilatacao e inversao de cores #cv2.imshow('F.B.',FrameBinarizado) #cv2.waitKey(10) _, cnts, _ = cv2.findContours(FrameBinarizado.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img,cnts,-1,(255,0,255),3) for c in cnts: #se a area do contorno capturado for pequena, nada acontece if cv2.contourArea(c) < AreaContornoLimiteMin: continue QtdeContornos = QtdeContornos + 1 #obtem coordenadas do contorno (na verdade, de um retangulo que consegue abrangir todo ocontorno) e #realca o contorno com um retangulo. (x, y, w, h) = cv2.boundingRect(c) #x e y: coordenadas do vertice superior esquerdo #w e h: respectivamente largura e altura do retangulo cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) #determina o ponto central do contorno e desenha um circulo para indicar CoordenadaXCentroContorno = (x+x+w)/2 CoordenadaYCentroContorno = (y+y+h)/2 PontoCentralContorno = (CoordenadaXCentroContorno,CoordenadaYCentroContorno) cv2.circle(img, PontoCentralContorno, 1, (0, 0, 0), 5) DirecaoASerTomada = CoordenadaXCentroContorno - (width/2) #em relacao a linha central #output da imagem #linha em azul: linha central / referencia #linha em verde: linha que mostra distancia entre linha e a referencia cv2.line(img,(width/2,0),(width/2,height),(255,0,0),2) if (QtdeContornos > 0): cv2.line(img,PontoCentralContorno,(width/2,CoordenadaYCentroContorno),(0,255,0),1) cv2.imshow('Analise de rota',img) cv2.waitKey(10) return DirecaoASerTomada, QtdeContornos #Programa principal #Setup dos GPIOs: GPIO.setmode(GPIO.BCM) GPIO.setup(GPIOMotor1, GPIO.OUT) GPIO.setup(GPIOMotor2, GPIO.OUT) GPIO.output(GPIOMotor1, GPIO.LOW) GPIO.output(GPIOMotor2, GPIO.LOW) camera = cv2.VideoCapture(0) camera.set(3,320) camera.set(4,240) #faz algumas leituras de frames antes de consierar a analise #motivo: algumas camera podem demorar mais para se "acosumar a luminosidade" quando ligam, capturando frames consecutivos com muita variacao de luminosidade. Para nao levar este efeito ao processamento de imagem, capturas sucessivas sao feitas fora do processamento da imagem, dando tempo para a camera "se acostumar" a luminosidade do ambiente for i in range(0,20): (grabbed, Frame) = camera.read() while True: try: (grabbed, Frame) = camera.read() if (grabbed): Direcao,QtdeLinhas = TrataImagem(Frame) if (QtdeLinhas == 0): print "Nenhuma linha encontrada. O robo ira parar." GPIO.output(GPIOMotor1, GPIO.LOW) GPIO.output(GPIOMotor2, GPIO.LOW) continue if (Direcao > 0): print "Distancia da linha de referencia: "+str(abs(Direcao))+" pixels a direita" GPIO.output(GPIOMotor1, GPIO.HIGH) GPIO.output(GPIOMotor2, GPIO.LOW) if (Direcao < 0): print "Distancia da linha de referencia: "+str(abs(Direcao))+" pixels a esquerda" GPIO.output(GPIOMotor1, GPIO.LOW) GPIO.output(GPIOMotor2, GPIO.HIGH) if (Direcao == 0): print "Exatamente na linha de referencia!" GPIO.output(GPIOMotor1, GPIO.HIGH) GPIO.output(GPIOMotor2, GPIO.HIGH) except (KeyboardInterrupt): GPIO.output(GPIOMotor1, GPIO.LOW) GPIO.output(GPIOMotor2, GPIO.LOW) exit(1)
Copie-o e salve-o na Rasberry PI Zero W como RoboLinha.py. Para executá-lo, utilize o comando abaixo:
python RoboLinha.py
Preparação do local para o robô rodar
O chão que o robô rodará deverá ser o mais claro possível e a linha a mais escuro possível. Idealmente, chão branco e linha preta. O motivo disso é que, quanto mais alto o contraste de cor entre linha e chão, mais fácil é para a linha ser detectada por visão computacional.
Agora é só colocar uma linha no chão e colocar o robô pra rodar!
Gostou do projeto do Robô seguidor de linha com Raspberry Pi Zero W e OpenCV? Deixe seu comentário logo abaixo.
Desculpe .. uma outra pergunta … como está sendo feita a alimentação da placa raspberry ?
Olá Reinaldo.
Todo o circuito é alimentado por 4 pilhas AAA.
Att.
Vitor Mattos.
MakerHero.
Ola boa tarde !!! Quando desligar a bateria tem que baixar o código novamente ? Grato
Olá Reinaldo.
Caso tenha sido gravado da forma correta, não irá perder a gravação ao reiniciar.
Att.
Vitor Mattos.
Suporte Técnico MakerHero.
Olá, é possivel no lugar de uma webcam usar a própria camera que conecta no rasp? (rasp modelo 4b)
Olá Victor.
Seria sim possível, porém teria que mudar a pinagem para o código entender o sinal da câmera.
Att.
Vitor Mattos.
MakerHero.
Olá, teria como fazer esse projeto só que com arduino?
Olá Maria.
Temos outro post que aborda o projeto com Arduino: https://www.makerhero.com/blog/projeto-robo-seguidor-de-linha-arduino/
Att.
Vitor Mattos.
Suporte Técnico MakerHero.
Boa tarde, meu nome é Renato. Fiz a compilação na Raspberry pi 3B. No final apareceu essa mensagem de erro no terminal.
compilation terminated.
make[2]: *** [modules/core/CMakeFiles/pch_Generate_opencv_core.dir/build.make:64: modules/core/precomp.hpp.gch/opencv_core_RELEASE.gch] Error 1
make[1]: *** [CMakeFiles/Makefile2:4118: modules/core/CMakeFiles/pch_Generate_opencv_core.dir/all] Error 2
make: *** [Makefile:163: all] Error 2
Após isso testei o código do robô no aplicativo Thonny e retornou esse erro:
ModuleNotFoundError: No module named ‘cv2’
eu queria uma atualização desse projeto, seria bem legal ^_^
O circuito pode ser usado como ponte H, para outros projetos?
Pode. Desde que respeite a corrente máxima no coletor do BC337. De acordo com o data sheet é 500mA contínua ou 1A alternada. Mas você acha módulos na internet por 15 reais que suportam correntes bem maiores, como por exemplo o L298N, que suporta até 6A em cada canal.
Vocês tem algum vídeo do carro funcionando?
O meu código fica dando erro de indentação
Bom dia meu nome e Robinson Rafael , gostaria de saber se tem como usar um raspberry pi 3 b, usar neste robo agv
Robinson, bom dia.
Sim, você pode utilizar a Raspberry Pi 3b neste projeto.
Atenciosamente,
Pedro Bertoleti
Minha compilação da erro quando tenta conectar com o serviço raspbian.raspberrypi.org
Bruno, bom dia.
Poderia colocar aqui as mensagens de erro? Pergunto pois, pela descrição dada está parecendo que sua Raspberry não está com conectada à Internet, porém com as mensagens de erro conseguirei verificar melhor o que está ocorrendo.
Atenciosamente,
Pedro Bertoleti
eu consegui, obrigado!
Ola boa noite. Primeiramente muito obrigado pelo blog e pelo excelente trabalho. Gostaria de saber posso substituir a câmera por um sensor de presença? e se essa configuração se aplica também ao raspberry pi b+?
acredito que não, pois o opencv trabalha com imagem
Olá, após rodar algumas vezes, quando detecta somente uma linha o programa da o seguinte erro
CoordenadaXCentroContorno = (x + x + w) / 2
UnboundLocalError: local variable ‘x’ referenced before assignment
Sabe de alguma coisa relacionada a isso ? estou tentando reproduzir usando uma raspberry pi 3 desde já agradeço;
Muito bom, obrigado por compartilhar o conhecimento!
Mas uma pergunta, consigo instalar o OpenCV no Raspberry Pi 3 usando este tutorial?
Renan, bom dia.
Primeiramente, muito obrigado pela leitura e elogio!
Sobre sua pergunta, sim, é possível instalar o OpenCV na Raspberry PI 3 usando este tutorial.
Atenciosamente,
Pedro Bertoleti
Uhum, mostra o video do robo andando please ??