UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM AUTOMAÇÃO INDUSTRIAL - PGAI

Livre

0
0
134
1 year ago
Preview
Full text

  

UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC

CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT

DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE

PROGRAMA DE PốS-GRADUAđấO EM AUTOMAđấO INDUSTRIAL - PGAI

  Formação: Mestrado em Automação Industrial

  

DISSERTAđấO DE MESTRADO OBTIDA POR

Fabrício Noveletto

  

Desenvolvimento de um Ambiente de Programação

Visual Orientado a Objetos para Robôs Móveis

  Apresentada em 10 / 10 / 2003 perante a banca examinadora:

  

Prof. Dr. Antonio Heronaldo de Sousa - Presidente - CCT/UDESC

Prof. PhD. Alcy Rodolfo Carrara - PUC/PR Prof. PhD. Marcelo da Silva Hounsell – CCT/UDESC Prof. Dr. Silas do Amaral - CCT/UDESC

  

UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC

CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT

DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE

PROGRAMA DE PốS-GRADUAđấO EM AUTOMAđấO INDUSTRIAL - PGAI

  DISSERTAđấO DE MESTRADO

Mestrando: FABRÍCIO NOVELETTO – Engenheiro Eletricista

Orientador: Prof. Dr. ANTONIO HERONALDO DE SOUSA – CCT/UDESC

  

Desenvolvimento de um Ambiente de Programação

Visual Orientado a Objetos para Robôs Móveis DISSERTAđấO APRESENTADA COMO REQUISITO ầ OBTENđấO DO TễTULO DE MESTRE EM AUTOMAđấO INDUSTRIAL DA UNIVERSIDADE DO ESTADO DE SANTA CATARINA, CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT, ORIENTADA PELO PROFESSOR DR. ANTONIO HERONALDO DE SOUSA.

  

Joinville, 10 de Outubro de 2003.

  AGRADECIMENTOS

  Ao amigo e orientador, Professor Antonio Heronaldo de Sousa por sua dedicação e sabedoria no acompanhamento em todos estes anos de estudo. A professora Regina de Felice Souza pelo seu incentivo, apoio e presteza. Ao Professor Marcelo da Silva Hounsell por suas significantes contribuições no decorrer deste trabalho. A todos os professores do Departamento de Engenharia Elétrica e a coordenação do Mestrado em Automação Industrial. A minha esposa Elaine por seu apoio irrestrito durante toda esta jornada. A família de minha esposa e toda minha família por estar sempre ao meu lado em todos os momentos de minha vida, em especial meu pai Ademar, minha mãe

  Eronildes e minha irmã Alessandra.

  Ao amigo Adriano Bresolin pelo companheirismo em todos estes anos de estudo. Por fim, agradeço a todos que de uma forma ou outra colaboraram para que este trabalho terminasse com sucesso.

  SUMÁRIO

SUMÁRIO ................................................................................................................... I

RESUMO ..................................................................................................................IV ABSTRACT................................................................................................................V INTRODUđấO GERAL ............................................................................................

  1 Motivação...................................................................................................... 1 Objetivo......................................................................................................... 1 Justificativa ................................................................................................... 2 Delimitação ................................................................................................... 3 Panorama geral da dissertação .................................................................... 3

  CAPÍTULO 1: ROBÔS MÓVEIS

  Introdução ..................................................................................................... 5

  1.1. Considerações iniciais........................................................................ 5

  1.2. Evolução dos robôs móveis ............................................................... 6

  1.3. Robôs móveis e suas aplicações ..................................................... 10

  1.3.1. Robôs industriais ................................................................... 10

  1.3.2. Robôs de serviço................................................................... 11

  1.3.3. Robôs de campo ................................................................... 11

  1.4. Característica dos robôs móveis ...................................................... 12

  1.5. O Robô Khepera .............................................................................. 13

  1.5.1. Motores e controle do motor.................................................. 15

  1.5.2. Sensores infravermelhos de proximidade e luz ambiente ..... 18

  1.5.3. O protocolo de comunicação serial ....................................... 22

  1.6. Linguagens de programação para robôs móveis ............................. 23

  1.6.1. Ambientes de programação para o robô Khepera................. 24

  CAPễTULO 2: PROGRAMAđấO VISUAL ORIENTADA A OBJETOS

  Introdução ........................................................................................................ 29

  2.1. Evolução das linguagens de programação ...................................... 29

  2.2. O paradigma da programação orientada a objetos .......................... 32

  2.3. Elementos da programação orientada a objetos .............................. 33

  2.3.1. Abstração .............................................................................. 34

  2.3.2. Encapsulamento.................................................................... 34

  2.3.3. Herança ................................................................................. 35

  2.3.4. Polimorfismo.......................................................................... 36

  2.4. O conceito de objeto ........................................................................ 37

  2.4.1. Métodos internos e variáveis públicas ................................... 40

  2.5. O conceito de classes ...................................................................... 41

  2.6. Hereditariedade................................................................................ 44

  2.7. Considerações finais sobre programação orientada a objetos......... 46

  2.8. Linguagem de Programação Visual ................................................. 47

  2.9. Alguns aspectos sobre o uso de linguagens visuais ........................ 48

  2.10. Linguagens visuais orientadas a objetos.......................................... 51

  CAPÍTULO 3: DESENVOLVIMENTO DO AMBIENTE K++

  Introdução ................................................................................................... 53

  3.1. A linguagem de programação K++................................................... 53

  3.1.1. Hierarquia e descrição de classes......................................... 54

  3.1.2. Tipologia e representação de dados ..................................... 56

  3.1.3. Estruturas de controle ........................................................... 58

  3.1.4. Operações dedicadas............................................................ 59

  3.2. O ambiente de programação K++ .................................................... 61

  3.3. Implementação do ambiente K++..................................................... 63

  3.4. As classes Serial e Khepera ............................................................ 64

  3.4.1. A classe Serial....................................................................... 64

  3.4.2. A classe Khepera .................................................................. 65

  3.4.2.1. Descrição dos métodos da classe Khepera................ 67

  CAPÍTULO 4: RESULTADOS OBTIDOS

  4.3.1.1. Definindo a classe Explorador .................................... 92

  Melhorias futuras e possíveis desdobramentos ........................................ 104

  CONCLUSÃO........................................................................................................ 103

  4.4. Programação avançada usando o Visual C++ ................................. 99

  4.3.2.2. Definindo o objeto BEETLE ........................................ 96

  4.3.2.1. Definindo a classe Besouro ........................................ 95

  4.3.2. Exemplo 2 – Robô que se move em direção à luz ................ 94

  4.3.1.2. Definindo o objeto AGV .............................................. 93

  4.3.1. Exemplo 1 – Robô Explorador............................................... 92

  Introdução ................................................................................................... 81

  4.3. Programação orientada a objetos usando o K++ ............................. 92

  4.2.4. Exemplo 4 – Robô Explorador............................................... 91

  4.2.3. Exemplo 3 – Robô Explorador............................................... 88

  4.2.2. Exemplo 2 – Robô AGV......................................................... 87

  4.2.1. Exemplo 1 – Robô AGV......................................................... 85

  4.2. Programação procedimental usando o K++ ..................................... 84

  4.1. Usando o ambiente de programação K++........................................ 81

  REFERÊNCIAS BIBLIOGRÁFICAS ...................................................................... 105 ANEXOS................................................................................................................ 109

  RESUMO

  Este trabalho apresenta um estudo sobre o desenvolvimento de um ambiente de programação para robô móvel, chamado K++. Foi usado como base para o desenvolvimento do ambiente o paradigma da programação orientada a objetos, conjuntamente com a programação visual.

  A principal característica deste ambiente é usar, em conjunto, estruturas gráficas e estruturas textuais para melhor representar dados e algoritmos. O K++ combina características como a reusabilidade da programação orientada a objetos e a acessibilidade da programação visual. O uso de estruturas visuais orientadas a objetos, melhoram a qualidade e a acessibilidade das informações trocadas no desenvolvimento de algoritmos para robôs móveis.

  Além disso, através de uma classe desenvolvida para implementar a comunicação com o robô móvel, o ambiente K++ permite simulações em tempo real. Neste sentido, os resultados dos testes com algoritmos desenvolvidos com o K++ foram amplamente satisfatórios.

  ABSTRACT

  This work presents a study on the development of a environment programming for mobile robots, called K++. It was used as base for the development of the environment the paradigm of the object-oriented programming, jointly with to visual programming.

  The main characteristic of this environment is to use, together, structures graphs and textual structures for best to represent data and algorithms. K++ combines characteristics as the reusability of the object-oriented programming and the accessibility of the visual programming.

  The use of visual structures object-oriented, they improve the quality and the accessibility of the information changed in the development of algorithms for mobile robots. Besides, through a class developed to implement the communication with the mobile robot, the K++ environment allows simulations in real time. In this sense, the results of the tests with algorithms developed with K++, they were thoroughly satisfactory.

  INTRODUđấO GERAL

  • Motivação

  Os robôs móveis estão cada vez mais presentes dentro das empresas e em outras diversas áreas, executando as mais variadas tarefas. Mas apesar do crescente número de aplicações onde é feito o uso da robótica móvel, a tarefa de programar um robô ainda oferece bastante dificuldade. Além da tecnologia sofisticada do próprio robô, os softwares empregados na sua programação são bastante complexos e exigem um alto grau de conhecimento relacionado à linguagem de programação. Sendo assim, a dificuldade em programar robôs faz com que menos pessoas possam realmente explorar as potencialidades dos robôs e de suas aplicações.

  • Objetivo Este trabalho tem por objetivo desenvolver uma linguagem de programação para robôs móveis, que minimize as dificuldades encontradas na maioria das linguagens de programação usadas para este propósito. Com isto objetiva-se aumentar o universo de pessoas capazes de desenvolver aplicações para robôs móveis. Para este trabalho utilizou-se como referência o robô móvel Khepera.

  Desta forma propôs-se utilizar os conceitos da programação orientada a objetos em conjunto com os conceitos da programação visual. A utilização destes conceitos deu origem a uma nova linguagem de programação visual orientada a objetos chamada de K++ (em alusão ao robô Khepera).

  A idéia central da linguagem K++ é combinar as principais características obtidas com o uso da programação visual e da programação orientada a objetos: a acessibilidade e a reusabilidade, respectivamente.

  Mais especificamente, além do desenvolvimento de um ambiente de programação para robôs móveis, o presente trabalho objetivou a implementação dos mecanismos de interação com um robô real, através da utilização das estruturas da linguagem K++.

  • Justificativa O ambiente de programação visual orientado a objetos para robôs móveis

  K++ se justifica pela necessidade do aprimoramento e desenvolvimento de novas ferramentas que possam auxiliar a programação e o uso desses robôs, uma vez que há um crescente número de estudos envolvendo o uso dos robôs móveis. (NOVELETTO, 2003a) (NOVELETTO, 2003b) (NOVELETTO, 2003c).

  Uma das dificuldades envolvidas na pesquisa de robôs móveis está no custo do próprio robô. O seu alto custo acaba muitas vezes inviabilizando bons projetos. Neste sentido, optou-se usar para a validação do ambiente K++ o robô móvel Khepera, que possui funcionalidades semelhantes às de robôs móveis de maior porte, sendo amplamente usado na comunidade acadêmica e científica. A grande vantagem do uso de robôs móveis de pequeno porte para o estudo e o desenvolvimento de aplicações está justamente em seu tamanho e custo reduzidos, pois permite a criação de ambientes para simulações com maior facilidade e com menor custo (MONDANA, 1993).

  Em geral a programação desse tipo de robô é realizada de forma textual, através do compilador GNU C ou com o uso de ambientes de programação visual como o LabVIEW® e o MatLab®. Um dos problemas no uso da programação textual está no elevado nível de envolvimento com detalhes intrínsecos à linguagem C. Já com o uso dos ambientes de programação visuais citados, por serem softwares fechados, ocorre uma perda na flexibilidade de programação do robô, uma vez que apenas o uso das bibliotecas desses ambientes limita o desenvolvimento de aplicações para o robô. Neste sentido, o K++ por ser um software aberto, permite que futuras modificações possam ser feitas de acordo com as necessidades do projeto.

  O K++ foi desenvolvido com a ferramenta de programação Visual C++, que usa a linguagem C++, dando suporte a programação orientada a objetos. Além disso, essa ferramenta visual permite que as alterações no software possam ser feitas com maior agilidade (KRUGLINSKI, 1996).

  No ambiente proposto (K++), o uso do paradigma da programação orientada a objetos em conjunto com a programação visual possibilita o reuso de funções e minimiza os erros de programação, garantindo desta forma um maior grau de produtividade no desenvolvimento de aplicações para o robô.

  O K++, por ser uma linguagem visual, usa uma simbologia gráfica para representar dados e classes e a programação é feita de forma estruturada baseada em arranjos gráficos. Além disso, a linguagem K++ utiliza ícones para representar as operações dedicadas efetuadas pelo robô.

  O desenvolvimento de algoritmos de forma visual possibilita a ocultação de detalhes de implementação, focando a programação no nível da aplicação. Já o uso de ícones e de elementos visuais de programação possibilita a criação de algoritmos de maneira bastante produtiva, facilitando a compreensão do algoritmo.

  • Delimitação

  A delimitação da pesquisa realizada neste trabalho se restringe ao desenvolvimento de uma linguagem visual de programação orientada a objetos para robôs móveis. Especificamente o ambiente permitirá que as aplicações desenvolvidas com o K++ sejam executadas diretamente em um robô Khepera real. Além disto, está previsto para um trabalho futuro, a geração do código em linguagem C relativo ao algoritmo desenvolvido no K++, para que o robô possa operar em modo autônomo.

  • Panorama geral da dissertação

  No capítulo 1 são feitas algumas considerações que têm por objetivo proporcionar uma visão mais ampla a cerca dos robôs móveis e suas aplicações. Com base no conhecimento dos aspectos gerais que englobam a robótica móvel, é possível compreender melhor a real necessidade de se criar ferramentas de desenvolvimento de aplicações.

  O capítulo 2 faz um breve apanhado sobre os principais tipos de linguagens de programação, e dá a sustentação teórica necessária para melhor compreender os objetivos embutidos na escolha da programação visual em conjunto com a programação orientada a objetos como plataforma para este trabalho.

  O capítulo 3 aborda os aspectos envolvidos no desenvolvimento do ambiente de programação visual orientado a objetos para robô móvel, chamado de K++. Estes aspectos contemplam a idéia de se usar os paradigmas da programação orientada a objetos em conjunto com a programação visual para melhorar o desenvolvimento de aplicações para robôs móveis com simulação em tempo real usando o próprio robô. Além do uso desses paradigmas de programação, o desenvolvimento do ambiente também leva em consideração os aspectos psicológicos de aprendizagem envolvidos no processo.

  O capítulo 4 mostra a implementação de algoritmos através do ambiente de programação K++. Neste capítulo também são abordados alguns aspectos sobre a utilização do ambiente K++, bem como, todo o processo de criação de algoritmos usando a programação procedimental e a programação orientada a objetos neste ambiente. Por fim, serão apresentados alguns aspectos sobre o uso de programação avançada para o desenvolvimento de novos métodos a partir do Visual C++.

  Encerrando o trabalho, são relatadas as conclusões do trabalho e sugeridas as propostas para trabalhos futuros.

  CAPÍTULO 1 ROBÔS MÓVEIS Introdução

  As considerações feitas neste capítulo têm por objetivo proporcionar uma visão mais ampla a cerca dos robôs móveis e suas aplicações. Com base no conhecimento dos aspectos gerais que englobam a robótica móvel, é possível compreender melhor a real necessidade de se criar ferramentas de desenvolvimento de aplicações.

1.1 Considerações iniciais

  De acordo com a Robotic Industries Association (RIA) um robô pode ser definido como sendo um manipulador programável multifuncional, capaz de mover materiais, partes, ferramentas ou dispositivos específicos através de movimentos variáveis programados para realizar uma variedade de tarefas (RUSSEL, 1995).

  O termo robô se origina da palavra tcheca robota, que significa trabalho

  

escravo. Este termo foi usado pela primeira vez em 1921 na peça teatral Rossum’s

Universal Robots, escrita pelo escritor tcheco Karel Capek (ASIMOV, 1994). Desde

  então a palavra robô vem sendo utilizada para se referir às máquinas que executam trabalhos para ajudar as pessoas.

  Ao longo da história o homem evoluiu criando mecanismos para facilitar sua vida. Neste sentido, os robôs vêm cumprindo um papel importante na vida do homem moderno estando presentes em um número cada vez maior principalmente dentro das indústrias.

  A competitividade do mercado estabelece a necessidade de se produzir com maior velocidade, maior qualidade e menores custos, o que justifica a crescente utilização de robôs dentro dos processos industriais. Outra importante aplicação na área de robótica é o uso de robôs móveis para a execução de tarefas consideradas de risco para o homem. Tarefas como a exploração de ambientes insalubres, manuseio de material radioativo, localização de minas terrestres e submarinas, são alguns exemplos das aplicações para os robôs móveis.

1.2 Evolução dos robôs móveis

  No final da década de 40, o professor W. Grey Walter, um renomado neurofisiologista, construiu duas tartarugas eletromecânicas bastante avançadas para aquela época (DI PAOLO, 1998). Seu interesse era estudar o comportamento destes robôs móveis, que por ele foram denominadas de tartarugas. Do ponto de vista mecânico e eletrônico, estas tartarugas eram bastante simples: os movimentos eram feitos por três rodas montadas em triciclo, sendo duas de propulsão e uma de direção, e eram comandadas por motores elétricos independentes para cada uma delas. Os sentidos eram determinados por um sistema de sensoriamento bastante simples, formado por um sensor de luz e um sensor de contatos, montados externamente. A alimentação de energia era fornecida por uma bateria comum, montada na parte de trás da tartaruga, e uma carapaça de plástico abrigava e protegia todo o conjunto. A figura 1.1 mostra uma tartaruga sem a carapaça.

Figura 1.1. Tartaruga eletromecânica desenvolvida por Walter O processador a bordo das tartarugas era extremamente simples: um circuito analógico com apenas duas válvulas eletrônicas, que comandavam os motores das rodas e de direção a partir da informação dos sensores. As tartarugas podiam fazer apenas duas coisas: evitar obstáculos grandes, recuando quando batia em algum, e seguir alguma fonte de luz. Quando a fonte de luz era muito intensa, o robô recuava, ao invés de avançar.

  Apesar dos notáveis estudos desenvolvidos por W. Grey Walter, alguns autores identificam como o primeiro robô móvel construído, um robô desenvolvido pelo Stanford Research Institute em 1968, chamado Shakey (GROOVER, 1988). Este robô tinha uma grande variedade de sensores, que incluíam uma câmara de vídeo e sensores de toque e navegava entre as salas do laboratório, enviando sinais de rádio a um computador, que permitia efetuar algumas tarefas como empurrar caixas e evitar obstáculos (NITZAN, 1985). O robô Shakey, ilustrado na figura 1.2, tinha uma unidade de processamento embarcada que coletava os sinais sensoriais e os enviava para um computador remoto que executava o processamento, transmitindo ao robô o comando que geraria a ação desejada.

Figura 1.2. Robô Shakey Em 1977 foram desenvolvidos alguns trabalhos com o veículo StanfordCart do Stanford Artificial Intelligence Laboratory (BOTELHO, 1996). O StanfordCart trabalhava em uma área plana, com obstáculos colocados separadamente e utilizava um sistema de navegação baseado no "parar e seguir", parando e fazendo leitura de seus sensores a cada metro percorrido, realizando o planejamento da rota a seguir (BRUMIT, 1992).

  Durante os anos 80 vários trabalhos foram desenvolvidos em todo o mundo, usando robôs baseados em reconhecimento de imagens através da visão. Outro importante estudo sobre robôs móveis foi o desenvolvimento do robô Khepera, da empresa K-Team, no Microcomputing Laboratory do Swiss Federal Institute of

  

Technology, com o apoio de outras entidades de pesquisa da Europa (MONDADA,

  1993). O robô Khepera tem a capacidade de desviar-se de obstáculos e seguir ou evitar fontes luminosas, além de permitir a utilização de algumas extensões, como sistemas de visão, rádio controle e um pequeno manipulador. Dado o seu tamanho extremamente reduzido e seu custo relativamente baixo, comparado a outros robôs no mercado, o robô Khepera tornou-se um sucesso dentro das universidades em várias partes do mundo (BOTELHO, 1996).

  Atualmente, principalmente nas grandes indústrias, tem-se destacado a utilização dos AGV’s (Automated Guided Vehicle), podendo executar desde tarefas simples de transporte, até soluções de transporte mais complexas.

  Uma outra aplicação muito importante para os robôs móveis está no campo da exploração espacial. Um exemplo deste tipo de robô é o robô Sojourner, visto na figura 1.3, que realizou uma das maiores façanhas da pesquisa do espaço pelo homem: a exploração à distância do planeta Marte.

Figura 1.3. Robô espacial Soujouner

  Atualmente o grande destaque entre os robôs móveis é o Honda Humanoid

  

