INSTITUTO FEDERAL DO ESPÍRITO SANTO PÓS-GRADUAÇÃO LATO SENSU EM ENGENHARIA ELÉTRICA COM ÊNFASE EM SISTEMAS INTELIGENTES APLICADOS À AUTOMAÇÃO ZOROASTRO SANTOLIM PIMENTA CONTROLE DOS MOVIMENTOS DE UM SISTEMA CARTESIANO PARA LOCALIZAÇÃO DE OBJETOS COM USO DE REALIMENTAÇÃO POR IMAGEM Vitória 2014 ZOROASTRO SANTOLIM PIMENTA CONTROLE DOS MOVIMENTOS DE UM SISTEMA CARTESIANO PARA LOCALIZAÇÃO DE OBJETOS COM USO DE REALIMENTAÇÃO POR IMAGEM Monografia apresentada ao Curso de Pós-Graduação Lato Sensu em Engenharia Elétrica com Ênfase em Sistemas Inteligentes Aplicados à Automação do Instituto Federal do Espírito Santo como requisito parcial para obtenção do certificado de Especialista em Sistemas Inteligentes Aplicados à Automação. Orientador: Prof. M. Sc. Paulo Henrique F. Zanadrea Vitória 2014 (Biblioteca Nilo Peçanha do Instituto Federal do Espírito Santo) P644c Pimenta, Zoroastro Santolim. Controle dos movimentos de um sistema cartesiano para localização de objetos com uso de realimentação por imagem / Zoroastro Santolim Pimenta. – 2014. 45 f. : il. ; 30 cm Orientador: Luis Eduardo Martins de Lima. Monografia (especialização) – Instituto Federal do Espírito Santo, Coordenadoria de Pós-Graduação em Engenharia Elétrica, Curso Pós-Graduação Lato Sensu em Engenharia Elétrica com Ênfase em Sistemas Inteligentes Aplicados à Automação, 2014. 1. Visão por computador. 2. Sistemas de controle por realimentação. 3. Processamento de imagens. 4. Engenharia elétrica. I. Lima, Luis Eduardo Martins de. II. Instituto Federal do Espírito Santo. III. Título. CDD 21 – 006.37 Dedico este trabalho primeiramente à Deus, que me deu a graça de estar aqui, também à minha família que durante o período deste curso precisou tolerar a minha ausência. Também aos meus amigos e colegas de curso, pois passamos juntos por todas as situações sempre com otimismo e companheirismo. AGRADECIMENTOS Agradeço a todos que contribuíram para a realização deste trabalho, em especial ao Bruno por ter cedido o material e aos professores por terem ensinado os conteúdos tão importantes para a realização deste, em especial ao meu orientador, por ter tido a paciência de sempre me apoiar durante estes meses de trabalho. RESUMO A visão computacional é definida como a construção de descrições explícitas e significativas de objetos físicos a partir de imagens. A utilização da visão computacional como ferramenta de um sistema de controle é o assunto deste trabalho, juntamente com o desenvolvimento de um sistema de controle de uma mesa XY e um cabeçote que se movimenta sobre ela, baseado em identificação de objetos através de suas cores. Também é objetivo deste trabalho embarcar toda a solução em uma plataforma de processamento computacional miniaturizada e autônoma, agregada à mesa XY. Palavras chave: Beaglebone. Controle. Embarcado. Processamento Digital de Imagens. Visão Computacional. Controle de Movimento de um Cabeçote Cartesiano. ABSTRACT Computer vision is defined as the construction of explicit and significant descriptions of physical objects on images. The use of computer vision as a control system feedback tool is the subject of this paper, along with the development of a control for an XY table and a moving head that is mounted on it, using color based object identification. It is also subject of this paper embed the complete solution in one small and autonomous processing platform, attached to the XY table. Keywords: Beaglebone. Control. Embedded. Digital Image Processing. Computer Vision. Control of a Cartesian Moving Head’s Position. SUMÁRIO 1 INTRODUÇÃO ................................................................................................... 9 2 PROCESSAMENTO DIGITAL DE IMAGENS .................................................. 10 3 VISÃO COMPUTACIONAL .............................................................................. 11 4 O MODELO PINHOLE ..................................................................................... 12 5 O PROBLEMA ................................................................................................. 14 6 DESENVOLVIMENTO ...................................................................................... 16 6.1 RECURSOS INICIAIS ...................................................................................... 16 6.2 CONSTRUÇÃO E ADEQUAÇÃO FÍSICA/MECÂNICA .................................... 16 6.3 ADEQUAÇÃO ELETRÔNICA........................................................................... 17 6.4 CONTROLE DOS MOTORES DOS EIXOS ..................................................... 19 6.5 ALGORITMO DE CAPTURA DE IMAGENS .................................................... 20 6.6 ESCOLHENDO O ALVO E O MARCADOR DO CABEÇOTE .......................... 22 6.7 IDENTIFICANDO O ALVO E O MARCADOR .................................................. 23 6.8 ALGORITMO DE FILTRAGEM DE CORES ..................................................... 24 6.9 DESCOBRINDO O CENTRO DO OBJETO ..................................................... 26 6.10 O PROBLEMA DO MODELO PINHOLE .......................................................... 28 6.11 SOLUÇÃO ADOTADA PARA O PROBLEMA DO MODELO PINHOLE ........... 29 6.12 MINIATURIZANDO E EMBARCANDO TODA A SOLUÇÃO............................ 32 7 RESULTADOS ................................................................................................. 35 8 CONCLUSÃO E TRABALHOS FUTUROS...................................................... 36 REFERÊNCIAS ................................................................................................ 37 APÊNDICE A ................................................................................................... 38 Códigos FONTE para controle dos motores de passo ..................................... 38 APÊNDICE B ................................................................................................... 42 Códigos FONTE para identificação de objetos pela webcam ........................... 42 9 1 INTRODUÇÃO Tentar, errar e consertar são funções básicas de qualquer sistema de controle, seja ele eletrônico, mecânico ou até biológico. Neste trabalho são colocados em prática várias áreas de conhecimento com um objetivo: o controle de um cabeçote em uma mesa XY, utilizando visão computacional. Durantes as seções de 2 a 5 são discutidos alguns conceitos, apenas para contextualizar o trabalho e também ressaltar a sua importância, transformando em prática os conhecimentos adquiridos em sala de aula. A partir da seção 6.1 são apresentadas passo a passo cada uma das atividades realizadas para a implementação e conclusão do trabalho proposto. Ao final são discutidas algumas possibilidades para a modificação e utilização do resultado alcançado de outras formas e em busca de outros objetivos. 10 2 PROCESSAMENTO DIGITAL DE IMAGENS Dentre os 5 sentidos fundamentais para os seres humanos, em geral, o que mais se destaca na nossa percepção do mundo, é o sentido da visão. A luz que é refletida pelos objetos que nos cercam chega até os nossos olhos, que por sua vez, traduzem as informações luminosas em outro tipo de informações, as quais são transmitidas para o cérebro. Não é por acaso que este é o mais desenvolvido dos 5 sentidos. É inicialmente pela visão que conseguimos detectar a distância de algum objeto, podemos determinar se este mesmo objeto está parado ou em movimento, nos orientamos através de marcações ou caminhos detectados através da visão, além de ser uma das realimentações para o controle do corpo humano. É através da visão que conseguimos diminuir a velocidade ao aproximar as mãos para apanhar um objeto, ao invés de derrubá-lo. Uma foto é um registro instantâneo de um momento, um acontecimento, uma situação. Sendo assim, utilizar a visão como um meio para a obtenção de informações do mundo é um recurso natural para nós. E com os recursos computacionais atuais, obter informações a partir de imagens se torna uma tarefa computacional factível. Uma imagem (em tons de cinza) é definida como uma função de duas dimensões, f(x, y), onde x e y são coordenadas espaciais e o valor de f em qualquer par de coordenadas (x, y) é chamado de intensidade ou nível de cinza da imagem naquele ponto. Quando x, y e f tem valores finitos e discretizados, a imagem é então chamada de imagem digital (GONZALEZ, 2011). Em uma imagem digital, cada par de coordenadas (x, y) recebe o nome de elementos de imagem (em inglês, picture elements ou pixels). O termo mais comum para definir um elemento de imagem digital é o Pixel. Derivado do campo de processamento digital de sinais, o processamento digital de imagens é a utilização de computadores digitais para o processamento de imagens digitais, onde tanto as informações de entrada do processo, quanto as informações resultantes, são imagens (GONZALEZ, 2011). 11 3 VISÃO COMPUTACIONAL A habilidade humana em extrair informações a partir do seu sistema visual se desenvolve desde o nascimento, através de um processo contínuo de identificação – extração – armazenamento. As imagens projetadas na metade esquerda da retina de cada olho são enviadas para o córtex visual primário do hemisfério esquerdo do cérebro, enquanto que as imagens projetadas na metade direita da retina de cada olho são enviadas para o córtex visual primário do hemisfério direito. Como as imagens projetadas na retina são invertidas, cada hemicampo visual é trabalhado no lado oposto do cérebro, ou seja, o hemicampo visual direito é trabalhado no centro visual esquerdo e o hemicampo visual esquerdo é trabalhado no centro visual direito do cérebro (GUIMARÃES, 2004). Apesar de toda a complexidade envolvida, a velocidade com que conseguimos identificar um objeto a partir de uma imagem é incrivelmente rápido e todo este processamento acontece em uma das áreas mais complexas do cérebro: o córtex visual. Cientistas e pesquisadores dedicam seus esforços em tentar desenvolver sistemas capazes de imitar a maneira como o sistema visual humano trabalha, desde os primeiros experimentos na década de 1950 (BALLARD, 1982). Ballard e Brown (BALLARD, 1982) definem a visão computacional como a construção de descrições explícitas e significativas de objetos físicos a partir de imagens. Em visão computacional o objetivo é bem diferente do processamento de imagens, onde ambos o insumo e o produto do processo são imagens, mas sim prover a uma máquina, a capacidade de perceber as informações contidas nestas imagens, sendo um pré-requisito para reconhecer, manipular e até pensar sobre os objetos ali presentes. O campo da visão computacional tem sido amplamente explorado em vários segmentos, como por exemplo na medicina, com diagnósticos por imagem. Também em aplicações militares, sendo usado como mecanismos de detecção de inimigos. Mais áreas incluem a indústria, exploração espacial, sistemas de controle, controle de crescimento de áreas de desmatamento, poluição, entre outras. 12 4 O MODELO PINHOLE Para uma máquina conseguir extrair informações de uma imagem, antes é necessário que esta imagem seja capturada, seja através de câmeras fotográficas, scanners, câmeras de vídeo ou outros. Os dispositivos que mais se assemelham aos olhos humanos, capturando imagens rapidamente, são as câmeras e inclusive existem alguns modelos de projeção disponíveis que mapeiam matematicamente a relação entre as coordenadas de um ponto tridimensional no campo de visão da câmera, com sua projeção bidimensional no plano da imagem. Um modelo que é amplamente aplicado para o mapeamento e calibração de câmeras é o modelo pinhole. Este modelo é baseado na idéia de que toda a luz disponível para contribuir com a geração da imagem, deve passar através de um orifício (o centro ótico da câmera), cujo diâmetro é muito próximo a zero, como representado na figura 1. O modelo pinhole também é utilizado para explicar o funcionamento de câmaras estenopeicas. Figura 1 - Representação de um modelo pinhole, ou câmara estenopeica Fonte: http://en.wikipedia.org/wiki/Pinhole_camera Figura 2 - Modelo ideal de câmera pinhole Fonte: (ALMONFREY, 2013) 13 Utilizando o modelo representado pela figura 2, é possível mapear o caminho de um raio sendo refletido pelo ponto p, atravessando o centro ótico da câmera, o ponto o e terminando no plano de projeção, no ponto p’. A imagem completa é apresentada invertida no plano de projeção, algo como acontece na retina humana. O ponto 𝑝 = [𝑋, 𝑌, 𝑍]𝑇 possui imagem 𝑝′ = [𝑥, 𝑦]𝑇 , sendo 𝑓 a distância focal (distância entre o plano de projeção e o centro ótico) da câmera. Através da semelhança de triângulos é possível mapear as igualdades das projeções (ALMONFREY, 2013): 𝑋 𝑍 𝑌 𝑦 = −𝑓 𝑍 𝑥 = −𝑓 (1) (2) 14 5 O PROBLEMA No início deste trabalho, desejava-se unir conhecimentos em várias áreas de estudo, como visão computacional, processamento de imagens, sistemas embarcados, programação avançada, sistemas de controle e inteligência artificial para a construção de uma solução prática. Primeiramente foi necessário identificar um problema, ao qual uma solução prática e englobando a maioria das áreas acima descritas, se encaixasse. Havia uma mesa construída em alumínio e teflon, a qual estava disponível, com dois motores de passo bipolares, cada um com características de alimentação e ângulo de passo diferentes. Cada um destes motores movimentava uma correia, um para o eixo X e outro para o eixo Y, uma imagem da mesa, inicialmente, era como a da Figura 3. Quando os motores são acionados, as correias de cada eixo movimentam um cabeçote (elemento final) dentro da área de atuação da mesa. Figura 3 - Foto inicial da mesa X Y Fonte: Autor, 2014 Também já estavam montados e disponíveis na mesa os circuitos para acionar os motores de passo. O problema é identificar e localizar um objeto na área de atuação da mesa XY. 15 Apenas a título de inspiração, vale a pena citar outros trabalhos que alcançaram objetivos multidisciplinares como o deste trabalho. Como um exemplo, um trabalho realizado utilizando visão computacional onde um quadrirotor é observado por câmeras e todo o processamento do seu controle é realizado em um tablet conectado ao sistema (JEONGWOON Kim, 2013). Outro exemplo é o controle de um braço robótico utilizando visão computacional, realizado por estudantes em uma univerdade na Espanha (CABRE, T.P., 2013), a aplicação de visão para ampliar a percepção sensorial de robôs executando tarefas (PAPANIKOLOPOULOS, N.P., 1995), controle de maquinários pesados (GARIBOTTO, G., 1997) e até um manipulador aquático (SMITH, J.S., 1994). Não foram encontrados, durante as pesquisas feitas, trabalhos com escopo mais semelhante ao deste, porém os trabalhos encontrados na área de visão computacional serviram de inspiração para este desenvolvimento. 16 6 DESENVOLVIMENTO 6.1 RECURSOS INICIAIS Para este trabalho, os recursos utilizados foram: A mesa X Y, como descrita anteriormente, juntamente com seu circuito acionador para os motores de passo; Fonte 12V e 5V, para alimentar as interfaces de controle dos motores de passo, a câmera e outros circuitos eletrônicos; Webcam modelo WC040 da marca Multilaser, com sensor de 300K pixels para vídeo; Arduino Uno R3, para testar e controlar as interfaces com os motores; IDE Arduino 1.0; Computador tipo PC, para realizar a programação, testes e verificações dos algoritmos gerados; Sistema Operacional Windows 7; Mathworks MatLab, versão Student 2012a; Ambiente de programação IDE Eclipse, para a programação dos algoritmos de visão computacional; Biblioteca de código aberto para visão computacional OpenCV, versões 2.1.0 e 2.4.1; Compiladores GNU C e GNU C++ versões 4.4 e 4.6 em ambientes Linux e Windows. BeagleBone White (256MB RAM, 700MHz CPU); Peças de cantoneira em alumínio. 6.2 CONSTRUÇÃO E ADEQUAÇÃO FÍSICA/MECÂNICA Um dos primeiros testes realizados, foi para confirmar que toda a estrutura mecânica estava funcionando corretamente: se os motores realmente movimentavam a correia e também se a correia movimentava o carrinho. Portanto desta maneira, também foi testada a parte eletrônica, os circuitos acionadores dos motores de passo. Definir a posição da câmera foi o passo seguinte. Com a câmera conectada ao computador, foi utilizado um programa de visualização nativo do sistema operacional para capturar imagens em tempo real. 17 Este teste foi necessário para se conseguir definir em qual altura, a partir do plano da mesa XY, a imagem gerada pela câmera capturava toda a área de movimentação do cabeçote. Foi necessário colocar a câmera a uma altura de 1,2m a partir do plano da mesa, para que toda a mesa fosse capturada nas imagens geradas. Para manter a câmera nesta posição, optou-se por uma fixação com armação do tipo tripé. Esta armação foi construída com cantoneiras de alumínio e fixada nas arestas e laterais da mesa. O resultado pode ser verificado na Figura 4, abaixo: Figura 4 - Foto da fixação da câmera na mesa XY Fonte: Autor, 2014 6.3 ADEQUAÇÃO ELETRÔNICA Os circuitos de acionamento dos motores de passo, originalmente eram formados por 2 controladores separados, cada um contendo um CI L297 e um CI L298, ambos sincronizados por uma fonte de clock formada por um circuito com um CI 555. O CI L297 é o responsável por gerar os sinais de fase para controle dos motores de passo e pode ser utilizado para controlar unipolares e bipolares (4 fases em um motor unipolar ou 2 fases em um motor bipolar) (STMICROELECTRONICS, 2001). Já o CI L298 é um controlador de potência, que contem encapsuladas, 2 pontes H, servindo para controlar 2 motores DC comuns ou 1 motor de passo (STMICROELECTRONICS, 2000). Para esta aplicação, um dos requisitos definidos era o de total autonomia do algoritmo de visão computacional, que estaria sendo executado no computador, sobre o 18 acionamento dos motores. Portanto foi preciso remodelar o circuito de controle, mantendo o CI de potência L298, para acionamento das fases dos motores, porém removendo o CI L297 e também o CI 555. No lugar do controlador removido, optou-se em utilizar uma solução que integrasse algum tipo de comunicação com a plataforma PC. A escolha do Arduino Uno R3 para realizar esta tarefa foi trivial, pela boa disponibilidade, documentação e preço da placa. O Arduino comunica-se com o PC através da porta USB, enviando e recebendo informações do computador via protocolo de transmissão serial. Outras opções de micro controladores foram analisadas, mas a solução adotada inicialmente foi a que estava imediatamente disponível, além de contar com muitas bibliotecas de código fonte aberto e exemplos disponíveis na internet. Foi utilizada a biblioteca nativa do sistema para controle de motores de passo. A Figura 5 mostra como foi montada a interface de controle para apenas um, dos dois motores de passo utilizados no sistema. Para a representação completa do circuito de testes, considerar a utilização de 2 CIs L298, um para cada motor, conectadas ao mesmo Arduino controlador. Observe que os diodos de proteção foram desconsiderados na montagem da Figura 5, mas todo o procedimento montado fisicamente seguiu as instruções do manual do CI (STMICROELECTRONICS, 2000), conforme diagrama esquemático da Figura 6. Figura 5 - Controle de um motor de passo utilizando Arduino Fonte: Autor, 2014 19 Figura 6 - Diagrama esquemático de controle de um motor de passo Fonte: http://www.hoelscher-hi.de acessado em 31/05/2014 6.4 CONTROLE DOS MOTORES DOS EIXOS Como mencionado anteriormente, foi utilizado o Arduino UNO R3 para o controle inicial dos motores de passo instalados na mesa, principalmente pela facilidade e rapidez para a geração de um protótipo funcional. O ambiente de programação do Arduino conta com uma biblioteca já instalada para controle de motores de passo, monopolares e bipolares. Esta biblioteca se chama Stepper. O programa de controle inicial, ainda sem a interação com o PC, executava duas funções básicas essenciais: a primeira era manter os motores parados e por consequência o cabeçote também não se movimentava. A segunda era movimentar cada motor para frente e para trás. Seguindo uma ordem específica, o movimento gerado no cabeçote era quadricular e sempre retornava ao ponto de origem. Estas duas funções exercidas pelo sistema de controle inicial tornavam outros desenvolvimentos e testes possíveis. Com os motores parados, era possível desenvolver o algoritmo necessário para capturar as imagens e filtrar apenas a posição do cabeçote e também somente a posição do alvo. Com os motores em movimento era possível gerar o algoritmo que acompanhava o percurso do cabeçote, quadro a quadro do vídeo, conforme apresentado na figura 7. 20 Figura 7 – Movimentação inicial do cabeçote Fonte: Autor, 2014 6.5 ALGORITMO DE CAPTURA DE IMAGENS Dentre algumas ferramentas de software para visão computacional, de código fonte aberto disponíveis na Internet, procurou-se utilizar a que contivesse uma boa documentação, suporte e grande número de exemplos. A biblioteca OpenCV (Open Computer Vision) é uma ferramenta modular que atende aos requisitos acima, com código fonte aberto com licença BSD e contendo centenas de algoritmos voltados para visão computacional. Alguns algoritmos disponíveis no OpenCV são voltados especificamente para atividades como (OPENCV DEV TEAM, 2013): Captura de vídeo (módulo vídeo); Processamento de Imagens (módulo imgproc); Calibração de sistemas de visão 3D (módulo calib3d); Detecção de objetos (módulo objdetect); Geração de interfaces gráficas (módulo highgui); Entre outras (módulos core, features2d, gpu, etc.). Através da documentação e utilizando o módulo de vídeo do OpenCV, tornou-se viável capturar a imagem da câmera, tendo a webcam conectada ao PC e um programa com uma função principal contendo as linhas abaixo: #include "cv.h" ... CvCapture* capture = 0; capture = cvCaptureFromCAM( 0 ); IplImage* frame = cvQueryFrame( capture ); 21 De acordo com o manual, a partir deste ponto do programa, uma imagem capturada da câmera, estará armazenada na variável frame. Se isto for feito sucessivas vezes, teremos uma sequência de imagens, ou seja, um filme. Este algoritmo foi testado em um PC executando o sistema operacional Windows 7 e a captura da imagem foi comprovada. As figuras 8 e 9 ilustram alguns exemplos de imagens que foram capturadas durante o processo de desenvolvimento, utilizando o mesmo algoritmo: Figura 8 – Captura para tentativa de calibração Fonte: Autor, 2014 Figura 9 – Captura do cabeçote Fonte: Autor, 2014 22 6.6 ESCOLHENDO O ALVO E O MARCADOR DO CABEÇOTE Uma vez que o programa em desenvolvimento já tem a habilidade de colher imagens que estão sendo geradas pela câmera, o próximo passo é escolher o alvo e também conseguir identificar a posição do cabeçote, para que assim possamos movimentar o cabeçote até a posição do alvo. Escolher o alvo e o marcador do cabeçote é uma tarefa importante e que necessita de uma análise delicada. É a partir desta escolha que poderá ser definido o tipo de algoritmo necessário para realizar a detecção dos objetos. Existem muitas formas de se identificar um objeto em uma imagem e o OpenCV tem ferramentas para muitas destas (OPENCV DEV TEAM, 2013). Objetos podem ser identificados por: Seu formato: quadrado, redondo, triangular, etc.; Sua cor; Características da imagem, como tamanho, orientação, histograma; Treinamento e comparação com um banco de dados de objetos conhecidos; E possivelmente outras características mais. Para cada tipo de identificação existe um grau de complexidade envolvida no algoritmo. Sendo assim, optou-se pela utilização do método com a menor complexidade: identificar os objetos através de suas cores: Prós: Poderá ser utilizado qualquer formato de objeto; Será identificado independentemente de sua posição, orientação, tamanho, etc.; Não haverá a necessidade de treinamento e nem a composição de um banco de dados para a comparação; Algoritmo simples. Contras: Só poderá haver um objeto de cada cor; O resto da imagem deve ser de cores diferentes das já escolhidas; A eficiência da detecção pode variar com a intensidade e cor da luz à qual os objetos estão expostos; 23 6.7 IDENTIFICANDO O ALVO E O MARCADOR Já definida a forma de identificação dos objetos, escolheu-se as cores separadamente para o alvo e o cabeçote, de forma empírica: havia um ruído de cor nas imagens geradas pela câmera, talvez pela qualidade da mesma, que poderia prejudicar a identificação de algumas cores. Monitorando as imagens geradas, eram colocados diversos objetos com cores variadas e daí observadas quais as cores que geravam menos ruídos. Para o alvo, foi escolhida a cor verde (Figura 11) e para o cabeçote, foi escolhida a cor vermelha. Desta maneira, não poderia haver na imagem nenhum outro objeto com as mesmas cores. Na figura 10 pode-se ver uma ilustração contendo os objetos em suas cores: Figura 10 – Captura da câmera, mostrando o cabeçote em vermelho e o alvo em Fonte: Autor, 2014 A figura 11 apresenta a imagem do objeto usado como alvo. Figura 11 – Objeto usado como alvo Fonte: Autor, 2014 verde 24 6.8 ALGORITMO DE FILTRAGEM DE CORES A informação sobre a cor de um ponto em uma imagem pode ser representada de diversas maneiras, dependendo somente de qual Espaço de Cores (chamado também de Sistema de Cores ou Modelo de Cores) está sendo utilizado (GONZALEZ, 2011). Por exemplo, no espaço de cores RGB, uma cor é representada pela mistura das cores primárias vermelho, verde e azul (Red, Green e Blue). Em outro espaço, o CMYK, a mesma cor é representada pela mistura das cores ciano, magenta, amarelo e preto (Cyan, Magenta, Yellow e Black). Dentro do Modelo RGB, também é possível mapear a variação da mistura das cores primárias que gerarão a nova cor de muitas maneiras. Uma destas maneiras é a representação HSV (Hue, Saturation e Value). Dentro do HSV é possível representar uma cor através de parâmetros de matiz (hue), saturação e valor (também interpretado como brilho). A matiz representa o tipo de cor, ou seja, vermelho, azul, amarelo, violeta, etc. A saturação representa a pureza da cor, quanto menor a pureza, mais cinza será a cor. O parâmetro Value (valor) define o brilho da cor. A figura 12 mostra como representar as cores vermelha e verde em RGB e HSV. Utilizando este mapeamento de cores para trabalhar em nossas imagens, é possível e de forma simples, separar os pontos de uma determinada cor ou faixa de cores e é exatamente o que é preciso para identificar o alvo e o cabeçote. Figura 12 – Representação das cores vermelha e verde em RGB e HSV Fonte: http://www.rapidtables.com/convert/color/rgb-to-hsv.htm em 31/05/2014 Através da Figura 12 fica simples perceber a vantagem em utilizar a representação HSV. Para variar da cor vermelha para a cor verde, foi necessário mudar apenas o valor de H, de 0 para 120. Os valores de saturação e valor continuaram os mesmos para ambas as cores. Já na representação RGB foram necessárias alterações em dois dos três parâmetros (R e G). 25 Todas as imagens capturadas pela câmera, utilizando o algoritmo discutido na seção 6.5 deste trabalho, tem as suas cores representadas em RGB e portanto, precisam ser convertidas para a representação HSV para que seja mais fácil a separação das cores que queremos identificar. Uma operação onde a entrada é uma imagem e a saída é outra imagem é uma operação de processamento de imagens. O OpenCV oferece uma função para a conversão entre estas representações: Primeiro é necessário criar uma imagem vazia para receber o resultado da função. Em seguida utilizar a função cvCvtColor que recebe a imagem original, realiza a conversão para HSV (utilizando o parâmetro CV_BGR2HSV) e salva o resultado na nova variável. IplImage* imgHSV = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3); cvCvtColor(frame, imgHSV, CV_BGR2HSV); Agora que o programa já tem as informações de todas as cores da imagem, é necessário realizar uma filtragem de cores, para que somente a cor de interesse seja mostrada. Todas as outras cores devem ser apagadas da análise, ou seja, neste caso, transformadas na cor preta. No OpenCV, uma função que realiza este processamento é cvInRange. Esta função recebe como entrada a imagem, duas faixas de cores e também a imagem para onde ela deve salvar o resultado. As faixas de cores, definidas em seus parâmetros, são as informações que realizam o filtro. A imagem resultado é formada apenas por pontos pretos e brancos, ou seja, é uma imagem binarizada. Os pontos em preto são pontos onde na imagem original representavam cores que estavam fora das faixas definidas. Já os pontos em brancos representam as cores que pretencem à faixa das cores desejadas. Definir as faixas para filtrar o verde e o vermelho dos objetos que precisamos identificar foi outra tarefa empírica, variando as faixas de valores de H, S e V até que somente o objeto na cor de interesse estivesse à mostra na imagem. Para isto, foi utilizado o MatLab 2012a e em seguida um software disponível nos exemplos da documentação do OpenCV para filtragem HSV. Um exemplo é o da imagem representada na figura 13: 26 Figura 13 – Filtragem de cores Fonte: Autor, 2014 Definidas as faixas de composição das cores do alvo e do cabeçote, aplicamos a função cvInRanges, salvando o resultado em uma nova imagem: IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1); cvInRangeS(imgHSV, cvScalar(lowerH,lowerS,lowerV), cvScalar(upperH,upperS,upperV), imgThresh); Este procedimento deve ser executado separadamente para encontrar e filtrar para uma imagem, somente o alvo e para outra imagem, somente o cabeçote. No final, teremos duas imagens parecidas com a imagem da direita na Figura 13. Variação possível para os parâmetros, no OpenCV: (0 ≤ H ≤ 180); (0 ≤ S ≤ 255); (0 ≤ V ≤ 255). Apenas para referência, as faixas adotadas para filtragem foram: Para o cabeçote (vermelho): (170 ≤ H ≤ 180); (108 ≤ S ≤ 255); (100 ≤ V ≤ 255); Para o alvo (verde): (69 ≤ H ≤ 95); (85 ≤ S ≤ 143); (0 ≤ V ≤ 255); Por se tratar de uma faixa de valores, a detecção apresenta uma certa tolerância quanto a variação de iluminação e cor dos objetos. Repare que na Figura 13, apesar de o reflexo da tampa na mesa também ser de tom esverdeado, este tom está fora da faixa de filtragem e não está sendo considerado. 6.9 DESCOBRINDO O CENTRO DO OBJETO O resultado da seção 6.8 é formado por duas imagens em preto e branco. Cada uma contendo vários pontos possivelmente próximos a um ponto central. 27 O objetivo agora é determinar qual é este ponto central, pois se não houverem outros objetos de cores semelhantes presentes na imagem, este ponto também será o centro do objeto. Momentos de uma imagem são quantidades escalares utilizadas para caracterizar uma função (imagem) e capturar suas características mais significativas (FLUSSER, 2009). Neste trabalho foram utilizados apenas três momentos, que caracterizam o centro de massa dos pontos brancos da imagem. Um para o eixo X, outro para o eixo Y e o terceiro que obtinha a área do objeto. Para o programa em desenvolvimento, a maneira de obter estes dois momentos está descrita no código abaixo: CvMoments *moments = (CvMoments*)malloc(sizeof(CvMoments)); cvMoments(imgThresh, moments, 1); double M10 = cvGetSpatialMoment(moments, 1, 0); // Momento do eixo X double M01 = cvGetSpatialMoment(moments, 0, 1); // Momento do eixo Y double Area = cvGetCentralMoment(moments, 0, 0); // Momento da área M00 // Captura a posição da centróide do objeto, baseado nos momentos da imagem binarizada posX = (M10/Area); posY = (M01/Area); Maiores detalhes sobre a função cvMoments podem ser encontrados na documentação da biblioteca OpenCV (OPENCV DEV TEAM, 2013), porém o importante a ser considerado aqui é que o resultado do código acima, sendo utilizado para processar as imagens em preto e branco do cabeçote e do alvo, é que conseguimos a posição central destes objetos na imagem, em valores de pontos (pixels). Na Figura 14, linha vermelha mostra a posição do cabeçote e a linha azul do alvo. Figura 14 – Identificando a posição dos objetos automaticamente Fonte: Autor, 2014 28 6.10 O PROBLEMA DO MODELO PINHOLE Consideremos novamente as equações (1) e (2) da seção 4: 𝑋 𝑌 Se 𝑥 = −𝑓 𝑍 e 𝑦 = −𝑓 𝑍 , observamos que 𝑋 𝑍 e 𝑌 𝑍 são proporções. Se mantivermos a proporção, variando o valor de Z e os valores de X e Y, teremos como resultado de (1) e (2) os mesmos 𝑥 e 𝑦. Isto é: se aumentarmos a distância de um objeto para a câmera e também aumentarmos sua posição X e Y em relação ao centro ótico, na mesma proporção, a posição do objeto na imagem resultante de duas dimensões, é a mesma (Figura 15). Figura 15 – Diferença de planos Fonte: Autor, 2014 Observe a Figura 16. Iniciando no ponto A para o cabeçote e no ponto B para o alvo, não haveria nenhum problema, pois o cabeçote estaria na posição correta para a câmera, ou seja, estaria acima do alvo. Porém o problema acontece quando o alvo não está próximo ao centro ótico da câmera. Figura 16 – Diferença de planos do cabeçote e do alvo Fonte: Autor, 2014 Se o alvo estiver em B1, a mesa deveria movimentar o cabeçote até o ponto onde ele estivesse logo acima do alvo e esta seria a posição correta, ou seja, A1. Só que ao 29 invés disto, para a câmera, o cabeçote estará acima do alvo quando sua posição for A1’. Para a câmera a vista seria como a da figura 17, seguindo o mesmo posicionamento de objetos da Figura 16: Figura 17 – Diferença de planos do cabeçote e do alvo vista da câmera Fonte: Autor, 2014 Se fossemos utilizar o sistema desta maneira, quando o alvo estivesse fora de alinhamento direto com o centro ótico da câmera, provavelmente estaríamos atuando sobre um espaço vazio ou apenas sobre uma beirada do alvo. 6.11 SOLUÇÃO ADOTADA PARA O PROBLEMA DO MODELO PINHOLE Já temos um modelo representativo da visão da câmera. Mesmo exibindo o problema acima, se utilizarmos as propriedades de semelhança de triângulos e as fórmulas do modelo Pinhole, conhecendo a diferença de distância para a câmera dos planos A (do cabeçote) e B (do alvo), é possível realizar um mapeamento do plano B sobre o plano A ou vice-versa. Porém, existe outro problema: a construção da câmera não é perfeita e as imagens geradas podem não ser também. O centro ótico da imagem pode não ser o mesmo que o centro da imagem. Pode haver distorções no caminho que a luz percorre através das lentes até o sensor ótico. O próprio sensor ótico pode estar inclinado ou fora de centralização com as lentes. Também teríamos problemas se aumentássemos a distância entre os planos, ou inclinássemos a câmera. Tudo teria de ser revolvido novamente. Enfim, para se utilizar o modelo Pinhole com a semelhança de triângulos, seria necessário resolver vários outros problemas. 30 Ao invés disto, este trabalho adota uma solução de calibração por observação, a qual baseia-se na observância da posição detectada pela câmera, para o alvo e para o cabeçote, nos quatro cantos limites da mesa, quando um está sobre o outro, como ilustrado na figura 18: Figura 18 – Exemplo de calibração por observação Fonte: Autor, 2014 A idéia é gerar uma função de transformação de coordenadas, onde uma leitura do ponto verde no plano B, seja transformada em uma leitura do ponto verde no plano A. Traçando uma reta do menor valor de X do ponto vermelho, até o maior valor de X do ponto vermelho e realizando o mesmo para o ponto verde e igualando as duas fórmulas, temos a partir dos dados de exemplo da Figura 18 uma fórmula de conversão das coordenadas do alvo no plano B para coordenadas no plano A, conforme mostrado na figura 19: 𝑥𝑉𝑑 − 52 𝑥𝑉𝑚 − 45 = 146 − 52 156 − 45 (3) 𝑥𝑉𝑚 = 1,18 × 𝑥𝑉𝑑 − 16,4 (4) 31 Figura 19 – Gerando uma fórmula de conversão de coordenadas Fonte: Autor, 2014 Aplicando o mesmo para o eixo Y: 𝑦𝑉𝑑 − 19 𝑦𝑉𝑚 − 3 = 113 − 19 111 − 3 (5) 𝑦𝑉𝑚 = 1,15 × 𝑦𝑉𝑑 − 18,8 (6) Utilizando as coordenadas transformadas do objeto verde, teríamos as duas representações de coordenadas sobre o mesmo plano, ilustrado na figura 20: Figura 20 – Coordenadas convertidas para o mesmo plano Fonte: Autor, 2014 32 6.12 MINIATURIZANDO E EMBARCANDO TODA A SOLUÇÃO A idéia de ter um computador trocando informações com o Arduino que por sua vez controla os motores da mesa é funcional, porém é pouco prática. Ter que ligar o computador, conectar a câmera e o Arduino, conectar a fonte de alimentação à mesa, etc. parece trabalhoso e restritivo. É muito melhor pensar em um sistema embarcado, onde o computador já está embutido na mesa e onde a fonte de alimentação pode ser uma só. O último passo do trabalho é considerar e implementar esta idéia e para isto substituir o Arduino e o computador pessoal (PC) por um sistema miniaturizado. A BeagleBone é um sistema de baixo custo, baseado no processador ARM Cortex A8, com 256MB de memória RAM e muitos recursos de hardware, como entradas analógicas (até 1.8V), I2C, SPI, Seriais e vários GPIOs disponíveis. Ela vem de fábrica com o sistema operacional Linux instalado, rodando a distribuição Ångström. Outra vantagem é que o sistema já vem com o OpenCV pré-instalado. A versão disponível do OpenCV pré instalado no sistema da BeagleBone é mais atual do que o que foi utilizado para gerar os algoritmos de detecção dos objetos. Portanto foi necessário realizar um ajuste no software antes de transferir e compilar para a placa. Para o controle dos motores de passo, a BeagleBone tem um número de GPIOs maior que o suficiente, porém não existe uma biblioteca bem difundida de controle destes motores, como existe para o Arduino. Neste caso foi necessário escrever as rotinas e os códigos para controlar os passos dos motores, a partir do zero. Os códigos gerados podem ser encontrados nos anexos deste trabalho. Para a realização do monitoramento da câmera e controle dos motores, o software foi dividido em dois módulos, cada um realizando exatamente uma destas duas funções. A comunicação entre estes dois módulos foi desenvolvida para ser realizada através de um espaço de memória compartilhada no sistema operacional. Assim, quando o software de detecção alterava a variável que continha informações da posição dos objetos, o software de controle imediatamente tinha acesso à estas novas coordenadas e podia tomar as providências para movimentar o cabeçote até o alvo. Um diagrama do fluxo de informações do algoritmo de controle rodando na BeagleBone pode ser visualizado na Figura 21. O código para o controle dos motores de passo, utilizando a BeagleBone foi disponibilizado para o público em forma de código fonte aberto e pode ser baixado em: https://github.com/zsantolim/beaglebone_stepper 33 Figura 21 – Diagrama do algoritmo Fonte: Autor, 2014 A substituição da placa Arduino pela placa BeagleBone e a conexão de um dos motores, foi ilustrada na figura 22: Figura 22 – Diagrama de conexão de um dos motores à BeagleBone Fonte: Autor, 2014 34 A figura 23 mostra a conexão da BeagleBone na mesa, já conectada aos circuitos de atuação sobre os motores: Figura 23 – BeagleBone conectada e controlando a mesa Fonte: Autor, 2014 A fixação da câmera sobre a mesa pode ser visualizada na figura 24: Figura 24 – Vista para a câmera Fonte: Autor, 2014 35 7 RESULTADOS Todos os passos do desenvolvimento foram muito bem sucedidos e tiveram resultados positivos. Tanto o algoritmo de captura de informações através das imagens da Webcam quanto o algoritmo de controle dos motores de passo desempenharam suas devidas funções e como resultado, a mesa precisa somente ser conectada à uma fonte de energia compatível (12V e 5V) para que em poucos segundos o sistema termine de inicializar e comece a controlar a mesa. A modularização dos algoritmos e o desenvolvimento de uma biblioteca de controle própria para esta aplicação têm o benefício de ser altamente customizáveis. Por exemplo, a biblioteca de controle dos motores de passo permite que facilmente alteremos os motores e em seu lugar sejam instalados outros com características distintas. A biblioteca é configurável para suportar diferentes tipos de motores de passo, com ângulos de passo e tempo de controle diferentes. Também é possível mudar as cores dos alvos, somente definindo uma nova faixa de filtragem no início do código do OpenCV. Todo o resto do programa continua o mesmo. O cabeçote sendo controlado e movimentado até alcançar a posição sobre o alvo, pode ser vista na figura 25: Figura 25 – O cabeçote sendo posicionado automaticamente sobre o alvo Fonte: Autor, 2014 36 8 CONCLUSÃO E TRABALHOS FUTUROS O objetivo multidisciplinar desejado no início do projeto foi alcançado com sucesso. Foram utilizadas muitas técnicas de processamento digital de imagens em conjunto com visão computacional. Além disto houve uso intenso das linguagens C e C++ durante todo o desenvolvimento. Um algoritmo de controle simplificado: observação, ação, medição do erro e correção foi desenvolvido. E ao final, toda a solução foi embarcada em um sistema miniaturizado. Apesar de concluído este trabalho permite diversas melhorias. Uma necessidade do sistema é a realização de auto calibração todas as vezes em que é ligado. Para o funcionamento e testes, as calibrações foram realizadas manualmente, porém, não é difícil de vislumbrar um sistema que contenha calibração automatizada das posições de borda (seção 6.11) dos objetos. O sistema de controle simplificado, realizado sobre a observação da posição do alvo, a posição do cabeçote e o erro entre estas duas distâncias, pode ser substituído por outros paradigmas de controle, como por exemplo o PID ou Fuzzy, pois com o poder de processamento disponível na BeagleBone, estes algoritmos não teriam problemas em ser executados. O sistema pode ser melhorado para a identificação de múltiplos objetos da mesma cor e a escolha, dentre estes objetos, de qual seria o objeto alvo. Se substituído o algoritmo de identificação dos objetos por cor, por outro método de identificação, como por exemplo identificação por forma ou tamanho, teríamos um equipamento capaz de classificar e separar vários objetos, podendo ser utilizado facilmente em aplicações comerciais e industriais. Na verdade, a união de todas as técnicas utilizadas neste trabalho, com algumas modificações e customizações pode ser aplicada em diversas áreas, podendo modernizar e aumentar a capacidade de várias aplicações existentes ou ser a base para inúmeras novas aplicações. 37 REFERÊNCIAS ALMONFREY, Douglas. Introdução à visão computacional. Vitória: Ifes, 2013. BALLARD, Dana Harry. Computer vision. Nova Iorque: Prentice-Hall, 1982. CABRE, T.P.; et al. Project-Based Learning Example: Controlling an Educational Robotic Arm With Computer Vision. IEEE Revista Iberoamericana de Tecnologias del Aprendizaje. IEEE. Vigo, v. 8, p. 135-142, agosto. 2013. COLEY, Gerald. BeagleBone rev. A6 System Reference Manual. BeagleBoard.org, 2012. Disponível em: <http://beagleboard.org/>. Acesso em 15. Nov. 2013. FLUSSER, Jan; ZITOVA, Barbara; SUK, Tomas. Moments and Moment Invariants in Pattern Recognition. Chichester: Wiley, 2009. GARIBOTTO, G.; et al. Computer vision control of an intelligent forklift truck. In: IEEE CONFERENCE ON INTELLIGENT TRANSPORTATION SYSTEM. IEEE. 1997. Boston. Anais... p. 589 – 594. GUIMARÃES, Luciano. A Cor Como Informação. 3 ed. São Paulo: Annablume, 2004. JEONGWOON Kim; SHIM, D.H. A vision-based target tracking control system of a quadrotor by using a tablet computer. In: INTERNATIONAL CONFERENCE ON UNMANNED AIRCRAFT SYSTEMS (ICUAS). IEEE. 2013. Atlanta. Anais... p. 1165 1172. OPENCV DEV TEAM. OpenCV Documentation. 2013. Disponível em: <http://docs.opencv.org>. Acesso em 7. Nov. 2013. PAPANIKOLOPOULOS, N.P. Integrating computer vision and control for visionassisted robotic tasks. In: THE 1995 AMERICAN CONTROL CONFERENCE. IEEE. 1995. Seatle. Anais... v.1, p. 904 – 908. SMITH, J.S.; YU, R. ; SARAFIS, I. ; LUCAS, J. Computer vision control of an underwater manipulator. In: OCEANS ENGINEERING FOR TODAY'S TECHNOLOGY AND TOMORROW'S PRESERVATION. IEEE. 1994. Brest. Anais... v.1, I/187 – I/192. STMICROELECTRONICS. L297 – Stepper Motor Controllers. Itália, 2001. Disponível em <http://www.st.com>. Acesso em 7. Set. 2013. STMICROELECTRONICS. L298 – Dual Full-Bridge Drivers. Itália, 2000. Disponível em <http://www.st.com>. Acesso em 7. Set. 2013. GONZALEZ, Rafael C.; WOODS, Richard E. Processamento Digital de Imagens. ed. 3. São Paulo: Pearson Education - Br, 2011. 640p. 38 APÊNDICE A CÓDIGOS FONTE PARA CONTROLE DOS MOTORES DE PASSO Arquivo StepperMotor.h #ifndef _STEPPERMOTOR_H_ #define _STEPPERMOTOR_H_ #include <stdio.h> #include <stdlib.h> #include "gpio.h" #define #define #define #define STEPPER_STATE_A STEPPER_STATE_B STEPPER_STATE_C STEPPER_STATE_D 0 1 2 3 typedef struct StepperMotor { int state; // Motor sequence state int gpioA1; // GPIO number for positive side of the first winding int gpioA2; // GPIO number for negative side of the first winding int gpioB1; // GPIO number for positive side of the second winding int gpioB2; // GPIO number for negative side of the second winding float stepSize; // The step size in degrees for this Stepper Motor int speed; // The desired RPM speed for the Stepper Motor } StepperMotor_t; void StepperMotor_setup(StepperMotor_t *motor); void StepperMotor_stop(StepperMotor_t *motor); void StepperMotor_step(StepperMotor_t *motor, int steps); #endif Arquivo StepperMotor.C #include "StepperMotor.h" void StepperMotor_setup(StepperMotor_t* motor) { int port, pin; // Initializes the ports and pins needed // Pin gpioA1 port = (motor->gpioA1) / 32; pin = (motor->gpioA1) % 32; setup_gpio_mem_map(port); gpio_setup(port,pin,OUTPUT); // Pin gpioA2 port = motor->gpioA2 / 32; pin = motor->gpioA2 % 32; setup_gpio_mem_map(port); gpio_setup(port,pin,OUTPUT); // Pin gpioB1 port = motor->gpioB1 / 32; pin = motor->gpioB1 % 32; setup_gpio_mem_map(port); gpio_setup(port,pin,OUTPUT); // Pin gpioB2 port = motor->gpioB2 / 32; pin = motor->gpioB2 % 32; setup_gpio_mem_map(port); gpio_setup(port,pin,OUTPUT); 39 // Initializes the state motor->state = 0; } void StepperMotor_stop(StepperMotor_t *motor) { gpio_write((motor->gpioA1)/32,(motor->gpioA1)%32,0); gpio_write((motor->gpioA2)/32,(motor->gpioA2)%32,0); gpio_write((motor->gpioB1)/32,(motor->gpioB1)%32,0); gpio_write((motor->gpioB2)/32,(motor->gpioB2)%32,0); } void StepperMotor_step(StepperMotor_t* motor, int steps) { float stepsPerRev, periodPerStep; long int uPeriodPerStep; int direction, stepsCount; // Calculates the number of steps for one revolution stepsPerRev = (360.0 / (motor->stepSize)); // Steps per revolution periodPerStep = (60.0 / (stepsPerRev * motor->speed)); // Period per step, based on the Steps Per Revolution and the Motor Speed uPeriodPerStep = (long int) (periodPerStep * 1000000); // Period per step in microseconds if (steps > 0) { direction = 1; stepsCount = steps; } else if (steps < 0) { direction = 0; stepsCount = -steps; } else return; while (stepsCount) { switch (motor->state) { case STEPPER_STATE_A: gpio_write((motor->gpioA1)/32,(motor->gpioA1)%32,1); gpio_write((motor->gpioA2)/32,(motor->gpioA2)%32,0); gpio_write((motor->gpioB1)/32,(motor->gpioB1)%32,0); gpio_write((motor->gpioB2)/32,(motor->gpioB2)%32,1); break; case STEPPER_STATE_B: gpio_write((motor->gpioA1)/32,(motor->gpioA1)%32,0); gpio_write((motor->gpioA2)/32,(motor->gpioA2)%32,1); gpio_write((motor->gpioB1)/32,(motor->gpioB1)%32,0); gpio_write((motor->gpioB2)/32,(motor->gpioB2)%32,1); break; case STEPPER_STATE_C: gpio_write((motor->gpioA1)/32,(motor->gpioA1)%32,0); gpio_write((motor->gpioA2)/32,(motor->gpioA2)%32,1); gpio_write((motor->gpioB1)/32,(motor->gpioB1)%32,1); gpio_write((motor->gpioB2)/32,(motor->gpioB2)%32,0); break; case STEPPER_STATE_D: gpio_write((motor->gpioA1)/32,(motor->gpioA1)%32,1); gpio_write((motor->gpioA2)/32,(motor->gpioA2)%32,0); gpio_write((motor->gpioB1)/32,(motor->gpioB1)%32,1); gpio_write((motor->gpioB2)/32,(motor->gpioB2)%32,0); break; default: return; break; } if (direction) { motor->state++; motor->state %= 4; } else { if (motor->state) motor->state--; else motor->state = STEPPER_STATE_D; 40 } stepsCount--; usleep(uPeriodPerStep); } } Arquivo main.c do Controlador dos Motores: #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include #include #include #include <stdio.h> <stdlib.h> "StepperMotor.h" "../../trab.h" #define PROPORCAO_X 0.265 #define PROPORCAO_Y 0.03357 StepperMotor_t motorY; StepperMotor_t motorX; void main(int argc, char *argv[]) { int shmId; trackingInfo_t *tracking; key_t key = SHARED_KEY; float erroX, erroY, aux; /* * Cria o segmento compartilhado de memória */ if ((shmId = shmget(key,sizeof(trackingInfo_t), IPC_CREAT | 0666)) < 0) { perror("Erro criando o segmento compartilhado de memoria: shmget"); exit(1); } /* * Atrela o segmento compartilhado à nossa variável */ if ((tracking = (trackingInfo_t *) shmat(shmId, NULL, 0)) == (trackingInfo_t *) -1) { perror("Erro atrelando o segmento de memoria aa variavel"); exit(1); } motorX.gpioA1 = 2*32 + motorX.gpioA2 = 2*32 + motorX.gpioB1 = 2*32 + motorX.gpioB2 = 2*32 + motorX.stepSize = 7.5; motorX.speed = 120; 10; 11; 12; 13; motorY.gpioA1 = 2*32 + motorY.gpioA2 = 2*32 + motorY.gpioB1 = 2*32 + motorY.gpioB2 = 2*32 + motorY.stepSize = 1.8; motorY.speed = 120; 6; 7; 8; 9; printf("Inicializando o motor...\n\r"); StepperMotor_setup(&motorX); StepperMotor_setup(&motorY); 41 while(1) { sleep(1); erroY = ((1.149*tracking->targetY) - 18.831 - tracking->trackedY); aux = erroY/PROPORCAO_Y; if (erroY > 2 || erroY < -2) StepperMotor_step(&motorY,(int)aux); else StepperMotor_stop(&motorY); erroX = ((1.180*tracking->targetX) - 16.40 - tracking->trackedX); aux = erroX/PROPORCAO_X; if (erroX > 2 || erroX < -2) StepperMotor_step(&motorX,(int)aux); else StepperMotor_stop(&motorX); } StepperMotor_stop(&motorX); StepperMotor_stop(&motorY); exit(0); } 42 APÊNDICE B CÓDIGOS FONTE PARA IDENTIFICAÇÃO DE OBJETOS PELA WEBCAM Arquivo main.c do programa de captura e identificação de objetos pela webcam: #include #include #include #include #include #include #include #include #include #include <time.h> <iostream> <sys/types.h> <sys/ipc.h> <sys/shm.h> <stdio.h> <stdlib.h> <fcntl.h> <signal.h> <fstream> //bibliotecas OpenCV #include "cv.h" #include "highgui.h" #include "ledcontrol.h" #include "trab.h" using namespace std; //#define VIDEO_CAPTURE "Capture0.avi" int lowerH=0; int lowerS=0; int lowerV=0; int upperH=180; int upperS=255; int upperV=255; int targetLowerH=69, targetUpperH=95; int targetLowerS=85, targetUpperS=143; int targetLowerV=0, targetUpperV=255; int trackedLowerH=170, trackedUpperH=180; int trackedLowerS=108, trackedUpperS=255; int trackedLowerV=100, trackedUpperV=255; int posX, posY, lastX, lastY; int targetPosX, targetPosY, targetLastX, targetLastY; int trackedPosX, trackedPosY, trackedLastX, trackedLastY; IplImage* GetThresholdedImage(IplImage* imgHSV); void findObject(IplImage* imgThresh); //This function threshold the HSV image and create a binary image IplImage* GetThresholdedImage(IplImage* imgHSV) { IplImage* imgThresh=cvCreateImage(cvGetSize(imgHSV),IPL_DEPTH_8U, 1); cvInRangeS(imgHSV, cvScalar(lowerH,lowerS,lowerV), cvScalar(upperH,upperS,upperV), imgThresh); IplConvKernel* erodeElement = cvCreateStructuringElementEx(3,3,0,0,CV_SHAPE_RECT); IplConvKernel* dilateElement = cvCreateStructuringElementEx(8,8,3,3,CV_SHAPE_RECT); cvErode(imgThresh,imgThresh,erodeElement); cvDilate(imgThresh,imgThresh,dilateElement); 43 cvReleaseStructuringElement(&erodeElement); cvReleaseStructuringElement(&dilateElement); return imgThresh; } void findObject(IplImage* imgThresh) { // Calcula os "raw moments" de 'imgThresh' // Referência: http://en.wikipedia.org/wiki/Image_moment CvMoments *moments = (CvMoments*)malloc(sizeof(CvMoments)); cvMoments(imgThresh, moments, 1); double M10 = cvGetSpatialMoment(moments, 1, 0); // Momento do eixo X double M01 = cvGetSpatialMoment(moments, 0, 1); // Momento do eixo Y double Area = cvGetCentralMoment(moments, 0, 0); // Momento da área M00 lastX = posX; lastY = posY; // Captura a posição da centróide do objeto, baseado nos momentos da imagem binarizada posX = (M10/Area + lastX)/2; posY = (M01/Area + lastY)/2; // Se a área é pequena, não há objeto *** MUDAR if(Area<50){ posX = lastX; posY = lastY; } free(moments); } int main() { CvCapture* capture=0; char str[150]; int shmId; trackingInfo_t *tracking; key_t key = SHARED_KEY; /* * Cria o segmento compartilhado de memória */ if ((shmId = shmget(key,sizeof(trackingInfo_t), IPC_CREAT | 0666)) < 0) { perror("Erro criando o segmento compartilhado de memoria: shmget"); exit(1); } /* * Atrela o segmento compartilhado à nossa variável */ if ((tracking = (trackingInfo_t *) shmat(shmId, NULL, 0)) == (trackingInfo_t *) -1) { perror("Erro atrelando o segmento de memoria aa variavel"); exit(1); } capture = cvCaptureFromCAM( 0 ); if(!capture){ printf("Capture failure\n"); return -1; } else { 44 cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH, 320); cvSetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT, 240); } IplImage* frame = cvQueryFrame( capture ); //iterate through each frames of the video while(true){ IplImage* frame = cvQueryFrame(capture); if(!frame) { break; } cvSetImageROI(frame,cvRect(62,22,200,160)); frame=cvCloneImage(frame); IplImage* imgHSV = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3); cvCvtColor(frame, imgHSV, CV_BGR2HSV); //Change the color format from BGR to HSV // TRACKED lowerH = trackedLowerH; upperH = trackedUpperH; lowerS = trackedLowerS; upperS = trackedUpperS; lowerV = trackedLowerV; upperS = trackedUpperV; IplImage* imgThresh = GetThresholdedImage(imgHSV); posX = trackedPosX; posY = trackedPosY; lastX = trackedLastX; lastY = trackedLastY; findObject(imgThresh); trackedPosX = posX; trackedPosY = posY; trackedLastX = lastX; trackedLastY = lastY; // TARGET lowerH = targetLowerH; upperH = targetUpperH; lowerS = targetLowerS; upperS = targetUpperS; lowerV = targetLowerV; upperS = targetUpperV; IplImage* imgThreshTarget = GetThresholdedImage(imgHSV); posX = targetPosX; posY = targetPosY; lastX = targetLastX; lastY = targetLastY; findObject(imgThreshTarget); targetPosX = posX; targetPosY = posY; targetLastX = lastX; targetLastY = lastY; tracking->trackedX = trackedPosX; tracking->trackedY = trackedPosY; tracking->targetX = targetPosX; tracking->targetY = targetPosY; printf("Target: (%3d,%3d) Tracker: (%3d,%3d)\n",targetPosX,targetPosY,trackedPosX,trackedPosY); //Clean up used images cvReleaseImage(&imgHSV); cvReleaseImage(&imgThresh); cvReleaseImage(&imgThreshTarget); cvReleaseImage(&frame); } cvDestroyAllWindows(); cvReleaseCapture(&capture); return 0; } Arquivo trab.h: #ifndef _TRAB_H_ #define _TRAB_H_ #define SHARED_KEY 2468 45 typedef struct trackingInfo { int int int int trackedX; trackedY; targetX; targetY; } trackingInfo_t; #endif Arquivo ledcontrol.h #ifndef _LEDCONTROL_H_ #define _LEDCONTROL_H_ #include #include #include #include #include #include #include #include <stdio.h> <stdlib.h> <sys/types.h> <sys/stat.h> <unistd.h> <assert.h> <fcntl.h> <sys/mman.h> void ledControl(int led, int state); #endif Arquivo ledcontro.cpp #include "ledcontrol.h" void ledControl(int led, int state) { FILE *f; char buffer[128]; if ((led > 3) || (led < 0)) return; snprintf(buffer, sizeof(buffer), "/sys/class/leds/beaglebone::usr%u/trigger", led); if ((f = fopen(buffer, "w")) == NULL) { perror("Cannot open led trigger file."); //assert(0); } if (state) fprintf(f, "default-on"); else fprintf(f, "none"); fclose(f); }