Robot, mostrado na figura 1.4 (MAIOR, 1998). Este robô se assemelha aos seres

  humanos, possuindo cabeça, tronco e membros, e possui um sofisticado sistema de equilíbrio, fazendo com que o robô possa subir escadas, se manter em pé e até mesmo chutar uma bola.

Figura 1.4. Robô humanóide Honda

1.3 Robôs móveis e suas aplicações

  Trabalhar com robôs móveis é uma tarefa bastante complexa, em que é necessário um alto grau de conhecimento sobre todos os aspectos que envolvem a aplicação. As tarefas realizadas por um robô móvel podem variar de aplicações menos complexas, como o transporte de carga dentro de uma planta industrial, a até sofisticados sistemas de exploração espacial. A interação do robô com o ambiente (os diversos sensores necessários, grau de segurança, etc) e o tipo do ambiente (planos, buracos, obstáculos fixos e móveis entre outros) determinam o grau de complexidade da aplicação.

  Atualmente no mercado mundial de robôs móveis, existem basicamente três tipos de robôs móveis, que podem ser divididos em industriais, de serviço e de campo.

1.3.1 Robôs industriais

  Os robôs industriais são geralmente plataformas móveis que executam tarefas pesadas, como, por exemplo, carregar grande quantidade de materiais e pintar grandes aviões, e geralmente, não têm uma autonomia muito grande (MONDANA, 1996). Um dos principais métodos de navegação utilizado por esse tipo de robô é através do uso de linhas pintadas no chão, que são usadas para definir o caminho que o robô deve seguir. Um exemplo deste tipo de robô industrial é o AGV, ilustrado na figura 1.5, e pode ser guiado de várias formas, sendo que as mais comuns são as trilhas pintadas no chão da fábrica e as trilhas magnéticas. Além disso, existem também os AGV’s guiados por laser, que são indicados para situações onde a estrutura da fábrica inviabiliza o uso de sensores ópticos e magnéticos e por fim, os AGV’s guiados por GPS, quando a aplicação exige transporte em ambiente externo.

Figura 1.5. Exemplo de uma célula de manufatura usando um AGV

  1.3.2 Robôs de serviço

  A robótica de serviço é reconhecida como uma importante área de aplicação para um futuro próximo. Sistemas de robótica móvel autônoma que executam tarefas de serviço podem ser utilizados por todos os tipos de aplicação como transporte, manipulação, exploração, vigilância, entre outros, em ambientes estruturados e com um mínimo de conhecimento prévio destes (FIRBY, 1993). Os robôs de serviço são utilizados principalmente para carregar materiais leves, para limpeza e para vigilância, como por exemplo, na limpeza nos metrôs da França.

  1.3.3 Robôs de campo

  Os robôs de campo são desenvolvidos para realizar tarefas em ambientes não estruturados e geralmente perigosos. Estes robôs têm evoluído muito nos últimos anos. Suas aplicações são a exploração espacial, mineração, limpeza de acidentes nucleares, desativação de minas terrestres (MÄCHLER, 1995) e submarinas, exploração submarina a grandes profundidades, navegação em auto-estradas, exploração de vulcões, e muitas outras (MONDANA, 1996). O sistema de navegação destes robôs, principalmente os usados em exploração espacial, unem a tele- operação com comportamentos reativos.

  Além das três categorias principais, pode-se dizer que também existem robôs usados para pesquisa, e são usados principalmente pela área acadêmica para o desenvolvimento de novas tecnologias. Muitas empresas que produzem robôs comerciais vendem versões para pesquisa de seus modelos. Um exemplo deste tipo de robô é o Khepera da empresa K-Team, que é amplamente utilizado na comunidade acadêmica.

1.4 Característica dos robôs móveis

  Basicamente um robô móvel é composto por um sistema de processamento de dados, sensores e atuadores, onde os dados coletados pelos sensores são processados e enviados ao sistema de processamento que analisa os dados e controla os atuadores (NITZAN, 1985).

  A principal característica de um robô móvel, como o próprio nome já diz, é a sua capacidade de locomoção. É esta capacidade de locomoção que possibilita aos robôs móveis um universo muito grande de aplicações.

  Os robôs móveis possuem três formas básicas de locomoção: rodas, corpos articulados e pernas, podendo utilizar uma ou a associação dessas configurações, geralmente através de motores e atuadores pneumáticos (GROOVER, 1988). A forma de locomoção do robô deve levar em conta vários fatores como a finalidade, o tipo de terreno em que opera, fonte de alimentação e autonomia de energia.

  Uma das principais características dos robôs móveis atuais é o uso de sensores externos que os tornam autônomos e capazes de operar em ambientes com obstáculos. Estes sensores permitem ao robô extrair informações do ambiente, levando-o a reagir às mudanças do mesmo de forma inteligente.

  Os sensores podem ser classificados em sensores com contato e sensores sem contato. Os sensores com contato ou táteis são dispositivos que indicam o contato entre eles e algum outro objeto, e são normalmente usados em robôs industriais ou manipuladores. Já os sensores sem contato, como os sensores de visão, ultra-som e infravermelho são os principais tipos de sensores usados na robótica móvel. Os sensores de visão são amplamente utilizados na robótica principalmente para fazer o mapeamento de ambientes. Já os sensores de ultra-som são usados na robótica para localização de superfície de objetos, determinação da posição do robô em um dado ambiente, esquemas de navegação e para detecção de obstáculos. Alguns robôs como o robô Khepera, por exemplo, também usam sensores infravermelhos para detectar objetos e medir a intensidade de luz ambiente (K-TEAM, 2002).

  A evolução tecnológica dos sensores e atuadores, em conjunto com o processamento dos dados, permitiu o desenvolvimento de uma variedade de robôs apta a lidar com vários tipos de aplicações, melhorando consideravelmente o desempenho e a confiabilidade destas máquinas. A figura 1.6 mostra um esquema básico de um robô móvel, composto de sensores, atuadores, CPU e fonte de energia.

Figura 1.6. Componentes de um robô

1.5 O Robô Khepera

  O Khepera é um robô móvel que possui funcionalidades semelhantes às de robôs móveis de maior porte. Ele pode ser controlado através de um cabo RS-232 conectado a porta serial do PC ou pode rodar em modo autônomo. Geralmente o Khepera é usado conectado ao PC nas fases de implementação e testes de programas. Após esta fase, a aplicação pode ser transferida diretamente para a memória do robô, possibilitando que o robô trabalhe em modo autônomo, ou seja, sem estar conectado ao PC (K-TEAM, 1999a) (K-TEAM, 1999b).

  O Khepera é um robô sofisticado que incorpora em sua unidade de processamento um processador Motorola 68331 de 32 bits funcionando a uma freqüência de 16 MHz e possui uma memória RAM de 256 Kbytes e uma memória EPROM que pode variar de 128 a 256 Kbytes de acordo com o modelo do robô.

  O movimento do robô é obtido através de dois servo-motores DC acoplados a caixas de redução 25:1 com um sofisticado sistema de sensoriamento obtido por sensores magnéticos e sinais de quadratura com uma resolução de 12 pulsos/mm.

  Além disso, o Khepera possui um sistema de baterias recarregáveis, que permitem uma autonomia de 30 minutos em movimento contínuo no modo autônomo.

  O sensoriamento do ambiente é feito por oito sensores infravermelhos (IR) com emissor e receptor no mesmo componente, que permitem a obtenção de valores de proximidade do robô em relação a objetos e níveis de luz ambiente.

  A comunicação com o robô é feita através de uma interface serial RS-232 com velocidade de comunicação de até 38 Kbps. Outra característica importante é a dimensão do robô que mede apenas 55 mm de diâmetro e 30 mm de altura. Também podem ser acrescentados ao módulo básico do robô, módulos para visão, rádio controle e um manipulador. A figura 1.7 mostra um robô Khepera com sistema de visão e garra, operando em modo autônomo (K-TEAM, 1999b).

Figura 1.7. Robô Khepera Com todas essas funcionalidades o robô Khepera possibilita o teste em ambientes reais de algoritmos desenvolvidos em simulações para o planejamento de trajetórias, desvio de obstáculos, processamento de dados sensoriais, inteligência artificial, entre muitos outros (LUND, 1996) (FLOREANO, 1996).

1.5.1 Motores e controle do motor

  Como já mencionado cada roda é movida por um motor DC acoplado a uma caixa de redução 25:1, onde um encoder incremental é colocado no eixo do motor, enviando 24 pulsos por rotação da roda. Isto permite uma resolução de 600 pulsos por rotação da roda, o que corresponde a 12 pulsos/mm da trajetória percorrida pelo robô (K-TEAM, 1999b).

  O processador do Khepera controla diretamente a energia enviada aos motores, além de ler os pulsos enviados pelos encoders (contadores digitais de posição). Uma rotina de interrupção detecta cada pulso do encoder incremental e atualiza um contador de posição da roda.

  A energia aplicada ao motor pode ser ajustada pelo processador fazendo o chaveamento ON/OFF a uma determinada freqüência por um dado tempo. A freqüência de chaveamento básica é constante e suficientemente alta para não deixar o motor reagir a um simples chaveamento. Desta forma, o motor reage a um tempo médio de energia, que pode ser modificado mudando o período em que motor fica chaveado em ON (K-TEAM, 1999b). Este método em que somente a proporção entre os períodos ON e OFF é modificada é chamado de PWM (pulse width modulation), e é ilustrado na figura 1.8.

  A figura 1.8, bem como as figuras ilustradas adiante neste capítulo foram mantidas conforme as referências bibliográficas para que não houvesse possíveis distorções na tradução dos termos utilizados.

Figura 1.8. O controle PWM (K-TEAM, 1999b)

  O valor PWM é definido pelo tempo que o motor fica chaveado em ON, sendo que este valor pode ser ajustado diretamente ou pode ser gerenciado por um controlador local para o motor. O controlador do motor pode executar o controle da velocidade ou da posição do motor, ajustando o valor correto do PWM de acordo com a velocidade real ou posição real dos encoders.

  O funcionamento dos motores também pode ser ajustado através de dois controladores PID executados em uma rotina de interrupção do processador. Cada termo destes controladores (Proporcional, Integral e Derivativo) é associado a uma constante Kp, Ki e Kd respectivamente.

  Nesse caso, o controle do motor pode ser efetuado de dois modos: o modo de velocidade e o modo de posição. A figura 1.9 ilustra os controladores do motor e os níveis de acesso permitido ao usuário.

Figura 1.9. Estrutura dos níveis de controles do motor e níveis de configuração do usuário (K-TEAM, 1999b)

  O modo de controle ativo é determinado pelo tipo de comando recebido. Se o controlador recebe um comando de controle de velocidade, este é chaveado para o modo de velocidade. Se o controlador recebe um comando de controle de posição, o modo de controle é a automaticamente chaveado para o modo de posição. Parâmetros de controle diferentes (Kp, Ki e Kd) podem ser ajustados para cada um dos dois modos de controle.

  Usando o modo de velocidade, o controlador terá como entrada um valor para a velocidade das rodas, e o controle do motor modificará a velocidade das rodas. A modificação da velocidade das rodas é feita da maneira mais rápida possível de forma abrupta, sendo que nenhuma limitação para a aceleração é considerada neste modo.

  Usando o modo de posição, o controlador terá como entrada a posição alvo da roda, com máxima aceleração e velocidade. Usando estes valores, o controlador do motor acelera a roda até que a velocidade máxima seja atingida e desacelera até alcançar a posição alvo. Este movimento segue um perfil trapezoidal que pode ser visto na figura 1.10.

Figura 1.10. Curvas de aceleração e desaceleração para o controle de posição (K-

  TEAM, 1999b) Os valores de entrada e o modo de controle podem ser mudados a qualquer momento. O controlador atualizará e executará o novo perfil no modo de posição, ou simplesmente seguirá com uma determinada velocidade até que novos valores sejam ajustados para o modo de velocidade.

  A velocidade dos motores usa como unidade pulso/10 ms, que corresponde a 8mm/s, atingindo uma velocidade máxima é de 127/10ms, que corresponde a 1m/s. Já, no controle de posicionamento do robô, a unidade retornada é equivalente a um pulso do encoder, que corresponde a 0,08 mm.

1.5.2 Sensores infravermelhos de proximidade e luz ambiente

  Os sensores infravermelhos (IR) estão dispostos em ângulos apropriados como mostrado na figura 1.11 (vista superior), cobrindo o campo de atuação do robô.

Figura 1.11. Posição dos sensores infravermelhos (K-TEAM, 1999b)

  Os sensores são montados de forma que emissor e receptor fiquem no mesmo componente, e podem efetuar dois tipos de medidas:

  • A luminosidade do ambiente. Esta medição é feita usando somente a parte receptora do dispositivo, sem haver a emissão de luz. Uma nova medição é feita a cada 20 ms. Durante estes 20 ms, os sensores são lidos seqüencialmente a cada 2,5 ms, totalizando desta forma a leitura de cada um dos oito sensores.
  • A luz refletida pelos obstáculos. Esta medição é realizada através da emissão de luz pela parte emissora do dispositivo. O valor retornado é a diferença entre a medição feita com emissão de luz e a medida feita sem emissão de luz (luz ambiente). De modo similar à leitura de luz ambiente, uma nova medida é feita a cada 20 ms e os sensores são lidos seqüencialmente a cada 2,5 ms.

  A sensibilidade na resposta dos sensores de proximidade (onde a luz é refletida pelo obstáculo) está diretamente relacionada ao tipo de material reflexivo. A escala de sensibilidade de proximidade possui uma resolução de 10 bits com valores entre 0 e 1023, sendo que quanto maior o valor mais próximo do obstáculo o robô se encontra. Estes valores dependem também da refletividade do obstáculo. Quanto melhor for a refletividade, maior será a sensibilidade do sensor. O plástico branco, por exemplo, tem uma ótima refletividade, enquanto uma madeira escura tem uma baixa refletividade. A figura 1.12 mostra a resposta dos sensores para alguns tipos de materiais como: plástico preto, isopor verde, esponja rosa, plástico branco, plástico cinza, madeira e cobre (K-TEAM, 2002a).

Figura 1.12. Valores medidos da luz refletida por vários tipos de objetos versus a distância do objeto (K-TEAM, 1999b)

  Os sensores de luminosidade funcionam de forma similar aos sensores de proximidade com uma escala de sensibilidade com resolução de 10 bits, sendo 450 o valor padrão aproximado para um ambiente escuro. Deste modo, quanto menor o valor retornado pelo sensor de luz ambiente, mais próximo da fonte de luz o robô se encontra. É importante salientar que estas medidas dependem fortemente de fatores como a distância da fonte de luz, sua cor, intensidade, posição vertical, entre outros. A figura 1.13 mostra os valores típicos de luz ambiente medidos pelos sensores a uma determinada distância de uma fonte de luz de 1 Watt (K-TEAM, 1999b) (K- TEAM, 1999b).

  .

Figura 1.13. Valores típicos para medida da luz ambiente versus distância de uma fonte de luz de 1 Watt (K-TEAM, 1999b)

  É necessário conhecer todos os aspectos do ambiente em que o robô está inserido, pois, além das imprecisões do ambiente, pode haver diferenças físicas entre as características de cada sensor. A figura 1.14 mostra uma medição feita por seis sensores de um mesmo robô colocado em condições idênticas. Pequenas diferenças de condições, como orientação vertical do sensor, condições de luz ambiente e a cor do piso, podem provocar diferenças nas medidas.

Figura 1.14. Valores típicos de luz refletida por um obstáculo versus distância do obstáculo para sensores do mesmo tipo em condições idênticas (K-TEAM, 1999b)

1.5.3 O protocolo de comunicação serial

  O controle do robô pode ser feito através de um computador usando uma porta de comunicação serial RS-232. A configuração dos parâmetros de comunicação da porta serial (baudrate, start bit, stop bit e bit de paridade) no computador deve estar de acordo com a configuração dos parâmetros de comunicação serial existente no hardware do robô (K-TEAM, 1999b). Esta configuração é feita através de jumpers localizados na parte superior do robô, como mostra a figura 1.15.

Figura 1.15. Posição e configuração dos jumpers (K-TEAM, 1999b)

  Nos modos 01, 02 e 03 a comunicação é feita com velocidade de 9600, 19200 e 38400 bps respectivamente. A comunicação entre o computador e o robô Khepera é feita enviando e recebendo mensagens no padrão ASCII. A comunicação se efetua através de comandos enviados do computador para o Khepera, e quando necessário, uma resposta é enviada do Khepera para o computador.

  A comunicação é sempre iniciada pelo computador, e é baseada em dois tipos de interações com o robô. O primeiro tipo é a interação com o robô através de ferramentas de controle, que possibilitam manipular aspectos mais específicos do robô como por exemplo, iniciar uma aplicação armazenada na memória ROM do robô ou simplesmente reiniciar o robô.

  O segundo tipo de interação é chamado de protocolo de controle, e permite controlar todas as funcionalidades do robô. Cada interação entre o robô e o computador é composta por:

  • Um comando, iniciando com uma ou duas letras maiúsculas no formato

  ASCII, seguido, se necessário, por um conjunto de parâmetros separados por virgula (de acordo com o comando executado) e finalizado por um retorno de carro ou avanço de linha, enviado do computador para o robô.

  • Uma resposta, iniciando com um ou dois caracteres minúsculos, iguais aos do comando de envio. Dependendo do comando, uma seqüência de números, separados por virgula, é enviada após o caractere inicial e finalizada por um retorno de carro ou avanço de linha, enviado do robô para o computador.

  Este protocolo de controle pode implementar até 18 diferentes comandos para o controle do robô (K-TEAM, 1999b).

1.6 Linguagens de programação para robôs móveis

  As linguagens de programação para robôs móveis geralmente são linguagens proprietárias, ou seja, feitas exclusivamente para a programação de um determinado robô. Um dos problemas desse tipo de linguagem é que a programação fica limitada a um conjunto predefinido de funções destinadas ao controle do robô. Muitas vezes, essa limitação imposta pela linguagem de programação não permite que as potencialidades do robô sejam exploradas na sua totalidade.

  Alguns modelos de robôs permitem que, além da sua própria linguagem de programação, suas funções sejam acessadas por meio de alguma outra linguagem. O acesso ao robô por meio de uma linguagem de alto nível proporciona um acréscimo importante no desenvolvimento de aplicações.

  Além das linguagens de programação para robôs móveis reais, existem ainda os ambientes de simulação. Através do modelamento matemático do robô é possível desenvolver um ambiente de simulação capaz de se aproximar de forma satisfatória dos modelos reais. Pelo fato de não ser necessária a presença do robô real, a grande vantagem dos ambientes simulados é o baixo custo de desenvolvimento.

1.6.1 Ambientes de programação para o robô Khepera

  O Khepera pode ser programado de várias maneiras. A maneira mais comum é enviar comandos ao robô usando um protocolo de comunicação específico, através de qualquer software de comunicação serial. Fica claro que estes programas não podem implementar nenhum tipo de algoritmo, pois eles não podem manipular os dados enviados e recebidos do robô. Para que se possa passar algum tipo de tarefa para o robô há a necessidade de programas mais sofisticados. Dentre os ambientes de programação mais conhecidos para a programação do Khepera existe o KTProject, que é uma interface gráfica para ambiente Windows desenvolvida pelo próprio fabricante do robô para que programas desenvolvidos em C sejam compilados e gravados diretamente na memória do Khepera, permitindo que o mesmo execute tarefas em modo autônomo. Outra importante ferramenta de programação é o GNU C Cross Compiler, usada para o desenvolvimento de aplicações nativas nas plataformas Windows, Linux e Sun (K-TEAM, 1999b) (K- TEAM, 2002b).

  O Khepera também pode ser programado através de ambientes de simulação. Um exemplo de ambiente de simulação é o Webots (CYBERBOTICS, 2003), um simulador para robôs móveis desenvolvido pela Cyberbotics. Este software, ilustrado na figura 1.16, tem se mostrado uma importante ferramenta para pesquisadores e professores que atuam na área de robótica, agentes autônomos, visão computacional e inteligência artificial. O simulador inclui modelos de dois robôs reais, o robô Alice e o robô Khepera produzidos pela empresa suíça K-Team, e permite incluir vários módulos, como módulo de visão e garras. O usuário pode programar virtualmente o robô usando C ou C++, que é compatível com o robô real. Um ambiente de edição 3D permite que o usuário crie os seus próprios cenários de trabalho.

Figura 1.16. Webots - Ambiente de simulação para o robô Khepera

  Também são utilizados outros ambientes para desenvolvimento de pesquisa com o robô Khepera, como o LabVIEW e o MatLAB. Esses dois ambientes possibilitam, através da instalação de plugins, efetuar a comunicação com o robô, através da porta serial do microcomputador (K-TEAM, 1999b).

  O LabVIEW (Laboratory Visual Environment Workbench) é um ambiente de programação visual orientada a fluxo de dados muito utilizado na área de engenharia, mais especificamente em aplicações de aquisição de dados, controle, simulação, processamento de sinais e análise (NATIONAL, 2000). Ele é composto de uma grande variedade de instrumentos virtuais, mais conhecidos por VI (virtual

  

instruments) que podem ser interligados para formar alguma aplicação. No caso do

  robô Khepera, os instrumentos virtuais básicos, são formados por motores e sensores e podem ser acessados através de painéis frontais. Com os painéis disponíveis para o controle dos motores é possível ler a posição e velocidade instantânea de cada roda, controlar a velocidade e posição de cada roda e configurar os parâmetros da curva de velocidade, por exemplo.

  Os painéis frontais são descritos por diagramas que estabelecem a lógica de programação desde o processamento das informações de entrada até a geração dos dados de saída. Estes diagramas são compostos por símbolos que representam os controles e indicadores do painel frontal e por operações e estruturas predefinidas na linguagem. Além das estruturas predefinidas, também é possível a criação de sub-rotinas.

  O painel de controle para configurar a velocidade de cada motor, mostrado na figura 1.17, pode alterar a velocidade dos motores através de botões deslizantes, ou manualmente, digitando o valor da velocidade no campo apropriado. A figura 1.18 mostra o diagrama correspondente ao painel frontal para o controle dos motores.

Figura 1.17. Painel frontal do controle de velocidade dos motoresFigura 1.18. Diagrama correspondente ao painel frontal para controlar a velocidade dos motores

  Da mesma forma, os valores retornados pelos sensores infravermelhos do robô podem ser visualizados através de um painel de controle. O painel de controle para leitura dos sensores, mostrado na figura 1.19, é composto por 8 medidores que indicam através de uma barra o valor de cada sensor.

Figura 1.19. Painel de sensores para mostrar a leitura dos sensores infravermelhos

  Através destes painéis de controle é possível desenvolver um grande número de aplicações para o Khepera. Um exemplo de uma aplicação para o robô Khepera usando o LabVIEW pode ser visto na figura 1.20, que mostra o painel de um veículo de Braitenberg. A estrutura de controle foi baseada no trabalho de V. Braitenberg (BRAITENBERG, 1984). A figura 1.21 mostra o diagrama VI que corresponde ao painel do veiculo de Braitenberg.

Figura 1.20. Painel do veículo de BraitenbergFigura 1.21. Diagrama do veículo de Braitenberg

  CAPÍTULO 2 PROGRAMAđấO VISUAL ORIENTADA A OBJETOS Introdução

  Este capítulo tem por objetivo fazer um breve apanhado sobre os principais tipos de linguagens de programação, e dar a sustentação teórica necessária para melhor compreender os objetivos embutidos na escolha da programação visual em conjunto com a programação orientada a objetos como plataforma para este trabalho.

2.1 A evolução das linguagens de programação

  As linguagens de programação basicamente evoluíram a partir do surgimento do primeiro computador. Inicialmente, as linguagens eram baseadas em um conjunto de códigos binários que representavam alguma instrução do processador. Essas linguagens são conhecidas como linguagens de máquina e, basicamente existe uma linguagem para cada família de processador. Apesar de simples, comparadas às linguagens de alto nível disponíveis atualmente, as linguagens de máquina são bastante eficientes no que diz respeito à velocidade de execução das instruções. O fato de se programar usando código binário ou hexadecimal, faz com que esse tipo de linguagem não seja a mais adequada para a descrição de um programa, uma vez que os programas desenvolvidos podem ser sofisticados e essa linguagem primitiva não é nem um pouco amigável ao programador. Uma evolução nas linguagens de baixo nível veio com o uso da linguagem Assembly, que faz uso de códigos mnemônicos, facilitando assim o desenvolvimento de programas (SETHI, 1996).

  O próximo passo no sentido da evolução das linguagens de programação foi as linguagens de alto nível não-estruturadas, que são mais sofisticadas que as linguagens de baixo nível e seus comandos não estão diretamente vinculados ao processador e sistema utilizados, permitindo seu uso em diferentes plataformas e tornando a linguagem mais flexível. Do mesmo modo, a semântica de seus termos torna-se mais genérica, não estando associada ao conjunto de instruções que irão efetivamente implementá-la durante a execução do programa, onde operações mais sofisticadas podem ser emuladas por seqüências de instruções mais simples do que em linguagem de máquina. As linguagens COBOL (1959) e BASIC (1963) são exemplos de linguagens não-estruturadas (GUDWIN, 1997). Apesar de melhorar consideravelmente o desenvolvimento de software, as linguagens de programação não-estruturadas geralmente não oferecem uma estrutura de controle adequada, admitindo, por exemplo, o uso de desvios incondicionais (GOTO) que dificultam a compreensão do código.

  Neste sentido, surgiram as linguagens de programação estruturadas, também conhecidas como procedimentais. Diferentemente das linguagens não-estruturadas, as linguagens estruturadas possibilitam o uso de estruturas de controle, que tornam possíveis operações como o teste de condições (IF – THEN – ELSE), o controle de repetição de blocos de código (FOR – WHILE – DO) e a seleção de alternativas (SWITCH - CASE). As linguagens procedimentais permitem que o código do programa seja separado em módulos chamados de funções ou procedimentos. As linguagens procedimentais são caracterizadas pela existência de algoritmos, que determinam uma seqüência de chamadas de procedimentos, que constituem o programa. As linguagens procedimentais mais comuns são o C, o PASCAL e o FORTRAN (GUDWIN, 1997).

  Existem também as linguagens de programação funcionais, que evidenciam um estilo de programação diferente das linguagens procedimentais. Esse tipo de linguagem tem um conjunto de regras sintáticas e semânticas que podem ser estendidas de muitas maneiras permitindo a criação de novas formas de expressar problemas de uma maneira muito inteligível e de fácil manutenção. A programação funcional enfatiza a avaliação de expressões, ao invés da execução de comandos. As expressões nessas linguagens são formadas utilizando-se funções para combinar valores básicos. As linguagens funcionais mais conhecidas são o LISP e o PROLOG.

  Seguindo a constante evolução das linguagens de programação, surgiu então, o paradigma da programação orientada a objetos. As linguagens orientadas a objetos foram originadas a partir da necessidade de se organizar o processo de programação em uma linguagem. Na verdade a programação orientada a objetos é uma técnica de programação. Uma linguagem é dita uma linguagem orientada a objetos, se ela suporta o estilo de programação orientada a objetos. Um dos objetivos da engenharia de software é separar o projeto de desenvolvimento de um software em diversos módulos, de tal forma que o seu desenvolvimento e manutenção possam ser implementados com baixo custo. No passado, os paradigmas da engenharia de software derivavam os módulos baseados na funcionalidade de um sistema que correspondiam basicamente a módulos procedimentais. O paradigma de orientação a objeto mudou essa concepção, idealizando a idéia de objetos, como módulos que se comunicam por meio de mensagens, encapsulando ao mesmo tempo dados e funções. A primeira linguagem orientada a objetos que se tem notícia é o Simula, desenvolvido em 1967. Posteriormente veio o Smalltalk em 1970 (GUDWIN, 1997). Atualmente, existe uma enorme diversidade de linguagens orientadas a objetos, abrangendo desde linguagens de propósito geral, até linguagens para multimídia e programação em lógica. Linguagens como o C++ incorporam características das linguagens de programação orientadas a objetos. Já a linguagem Java é totalmente orientada a objetos.

  A evolução na capacidade de processamento do hardware também permitiu que surgissem novas linguagens de programação, como as linguagens visuais, que aproveitam essa capacidade de processamento para o desenvolvimento de ambientes gráficos (WHITLEY, 1996). Pode-se dizer que uma informação visual é mais fácil de ser entendida do que uma informação textual, portanto, a especificação de um programa por meio de diagramas, ícones e outros recursos visuais tende a tornar a programação mais fácil, permitindo que um grupo maior de usuários tenha acesso ao desenvolvimento de programas. Uma representação visual de um programa normalmente limita a flexibilidade dos programas que podem ser desenvolvidos, mas em contrapartida, as linguagens de programação visual permitem que programas sejam elaborados com mais facilidade e de forma mais rápida do que a programação somente textual.

  Seguindo as características da programação visual, existem também as linguagens de programação que não são somente visuais, mas têm parte de sua especificação determinada de forma visual. Normalmente este tipo de linguagem de programação visual não é simplesmente uma linguagem, mas uma ferramenta de programação que adota uma linguagem de programação textual, com recursos de programação visual, a fim de melhorar a produtividade no desenvolvimento de aplicações. Em outros casos, as ferramentas de programação visual efetivamente participam da elaboração do programa, como por exemplo na determinação de hierarquia de classes e métodos, em linguagens orientadas a objeto. As linguagens incluídas nesta categoria são normalmente desenvolvimentos de linguagens de programação convencional ou textual, acrescidas das ferramentas visuais. Algumas linguagens que usam esse tipo de recurso são o Delphi, o Visual Basic e o Visual C++ (GUDWIN, 1997).

  Por fim, é possível citar as linguagens de programação visual puras, onde o programa é descrito exclusivamente por meio de gráficos e diagramas. Neste tipo de programação, o programa é elaborado através de diagramas com nós e arcos, sendo que os nós representam módulos de software, a partir de uma biblioteca prévia de módulos, e os arcos determinam a conexão entre os diferentes módulos. Devido à forte dependência dos módulos de software, o uso deste tipo de linguagem torna-se bastante específico, limitando desta forma o universo de aplicações. Um exemplo deste tipo de linguagem é o Simulink, que trabalha em conjunto com o Matlab e é usado para simulação de sistemas dinâmicos (GUDWIN, 1997).

2.2 O paradigma da programação orientada a objetos

  Um dos principais objetivos da programação orientada a objetos é reduzir a complexidade no desenvolvimento de software e aumentar sua produtividade. Neste sentido, o aumento da complexidade dos ambientes computacionais que se caracterizam por sistemas heterogêneos, podem se beneficiar com o uso da programação orientada a objetos (VOSS, 1991).

  A programação orientada a objetos não tem a intenção de substituir a programação estruturada tradicional, podendo então, ser considerada como uma evolução de práticas que são recomendadas na programação estruturada (LEE, 2002). O modelo de objetos permite a criação de bibliotecas que tornam efetivos o compartilhamento e a reutilização de código, reduzindo o tempo de desenvolvimento e, principalmente, simplificando o processo de manutenção das aplicações.

  A grande dificuldade para compreender a programação orientada a objetos é a diferença de abordagem do problema, pois, enquanto a programação estruturada tem como principal foco as ações através de procedimentos e funções, a programação orientada a objetos se preocupa com os objetos, suas propriedades e seus relacionamentos.

2.3 Elementos da programação orientada a objetos

  A utilização da programação orientada a objetos permite um aumento de produtividade pelo reuso de elementos de software. Ela também contempla as características psicológicas do programador, pois nos modelos de aprendizagem, a organização da informação de entrada conduz à aprendizagem mais rápida e à melhor memorização (SOUSA, 1999). A recuperação da informação armazenada se torna muito mais fácil se esta é organizada desde o início. Os itens na memória de longo prazo são tanto mais prontamente recuperados quanto mais se acham estruturados ou categorizados. O estratagema dos métodos propostos para o desenvolvimento de sistemas de memória consiste em aprender a organizar as informações que devemos aprender, de modo que elas possam ser novamente encontradas em nossa memória, quando necessário. Este ponto vai ao encontro da estruturação da programação orientada a objetos. Os objetos (entidades) são organizados ou categorizados em classes, formando uma estrutura hierarquizada, possibilitando uma boa organização da informação, que é captada pelas pessoas envolvidas no desenvolvimento dos programas.

  Por fim, pode-se dizer que o desenvolvimento de um programa orientado a objetos, é baseado em quatro princípios básicos: abstração, encapsulamento, herança e polimorfismo, que serão discutidos a seguir.

  2.3.1 Abstração

  A abstração é o processo de extrair as características essenciais de um objeto real e é necessária para se ter um modelo o mais exato possível da realidade sobre o qual se possa operar. O conjunto de características resultante da abstração forma um tipo de dados abstrato com informações sobre seu estado e comportamento. A abstração no entanto pode não produzir os mesmos resultados para diferentes observadores, pois, dependendo do contexto onde é utilizada, a abstração de um objeto descrito por uma pessoa pode ser diferente na visão de outra. No desenvolvimento de software a abstração é o mecanismo básico utilizado para realização da análise do domínio da aplicação, e cada passo do processo de engenharia de software é um aprimoramento do nível de abstração da solução (PRESSMAN, 1995).

  2.3.2 Encapsulamento

  Através do encapsulamento, os atributos de um objeto só podem ser alterados por métodos definidos na própria classe, garantindo desta forma a integridade dos atributos do objeto. A única maneira de um objeto alterar os atributos de um outro objeto é através da ativação de um de seus métodos por uma mensagem. Este conceito, onde atributos e métodos são visíveis apenas através de mensagens, é conhecido como encapsulamento. O encapsulamento funciona como uma proteção para os atributos e métodos, além de tornar explícito qualquer tipo de comunicação com o objeto (WINBLAND, 1993).

  Um exemplo prático de encapsulamento no mundo real pode ser descrito por um vídeo cassete. Em um vídeo cassete existe um número determinado de funções que podem ser executadas, como por exemplo, avançar, voltar, gravar, tocar, parar e ejetar a fita. Dentro do vídeo cassete, porém, há várias outras funções sendo realizadas como acionar o motor, desligar o motor, acionar o cabeçote de gravação, liberar o cabeçote, e outras operações mais complexas. Essas funções são escondidas dentro do mecanismo do vídeo cassete e não temos acesso a elas diretamente. Quando a tecla play é pressionada, o motor é ligado e o cabeçote de reprodução é acionado, sem que para isso, seja necessário saber como é feito internamente.

  A principal vantagem do encapsulamento é poder esconder a complexidade do código e proteger os dados, permitindo o acesso a eles apenas através de métodos evitando que seus dados sejam corrompidos por aplicações externas.

  No exemplo do vídeo cassete, quando o método gravar é acionado, o aparelho grava as informações presentes na fita de vídeo em um formato padrão, que poderá ser lido por outros aparelhos similares. Se não existisse o método gravar e fosse possível o acesso direto à fita, cada pessoa poderia estabelecer uma maneira diferente de gravar informações, fazendo com que o processo de se gravar uma fita de vídeo se tornasse uma tarefa complicada, pois o usuário teria que conhecer toda a complexidade do aparelho. Isso acarretaria numa perda de compatibilidade pois não haveria uma forma de proteger os dados para que fossem sempre gravados em um formato padrão. Neste caso, o encapsulamento funciona tanto para proteger os dados, como para simplificar o uso de um objeto.

  Outro exemplo pode ser dado pelos aplicativos desenvolvidos para a plataforma Windows. Em aplicações deste tipo, quando em alguma parte do programa onde é necessário o uso de um botão OK e um botão Cancela, por exemplo, não é preciso entender como ele foi escrito, nem saber quais são seus dados internos. Basta saber como construí-lo, mudar o texto que contém a informação do botão e depois incluí-lo no programa, sem correr o risco de corromper a estrutura interna do botão, definida em uma biblioteca padrão.

2.3.3 Herança

  O conceito de herança permite definir uma nova classe, com base em uma já existente. Uma classe-filha pode ser criada herdando os membros de dados e os métodos de uma classe-pai. Assim, novas classes podem ser criadas pela especialização da classe-pai, formando uma hierarquia de classes (WINBLAND, 1993).

  De uma maneira bastante simples, pode-se dizer que herança é o aproveitamento e extensão das características de uma classe existente. Na natureza existem muitos exemplos de herança. No reino animal, os mamíferos herdam a característica de terem uma espinha dorsal por serem uma subclasse dos vertebrados, podendo ainda, acrescentar a essa característica, outras, como ter sangue quente e amamentar. Os roedores herdam todas as características dos vertebrados e mamíferos e acrescentam outras, e assim por diante. Em programação, a herança ocorre quando um objeto aproveita a implementação (estruturas de dados e métodos) de um outro tipo de objeto para desenvolver uma especialização dele. A herança permite a formação de uma hierarquia de classes, onde cada nível é uma especialização do nível anterior, que é mais genérico.

  Em programação orientada a objetos, é comum desenvolver estruturas genéricas para depois ir acrescentando detalhes. Isto simplifica o código e permite uma organização maior de um projeto, fazendo com que seja possível a reutilização de código. Se for preciso implementar um botão azul e já temos uma classe que define as características de um botão cinza, podemos fazer com que nossa implementação seja uma subclasse do botão cinza, e depois estender suas características. Desta forma, nos concentramos apenas no necessário, que neste caso é a cor do botão, e evitamos ter que definir novamente todas as características para um novo botão.

2.3.4 Polimorfismo

  O termo polimorfismo significa ter muitas formas. Em programação, esta definição pode ser traduzida pelo envio de uma mesma mensagem para um conjunto de objetos, onde cada objeto responde de forma diferente em função da mensagem recebida (WINBLAND, 1993). Polimorfismo também pode ser definido como sendo a propriedade de se utilizar um mesmo nome para fazer coisas diferentes. Por exemplo, mandar alguém correr pode produzir resultados diferentes dependendo da situação. Se a pessoa estiver parada e em pé, irá obedecer à ordem simplesmente correndo. Se a pessoa estiver no volante de um carro, o resultado será o aumento da pressão do pé no acelerador.

  Em programação, uma mesma mensagem poderá provocar um resultado diferente, dependendo dos argumentos que foram passados e do objeto que o receberá. Por exemplo, o envio de uma instrução que desenhe para uma subclasse de polígono, poderá desenhar um triângulo, um retângulo ou um pentágono, dependendo da classe que receber a instrução. Isto é muito útil para escrever programas versáteis, que possam lidar com vários tipos diferentes de objetos.

2.4 O conceito de objeto

  Objetos são a base da tecnologia orientada a objetos e consistem de modelos ou abstrações de objetos reais e preservam as características essenciais de um objeto real, ou seja, o seu estado e o seu comportamento. Desta forma qualquer objeto real pode ser abstraído para filtrar seu estado e comportamento. A Tabela 1 mostra alguns exemplos.

Tabela 2.1. Exemplos de objetos reais

OBJETO ESTADO COMPORTAMENTO

  velocidade, marcha, cor, modelo, troca marchas, acelera, dá Carro marca partida, freia mia, dorme, se estica, brinca,

  Gato nome, raça, com fome, com preguiça caça Caneta cor, nível de tinta escreve, coloca mais tinta liga, grava, toca, avança, volta,

  Toca-fitas ligado, girando, tocando, gravando para Em programação orientada a objetos, o estado de um objeto é representado por membros de dados, e seu comportamento implementado com métodos, sendo que, a única forma de mudar o estado dos dados é através de métodos. Por exemplo, para mudar o estado do vídeo cassete para ligado, é necessário utilizar o método liga. A figura 2.1 representa um objeto de software.

Figura 2.1. Representação de um objeto de software

  Tudo o que o objeto de software sabe (seu estado) e tudo o que ele pode fazer (o seu comportamento) é determinado pelos seus membros de dados e pelos seus métodos.

  Para entender melhor o conceito de objeto, no exemplo a seguir, um avião Cessna 185 será representado através de um objeto de software, neste caso, um objeto chamado ppHCC, como mostra a figura 2.2. Neste objeto, as variáveis que indicam o estado atual do avião são dadas pela velocidade, direção e altitude, podendo ter seus valores alterados através dos métodos públicos partida(), acelera(), leme(direção), aileron(direção), elevador(posição) e altímetro(). A mudança no estado das variáveis do avião pode, por exemplo, fazer o avião ganhar altitude, velocidade ou mudar de direção.

Figura 2.2. O objeto ppHCC

  Sabe-se que um objeto sozinho não tem muita utilidade, portanto, um avião sem piloto, não teria muita utilidade. Para que um objeto seja funcional, é necessário que ele interaja com outros objetos, para que desta maneira se possa construir modelos de comportamento mais complexo.

  Um objeto solicita a outro que execute uma determinada atividade através de uma mensagem, podendo também transmitir informações de um objeto para outro (JONES, 2001). No exemplo do avião Cessna, é necessário que haja um outro objeto, neste caso um objeto piloto, que envie mensagens para o avião para que ele possa sair do chão. No exemplo ilustrado na figura 2.3, um avião voa a uma velocidade de 280 Km/h e 2,5 Km de altura, onde um objeto Dumont (um piloto) invoca o método elevador() sobre o objeto ppHCC com o parâmetro (+2). O método é então executado, fazendo o elevador se mover duas posições para cima, aumentando a altitude e reduzindo a velocidade (mudando o estado dos dados do objeto ppHCC).

Figura 2.3. Troca de mensagens entre objetos

2.4.1 Métodos internos e variáveis públicas

  A única maneira de se mudar qualquer estado do avião é através dos seus métodos públicos, já que os campos de dados são privativos e o seu acesso direto não é permitido ao mundo externo. Se os campos de dados fossem públicos, seria possível alterá-los diretamente, podendo comprometer o funcionamento normal do objeto. A figura 2.4 ilustra uma nova versão do avião Cessna ppHCC, onde é possível alterar diretamente os valores dos membros de dados, sem usar os métodos. Pela notação utilizada, é possível notar que agora os membros de dados direção, altitude e velocidade podem ser acessados publicamente.

Figura 2.4. Membros de dados acessados publicamente

  Isto pode ser comparado a mover o leme, aileron e elevador com a mão, em vez de usar o manche e o pedal, tornando o avião mais difícil de usar e comprometendo a sua segurança. A maioria das linguagens de programação permite o uso de campos de dados públicos. Geralmente, as desvantagens desse procedimento são maiores que as vantagens; portanto, é aconselhável que se use o encapsulamento dos dados pelos métodos sempre que possível.

  Um objeto complexo pode ter centenas de métodos, mas geralmente, alguns poucos métodos interessam a quem vai usá-lo. Por exemplo, quem dirige um carro não precisa saber de coisas como injetar combustível e provocar faísca. E precisa somente ligar a ignição. Neste caso, ligar a ignição é um método público, que interessa ao motorista; já injetar combustível e provocar faísca são métodos internos (ou privados) ao funcionamento do automóvel, que podem ser chamados por outros métodos sem que o motorista precise se preocupar.

2.5 O conceito de classes

  Uma classe é uma descrição de um conjunto de objetos quase idênticos, e consiste em métodos e dados que resumem as características comuns de um conjunto de objetos (WINBLAND, 1993). As características de estado e comportamento do objeto ppHCC ilustrado nos exemplos anteriores, são características de qualquer avião Cessna 185 que sai da fábrica. Estas características são definidas nas classes que podem ser vistas como a planta usada para construir o avião.

  A figura 2.5 representa uma classe, e pode-se observar a semelhança com o diagrama utilizado para representar os objetos. O retângulo levemente apagado sugere que a classe é apenas um molde onde se encaixam os objetos. Apesar desta semelhança, a classe não é um objeto, pois não possui um estado, nem um comportamento específico, portanto, uma classe não é algo que existe e que possa ter estados. De uma maneira geral, uma classe pode ser definida como um gabarito com o qual objetos podem ser construídos.

Figura 2.5. Representação de uma classe

  Por exemplo, com as plantas que contêm o projeto de um avião Cessna, pode-se construir vários aviões Cessna. O projeto inclui as especificações do motor, da fuselagem, da asa, desenhos em perspectiva, moldes, entre outros, e descreve em detalhes tudo o que o Cessna pode fazer, ou seja, como vai funcionar o elevador, o aileron, o leme, o trem de pouso e outros. Portanto, contêm tudo o que um Cessna tem, mas não é um Cessna.

  Para produzir os aviões ppHCC, ppHCD, ppHCE, todos Cessna 185, define- se uma classe chamada Cessna, como ilustra a figura 2.6. Esta classe possui a s declarações dos membros de dados que serão utilizadas para armazenar a direção, velocidade e altitude, além da implementação dos métodos que cada Cessna deve ter.

Figura 2.6. Representação da classe Cessna

  Os objetos são instâncias das classes, pois, quando um objeto é criado, disse que o mesmo é instanciado a uma classe. Cada instância é uma entidade individual e pode receber valores para os seus membros de dados e modificá-los através de métodos. Com uma fábrica pode-se fabricar 100 aviões, que podem então ter velocidades, altitudes e direções diferentes. Tendo criado a classe Cessna, pode-se criar vários aviões com as mesmas características básicas. Por exemplo, se ppHCC, ppHCD e ppHCE são instâncias da classe Cessna, todos têm uma velocidade, uma altitude e uma direção distintas e podem dar partida, acelerar, subir e descer. Neste caso ppHCC pode estar a 350 Km/h voando para o Norte, enquanto ppHCD pode estar estacionado no hangar.

  A grande vantagem de se usar classes é a reutilização do código. Toda a estrutura de um objeto é definida na classe, que depois é utilizada quantas vezes for necessário para criar vários objetos. Com orientação a objetos, o programador se preocupa com o objetivo principal do programa que quer desenvolver, sem precisar ficar preocupado com detalhes como programação de botões, molduras, interfaces gráficas, hélices e outros. Simplesmente o programador usa objetos existentes, para montar a infraestrutura do seu projeto e se concentra na implementação específica do problema que pretende resolver.

2.6 Hereditariedade

  A hereditariedade constitui-se de uma vantagem exclusiva das linguagens orientadas a objetos, a de estender e refinar tipos de dados e funcionalidade sem duplicações e sem ter de acessar o código fonte original (WINBLAND, 1993).

  As características altitude, velocidade e direção, podem estar definidas em uma classe de aviões chamada de Aeronave, que declara métodos partida(), acelera(), leme(direção), aileron(direção), elevador(posição) e altímetro(). Esses métodos e dados podem ser herdados pelas classes Jato e Hélice que são tipos de aeronaves. A classe Cessna pode ser uma subclasse de Hélice e a classe Jato pode ter outras subclasses como F14 e Boing737, por exemplo.

  Em cada classe, métodos novos podem ser criados, outros podem ser redefinidos e novos campos de dados podem ser declarados. Cada subclasse adiciona alguma nova funcionalidade à classe-pai e continua sendo um objeto do tipo definido pela raiz das classes. Ou seja, tanto Cessna, Piper, Airbus300, e

  

Boeing737 são aviões, mas Cessna e Piper são movidos à hélice, e Airbus300 e

  Boing737 são movidos a jato. A figura 2.7 ilustra uma hierarquia de classes formada pelas subclasses da classe Aeronave. É importante observar esta relação de ser no projeto de sistemas orientados a objetos.

Figura 2.7. Hierarquia de classes da classe Aeronave

  Todos os métodos e dados públicos de uma classe-pai são herdados pelas suas classes-filha. Desta forma, se a classe Aeronave já define o método partida(), não há necessidade da classe Cessna redefini-lo, ou seja, os objetos criados com a classe Cessna já nascem com um método partida().

  Na maioria das vezes, a forma como uma classe genérica define uma ação não serve para classes mais específicas. Provavelmente a partida na classe

  

Aeronave seja manual. Neste caso, a subclasse pode sobrepor ou sobrecarregar os

  métodos herdados. O Cessna, por exemplo, pode ou não definir um novo método

  

partida(tipo) que suporte tanto partida manual como elétrica, mantendo o método

partida() para acionamento manual (sobrecarga), ou simplesmente reescrever

partida() para que faça somente partida elétrica (sobreposição). A classe Cessna,

  por exemplo, pode acrescentar novos métodos como baixarTremdePouso() e

  

horizonteArtificial() (para ler as indicações do horizonte artificial). A classe Jato deve

  sobrepor os métodos partida(), elevador(), leme() com uma implementação específica para um jato genérico, e talvez acrescentar flaps(), pilotoAutomatico(). A classe Jato também pode acrescentar novas variáveis, como capacidadeDeCarga e númeroDePassageiros, para caracterizar melhor os objetos que serão produzidos.

2.7 Considerações finais sobre programação orientada a objetos

  A grande vantagem do paradigma da programação orientada a objetos é o seu caráter unificador: trata todas as etapas do desenvolvimento de sistemas e ambientes sob uma única abordagem. Nesse sentido, a metodologia orientada a objetos cria uma representação do domínio de problema do mundo real e a leva a um domínio de solução que é o software (PRESSMAN, 1995).

  A programação orientada a objetos tornou-se atrativa devido a vantagens como, reuso de código, facilidade para ampliação e manutenção de aplicações, sendo que, o reuso de código é, sem dúvida, reconhecido como a maior vantagem da utilização da programação orientada a objetos, pois permite que programas sejam escritos mais rapidamente. Nos dias atuais, devido à grande competitividade do mercado, as empresas estão sempre buscando melhorar seus serviços. O grau de informatização de todo o processo empresarial é fundamental para que as empresas se tornem competitivas. Portanto, a possibilidade de que seus sistemas possam ser desenvolvidos e alterados mais rapidamente torna-se uma grande vantagem competitiva. Neste sentido, a programação orientada a objetos, através do reuso de código, traz uma grande contribuição nesta área, possibilitando o desenvolvimento de novos sistemas utilizando código já existente.

  Além do aumento de produtividade oferecido pela reuso de código, o uso da tecnologia orientada a objetos pode resultar em maior eficiência porque reduz o risco de erro humano. As estruturas dos programas permanecem intactas e a alteração propaga-se naturalmente pela hierarquia de classes (WINBLAND, 1993).

  A possibilidade de expansão da aplicação, também chamada de extensibilidade, pode ser vista como a capacidade de uma aplicação crescer facilmente sem aumentar demasiadamente a sua complexidade ou comprometer o seu desempenho. Pode-se dizer também que, a extensibilidade e a herança freqüentemente caminham lado a lado (JONES, 2001). A programação orientada a objetos é adequada ao desenvolvimento de grandes sistemas uma vez que é possível construir e ampliar um sistema agrupando objetos, fazendo-os trocar mensagens entre si. O encapsulamento proporciona ocultamento e proteção da informação, sendo que o acesso a objetos somente pode ser realizado através das mensagens que ele está habilitado a receber. Nenhum objeto pode manipular diretamente o estado interno de outro objeto. Se houver necessidade de alterar as propriedades de um objeto ou a implementação de algum método, os outros objetos não sofrerão nenhum impacto, desde que a interface permaneça idêntica. Isto diminui em grande parte os esforços despendidos em manutenção. Além disso, para utilizar um objeto, o programador não necessita conhecer a fundo a sua implementação.

  O polimorfismo torna o programa mais enxuto, claro e fácil de compreender, pois, sem polimorfismo, o programa teria grandes listas de métodos com nomes diferentes, mas comportamento similar. Na programação, a escolha de um entre os vários métodos seria realizada por estruturas de múltipla escolha muito grandes. Em termos de manutenção, isto significa que o programa será mais facilmente entendido e alterado.

  A herança também torna a manutenção mais fácil, pois, se uma aplicação precisa de alguma funcionalidade adicional, não é necessário alterar o código atual, simplesmente cria-se uma nova geração de uma classe, herdando o comportamento antigo, e adicionando-se um novo comportamento ou redefinindo-se o comportamento antigo.

2.8 Linguagem de Programação Visual

  Embora existam muitas definições para o termo linguagem de programação visual ou linguagem de programação gráfica, uma notação comum para todos que usam programação visual é que um programa pode ser chamado de visual se este usa representações gráficas de forma significativa no processo de programação (SHU, 1988).

  Nos últimos anos, muitos trabalhos sobre programação visual foram desenvolvidos, resultando em uma grande quantidade de programas visuais e sistemas de visualização. Algumas destas linguagens visuais são desenvolvidas especificamente para facilitar o processo de programação em uma determinada aplicação, deixando o objetivo principal para o domínio da aplicação. A filosofia por trás das linguagens de programação visual é que programas criados com imagens e gráficos são mais fáceis de entender do que os criados através de linguagens textuais, mas também existem evidências empíricas contra o uso de linguagens visuais em certas aplicações (RAJEEV, 1993) (WHITLEY, 1996). Existem também muitos fatores que devem ser levados em consideração quando se estuda a viabilidade do uso de uma linguagem de programação visual ou um sistema de visualização, como por exemplo a natureza do domínio da aplicação, a habilidade dos programadores e usuários e o ambiente em que a linguagem será implementada.

  As linguagens de programação visuais representam de forma direta a estrutura dos dados e algoritmos, aumentando a habilidade do programador na construção e compreensão dos programas. Nas linguagens textuais tradicionais, a estrutura dos dados e algoritmos é codificada em forma de texto. Já nas linguagens visuais, o ambiente visual remove a camada de detalhes de implementação presente na programação textual, como por exemplo detalhes de sintaxe e a necessidade de decorar identificadores e associá-los aos seus respectivos tipos, e permite que o programador manipule diretamente a estrutura do programa.

2.9 Alguns aspectos sobre o uso de linguagens visuais

  É notório que o ser humano apresenta uma maior habilidade para interpretar um algoritmo através de uma linguagem gráfica do que em uma linguagem textual. A representação gráfica de um algoritmo permite observar a aplicação como um todo, tornando mais fácil a compreensão do mesmo. A figura 2.8 mostra um exemplo onde é possível observar que a representação gráfica permite visualizar, de forma direta, os caminhos de ligação (M) entre as instâncias (I), enquanto que na representação textual esses caminhos são vistos de forma indireta.

Figura 2.8. A representação gráfica e a representação textual

  Pesquisas realizadas na área da neurocognição mostraram que o hemisfério esquerdo do cérebro processa as informações de maneira seqüencial, verbal e lógica, enquanto que no hemisfério direito, as informações são processadas de maneira simultânea, visual e espacial (SPRINGER, 1985).

  É possível, então, afirmar que as técnicas textuais ou verbais de descrição de algoritmos, como os pseudocódigos e as linguagens de programação textuais tradicionais, ativam de forma mais intensa os recursos neurocognitivos do hemisfério esquerdo do cérebro, pois este tipo de construção de algoritmos possui mais estímulos seqüenciais, verbais e lógicos. Isto ocorre porque nas técnicas de programação textual, a quantidade de informação espacial não é significativa, fazendo com que o hemisfério direito do cérebro não contribua significativamente no processo de compreensão, já que desta forma, somente parte do cérebro está sendo acionada. Seguindo a mesma linha de raciocínio, nas técnicas de programação visual, os mecanismos gráficos utilizados para a compreensão de algoritmos, como por exemplo, os fluxogramas estruturados, tendem a estimular os dois hemisférios cerebrais, pois, apesar de trabalhar com pouca informação verbal, a programação visual possui informações seqüenciais e lógicas e obviamente, muita informação visual (SPRINGER, 1985).

  A figura 2.9 mostra que para uma informação de entrada na forma textual, a área ativa (área escura) no cérebro é o hemisfério esquerdo, já para uma informação de entrada visual, como ilustra o exemplo, o cérebro passa a ativar os dois hemisférios.

Figura 2.9. O processamento de informações no cérebro

  Estudos realizados por Ben A. Calonni e Donald J. Bagert (CALONNI, 1994) a respeito do uso de linguagens gráficas para desenvolvimento de algoritmos mostraram que o uso de linguagens visuais torna a tarefa de programar mais intuitiva, facilitando desta forma a aprendizagem e compreensão de algoritmos.

  Com base nessas premissas, estes estudos proporcionaram o desenvolvimento de uma linguagem de programação procedimental baseada em ícones, denominada de BACCII (Ben A. Calloni Coding Iconic Interface). Foram então, realizados testes comparativos entre o BACCII e o PASCAL. Nos testes propostos, alunos das primeiras fases de graduação do curso de Engenharia Elétrica foram divididos em turmas distintas: um grupo usou o BACCII e o outro grupo usou o PASCAL, para desenvolvimento de algoritmos. Após uma análise estatística do desempenho dos alunos, concluiu-se que a linguagem visual foi mais eficaz que a linguagem textual, melhorando a aprendizagem e compreensão de algoritmos.

  Este resultado vai ao encontro ao trabalho realizado por David A. Scanlan na área de compreensão de algoritmos, que após testes realizados concluiu que os métodos gráficos possibilitam a abstração necessária dos detalhes de sintaxe, apresentando-se melhores que os métodos textuais nos processos cognitivos para o ensino do desenvolvimento de algoritmos (SCALAN, 1989).

2.10 Linguagens visuais orientadas a objetos

  A programação visual orientada a objetos é uma programação que combina as técnicas das linguagens de programação orientada a objetos e da programação visual em uma só linguagem. O termo linguagem visual orientada a objetos se aplica às linguagens de programação orientada a objetos que possuem uma sintaxe visual, como também se aplica às linguagens orientadas a objetos que possuem uma sintaxe textual, mas que tenham um ambiente visual de programação (BURNETT, 1995). É importante salientar que um determinado programa que possua uma saída visual e que tenha sido desenvolvido usando uma linguagem orientada a objetos, não necessariamente é um programa visual orientado a objetos. É preciso que seja significativo o uso de representações gráficas no processo de programação para que uma linguagem possa receber o termo visual. A figura 2.10 descreve uma classificação geral para as linguagens orientadas a objetos.

Figura 2.10. Classificação das linguagens orientadas a objetos

  Outro fator importante no uso da programação orientada a objetos em conjunto com a programação visual é a possibilidade, por exemplo, de visualizar graficamente as hierarquias de classes, o que não ocorre com a programação orientada a objetos apresentada na forma textual.

  A programação visual oferece à programação orientada a objetos a capacidade de descrever visualmente os componentes de uma classe, bem como representar graficamente a hierarquia das classes e o comportamento dos objetos. De modo similar, a programação orientada a objetos pode oferecer a programação visual, um maior alcance no domínio de suas aplicações, já que a programação visual se consolidou somente no uso de pequenas aplicações. Desta forma, ao fazer o reuso de objetos existentes, as linguagens visuais podem implementar aplicações maiores e permitir um melhor reuso de código entre aplicações.

  CAPÍTULO 3 DESENVOLVIMENTO DO AMBIENTE DE PROGRAMAđấO K++ Introdução

  Este capítulo abordará os aspectos envolvidos no desenvolvimento do ambiente de programação visual orientado a objetos para robô móvel, chamado de K++. O ambiente proposto contempla a idéia de se usar os paradigmas da programação orientada a objetos em conjunto com a programação visual, para um melhor desenvolvimento de aplicações para robôs móveis com simulação em tempo real usando o próprio robô. Além do uso desses paradigmas de programação, o desenvolvimento do ambiente também levou em consideração os aspectos psicológicos de aprendizagem envolvidos no processo. É importante também salientar que as aplicações criadas no ambiente aqui proposto, foram desenvolvidas baseadas no uso do robô móvel Khepera.

3.1 A linguagem de programação K++

  O ambiente de programação K++ foi baseado em um trabalho que propunha o desenvolvimento de um ambiente de programação visual orientado a objetos para microcontroladores (SOUSA, 1996a) (SOUSA, 1998). Este ambiente de programação, conhecido como O++, tem como característica principal, o desenvolvimento de aplicações para microcontroladores através de uma interface visual. A aplicação desenvolvida no ambiente de programação O++ é convertida em um código C e posteriormente compilada, gerando um programa executável para ser enviado ao microcontrolador a fim de executar a aplicação propriamente dita.

  De maneira semelhante, o ambiente de programação K++ também descreve os algoritmos para implementação de aplicações de forma visual, porém, além de poder gerar código para o robô, ele também permite a simulação de algoritmos em tempo real.

  A linguagem de programação K++ foi projetada baseada nos conceitos da programação orientada a objetos, utilizando para isso, um subconjunto de declarações textuais em C++, que é uma linguagem orientada a objetos. Além disso, o K++ introduz uma simbologia gráfica para representar classes e tem suporte a programação estruturada baseada na técnica de arranjos gráficos proposta por Nassi-Shneiderman, conhecidas como cartas NS (NASSI, 1984).

  A linguagem de programação K++ foi desenvolvida com o objetivo de minimizar as limitações existentes nas linguagens de programação atuais para o Khepera, permitindo que se possa desenvolver aplicações de forma mais didática e com maior produtividade. Isso é obtido, pois a linguagem K++ não requer um alto grau de conhecimento de programação, como acontece com o desenvolvimento utilizando-se linguagem textual (linguagem C++, por exemplo). Além disso, a linguagem K++ não limita o desenvolvimento de aplicações, uma vez que esse desenvolvimento não está centrado apenas no uso de funções pré-definidas em suas bibliotecas, como ocorre em algumas linguagens visuais, tais como o LABVIEW® e MATLAB®. A principal característica da linguagem K++ é a diversidade de funções e aplicações que podem ser desenvolvidas para o robô, a partir de classes que contemplem um variado número de métodos para o controle do robô.

3.2 Hierarquia e descrição de classes

  A linguagem K++ representa a hierarquia de classes através de um diagrama baseado em ícones. As classes são descritas com um nome, na forma textual, e uma figura representada por um ícone. Na programação visual é bastante comum o uso de ícones para a representação de objetos de software, pois a escolha apropriada de um ícone para expressar os objetos representados pela classe tornam a programação mais amigável. Além disso, a hierarquia de classes é visualizada no ambiente na forma top-down, ou seja, a hierarquia é determinada pelos níveis de cima para baixo. A figura 3.1 ilustra um exemplo de hierarquia de classes, onde é possível observar as derivações a partir do nível mais alto da hierarquia. Nesta figura, pode-se observar que a classe I é derivada da classe H, e seguindo o mesmo raciocínio, a classe H é derivada da classe F, que é derivada da E, que por sua vez é derivada da classe C e, finalmente, a classe C é derivada da classe A. O uso dessa estrutura visual torna evidente a relação existente entra as classes, pois como mostra a figura 3.1, pode-se visualizar de forma direta que a classe I é herdeira da classe C, não sendo necessário recorrer a relações intermediárias.

Figura 3.1. Exemplo de hierarquia de classes

  A descrição de composição das classes na linguagem K++ é feita baseada em uma notação proposta por Wasserman (WASSERMAN, 1990). Esta notação determina que toda classe pode ser descrita de forma visual, como sendo um retângulo com o nome da classe, onde esse retângulo delimita a área usada para as declarações da classe. Aliada a notação já existente, foi acrescida à informação textual uma representação icônica para auxiliar a descrição da natureza da classe.

  A área delimitada pelo retângulo é usada para introduzir os membros de dados e os métodos que compõem a classe, podendo os atributos serem públicos ou protegidos. Os elementos definidos como protegidos podem ser acessados somente pela classe ou por seus herdeiros, enquanto que os elementos definidos como públicos, podem ser acessados e manipulados fora da classe.

  A diferenciação entre membros de dados públicos e privados é feita de forma visual, ou seja, o elemento que estiver totalmente dentro da área delimitada pelo retângulo é definido como protegido, ao passo que os elementos que estiverem sobre a fronteira delimitada pelo retângulo são considerados públicos. A figura 3.2 ilustra a descrição de uma classe chamada jogador, onde é possível observar um método público chamado Chutar e um método protegido chamado Correr, bem como, dois membros de dados protegidos chamados força e posição.

Figura 3.2. Exemplo de descrição de classe

3.1.1. Tipologia e representação de dados

  A linguagem K++ combina a informação textual para nomear um membro de dado ou um método com uma simbologia gráfica que representa o tipo do membro de dado ou do valor de retorno do método. Esta informação textual é inserida na representação gráfica, facilitando a compreensão dos elementos inseridos no contexto.

  A simbologia usada para representar os tipos de dados foi baseada parcialmente, em uma notação sugerida na literatura (CALLONI, 1994). Algumas modificações foram efetuadas no sentido de compor de maneira adequada os tipos de membros de dados usados na linguagem K++. Conforme pode ser observado na figura 3.3, um dado do tipo inteiro é representado por um retângulo com os cantos arredondados, o que sugere o arredondamento do valor, e um dado do tipo real é representado por um retângulo com os cantos pontiagudos, o que sugere a precisão do valor. É possível observar que um retângulo com cantos arredondados pode ser inserido em um retângulo com cantos pontiagudos, portanto, pode-se dizer que o retângulo com cantos arredondados está contido no retângulo com cantos pontiagudos, da mesma forma que o conjunto dos números inteiros está contido no conjunto dos números reais.

  Os membros de dados do tipo lógico são representados por uma circunferência parcialmente preenchida, sugerindo os valores de falso e verdadeiro. Para os membros de dados do tipo caractere, os mesmos são representados por um triângulo, em alusão à letra grega delta.

  Seguindo o mesmo raciocínio, a simbologia que representa os tipos de dados descritos por estruturas homogêneas ou arrays é feita através de uma representação gráfica, que usa uma coleção de elementos iguais ao do tipo base do membro de dado.

  Os membros de classe, ou seja, os membros comuns a todos os objetos de uma classe, são representados por um retângulo de linha dupla, com o objetivo de diferencia-los dos membros de instância.

  A notação utilizada para representação de um objeto é feita usando um ícone de identificação referente à classe a qual o objeto pertence, em conjunto com uma descrição textual com o nome do objeto.

Figura 3.3. Representação dos membros de dados Outra característica importante presente na linguagem K++ é a forma como a linguagem distingue as diferenças entre membro de dados local, global e parâmetros. Esta distinção é feita utilizando uma relação espacial entre o retângulo delimitador do membro de dado com o retângulo delimitador do módulo onde o membro de dado foi definido, ou seja, se o membro de dado estiver totalmente dentro do módulo, ele é definido como local; por outro lado, se o mesmo estiver totalmente fora do módulo, ele é definido como global. Seguindo a mesma analogia, caso o membro de dado esteja na fronteira do módulo, o mesmo é considerado como um parâmetro de entrada. A figura 3.4 ilustra a definição de membro de dado local, global e parâmetro.

Figura 3.4. Representação dos tipos de dados

3.1.2 Estruturas de controle

  As estruturas de controle usadas para programação na linguagem K++ são baseadas no uso de arranjos gráficos propostos por Nassi-Shneiderman (NASSI, 1994), também chamadas de cartas NS.

  A utilização das cartas NS, mostradas na figura 3.5, permite a construção de programas estruturados, substituindo os desvios incondicionais por arranjos embutidos. Este tipo de diagrama usa estruturas visuais para representar o fluxo de controle e informações textuais para descrever os dados manipulados, testes de condições, indicação de número de repetições, entre outras.

Figura 3.5. Estruturas de controle usadas no K++

3.1.3 Operações dedicadas

  A linguagem K++ possui um grupo de operações dedicadas, destinadas especificamente ao domínio da aplicação. As operações dedicadas possibilitam descrever a aplicação através de recursos que representam os métodos desenvolvidos especificamente para o robô Khepera. Estes recursos são representados por ícones que definem a natureza da operação. Neste sentido, cada ícone foi desenvolvido de forma que a identificação da operação seja a mais intuitiva possível. É possível observar na figura 3.6, por exemplo, que a orientação das setas representa o sentido de deslocamento do robô.

Figura 3.6. Operações dedicadas no K++

  As operações dedicadas indicam basicamente a natureza do processamento que se deseja realizar, sendo necessário, portanto, que sejam indicados os parâmetros que serão usados na operação. Esses parâmetros geralmente são constantes, identificadores de membro de dados, entre outros. A figura 3.7 mostra um exemplo de operação em um módulo desenvolvido com o K++, onde é possível observar no módulo de partida ou principal, a definição de um membro de dado do tipo inteiro (speed) e, em seguida, o escopo do módulo principal, onde é definida uma determinada aplicação.

Figura 3.7. Exemplo de operações em um módulo em K++

3.2 O ambiente de programação K++

  A figura 3.8 ilustra o ambiente K++, que foi estruturado para trabalhar com três vistas conjuntas. A primeira, chamada de vista de catálogo (lado esquerdo do ambiente), é usada para organizar os elementos do programa (classes, referência ao código e dados). A segunda, chamada de vista de script (lado direito), é usada para desenho de código, descrição de classes e descrição de dados. E a última, chamada vista de saída (lado inferior), é usada para exibir mensagens de erro e ou advertências ao usuário.

Figura 3.8. Vista principal do ambiente de programação K++

  Para promover a facilidade de uso e reduzir a taxa de erro do usuário, a interface homem-máquina, no ambiente K++, utiliza dois mecanismos importantes: a visualização automatizada dos elementos da vista de catálogo e a troca de informações entre as vistas do ambiente, por meio do recurso drag-and-drop (arrastar e soltar).

  Além disso, a linguagem K++ possui um conjunto de operações dedicadas, dispostas em uma barra de ferramentas, que permite desenvolver uma ampla variedade de programas para o robô Khepera. O uso de um grupo de operações dedicadas facilita a implementação das aplicações, minimizando erros de programação e acelerando o desenvolvimento de aplicações. Atualmente as operações contemplam as principais funções para controlar o robô, como andar, parar, girar, ler sensores de proximidade e luz ambiente, entre outros. Essas funções são obtidas através da classe Khepera, desenvolvida em C++ especificamente para o robô Khepera e incorporada ao ambiente K++ para poder realizar simulação em tempo real.

  É importante salientar que, apesar da linguagem K++ permitir o uso de conceitos orientados a objetos, é possível a implementação de aplicações usando estruturas puramente procedimentais. A linguagem K++ não exige que as aplicações sejam implementadas usando a programação orientada a objetos, ou seja, pode-se implementar aplicações usando a programação procedimental. Isto se deve ao fato de muitos programadores desse tipo de aplicação não terem conhecimento sobre a programação orientada a objetos. Assim, o universo de programadores que podem utilizar a linguagem K++ é ampliado, mesmo que não sejam exploradas todas as suas potencialidades.

3.3 Implementação do ambiente K++

  O ambiente de programação K++, como citado anteriormente, foi desenvolvido a partir de um ambiente de programação (O++) para microcontroladores (SOUSA, 1999). Neste sentido, o desenvolvimento do ambiente K++ foi focado principalmente nas alterações intrínsecas ao domínio da aplicação, aproveitando então, a estrutura básica já existente. Apesar de manter a estrutura básica, algumas funcionalidades do ambiente foram modificadas, tendo em vista a grande diferença existe entre os domínios das aplicações propostas.

  O programa escolhido para desenvolver o K++ foi baseado no próprio desenvolvimento do ambiente original, ou seja, seguindo as mesmas premissas. Optou-se por continuar o trabalho usando o Visual C++, que é um ambiente de desenvolvimento que explora as potencialidades da programação visual. O uso da metodologia orientada a objetos, aliada aos recursos de manipulação de estruturas gráficas, faz do Visual C++, uma importante ferramenta para o desenvolvimento de softwares (KRUGLINSKI, 1996) (HOLZNER, 2001).

  Serial e Khepera

3.4 As classes

  São as classes Serial e Khepera que possibilitam ao ambiente de programação K++ interagir com o robô Khepera. Todo processo de comunicação serial com o robô, bem como todos os métodos que permitem que as operações do ambiente K++ controlem o Khepera em tempo real são obtidas com o uso destas classes. A

figura 3.9 mostra como o ambiente K++ depende das classes Khepera e Serial para implementar a comunicação com o robô. No anexo I estão os códigos dessas

  classes implementados em C++.

Figura 3.9. Uso das classes Khepera e Serial pelo Ambiente K++

3.4.1 A classe Serial

  A classe Serial é responsável pela comunicação entre o robô e o microcomputador, sendo, portanto, indispensável que seu controle ocorra de forma confiável. Apesar dos conceitos a respeito da comunicação serial serem bastante conhecidos, desenvolver um programa para trabalhar com comunicação serial não é sempre uma tarefa fácil (CAMPBELL, 1993). Cada tipo de aplicação impõe seu grau de complexidade, fazendo com que seja necessário ter um bom conhecimento do domínio da aplicação em desenvolvimento. A classe Serial desenvolvida para trabalhar com o robô Khepera usa características de controle específicas ao robô. Ela incorpora o controle dos dados enviados e recebidos, baseado no protocolo de comunicação usado pelo robô. Apesar de internamente bastante complexa, a classe Serial permite, através da própria definição de classe, ser utilizada de maneira simples dentro do ambiente de programação. Os métodos descritos pela classe Serial permitem definir qual a porta de comunicação será utilizada, qual a velocidade de comunicação entre o microcomputador e o robô, abrir e fechar o canal de comunicação e enviar e receber pacotes de dados já formatados nos padrões do protocolo de comunicação do robô, além de controlar os sinais de comunicação como CTS (clear to send), DSR (data set ready) e timeout (CAMPBELL, 1993).

3.4.2 A classe Khepera

  A classe Khepera permite que o ambiente de programação K++, além de gerar código para o robô Khepera, possa trabalhar em modo autônomo, e possa simular algoritmos em tempo real. A linguagem K++ incorpora a classe Khepera em conjunto com a classe Serial para enviar e receber comandos em um formato baseado em um protocolo de comunicação desenvolvido pela K-Team [K-TEAM] especificamente para o robô. Esses comandos são encapsulados nos métodos definidos para a classe Khepera e usados pelo ambiente K++.

  Os nomes de alguns métodos como MoveForward, MoveBackward, TurnLeft e TurnRight foram inspirados na linguagem LOGO (PAPERT, 1985) (HARVEY, 1997) e seguem a característica de uma linguagem de programação voltada para o ensino, expressando de forma clara e didática, todos os métodos desenvolvidos para o robô Khepera. A linguagem LOGO é uma linguagem de descrição de movimentos em um plano, concebida com propósitos educacionais consagrada no meio acadêmico e totalmente adequada para ser adequada no controle de robôs móveis. Baseado neste raciocínio, o restante dos métodos seguiu as mesmas características, ou seja, foram definidos com nomes, que, por si só, sugerem a função do método. A tabela 3.1 mostra os métodos descritos pela classe Khepera.

Tabela 3.1. Métodos utilizados para o controle do robô

  Métodos Descrição dos métodos

  Move robô para frente em linha reta com MoveForward (velocidade) determinada velocidade (mm/s)

  Move robô para traz em linha reta com MoveBackward (velocidade) determinada velocidade (mm/s)

  Desliga os motores M1 (motor direito) e M2 StopMotors( )

  (motor esquerdo) Zera contadores de posição de ambos os

  ResetCountMotors( ) motores Retorna a leitura dos 8 sensores de luz

  ReadLightSensors (valor [8]) ambiente Retorna a leitura dos 8 sensores de

  ReadProxSensors (valor [8]) proximidade Retorna a leitura da posição dos motores M1 e

  ReadMotorPosition(valor [2]) M2 Retorna a leitura da velocidade dos motores M1

  ReadMotorSpeed(valor [2]) e M2 Gira o robô à esquerda sobre seu eixo com

  TurnLeft(ângulo) dado ângulo (graus) Gira o robô à direita sobre seu eixo com dado

  TurnRight(ângulo) ângulo (graus) Move robô em linha reta por uma dada

  MoveLine(distância, velocidade) distância (mm) e com uma dada velocidade (cm/s) Faz o robô desviar de um obstáculo girando um

  ObstacleSkipper (ângulo) determinado ângulo sobre seu eixo Faz o robô girar no sentido da fonte de luz de

  LightFinder(valor) maior intensidade, retornando o valor de saturação da luz Seta amplitude do PWM para os motores M1 e

  SetPWM(motor1,motor2) M2

  SetSpeedProfile(velocidade1, Seta velocidade e aceleração para o controle aceleração1, velocidade2, de posição dos motores M1 e M2 aceleração2) Seta parâmetros do PID para o controle de

  SetPIDposition(Kp,Ki,Kd) posição

  Com estes métodos, é possível implementar uma grande variedade de movimentos com o robô, como mover para frente e para trás, girar e fazer curvas, e implementar vários controles sobre o robô, como ler os sensores de proximidade e luz ambiente, ler a velocidade dos motores, ler a posição dos motores, configurar parâmetros de controle, entre outros. Esta variedade de métodos permite o desenvolvimento de um grande número de aplicações, como algoritmos para seguir trajetórias (BIANCHI, 2001) (LUND, 1996), algoritmos para veículos de Braitenberg (BRAINTENBERG, 1984) entre outros. É importante salientar que, apesar da classe

  

Khepera possuir esta variedade de métodos já implementados, nada impede que um

  programador acrescente novos métodos a partir dos métodos existentes. Além disso, a classe Khepera, por ser baseada na orientação a objetos, permite o reuso de seu código em alguma outra aplicação para o robô Khepera desenvolvida com o Visual C++.

3.4.2.1 Descrição dos métodos da classe Khepera

  Todos os métodos descritos a seguir foram desenvolvidos baseados em um determinado robô Khepera. Sabendo que os dados de odometria podem mudar de robô para robô, o ambiente K++ permite configurar alguns parâmetros importantes, como a resolução do encoder e distância entre os eixos das rodas. Portanto, os métodos explicados a seguir são baseados nos valores obtidos com um determinado robô Khepera que foi usado nos experimentos.

  • MoveForward (velocidade)

  Este método é responsável pelo deslocamento do robô e tem como parâmetro de entrada um valor de velocidade dado em pulso/10ms. Este valor é atribuído aos dois motores simultaneamente e pode variar de 1 pulso/10ms, que corresponde a uma velocidade de 8 mm/s (velocidade mínima), a até 127 pulsos/10ms, que corresponde a aproximadamente 1 m/s (velocidade máxima). Como o parâmetro de entrada (velocidade) é dado em pulso/10ms, as velocidades possíveis para o robô ocorrem numa escala de 8 mm/s em 8 mm/s, ou seja, as velocidades serão escalonadas em saltos de velocidade (8 mm/s, 16 mm/s, 24 mm/s ...1016 mm/s). Para fins didáticos, optou-se por usar como unidade de velocidade o cm/s, sendo possível obter valores em uma escala unitária aproximada (1 cm/s, 2 cm/s, 3 cm/s ...100 cm/s), o que não comprometeu a eficiência do robô para as aplicações estudadas.

  • MoveBackward (velocidade)

  De modo similar ao método MoveForward, este método faz com que o robô ande para trás em linha reta com uma determinada velocidade até que algum outro comando seja enviado ao robô. A inversão no sentido de deslocamento do robô é obtida simplesmente invertendo-se o sinal do parâmetro de entrada. Os valores usados para a velocidade são os mesmos usados no método MoveForward.

  • StopMotors ( )

  O método StopMotors, como o próprio nome sugere, faz os motores pararem simultaneamente. O método StopMotors funciona de forma similar ao método

  

MoveForward, ou seja, se for atribuída uma velocidade zero ao parâmetro de

  entrada em MoveForward, o efeito sobre o robô será o mesmo obtido pelo uso do método StopMotors. Apesar de simples, o uso de um método exclusivo para interromper o deslocamento do robô torna o entendimento do algoritmo mais fácil.

  • ResetCountMotors ( )

  O método ResetCountMotors(), ao ser acionado, zera o valor dos contadores dos motores. Apesar de simples, este método é muito utilizado em aplicações em que se precisa conhecer a posição do robô dentro do ambiente, pois o seu posicionamento é baseado exclusivamente na contagem dos encoders dos motores.

  • ReadLightSensors ( int valor [8] )

  O método ReadLightSensors é responsável pela leitura dos valores dos 8 sensores de luz ambiente. Ao ser acionado, este método retorna um valor para cada sensor e os armazena em um vetor de oito posições, sendo que cada sensor varia de 0 (ambiente com muita luminosidade) a valores próximos a 450 (ambiente escuro). Este método pode ser usado, por exemplo, em alguma aplicação em que o robô tenha que seguir uma fonte de luz. A figura 3.10 ilustra um exemplo com uma vetor de valores para os sensores de luminosidade de acordo com a distância da fonte de luz.

Figura 3.10. Leitura dos sensores de luz ambiente

  • ReadProxSensors ( int valor [8] )

  De maneira similar ao método ReadLightSensors, o método

  

ReadProxSensors é responsável pela leitura dos valores dos sensores de

  proximidade e é utilizado para detectar obstáculos na trajetória do robô. Este método retorna o valor de cada sensor e os armazena em uma matriz de 8 posições, sendo que cada valor varia de 0 (robô longe do obstáculo) a 1023 (robô próximo ao obstáculo). Vale lembrar que a distância em que o robô se encontra do obstáculo depende diretamente do tipo de material do objeto. A figura 3.11 mostra um exemplo com um vetor de valores para os sensores de proximidade de acordo com a distância do obstáculo.

Figura 3.11. Leitura dos sensores de luz proximidade

  • ReadMotorPosition ( int valor [2] )

  Este método faz a leitura de cada um dos encoders acoplados às rodas do robô (um pulso do encoder corresponde a 0,08 mm). Neste caso, por exemplo, para um deslocamento da roda de 10 cm, o valor lido para o encoder desta roda será equivalente a 1250 pulsos. O número de pulsos para cada encoder pode variar em uma escala de 32 bits.

  • ReadMotorSpeed ( int valor [2] )

  Este método faz a leitura da velocidade instantânea para cada um dos motores do robô (cada pulso/10ms corresponde a 8 mm/s). A velocidade do robô pode variar de 8 mm/s a aproximadamente 1 m/s.

  • TurnLeft (ângulo, velocidade)

  O método TurnLeft tem como parâmetros de entrada o valor do ângulo (graus) e velocidade (mm/s) de giro do robô. Neste método o motor do lado direito do robô gira no sentido horário e o motor do lado esquerdo no sentido oposto, fazendo o robô girar sobre seu próprio eixo para o lado esquerdo. Internamente o método leva em consideração o diâmetro do robô para poder fazer a conversão do valor do ângulo de giro, dado em graus, para um valor correspondente a leitura do

  

encoder. A figura 3.12 ilustra o processo de conversão das unidades, que funciona

  da seguinte forma: sabendo que a distância entre as rodas do robô é de 53 mm, pode-se facilmente deduzir que a circunferência completa descrita pelo giro de somente uma roda é de aproximadamente 166,5 mm. Vale lembrar que os valores aqui definidos como constantes (distância entre as rodas do robô e resolução do

  

encoder) foram baseados em um determinado robô Khepera usado no

  desenvolvimento deste trabalho. Em uma breve analogia é possível deduzir que, se 166,5 mm equivale a uma volta completa do robô sobre seu eixo (360°), o percurso percorrido pela roda para avançar 1° é de aproximadamente 0,46 mm. Por fim, sabendo que um pulso do encoder equivale a 0,08 mm, conclui-se que 1° de giro do robô equivale a aproximadamente 5,78 pulsos do encoder da roda. Desta forma, o método executa um loop fazendo a leitura da posição do encoder até que o valor lido seja igual ao calculado para a distância percorrida no giro.

  Para o robô girar um ângulo de 90° (ver figura 3.13), por exemplo, o valor lido no encoder deverá ser de aproximadamente 520 pulsos, ou seja, o robô irá girar até que o contador de pulsos do encoder chegue a 520.

Figura 3.12. Processo de conversão de unidades para o método TurnLeftFigura 3.13. Exemplo de giro com o método TurnLeft

  • TurnRight (ângulo, velocidade)

  De maneira similar ao método TurnLeft, o método TurnRight tem como parâmetro de entrada o ângulo (graus) e a velocidade (mm/s) de giro do robô. Neste método, o motor do lado direito do robô gira no sentido anti-horário e o motor do lado esquerdo no sentido horário, fazendo com que o robô gire sobre seu próprio eixo para o lado direito. Os procedimentos internos do método são os mesmos do método TurnLeft.

  • MoveLine (distância, velocidade)

  O método MoveLine facilita a execução de trajetórias em linha reta quando o objetivo é se deslocar por uma determinada distância. Este método recebe como parâmetros de entrada os valores da distância (mm) a ser percorrida e a velocidade (cm/s) de deslocamento do robô. O método MoveLine usa internamente os métodos

  

MoveForward, ReadMotorPosition e ReadProxSensors, como mostra o código

descrito na figura 3.14.

  Apesar de sua implementação não ser muito complexa, este método pode ser considerado um método avançado e que facilita consideravelmente a implementação de algoritmos. No próximo capítulo, serão apresentados alguns exemplos de aplicações, onde é possível observar a importância do método

  MoveLine.

  Este método é baseado na resolução do encoder que faz a leitura da posição dos motores, portanto, os cálculos e conversões demonstrados a seguir foram baseados no robô Khepera usado neste trabalho. Eventuais diferenças na resolução do encoder devem ser consideradas e podem ser alteradas nos parâmetros de configuração do Kephera disponível no ambiente de programação K++.

Figura 3.14. Código do método MoveLine

  Inicialmente o valor da distância é transformado em posições do encoder e então o robô começa a se deslocar através da chamada do método MoveForward, executado internamente ao método MoveLine, com uma determinada velocidade. Durante o percurso o método ReadMotorPosition é executado dentro de um loop, de forma a ler a posição dos encoders até que o valor lido seja igual ao valor calculado para a distância. Sabendo que a cada pulso de encoder, o robô se move 0,08 mm, é fácil concluir que 1 mm de deslocamento do robô equivale a 12,5 pulsos do encoder. O método MoveLine também implementa a detecção de obstáculos. Para isso, faz-se uso do método ReadProxSensor dentro do mesmo loop, verificando instantaneamente a leitura dos sensores de proximidade. É possível observar no algoritmo que é feita somente a leitura dos quatro sensores frontais, pois, como o movimento do robô é para frente em linha reta, não há necessidade de leitura dos sensores laterais e traseiros.

  Também pode ser observado, que é determinado um valor constante para o valor de saturação dos sensores chamado m_prox, que pode chegar até 1023. O valor padrão de m_prox é 800, podendo ser mudado de acordo com o tipo de material do obstáculo. Neste caso, quanto maior o valor de m_prox, mais próximo do obstáculo o robô parará. Em algumas aplicações não é interessante que o robô se aproxime demais do obstáculo, portanto, quanto o valor para m_prox ser diminuído. O valor de m_prox pode ser ajustado nos parâmetros de configuração do robô existente no ambiente de programação K++.

  Sendo assim, caso exista um obstáculo na trajetória do robô, o robô ficará parado em frente ao obstáculo até que o obstáculo seja removido, seguindo então até a posição final dada pelo parâmetro de distância. Um exemplo de uso para esta importante funcionalidade seria o robô trabalhando como um AGV em um chão de fábrica. Caso algum objeto ou pessoa, por exemplo, se encontre na trajetória do robô, o mesmo parará, evitando uma colisão. Assim que o objeto em questão sair do percurso do robô, o mesmo seguirá em frente até o seu destino. Esta situação é mostrada na figura 3.15.

Figura 3.15. Tratamento de obstáculos usado no método MoveLine

  • ObstacleSkipper (ângulo)

  O método ObstacleSkkiper tem uma grande importância no desenvolvimento de aplicações que envolvem principalmente a exploração de ambientes. A sua finalidade básica é encontrar e desviar de obstáculos. Neste caso, quando o robô encontra um obstáculo, ele pára e gira em torno de seu eixo por um dado ângulo, evitando desta forma o obstáculo. Internamente, como pode ser visto na Figura 3.16, o método ObstacleSkkiper executa continuamente uma rotina de verificação dos sensores de proximidade, até que um obstáculo seja encontrado. Esta condição acontece quando o valor de um dos sensores é maior que o valor da constante de saturação m_prox, já comentada no método MoveLine.

  Ao encontrar o obstáculo, o robô primeiramente parará, e em seguida é feito um cálculo da soma dos sensores de cada lado do robô. É baseado nestes valores que o algoritmo define para que lado o robô deve girar. Nesse caso, se a soma dos valores dos sensores do lado esquerdo (sensores 0, 1, 2 e 7) for maior que a soma dos valores dos sensores do lado direito (sensores 3, 4, 5, 6), o robô girará para a direita, caso contrário o robô girará para a esquerda. Vale lembrar que quanto maior o valor retornado pelo sensor de proximidade, mais próximo do obstáculo o robô se encontra, portanto, se a soma dos sensores do lado esquerdo é maior que a soma dos sensores do lado direito, fica claro que a maior parte do obstáculo está do lado esquerdo, portanto o robô deve girar para a direita a fim de se desviar do obstáculo.

Figura 3.16. Código interno ao método ObstacleSkipper para desvio de obstáculo

  • LightFind (valor)

  Este é outro importante método existente na classe Khepera. Sua finalidade básica é a de encontrar a direção da maior fonte de luz. Apesar de internamente este método ser bastante complexo, o seu uso no ambiente de programação é bastante simples, sendo executado através da operação LightFinder. O funcionamento do método pode ser observado na figura 3.17, onde é ilustrada uma situação onde o sensor 4 recebe a maior incidência de luz. Basicamente, o método faz a leitura dos sensores de luminosidade e analisa qual sensor tem o menor valor (condição de maior intensidade de luz), fazendo o robô girar no sentido do sensor que retornou o menor valor. O giro do robô é baseado na posição do sensor localizado no robô, ou seja, cada sensor possui uma localização com referência no centro do robô. Por exemplo, o sensor 0 localizado na lateral esquerda do robô, está a 90° do centro do centro do robô. Baseado nestas considerações, a tabela 3.2 mostra os valores de giro do robô de acordo com o sensor que recebe a maior intensidade de luz.

Tabela 3.2. Ângulo de giro do robô de acordo com a luminosidade nos sensores

  Sensor Ângulo de giro

  Sensor 0 90° à esquerda Sensor 1 45° à esquerda Sensor 2 10° à esquerda Sensor 3 10° à direita Sensor 4 45° à direita Sensor 5 90° à direita Sensor 6 170° à direita Sensor 7 170° à esquerda

  Observando novamente a figura 3.17 fica claro que, como o sensor 4 está recebendo a maior quantidade de luz, o sentido do giro será para direita e o valor do ângulo de giro será de 45°; portanto, o robô girará 45° à direita.

Figura 3.17. Funcionamento do método LightFind

  • SetPWM(M1,M2)

  Este método permite que os valores do PWM dos motores sejam alterados de acordo com a necessidade da aplicação. A menor razão PWM é igual a zero (0%), sendo que 255 corresponde à razão de avanço máximo e –255 corresponde a razão de avanço mínimo.

  • SetSpeedProfile(V1,A1,V2,A2)

  O método SetSpeedProfile permite que a curva característica para a velocidade e aceleração do controle de posição seja alterada. O parâmetro

  

max_speed, conforme ilustrado na figura 3.18, indica a máxima velocidade

  alcançada durante o deslocamento. A unidade de velocidade para este caso é o pulso/10 ms que corresponde a 8 mm/s. A unidade de aceleração é o

  2

  [(pulse/256)/10ms)/10 ms], que corresponde a 3,125 mm/s . O valor padrão para a velocidade máxima é igual a 20 e a aceleração 64.

Figura 3.18. Curva característica de aceleração e velocidade

  • SetPIDposition(Kp,Ki,Kd)

  Este método configura os parâmetros PID (Kp, Ki, Kd) para o controlador de posição. Nas aplicações desenvolvidas com o K++ não foram feitas alterações em nenhum destes parâmetros, o que não impede que algum outro tipo de aplicação altere estes valores de acordo com sua necessidade. Os valores padrão para os parâmetros do PID são os seguintes: Kp = 3000, Kd = 4000 e Ki = 20.

  CAPÍTULO 4 RESULTADOS OBTIDOS Introdução

  O objetivo deste capítulo é demonstrar a implementação de algoritmos através do ambiente de programação K++. Serão também abordados alguns aspectos sobre a utilização do ambiente K++, bem como, todo o processo de criação de algoritmos usando a programação procedimental e a programação orientada a objetos neste ambiente. Por fim, serão apresentados alguns aspectos sobre o uso de programação avançada para o desenvolvimento de novos métodos a partir do Visual C++.

4.1 Usando o ambiente de programação K++

  Como já mencionado, o objetivo do ambiente de programação K++ é facilitar o desenvolvimento de algoritmos para robôs móveis. Neste sentido, a visualização automatizada dos elementos da vista de catálogo e a troca de informação entre as vistas do ambiente, por meio do recurso drag and drop, são elementos fundamentais na utilização do ambiente K++.

  Conforme ilustra a figura 4.1, a vista de catálogo é composta por quatro árvores, que armazenam informações sobre a hierarquia das classes, funções definidas pelo usuário, dados relativos ao módulo em foco e as possíveis mensagens que podem ser ativadas, bem como as funções que podem ser chamadas.

  A árvore que armazena a hierarquia das classes (figura 4.1a) é montada diretamente pelo usuário, de acordo com a estruturação das classes usadas na aplicação. As demais árvores são montadas seguindo uma estruturação própria, que é pertinente ao tipo de informação que ela armazena. Particularmente, as árvores de dados e mensagens são montadas automaticamente, de acordo com o contexto da área de desenho (vista de script).

Figura 4.1. As árvores de classes, funções, dados e chamada

  A árvore que armazena as funções (figura 4.1b) é estruturada em quatro ramos, sendo o primeiro ramo o módulo de partida (main) do programa. Os demais ramos correspondem ao tipo de retorno da função (int, real, void).

  A árvore que armazena os dados (figura 4.1c) é estruturada de acordo com o escopo de seus elementos e é dividida em quatro ramos. O primeiro ramo está relacionado com os dados globais, o segundo corresponde aos dados definidos localmente, o terceiro corresponde aos dados advindos de parâmetros de entrada e o último ramo armazena os membros de dados dos objetos acessíveis pelo módulo em foco.

  A árvore de chamada de módulo (figura 4.1d) é organizada em duas seções. A primeira está relacionada com as mensagens que podem ser ativadas e a segunda é formada por métodos ou funções que podem ser chamadas. As mensagens são categorizadas seguindo o escopo dos objetos acessíveis ao módulo selecionado. Por fim, as funções são organizadas seguindo o valor de retorno.

  É importante salientar que a visualização das árvores é ativada automaticamente, de acordo com o tipo de desenho que o usuário está realizando na vista de script. Estas árvores também podem ser ativadas pelo usuário, utilizando diretamente os botões localizados acima da estrutura das árvores.

  A montagem das árvores de dados (figura 4.1c) e de módulos (figura 4.1b) é feita automaticamente, seguindo o contexto da operação selecionada e do módulo ativo. Ao selecionar uma operação, o ambiente K++ busca os elementos de dados definidos no escopo do módulo ao qual pertence a operação, monta a árvore de dados e efetua sua visualização. Quando a operação selecionada for de ativação de mensagens ou de chamada de função, o ambiente K++ monta e visualiza a árvore de funções e funções membros, visíveis ao módulo ao qual pertence a operação.

  Desta forma, o ambiente K++ altera a disposição das informações disponíveis de acordo com o contexto da operação em uso. A forma automatizada de disponibilizar ao usuário somente aquilo que está à disposição para o seu uso facilita e agiliza o desenvolvimento dos algoritmos. Isto também possibilita uma diminuição nos erros do usuário, pois, a organização das informações segue o escopo do módulo que está sendo construído.

  Outra característica importante do ambiente é que, quando uma alteração é feita em algum elemento do programa, essa alteração é atualizada automaticamente em todas as operações que referenciam este elemento dentro do programa, evitando assim a propagação de erros sintáticos de nomes.

  Por fim, o ambiente K++ permite que alterações nos parâmetros de configuração do robô e nos parâmetros relativos ao ambiente em que o robô se encontra sejam feitas de maneira simples. No caso dos parâmetros de configuração do robô, é possível alterar o valor da resolução do encoder e o valor da distância entre os eixos das rodas do robô. Para os parâmetros de configuração do ambiente, é possível determinar quais os valores de saturação para os sensores de proximidade e luz ambiente. Dependendo da aplicação, por exemplo, pode não ser interessante que o robô se aproxime demasiadamente do obstáculo. Neste caso, deve-se diminuir o valor de saturação para os sensores de proximidade. Caso a aplicação, por exemplo, não necessite que o robô se aproxime demais da fonte de luz, o valor de saturação dos sensores de luz ambiente deve ser aumentado. A

figura 4.2 mostra a caixa de diálogo de configuração de parâmetros disponíveis no ambiente K++.Figura 4.2. Diálogo de configuração de parâmetros no ambiente K++

4.2 Programação procedimental usando o K++

  O uso da programação procedimental no ambiente K++ permite que aplicações menos complexas sejam desenvolvidas de maneira mais simples, pois é comum que muitos programadores, que trabalham no desenvolvimento de software para robôs, não tenham familiaridade com a programação orientada a objetos, justificando assim, o uso da programação procedimental. A programação orientada a objetos é indicada para aplicações mais complexas, onde o reuso de código é fundamental para a implementação de algoritmos. Portanto, é interessante que o programador se familiarize com o ambiente, primeiramente desenvolvendo aplicações mais simples através da programação procedimental. A seguir serão dados alguns exemplos de aplicações desenvolvidas usando apenas a programação procedimental.

4.2.1 Exemplo 1 – Robô AGV

  O primeiro exemplo é dado por uma aplicação que tem a finalidade de andar por um determinado percurso, simulando o deslocamento de um AGV em um chão de fábrica, conforme ilustra a figura 4.3.

Figura 4.3. Robô Khepera fazendo um percurso definido em um chão de fábrica

  Nesta aplicação, o robô deverá partir de um ponto inicial e percorrer um percurso quadrangular. Para isso será necessário o uso de duas operações para o desenvolvimento do algoritmo: a operação MoveLine e a operação TurnLeft.

  Neste exemplo, as operações são definidas apenas dentro do módulo de partida (main), e pode ser visualizado na figura 4.4.

Figura 4.4. Algoritmo do exemplo 1 desenvolvido no K++

  As operações contidas no módulo main são executadas na forma seqüencial de cima para baixo, a não ser que haja alguma estrutura que permita desvios, como as estruturas do tipo if, for e while. Neste caso, pode-se observar que o algoritmo é executado seqüencialmente até a última instrução. Primeiramente é executado o método MoveLine, com parâmetro de entrada igual a 100 para a distância e 5 para a velocidade, fazendo o robô andar em linha reta por 100 mm a 5 cm/s, para em seguida executar o método TurnLeft, com parâmetro de entrada igual a 90, fazendo o robô girar sobre seu eixo em 90°. Repetindo esta seqüência, como ilustrado na figura 4.4, e acompanhando o raciocínio inicial, é fácil perceber que o robô percorrerá uma trajetória quadrada.

  Vale lembrar que, como descrito no capítulo 3, a operação MoveLine implementa internamente a detecção de obstáculos frontais, ou seja, caso algum objeto ou pessoa se encontre em frente ao robô, o mesmo ficará parado até que o obstáculo seja removido.

4.2.2 Exemplo 2 – Robô AGV

  O segundo exemplo apresentado na forma de programação procedimental é semelhante ao primeiro exemplo, mudando somente a forma de escrever o algoritmo. No exemplo 1, pode-se notar que o algoritmo repete em seu código várias vezes os mesmos comandos. Baseado nesta observação e fazendo uso de um novo recurso do ambiente K++, é possível simplificar consideravelmente o algoritmo escrito no exemplo 1 através do uso de uma estrutura de controle for, como mostra a figura 4.5.

Figura 4.5. Algoritmo do exemplo 2 usando estrutura de controle

  Através do uso desta estrutura de controle do tipo for, ou seja, uma estrutura de controle que implementa iterações, as operações MoveLine e TurnRight, são executadas dentro de um loop. Para efetuar a estrutura for é necessária uma variável de controle, neste caso a variável counter, que é definida no escopo do módulo main, conforme ilustra a figura 4.6. Portanto, repetindo este loop 4 vezes, é possível observar, seguindo o mesmo raciocínio do exemplo 1, que o robô executará uma trajetória quadrada.

Figura 4.6. Definição da variável counter no módulo main

4.2.3 Exemplo 3 – Robô Explorador

  Este exemplo simula uma aplicação de exploração de ambiente. O objetivo é fazer com que o robô navegue continuamente dentro de um determinado ambiente, desviando-se de obstáculos, como ilustra a figura 4.7.

Figura 4.7. Exemplo de exploração de ambiente com obstáculos Diferentemente dos primeiros exemplos, a aplicação de exploração de ambiente envolve o uso de um número maior de operações para o desenvolvimento do algoritmo. Neste caso, o algoritmo é formado por estruturas de controle do tipo

  

seqüência (atribuição), if (decisão) e do/while (repetição condicional) e pelas

  operações MoveForward, ReadProxSensors, TurnLeft, TurnRight e StopMotors, conforme descrição realizada no capítulo 3.

  Assim como nos exemplos anteriores, este algoritmo é totalmente desenvolvido dentro do módulo principal e, além disto, faz uso de algumas variáveis, também definidas no escopo do módulo main. A figura 4.8 ilustra as variáveis definidas no módulo main.

Figura 4.8. Definição das variáveis no módulo main

  É possível observar, conforme ilustra a figura 4.9, que o algoritmo é executado continuamente (loop eterno) dentro de uma estrutura de controle do tipo

  

do/while. A primeira instrução do loop principal é a operação MoveForward, que faz

  o robô se movimentar para frente em linha reta com uma velocidade de 5 cm/s. Em seguida, o algoritmo entra em um loop secundário (estrutura do/while), com o objetivo de fazer continuamente a leitura dos sensores frontais de proximidade. Após a leitura dos sensores, é atribuída às variáveis esq e dir, a soma da leitura dos sensores de proximidade do lado esquerdo e direito, respectivamente. Este loop é finalizado quando a variável soma, obtida por uma operação que faz a soma das variáveis esq e dir, atinge um valor maior que 1000. Vale lembrar que quanto maior o valor da variável soma, mais próximo do obstáculo o robô se encontra. Portanto, quando o robô se aproxima de um obstáculo, a variável soma atinge o valor de saturação (neste caso, maior que 1000), e o loop secundário é finalizado. Em seguida, através de uma estrutura de controle do tipo decisão (if), o algoritmo compara as variáveis esq (soma dos sensores do lado direito do robô) e dir (soma dos sensores do lado direito do robô) e, caso a soma dos sensores do lado esquerdo seja maior que a soma dos sensores do lado direito, o robô irá girar 90° para a direita. Isto significa dizer que os maiores valores de saturação se encontram no lado esquerdo do robô, ou seja, a maior parte do obstáculo está do lado esquerdo do robô. Portanto, neste caso, o robô girará 90° para a direita e o algoritmo voltará para o inicio do loop principal.

Figura 4.9. Algoritmo para exploração de ambiente no K++

4.2.4 Exemplo 4 – Robô Explorador

  O objetivo desta aplicação, assim como no exemplo 3, é a de explorar um determinado ambiente, evitando obstáculos. Para este exemplo, foi usada uma operação, chamada de ObstacleSkipper, desenvolvida especificamente para o desvio de obstáculos.

  Como ilustrado na figura 4.10, é possível observar que, com o uso desta operação específica, o algoritmo desenvolvido ficou menor do que o algoritmo do exemplo anterior, ou seja, toda a parte de leitura de sensores, verificação de saturação dos sensores e comparação dos valores obtidos, foi substituída por uma única operação. Neste caso, a substituição de um conjunto de operações por uma única operação equivalente, facilita o desenvolvimento do algoritmo, pois o uso de um número expressivo de estruturas visuais torna o seu entendimento mais difícil.

Figura 4.10. Algoritmo simplificado para exploração de ambiente no K++

  Outra observação importante neste exemplo é que o mesmo não faz uso de variáveis para o controle do algoritmo, pois a própria operação ObstacleSkipper implementa internamente as variáveis de controle.

4.3 Programação orientada a objetos usando o K++

  A principal característica do ambiente de programação K++ é a possibilidade de descrever visualmente algoritmos usando programação orientada a objetos. Como se sabe, o uso da programação orientada a objetos é indicada no desenvolvimento de aplicações que necessitam de reuso de código, ocultação de informação, dentre outros. Neste sentido, o exemplo dado a seguir, mostra uma aplicação onde é possível observar a hierarquia de classes e o reuso de código, além de outros conceitos de programação orientada a objetos.

4.3.1 Exemplo 1 – Robô Explorador

  Este exemplo ilustra uma aplicação onde o robô tem como objetivo navegar por um determinado ambiente, desviando de obstáculos quando necessário. Como a intenção é desenvolver o algoritmo usando programação orientada a objetos, o primeiro passo para iniciar o algoritmo é criar uma classe que implemente a navegação e o desvio de obstáculos.

4.3.1.1 Definindo a classe Explorador

  Inicialmente, cria-se uma classe chamada Explorador (em alusão a exploração de ambientes inóspitos), que como o próprio nome sugere, tem por objetivo fazer a exploração de um dado ambiente. Para tal, serão implementados dois métodos nesta classe: o método mover e o método navegar, conforme ilustra a figura 4.11.

  O método mover faz com que o robô se mova no ambiente, verificando a presença de obstáculos, através da leitura dos sensores de proximidade do robô. Internamente, o método mover executa a operação MoveForward, que faz o robô se deslocar para frente em linha reta com uma dada velocidade, neste caso 5 cm/s, e em seguida executa a operação ObstacleSkipper, que faz o robô parar ao encontrar um obstáculo, e girar com determinado ângulo, neste caso 45°, fazendo o robô desviar-se da trajetória de colisão com o obstáculo.

  O método navegar, implementa a navegação contínua do ambiente através da chamada do método mover, dentro de um loop eterno.

Figura 4.11. A classe Explorador

4.3.1.2 Definindo o objeto AGV

  O próximo passo no desenvolvimento orientado a objeto, agora que já existe uma classe definida para a exploração de ambientes, é criar um objeto e torna-lo uma instância desta classe. Como já mencionado no capítulo 2, a classe é somente um molde para a criação de objetos, ou seja, é preciso criar algum objeto para que se possa interagir com o mundo real. Neste sentido, é criado então um objeto chamado AGV (em alusão a capacidade de navegação dos AGV’s) pertencente à classe Explorador. A figura 4.12a mostra o escopo do módulo main, onde é criado um objeto chamado AGV, pertencente à classe Explorador. Por fim, a aplicação é executada a partir de uma operação de chamada de método, onde é enviada uma mensagem para o objeto AGV começar a navegar (ver figura 4.12b).

Figura 4.12. Módulo de partida do exemplo 1

4.3.2 Exemplo 2 – Robô que se move em direção à luz

  Este segundo exemplo ilustra uma nova aplicação onde o robô tem como objetivo, além de navegar por um determinado ambiente, encontrar uma fonte de luz fixa em algum ponto do ambiente explorado, desviando de obstáculos quando necessário. Neste caso, será usado o mesmo algoritmo desenvolvido no exemplo anterior, já que o objetivo aqui é demonstrar o reuso de código.

  Como já existe uma classe que implementa a navegação e o desvio de obstáculos, será necessário somente definir uma nova classe que implemente a navegação em direção à luz. É importante observar que aqui é utilizado o conceito de herança, onde uma nova classe é criada a partir de uma classe existente. Vale lembrar que definindo uma nova classe a partir de uma classe existente, esta nova classe herdará todos os métodos da classe-pai.

4.3.2.1 Definindo a classe Besouro

  A classe Besouro (em alusão a uma espécie de besouro que voa na direção da luz) é criada como uma especialização da classe-pai, Explorador. O objetivo desta nova classe é explorar um determinado ambiente, navegando até encontrar a fonte de luz de maior intensidade existente neste ambiente.

  A classe Besouro, ilustrada na figura 4.13, define apenas o método

  

procuraLuz, e herda os métodos da classe-pai Explorador. Este método executa,

  primeiramente, através de uma estrutura de controle do tipo do/while, a operação

  

LightFinder, que verifica a intensidade de luz ambiente em cada sensor, e gira o

  robô no sentido da maior intensidade de luz, retornando o menor valor registrado pelos sensores. Neste caso, quanto menor o valor retornado pelos sensores, maior é a intensidade da luz. Após a execução da operação LightFinder, o método procuraLuz executa o método mover da classe-pai Explorador.

  Na verdade, este é um ponto chave da orientação a objeto: o reuso de código. Aqui, é possível observar que não é preciso definir um novo método mover para que a classe Besouro possa se deslocar no ambiente. Basta usar o método mover já definido na classe Explorador, pois a classe Besouro é filho da classe-pai

  

Explorador. Sendo assim, através do conceito de herança, a classe Besouro herda

todos os métodos definidos na classe-pai, Explorador.

  Por fim, no método procuraLuz, a condição de saída da estrutura do/while é dada pela comparação entre o valor retornado pelo sensor (menor valor) e o valor de saturação da luz, ou seja, caso o valor retornado pelo sensor seja menor que o valor de saturação da luz (condição de proximidade da luz), a aplicação é finalizada.

Figura 4.13. Classe Besouro

4.3.2.2 Definindo o objeto BEETLE

  A figura 4.14a mostra o escopo do módulo main da aplicação, onde é criado um novo objeto chamado BEETLE (besouro em inglês), pertencente à classe

  

Besouro. Como já mencionado, a classe Explorador tem como objetivo a exploração

  de ambientes e a classe Besouro, uma especialização da classe Explorador, tem como objetivo navegar (explorar) na direção da maior intensidade de luz do ambiente.

  A figura 4.14b mostra o módulo main, onde é possível observar a operação de envio de mensagem ao objeto BEETLE, executando o método da classe Besouro,

  

procuraLuz. Vale lembrar que o método procuraLuz, apesar de pertencer a classe

Besouro, ativa o método mover pertencente a classe-pai, Explorador.

  Sendo assim, é possível perceber neste exemplo o reuso efetivo de código, onde uma nova classe (classe Besouro) foi criada a partir de uma classe já existente (classe Explorador), herdando desta forma, todos os métodos definidos na classe- pai.

Figura 4.14. Descrição do modulo main

  A figura 4.15 ilustra a árvore de classes e a árvore de chamada relativa a este exemplo, onde é possível visualizar a ligação entre as classes Besouro e Explorador.

Figura 4.15. Visualização das árvores de classes e chamadas

  Como já mencionado, no ambiente de programação K++, quando uma mensagem é inserida no escopo de um módulo, a árvore de chamadas é mostrada automaticamente no ambiente, bastando apenas arrastar (recurso drag and drop) o método a ser executado pela chamada da mensagem. Este recurso é muito importante, pois permite ao programador visualizar, de forma bastante clara a ligação existente entre os objetos definidos pelas classes e seus respectivos métodos que podem ser usados na aplicação em foco, facilitando assim o desenvolvimento dos algoritmos e evitando erros de programação.

  Por fim, este exemplo mostrou de uma maneira didática as vantagens do uso da programação orientada a objetos. A criação de uma nova classe a partir de uma classe já existente reforça a idéia do reuso de código através da herança dos métodos descritos na classe-pai, e a comunicação dos objetos através do envio de mensagens facilita a compreensão dos algoritmos desenvolvidos.

4.4 Programação avançada usando o Visual C++

  Além das funcionalidades do ambiente de programação K++, a classe

  

Khepera usada no ambiente permite também que outras aplicações possam ser

  desenvolvidas usando o Visual C++. A grande vantagem desta abordagem está no fato de que a biblioteca de operações do K++ pode ser expandida de acordo com a necessidade das aplicações, bastando para isso, o conhecimento do programador para desenvolver novas operações. Apesar do K++ ser um software aberto, fazer alterações em suas bibliotecas requer uma certa habilidade do programador. Mas o fato do K++ ter sido desenvolvido usando os conceitos da programação orientada a objetos permite através do reuso de código, o desenvolvimento de novas operações de maneira bastante produtiva.

  Neste sentido, o exemplo a seguir mostra uma aplicação complexa usando a classe Khepera. O objetivo aqui é mostrar que é possível criar algoritmos para aplicações complexas a partir das operações já definidas pela classe Khepera. A figura 4.16 ilustra o exemplo em que o robô irá se deslocar até um dado ponto P.

Figura 4.16. O robô se move até o ponto P Também é possível observar na figura 4.16 que caso haja algum obstáculo o robô irá desviar da trajetória desse obstáculo e seguir novamente em direção ao ponto P. O algoritmo dessa aplicação é apresentado em forma de fluxograma na figura 4.17, o que facilita a compreensão do mesmo.

Figura 4.17. Fluxograma da aplicação que move robô até o ponto P

  Inicialmente, o robô é colocado em um determinado ponto do ambiente dado pela coordenada (0,0). Neste exemplo, o ponto P fica acima e a direita do ponto inicial, assim o robô girará para o lado direito até apontar sua frente em direção ao ponto P, e em seguida irá se locomover até atingir o ponto final. Nesse exemplo nota-se a existência de um obstáculo na trajetória do robô. Portanto, para atingir o ponto P, o robô precisa primeiro se desviar deste obstáculo, para depois seguir até o ponto final. Neste caso, quanto o robô encontra o obstáculo, pára, gira 90° para direita e anda a distância equivalente ao seu diâmetro, com o objetivo de tirá-lo da trajetória do obstáculo.

  O próximo passo é calcular a nova posição do robô e determinar qual o ângulo de giro que o robô deve efetuar para que sua frente aponte novamente para o ponto P. Feito isso, o robô irá se locomover novamente em direção ao ponto final.

  Vale lembrar que, caso haja um novo obstáculo à frente do robô, ele irá repetir todo o procedimento de desvio e cálculo de posição atual até que o robô atinja seu alvo, o ponto P.

  Nesta aplicação o cálculo da posição atual do robô é baseado somente na leitura dos encoders das rodas através do uso do método ReadMotorPosition. A dificuldade de basear-se somente na leitura dos encoders está no fato de que, por exemplo, quando o robô encontra um obstáculo, ele girará sobre seu eixo, fazendo com que o valor dos encoders sejam alterados, sendo que o robô não saiu do seu lugar geométrico. Portanto, foi necessária uma série de cálculos e considerações para resolução deste problema. De qualquer forma, os resultados obtidos com o uso deste algoritmo atingiram o objetivo principal, que era o desenvolvimento de aplicações complexas usando os métodos da classe Khepera. No anexo II, é possível observar a complexidade do algoritmo que executa esta aplicação.

  É importante salientar que, dependendo da habilidade do programador, a classe Khepera pode ser adaptada para ser utilizada em alguma outra linguagem de programação como Java, por exemplo, que é uma linguagem extremamente usada para desenvolver aplicações para Internet. Neste sentido, uma aplicação desenvolvida em Java e que use as classes Khepera e Serial poderia controlar um robô Khepera através da Internet. Vale lembrar também que a classe Serial desenvolvida para fazer a comunicação com o robô Khepera através da porta serial, pode ser usada para qualquer outro tipo de robô que também use este tipo de interface.

  Desta forma, o uso das classes Khepera e Serial permite expandir o universo de aplicações para robôs móveis, seja através da criação de novas operações no ambiente K++ ou pelo uso dessas classes em alguma outra linguagem de programação.

  CONCLUSÃO

  Nos dias atuais, ainda se perde muito tempo no estudo e desenvolvimento de aplicações para robôs móveis. Assim, o presente trabalho contempla uma real necessidade no desenvolvimento de softwares específicos para a área de robótica móvel. Com o uso de um ambiente de programação como o K++, que incorpora a produtividade da programação orientada a objetos e a versatilidade da programação visual, a distância existente entre as etapas do projeto podem ser minimizadas.

  Além disso, a utilização deste ambiente estende seus benefícios a um grupo de pessoas que necessariamente não precisam ser especialistas em programação, ampliando desta forma a utilização da ferramenta. Isto se deve ao fato de que o ambiente K++ possui uma interface amigável, que facilita a programação e o desenvolvimento de aplicações para o robô khepera. A escolha da metodologia orientada a objetos e da linguagem de programação C++, atendeu as necessidades do projeto, que levou em consideração a capacidade de reusar códigos.

  Outro aspecto importante do K++ é a possibilidade de desenvolver novas ferramentas para trabalhos futuros baseados em robôs programados em C, através da modelagem de uma nova classe que contemple os aspectos funcionais deste robô, ampliando o universo de aplicações para diferentes tipos de robôs.

  Também é importante salientar que os resultados obtidos nos testes com o ambiente de programação K++ foram satisfatórios. Através do desenvolvimento das aplicações para o robô, as vantagens do uso da programação visual e da programação orientada a objetos tornaram-se evidentes.

  Melhorias futuras e possíveis desdobramentos

  A facilidade de criar aplicações é resultado de uma boa interface visual e de um bom projeto de software. Apesar disto, o ambiente K++ possui ainda algumas limitações que dificultam o desenvolvimento de aplicações mais complexas. Atualmente, o ambiente suporta somente dados dos tipos real e inteiro. Apesar de estar projetado para trabalhar com dados booleanos e vetores, pequenas modificações devem ser feitas para que o ambiente possa trabalhar com esses outros tipos de dados. Apesar de não ter sido comentado durante o texto, o ambiente K++ ainda permite converter parcialmente o programa criado visualmente em um código C, faltando ainda ajustar o mecanismo de tradução. Como o objetivo deste trabalho era efetuar a controlar o robô em tempo real, não houve preocupação em gerar o código C da aplicação. Em um trabalho futuro, a implementação da geração do código C permitirá que o programa simulado no robô seja enviado a sua memória para que o mesmo trabalhe em modo autônomo.

  Outra importante melhoria pode ser obtida através da inclusão de novos elementos ao robô como visão, atuadores (garras) e rádio-controle. A inclusão de novos elementos ao ambiente K++ é uma relativamente complexa e exige um bom conhecimento do ambiente Visual C++ e da estrutura interna do K++. De qualquer maneira, a grande vantagem de um ambiente como o K++ é o fato de ser um software que permite que o programador possa modificá-lo de acordo com as suas necessidades.

  Por fim, um importante desdobramento deste trabalho é a sua aplicação como ferramenta de apoio ao ensino de programação. O uso de um ambiente de programação visual para robôs móveis pode facilitar a compreensão dos algoritmos, melhorando desta forma o aprendizado, pois concilia os conceitos de programação com uma aplicação do mundo real.

  REFERÊNCIAS BIBLIOGRÁFICAS ASIMOV, I., 1994, “Visões de robô”, São Paulo: Círculo do Livro, 424 p.

  BIANCHI, R.A.C., Simões, A.S., Costa, A.H.R., 2001, “Comportamentos Reativos para Seguir Pistas em Robô Móvel Guiado por Visão”, V Simpósio Brasileiro de Automação Inteligente. BOTELHO, S. S. C., 1996, “Desenvolvimento de sistemas inteligentes para controle de robôs móveis”, Dissertação de mestrado em Ciências da Computação –

  Instituto de Informática, Universidade Federal do Rio Grande do Sul, Porto Alegre, 124 p. BRAITENBERG, V., 1984, “Vehicles: experiments in synthetic psychology”, Cambridge, MIT Press, 152 p. BRUMITT, B. L., Coulter, R. C., Stentz, A., 1992, “Dynamic trajectory planning for a cross-country navigator”, Boston Proceedings of SPIE Symposium on Mobile

  Robots. BURNETT, M. M., et al, 1995. Visual Object-Programming: Concepts and Environments, Prentice Hall.

  CALLONI, B. A., Bargert, D. J., 1994, “Iconic programming in BACCII vs Textual Programming: which is a better Learning Environment?”, SIGCSE Bulletin vol.

  26, n° 1, pp. 188-192. CAMPBELL, J., 1993, “C Programmer's Guide to Serial Communication”, Sams, pp.

  407-598. CYBERBOTICS, 2003, “Webots Reference Manual”, Cyberbotics, Disponível Em http:// cyberboticspc1.epfl.ch/cdrom/common/doc/webots/reference/reference.pdf

  DI PAOLO E. A.,1998, “An investigation into the evolution of communication”, Adaptive Behavior journal, vol. 6, n° 2, pp. 285-324

  FIRBY, R. James, 1993, “An architecture for a synthetic vacuum cleaner”, AAAI Fall Symposium, Series Workshop on instantiating realworlds agents. 9 p. FLOREANO, D., Mondana, F., 1996, “Evolution of homing navigation in a real mobile robot”, IEEE Transactions on Systems, Man, and Cybernetics – Part B, vol. 26, pp. 396-407

  GROOVER, M. P., WEISS, M., NAGEL, R. N., et al, 1988, “Robótica: tecnologia e programação”, São Paulo: McGraw-Hill, 401 p. GUDWIN, R. R., 1997, “Linguagens de programação”, Notas de aula, 17 p.

  Disponível em http://www.eng.uerj.br/~araujo/disciplinas/Caract/ling_prog.pdf HARVEY, B., 1997, “Computer Science Logo Style”, MIT Press, vol. 1, Cambridge, pp. 17-39 HOLZNER, S., 2001, “C++ Black Book”, Makron Books, 646 p. JONES, M. J., 2001, “Fundamentos do desenho orientado a objeto com UML”, Makron Books, 462 p.

  KRUGLINSKI, D. J., 1996, “Inside Visual C++”, Microsoft Press, 843 p. K-TEAM, 1999a, “Khepera BIOS Manual”, K-Team, Switzerland, 121 p. Disponível em http://www.k- team.com/download/khepera.html K-TEAM, 1999b, “Khepera User Manual”, K-Team, Switzerland, 52 p. Disponível em http://www.k- team.com/download/khepera.html K-TEAM, 2002a, “IR Sensors Report”, K-Team, Switzerland, 19 p. Disponível em http://www.k-team.com/download/khepera.html K-TEAM, 2002b, “Khepera Programming Manual”, K-Team, Switzerland, 48 p.

  Disponível em http://www.k- team.com/download/khepera.html LEE, R. C., Tepfenhart, W. M., 2002. “UML and C++ - Guia prático de desenvolvimento orientado a objeto”, Makron Books, pp. 23-39.

  LUND, H.H., Cuenta, E.V., Hallan, J., 1996, “A Simple Mobile Real-team Robot Tracking System”, Technical Paper 41, Department Artificial of Intelligence, University of Edinburgh.

  MÄCHLER P., 1995, "Detection Technologies for Anti-Personnel Mines", Autonomous Vehicles in Mine Countermeasures Symposium, Monterey, California, pp. 150-154.

  MAIOR, D. S., 1998, “Os robôs estão chegando”, Ciência Hoje - suplemento tecnologia, vol. 23, n°136. MONDANA, F., Wrinkled, E., Ienne, P., 1993, “Mobile Robot Miniaturization: A Tool

  For Investigation In Control Algorithms”, Experimental Robotics III - Proceedings of the Third International Symposium on Experimental Robotics, October 28-30, Kyoto,Japan, pp. 501-513 MONDANA, F., Floreano, D., 1996, “Evolution and mobile autonomous robotics”, Towards Evolvable Hardware; The Evolutionary Engineering Approach, Berlin, pp. 221-249

  NASSI, I., Shnneiderman, B., 1984, “Flowchart Techniques for Structured Programming”, ACM Sigplan Notices, Vol. 8, n° 8, p. 723-728. NATIONAL, 2000, “LABVIEW® Manuals”, National Instruments, 272 p NITZAN, D., 1985, “Development of intelligent robots: achievements and issues”, IEEE Journal of Robotics and Automation, vol.1, n°1, pp. 3-13. NOVELETTO F., Sousa, A. H., Hounsell, M. S., 2003, “Desenvolvimento de um

  Ambiente de Programação Visual para Robôs Móveis”, III Congresso Brasileiro de Computação – CBComp, pp. 914-924. NOVELETTO F., Sousa, A. H., Hounsell, M. S., 2003, “Desenvolvimento de um

  Ambiente de Programação Visual para Robôs Móveis”, I2TS'2003 – International Information and Telecommunication Technologies Symposium – (IEEE Seção Sul Brasil) – Workshop III CBComp Best Papers.

  NOVELETTO F., Sousa, A. H., Hounsell, M. S., 2003, “The development of a visual programming environment for mobile robots”, COBEM-2003 – 17° Congresso Brasileiro de Engenharia Mecânica . PAPERT, S., 1985, “Logo: Computadores e Educação”, Editora Brasiliense, 254 p. PRESSMAN, R. S., 1995, “Engenharia de Software”, Makron Books, 1056 p RAJEEV K. P., Burnett, M. M., 1993, “Is It Easier to Write Matrix Manipulation

  Programs Visually or Textually? An Empirical Study”, Proceedings of the 1993 IEEE Symposium on Visual Languages, pp. 344-351. RUSSEL, S. J., Norvig, P.,1995, “Artificial Intelligence: A Modern Approach”, Prentice Hall, 932 p. SCALAN, D. A., 1989, “Structured Flowcharts Outperform Pseudocodes: An

  Experimental Comparation”, IEEE Software vol. 6, n° 5, pp. 28-36 SETHI, R., 1996, "Programming Languages: Concepts and Constructs", Second edition, Addison – Wesley, 624 p.

  SHU N.C., 1988. Visual Programming, Van Nostrand Reinhold Company Inc.,New York.

  SOUSA, A. H., Ferreira, E. C., Silva, M. A. Vieira, 1996a, “ONAGRO – A Graphical Environment to the Development of Microcontrollers Software”, Proceedings of WAC-96: Second World Automation Congress, Montpellier-France, p. 301 – 306

  SOUSA, A. H., Ferreira, E. C., 1998, “O++: A Visual Object-Oriented Language for Embedded Systems”, Proceedings of ISSCI-98: International Symposium on Soft Computing for Industry, Achorage/AK/USA.

  SOUSA, A. H., “Uma proposta de linguagem visual orientada a objetos para programação de microcontroladores”, Campinas, 1999, 116 p. Tese de Doutorado – Engenharia Elétrica – Universidade Estadual de Campinas. SPRINGER, S. P., Deutsch, G., 1985, “Left Brain, Right Brain”, W. H. Freeman and

  Company, New York

  VOSS, G, 1991, “Object-Oriented Programming: An Introduction”, McGraw-Hill, 584 p WASSERMAN, A. I., et al, 1990, “The Object-Oriented Structured Design Notation for

  Software Design Representation”, IEEE Computer, vol. 23, n° 3, p 50 – 63 WHITLEY K. N., 1996, “Visual Programming Languages and the Empirical Evidence for and Against”, Journal of Visual Languages and Computing, vol. 8, 19 p WINBLAND, A. L., Edwards, S. D., King, D. R., 1993, “Software Orientado ao Objeto”, Makron Books, 314 p.

  ANEXO I

  Implementação da classe Serial em C++ Arquivo: serial.cpp

ARQUIVO SERIAL.CPP

  // Serial.cpp // by ANTONIO HERONALDO DE SOUSA E FABRICIO NOVELETTO // FACULDADE DE ENGENHARIA DE JOINVILLE // SETEMBRO 2002 #include "stdafx.h" #include "Serial.h" CSerial::CSerial() { memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) ); memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) ); m_hIDComDev = NULL; m_bOpened = FALSE;

  } CSerial::~CSerial() { Close(); } BOOL CSerial::Open( int nPort, int nBaud ) { if( m_bOpened ) return( TRUE ); char szPort[15]; char szComParams[50]; DCB dcb; wsprintf( szPort, "COM%d", nPort ); m_hIDComDev = CreateFile( szPort, GENERIC_READ | GENERIC_WRITE, 0, NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL );

if( m_hIDComDev == NULL ) return( FALSE ); memset( &m_OverlappedRead, 0, sizeof( OVERLAPPED ) ); memset( &m_OverlappedWrite, 0, sizeof( OVERLAPPED ) ); COMMTIMEOUTS CommTimeOuts; CommTimeOuts.ReadIntervalTimeout = 0xFFFFFFFF; CommTimeOuts.ReadTotalTimeoutMultiplier = 0;

  CommTimeOuts.ReadTotalTimeoutConstant = 0; CommTimeOuts.WriteTotalTimeoutMultiplier = 0; CommTimeOuts.WriteTotalTimeoutConstant = 5000; SetCommTimeouts( m_hIDComDev, &CommTimeOuts ); wsprintf( szComParams, "COM%d:%d,n,8,1", nPort, nBaud ); m_OverlappedRead.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); m_OverlappedWrite.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); dcb.DCBlength = sizeof( DCB ); GetCommState( m_hIDComDev, &dcb ); dcb.BaudRate = nBaud; dcb.ByteSize = 8; unsigned char ucSet; ucSet = (unsigned char) ( ( FC_RTSCTS & FC_DTRDSR ) != 0 ); ucSet = (unsigned char) ( ( FC_RTSCTS & FC_RTSCTS ) != 0 ); ucSet = (unsigned char) ( ( FC_RTSCTS & FC_XONXOFF ) != 0 ); if( !SetCommState( m_hIDComDev, &dcb ) || !SetupComm( m_hIDComDev, 10000, 10000 ) || m_OverlappedRead.hEvent == NULL || m_OverlappedWrite.hEvent == NULL ){ DWORD dwError = GetLastError(); if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent ); if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent ); CloseHandle( m_hIDComDev ); return( FALSE ); } m_bOpened = TRUE; return( m_bOpened );

  } BOOL CSerial::Close( void ) { if( !m_bOpened || m_hIDComDev == NULL ) return( TRUE );

if( m_OverlappedRead.hEvent != NULL ) CloseHandle( m_OverlappedRead.hEvent );

if( m_OverlappedWrite.hEvent != NULL ) CloseHandle( m_OverlappedWrite.hEvent );

CloseHandle( m_hIDComDev ); m_bOpened = FALSE; m_hIDComDev = NULL; return( TRUE );

  } BOOL CSerial::WriteCommByte( unsigned char ucByte ) { BOOL bWriteStat; DWORD dwBytesWritten; bWriteStat = WriteFile( m_hIDComDev, (LPSTR) &ucByte, 1, &dwBytesWritten, &m_OverlappedWrite ); if( !bWriteStat && ( GetLastError() == ERROR_IO_PENDING ) ){ if( WaitForSingleObject( m_OverlappedWrite.hEvent, 1000 ) ) dwBytesWritten = 0; else{ GetOverlappedResult( m_hIDComDev, &m_OverlappedWrite, &dwBytesWritten, FALSE ); m_OverlappedWrite.Offset += dwBytesWritten; }

  } return( TRUE ); } int CSerial::SendData( const char *buffer, int size ) { if( !m_bOpened || m_hIDComDev == NULL ) return( 0 ); DWORD dwBytesWritten = 0; int i; for( i=0; i<size; i++ ){ WriteCommByte( buffer[i] ); dwBytesWritten++; } return( (int) dwBytesWritten ); } int CSerial::ReadDataWaiting( void ) { if( !m_bOpened || m_hIDComDev == NULL ) return( 0 ); DWORD dwErrorFlags;

  COMSTAT ComStat; ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat ); return( (int) ComStat.cbInQue ); } int CSerial::ReadData( void *buffer, int limit ) { if( !m_bOpened || m_hIDComDev == NULL ) return( 0 ); BOOL bReadStatus; DWORD dwBytesRead, dwErrorFlags; COMSTAT ComStat; ClearCommError( m_hIDComDev, &dwErrorFlags, &ComStat ); if( !ComStat.cbInQue ) return( 0 ); dwBytesRead = (DWORD) ComStat.cbInQue; if( limit < (int) dwBytesRead ) dwBytesRead = (DWORD) limit;

bReadStatus = ReadFile( m_hIDComDev, buffer, dwBytesRead, &dwBytesRead,

&m_OverlappedRead ); if( !bReadStatus ){ if( GetLastError() == ERROR_IO_PENDING ){

  WaitForSingleObject( m_OverlappedRead.hEvent, 2000 ); return( (int) dwBytesRead ); } return( );

  } return( (int) dwBytesRead ); } int CSerial::ReadCommand(char *lpBuffer) { time_t t0, t0a, t1, t1a; int szBuffer; int nBytesRead; int ret, flag; char lpBuffer_aux[100]; lpBuffer[0]='\0'; lpBuffer_aux[0]='\0'; t0a = time(NULL); do

  { t0 = time(NULL); do { szBuffer=ReadDataWaiting(); t1 = time(NULL);

  } while ((szBuffer==0) && ((t1-t0) < 2)); if (szBuffer!=0) { nBytesRead = ReadData(lpBuffer_aux, szBuffer); lpBuffer_aux[szBuffer]='\0'; strcat(lpBuffer,lpBuffer_aux);

  } szBuffer = strlen(lpBuffer); if (szBuffer > 0) { if (lpBuffer[szBuffer-1] == 10) { flag = 0; } else { flag = 1; }

  } else { flag = 1; } t1a = time(NULL);

  } while (flag && ((t1a-t0a) < 2) ); if (szBuffer > 2) { lpBuffer[szBuffer-2] = '\0'; //lpBuffer[szBuffer-2] = '\0'; ret = szBuffer - 2; } else ret = -1; return (ret); } int CSerial::WriteCommand(char *lpBuffer, char *lpBuffer_ret) { int ret; char lpBuffer_trans[50]; int len = strlen(lpBuffer); strcpy(lpBuffer_trans, lpBuffer); lpBuffer_trans[len] = 13; lpBuffer_trans[len+1] = 10; lpBuffer_trans[len+2] = '\0';

  SendData(lpBuffer_trans, strlen(lpBuffer_trans)); ret = ReadCommand(lpBuffer_ret); if (ret != -1) { if (lpBuffer_ret[0] == (lpBuffer_trans[0])) //+32)) ret = 1; else ret = -1;

  } return(ret); }

ANEXO II

  Implementação da classe Khepera em C++ Arquivo: khepera.cpp

ARQUIVO KHEPERA.CPP

  // khepera.cpp // by ANTONIO HERONALDO DE SOUSA E FABRICIO NOVELETTO // FACULDADE DE ENGENHARIA DE JOINVILLE // SETEMBRO 2002 #include "stdafx.h" #include "khepera.h" #include <math.h>

  IMPLEMENT_SERIAL(CKhepera, CObject, 0); CKhepera::CKhepera() { m_prox = PROX_SATURATION; m_axis = AXIS_DISTANCE; m_encoder = ENCODER_RESOLUTION; m_light = LIGHT_SATURATION; }

  CKhepera::~CKhepera() { } BOOL CKhepera::Open( int nPort, int nBaud ) { return( serial.Open(nPort,nBaud)); } BOOL CKhepera::Close( void ) { return(serial.Close()); } // ******************************************* // Move robô para frente // ******************************************* void CKhepera::MoveForward(int Motor1, int Motor2) { char lpBuffer_ret[50]; char buf[50]; char charMotor1[10], charMotor2[10]; _itoa(Motor1,charMotor1,10); _itoa(Motor2,charMotor2,10); strcpy(buf, "D,"); strcat(buf, charMotor1); strcat(buf, ","); strcat(buf, charMotor2); serial.WriteCommand(buf,lpBuffer_ret); }

  // ******************************************* // Move robô para traz // ******************************************* void CKhepera::MoveBackward(int Motor1, int Motor2) { char lpBuffer_ret[50]; char buf[50]; char charMotor1[10], charMotor2[10]; _itoa(Motor1,charMotor1,10); _itoa(Motor2,charMotor2,10); strcpy(buf, "D,"); strcat(buf, charMotor1); strcat(buf, ","); strcat(buf, charMotor2); serial.WriteCommand(buf,lpBuffer_ret); } // ************************************************* // Para motores do robô // ************************************************* void CKhepera::StopMotors() { char lpBuffer_ret[50]; lpBuffer_ret[0]='\0'; serial.WriteCommand("D,0,0",lpBuffer_ret); } // ************************************************* // Leitura dos contadores dos motores // ************************************************* void CKhepera::ReadMotorPosition(int* count_motors) { char lpBuffer_ret[50]; serial.WriteCommand("H",lpBuffer_ret); CString strCountMotors(lpBuffer_ret); CString Count_Motors_Str[10]; CString s1; CString s2(""); int z = 0; int x = strCountMotors.GetLength(); for( int i = 2; i < x; ++i ) { s1 = strCountMotors.GetAt(i); if (s1 == ",")

  { z++; Count_Motors_Str[z] = s2; s2 = "";

  } else s2 = s2 + s1; } Count_Motors_Str[2] = s2; for( int ii = 1; ii < 3; ++ii ) { count_motors[ii-1] = atoi(Count_Motors_Str[ii]); } } // ************************************************* // Leitura dos sensores de Proximidade // ************************************************* void CKhepera::ReadProxSensors(int* val_sensor) { char lpBuffer_ret[50]; serial.WriteCommand("N",lpBuffer_ret); CString strSensors(lpBuffer_ret); CString Val_Sensor_Str[10]; CString s1; CString s2(""); int z = 0; int x = strSensors.GetLength(); for( int i = 2; i < x; ++i )

  { s1 = strSensors.GetAt(i); if (s1 == ",") { z++; Val_Sensor_Str[z] = s2; s2 = ""; } else s2 = s2 + s1;

  } Val_Sensor_Str[8] = s2; for( int ii = 1; ii < 9; ++ii ) // ???????? { val_sensor[ii-1] = atoi(Val_Sensor_Str[ii]); } }

  // ************************************************* // Leitura dos sensores de luminosidade // ************************************************* void CKhepera::ReadLightSensors(int* val_sensor) { char lpBuffer_ret[50]; serial.WriteCommand("O",lpBuffer_ret); CString strSensors(lpBuffer_ret); CString Val_Sensor_Str[10]; CString s1; CString s2(""); int z = 0; int x = strSensors.GetLength(); for( int i = 2; i < x; ++i )

  { s1 = strSensors.GetAt(i); if (s1 == ",") { z++; Val_Sensor_Str[z] = s2; s2 = ""; } else s2 = s2 + s1;

  } Val_Sensor_Str[8] = s2; for( int ii = 1; ii < 9; ++ii ) // ???????? { val_sensor[ii-1] = atoi(Val_Sensor_Str[ii]); } } // ************************************************* // Gira robô à esquerda // ************************************************* void CKhepera::TurnLeft(double degrad, int speed) { int count_motors[10]; MoveForward(-speed,speed); ReadMotorPosition(count_motors); int CountStop = (count_motors[1] + (int) (m_axis * degrad)); do { ReadMotorPosition(count_motors); } while(count_motors[1]<CountStop); StopMotors(); }

  // ************************************************* // Gira robô à direita // ************************************************* void CKhepera::TurnRight(double degrad, int speed) { int count_motors[10]; MoveForward(speed,-speed); ReadMotorPosition(count_motors); int CountStop = (count_motors[0] + (int)(m_axis * degrad)); do

  { ReadMotorPosition(count_motors); } while(count_motors[0]<CountStop); StopMotors(); } // ************************************************************ // Move robô por dada distância com dada velocidade // ************************************************************ void CKhepera::MoveLine(int distance, int angle, int speed) { int count_motors[10]; int val_sensor[10]; TurnRight(angle); MoveForward(speed,speed); ReadMotorPosition(count_motors); int CountStop = (count_motors[0] + (int)(12.5 * distance)); do

  { ReadMotorPosition(count_motors); ReadProxSensors(val_sensor); // Se existir obstaculo PARE!

  • // if (val_sensor[1]>m_prox || val_sensor[2]>m_prox || val_sensor[3]>m_prox || val_sensor[4]>m_prox) { StopMotors(); } else MoveForward(speed,speed);
  • // } while (count_motors[0]<CountStop); StopMotors(); }

  // ************************************************* // Move robô até um dado ponto P // ************************************************* void CKhepera::MoveToPoint(int Xfinal, int Yfinal, int* coordXY) { int Contador, CountStop; int x_ref, y_ref; int count_motors[2]; int val_sensor[8]; int flagStop = 0; double eixoX, eixoY, distance, angleDeg, angleRad; double pi = 3.1415926535; int x_atual = 0; int y_atual = 0; do { eixoX = (double) Xfinal - x_atual; eixoY = (double) Yfinal - y_atual; distance = sqrt(eixoX*eixoX + eixoY*eixoY); angleRad = atan(eixoY / eixoX); angleDeg = (angleRad*180.0 / pi); if (eixoX > 0)

  {

  • TurnRight(90 angleDeg); } else {
  • TurnLeft(90 angleDeg); } ResetMotors(); MoveForward(5,5); CountStop = (int)(12.5 * distance); do {

  ReadMotorPosition(count_motors); ReadProxSensors(val_sensor); Contador = (int)(count_motors[0]*m_encoder); x_ref = (int)(Contador * cos(angleRad)); y_ref = (int)(Contador * sin(angleRad)); } while ( (count_motors[0]<CountStop) && (val_sensor[0]<m_prox) && (val_sensor[1]<m_prox) &&

  (val_sensor[2]<m_prox) && (val_sensor[3]<m_prox) && (val_sensor[4]<m_prox) && (val_sensor[5]<m_prox)); if (x_atual > Xfinal) x_atual = x_atual - x_ref; else x_atual = x_atual + x_ref; if (y_atual > Yfinal) y_atual = y_atual - y_ref; else y_atual = y_atual + y_ref; if (count_motors[0]>=CountStop) StopMotors(); else { int sentido_giro = -1; int left_sensors = val_sensor[0]+val_sensor[1]+val_sensor[2]; int right_sensors = val_sensor[3]+val_sensor[4]+val_sensor[5]; if (left_sensors > right_sensors) { sentido_giro = 1; // gira para direita

  TurnRight(90); } else { sentido_giro = 0; // gira para esquerda TurnLeft(90);

  } int sair = 0; ResetMotors(); MoveForward(2,2); while (!sair) { ReadProxSensors(val_sensor); if ((sentido_giro == 1) && (val_sensor[0] < 1)) sair = 1; if ((sentido_giro == 0) && (val_sensor[5] < 1)) sair = 1; }

  MoveLine(60,0,2); // anda o diametro do robo ReadMotorPosition(count_motors); Contador = (int)(count_motors[0]*m_encoder); x_ref = (int)(Contador * sin(angleRad)); y_ref = (int)(Contador * cos(angleRad)); if (sentido_giro == 1) { x_atual = x_atual + x_ref; y_atual = y_atual - y_ref; TurnLeft(180 - angleDeg + 3);//ERRO = 3 } else { x_atual = x_atual - x_ref; y_atual = y_atual + y_ref;

  TurnRight(angleDeg); } } } while ( ((x_atual <= (0.9*Xfinal)) || (x_atual >= (1.1*Xfinal))) || ((y_atual <= (0.9*Yfinal)) || (y_atual >= (1.1*Yfinal))) );

  StopMotors(); } // ************************************************* // Zera os contadores dos motores // ************************************************* void CKhepera::ResetMotors() { char lpBuffer_ret[50]; serial.WriteCommand("G,0,0",lpBuffer_ret); } // ************************************************* // Código para Desvio de Obstáculo // ************************************************* void CKhepera::ObstacleSkipper(int val_angle) { int val_sensor[10]; do { ReadProxSensors(val_sensor); } while ( val_sensor[0]<m_prox && val_sensor[1]<m_prox && val_sensor[2]<m_prox && val_sensor[3]<m_prox && val_sensor[4]<m_prox && val_sensor[5]<m_prox); StopMotors(); int leftSensors = val_sensor[0] + val_sensor[1] + val_sensor[2]; int rightSensors = val_sensor[3] + val_sensor[4] + val_sensor[5]; if (leftSensors < rightSensors) {

  TurnLeft(val_angle); } else {

  TurnRight(val_angle); } }

  // ************************************************* // Código para encontrar luz // ************************************************* int CKhepera::LightFinder(int val_sat) //int CKhepera::LightFinder() { int val_light; char lpBuffer_ret[50]; if (val_sat == -1) val_sat = m_light; serial.WriteCommand("O",lpBuffer_ret); CString strSensors(lpBuffer_ret); CString Val_Sensor_Str[10]; CString s1; CString s2(""); int z = 0; int x = strSensors.GetLength(); int val_sensor[8]; for( int i = 2; i < x; ++i ) { s1 = strSensors.GetAt(i); if (s1 == ",")

  { z++; Val_Sensor_Str[z] = s2; s2 = "";

  } else s2 = s2 + s1; }

  Val_Sensor_Str[8] = s2; for( int ii = 1; ii < 9; ++ii ) // ???????? { val_sensor[ii-1] = atoi(Val_Sensor_Str[ii]); } // Verifica qual sensor tem o menor valor (mais luz) int lightSensor=512; int numSensor=0; for( int iii = 0; iii < 8; ++iii ) { if (val_sensor[iii] < lightSensor) { numSensor = iii; lightSensor = val_sensor[iii];

  } } if (lightSensor <= val_sat) val_light=1; else val_light=0; switch(numSensor) { case 0: TurnLeft(90); break; case 1: TurnLeft(45); break; case 2: TurnLeft(10); break; case 3: TurnRight(10); break; case 4: TurnRight(45); break; case 5: TurnRight(90); break; case 6: TurnRight(170); break; case 7: TurnLeft(170); break; } return (val_light); } void CKhepera::SetValues(float a, float e, int l, int p) { m_axis = a; m_encoder = e; m_light = l; m_prox = p; } void CKhepera::GetValues(float &a, float &e, int &l, int &p) { a = m_axis; e = m_encoder; l = m_light; p = m_prox;

  } void CKhepera::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << (WORD) m_light; ar << (WORD) m_prox; ar << m_axis; ar << m_encoder; } else { WORD wTemp; ar >> wTemp; m_light = (int)wTemp; ar >> wTemp; m_prox = (int)wTemp; ar >> m_axis; ar >> m_encoder; } }

Novo documento

Tags

Documento similar

UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE MESTRADO EM ENGENHARIA ELÉTRICA SUZANA RIBAS DE ALMEIDA
0
1
116
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGIAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA – PPGEEL
0
0
137
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA
1
2
140
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEEL
0
1
133
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEEL
0
0
65
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA - DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEEL
0
0
160
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA
0
1
302
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA – PPGEEL MESTRADO PROFISSIONAL EM ENGENHARIA ELÉTRICA
0
1
113
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE COORDENAÇÃO DO PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA – PPGEEL Formação: Mestrado Profissional em Engenharia Elétric
0
0
147
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEEL
0
0
156
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEEL HORÁCIO BECKERT POLLI
0
1
111
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT CURSO DE ENGENHARIA ELÉTRICA
0
0
146
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE PROGRAMA DE PÓS-GRADUAÇÃO EM ENGENHARIA ELÉTRICA - PPGEE
0
1
190
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE MESTRADO PROFISSIONAL EM ENGENHARIA ELÉTRICA
0
0
152
UNIVERSIDADE DO ESTADO DE SANTA CATARINA – UDESC CENTRO DE CIÊNCIAS TECNOLÓGICAS – CCT DEPARTAMENTO DE ENGENHARIA ELÉTRICA – DEE MESTRADO EM ENGENHARIA ELÉTRICA DIOGO LUIZ LEMES DA CRUZ
0
0
101
Show more