Criação de um Dispositivo de Navegação em Mundos Virtuais

(Bicicleta Virtual)

Desenvolvido no âmbito do grupo de realidade Virtual da PUCRS
(Financiado com recursos da FAPERGS e da PUCRS)

Leandro Luis Dias(Bolsista)
Carlos Moreira
(Aluno de Trab. de Conclusão)
Gustavo Becker (Aluno de Trab. de Conclusão)
Emmanuel Kodjaghlanian (Aluno de Trab. de Conclusão)
Lúcio Mauro Duarte(Bolsista)
Prof. Msc Márcio Serolli Pinho(Orientador)

Este relatório está dividido em duas partes: a primeira, descreve resumidamente os resultados do projeto, a segunda detalha os aspectos técnicos do mesmo.


Parte 1

Descrição Resumida dos Resultados do Projeto

1. Resumo do Projeto

Uma das tarefas mais complicadas quando se trabalha com mundos virtuais tridimensionais é a navegação. Em geral, este processo envolve o uso de botões, teclas e outros artifícios, pouco naturais ao processo de "andar por algum lugar". Exemplos disto são os navegadores ou ambientes como Nestcape, Virtus, Internet Explorer, Worldtoolkit e outros.

Pensando nisto este projeto criou uma ferramenta de navegação mais natural. Em uma bicicleta, ligamos um conjunto de dispositivos elétricos e, pela interpretação das informações obtidas destes equipamentos, efetuamos o deslocamento dentro de um mundo virtual previamente criado.

A exibição do mundo virtual é feita utilizando a biblioteca de rendering tridimensional em tempo real OPENGL. As imagens geradas são exibidas em um óculos de realidade virtual que fornece informações à respeito da movimentação da cabeça do usuário, permitindo, assim, a exibição de uma imagem ainda mais realista, adaptável à movimentação da cabeça do usuário. A fim de deixar documentado o estudo realizado sobre OpenGL, criamos uma homepage(http://www.inf.pucrs.br/cg) que já está em uso desde maio de 1998.

A leitura dos dados da bicicleta é realizada pela captura dos movimentos dos pedais e da direção. No guidão da bicicleta instalamos um resistor variável(potenciômetro) que com os movimentos do guidão altera sua resistência. Este dado analógico, convertido para forma digital é então lido pelo computador e usado para atualizar a imagem que está sendo exibida ao usuário.

Para o deslocamento no mundo virtual é usada a informação proveniente do giro da roda ou dos pedais da bicicleta. Este dado é usado para dar a velocidade do descolamento no mundo virtual.

No esquema a seguir é apresentado um modelo da arquitetura desenvolvida. Cabe ressaltar que neste projeto ainda não tratamos aspectos relativos à movimentação física da bicicleta(inclinação, trepidação, levantamento).

 

2. Resultados Atingidos com o Projeto

2.1 Resultado Geral

Dando continuidade a implantação do Laboratório de Realidade Virtual da PUCRS, este projeto teve como objetivo criar um Dispositivo de Navegação em Mundos Virtuais através da captura dos movimentos de um bicicleta.

Para coroar o trabalho, o Museu de Ciências e Tecnologia da PUCRS, convidou para que o projeto seja apresentado, de forma permanente, em sua área de experimentos tecnológicos. Os procedimentos para tanto já estão sendo desenvolvidos.

2.2 Resultados Específicos

Com o desenvolvimento deste projeto foi possível:

Dentro desta perspectiva o bolsista desenvolveu ou auxiliou o prof. Orientador na nas seguintes tarefas:

3. Conclusão

O projeto obteve êxito em seu desenvolvimento.

A divulgação do trabalho, junto ao Museu de Ciências e Tecnologia da PUCSR está proporcioando uma grande divulgação do projeto.

Participamos da Feira de Iniciação Científica da UFRGS.

Considerando que o objetivo central do projeto é difundir o uso de Realidade Virtual considero que estamos conseguindo atingi-lo.

Para o ano 1999 estamos solicitando a renovação da bolsa de Iniciação Científica.

 4. Perspectiva de Continuidade ou Desdobramento do Trabalho

Para o ano de 1999 pretendemos:


Parte 2

Detalhamento Técnico do Projeto

1. Introdução

A seguir são apresentados os aspectos técnicos envolvidos no desenvolvimento do projeto de criação de uma Bicicleta Virtual.

O projeto implementa um periférico para navegação em ambientes virtuais que consiste basicamente de uma bicicleta e um dispositivo de visualização (HMD - Head-Mounted Display) dotado de um rastreador. Estes equipamentos com o auxílio de outros dispositivos para leitura dos movimentos proporcionam ao usuário a sensação de imersão no ambiente virtual.

Os dados lidos pelo sistema podem ser resumidos em: direção de locomoção, velocidade de locomoção e alvo de visão.

Os dados relativos a locomoção do usuário dentro do sistema são informados através da bicicleta. Para a direção de locomoção, utiliza-se um potenciômetro e para a velocidade de locomoção utiliza-se um dínamo. Ambos geram saídas analógicas que devem ser convertidas para sinais digitais. Assim, estes dispositivos são ligados a uma placa de conversão analógica/digital, que é instalada ao barramento ISA do computador.

Na seção 2 descreveremos esta placa mais detalhadamente. Para tratamento dos dados fornecidos pela AX5210, foi adquirida uma biblioteca que realiza esta conversão.

O alvo de visão, ou seja, a direção para onde o usuário está olhando é fornecida por um dispositivo de rastreamento (tracker). Conforme os movimentos da cabeça do usuário, o tracker envia as coordenadas ao sistema para atualização da imagem a ser mostrada. O HMD que estamos utilizando (I-Glasses!), já possui acoplado um tracker próprio, como será descrito no capítulo 3. Uma biblioteca específica para a leitura do traker pode ser encontrada na página de projetos do GRV.

A navegação do usuário ocorre dentro de um mundo virtual. Para exibição deste, é utilizada uma biblioteca de Rendering 3D, chamada OpenGL. Uma descrição mais detalhada desta biblioteca é dada na página da Disciplina de Computação Gráfica da Faculdade de Informática da PUCRS.

A modelagem dos cenários onde se desenvolve a navegação é feito por um Editor de Cidades.

Apresentamos na figura a seguir uma esquematização abrangente dos periféricos e dispositivos envolvidos no sistema.

fig1_1.jpg (24125 bytes)

Figura 1.1 - Descrição Geral do Sistema

 2. Aquisição dos Dados da Bicicleta

2.1 Placa Conversora Analógico/Digital AX5210

A função básica de um sistema de conversão analógica é converter um sinal de entrada analógico para um formato correspondente(digital) que o computador possa ler.

É uma característica normal ao processo de conversão o chamado erro de quantificação. Este erro se deve ao fato de que para realizar a conversão A/D é necessário definir faixas de entrada para a voltagem. Por exemplo, se tivermos uma resolução de (1 contagem)/(100mV), teremos a mesma saída digital num intervalo de 100mV, tendo um erro de quantificação de ± 50mV. Na prática, este erro pode ser um pouco maior, devido às imprecisões características das tensões de referência.

Todas as técnicas de conversão necessitam do emprego de comparadores lógicos. Estes comparadores funcionam como uma porta lógica, recebendo duas tensões analógicas como entrada (Va e Vb). É feito uma comparação entre elas de modo que se Va>Vb, a saída será 1, caso contrário será 0.

Dois dos métodos de conversão A/D são o de conversor controlado por contador e o de aproximações sucessivas. O primeiro é mais lento, sendo necessários 2N-1 períodos de relógio (15 para um conversor de 4 bits) para realizar a conversão. O método das aproximações sucessivas é mais rápido, exigindo, em geral, N comparações para uma comparação de N bits.

A AX5210 é uma placa interna para PC que permite a aquisição de dados analógicos externos. São exemplos de dados analógicos: pressão, temperatura, umidade, corrente, tensão, etc.

Esta placa, fabricada pela AXIOM Technology, possui um conversor A/D de 12 bits com 16 canais de entrada analógica e velocidade de comunicação com a memória de 30Khz. Para cada entrada é possível definir um valor particular de ganho, correspondente ao nível do sinal na entrada. Este processo, chamado de calibração, é que irá definir o valor da saída digital conforme uma tensão de entrada.

A rotina de leitura dos dados gerados por esta placa está descrita na seção 2.5.

Estamos desenvolvendo uma nova versão do proejto utilizando um outro dispositivo de conversão A/D chamado TNG3, fabricado pela MINDTEL.

2.2 Placa de Proteção

O intervalo de voltagem aceito pela placa AX5210 é de –20V a +20V. Valores fora deste intervalo poderão danificar a placa. Como uma das fontes de tensão é um dínamo, que gera uma quantidade de tensão proporcional à velocidade que seu eixo está sendo rotado, foi necessário construir uma Placa de Proteção para evitar que a corrente elétrica superasse o valor máximo suportado e queimasse a Placa de Conversão A/D ou outros acessórios do microcomputador.

Conforme pode ser notado nas Figuras 2.1 e 2.2 , vê-se que para cada uma das entradas dos canais da placa conversora existem circuitos que protegem o equipamento e o microcomputador de correntes elevadas e sobre-tensões. A finalidade dos diodos (D) é de garantir que não haverá uma tensão muito grande que não seria suportada pela placa conversora. O resistor R tem a finalidade de baixar a corrente do sinal de entrada no canal da placa conversora. Esta Placa de Proteção foi criada pelo bolsista Cícero Zanoni com o apoio técnico do IPCT.

fig2_1.gif (2340 bytes)

Figura 2.1 - Esquema de Proteção para utilização do potenciômetro

 

fig2_2.gif (2304 bytes) 

Figura 2.2 - Esquema de Proteção para utilização do dínamo

2.3 Potenciômetro para Leitura da Direção

Para possibilitar a leitura da direção da bicicleta, ou seja, para que lado o usuário está girando o guidom, foi utilizado um potenciômetro comum. Os potenciômetros, também chamados de resistores variáveis, permitem variar a tensão que passará por eles. O desenho do circuito utilizado é mostrado na Figura 2.3.

fig2_3.gif (1767 bytes)

Figura 2.3 - Esquema elétrico de conexão do potenciômetro

A tensão que passa pelo potenciômetro é gerada por uma fonte de corrente elétrica contínua. Conforme é mostrado na Figura 2.4, esse adaptador é conectado na Placa de Proteção, assim como o potenciômetro. Desse saem três fios e daquele apenas o seu conector. Um dos fios do potenciômetro é o que transporta a corrente elétrica de entrada(deve ser conectado à entrada 1). O segundo transporta a corrente elétrica de saída e deve ser conectado à entrada 6. O terceiro fio é o terra(deve ser conectado à entrada 11). Desta forma, quando o potenciômetro é girado a corrente elétrica que está saindo aumenta ou diminui de acordo com o lado em que se está girando.

fig2_4.gif (3558 bytes)

Figura 2.4 - Esquema de conexão do Potenciômetro na Placa de Proteção

Para acoplar o potenciômetro à bicicleta foi necessário a construção de um pequeno suporte, como é mostrado na Figura 2.5. Esse suporte possui um furo por onde passa o cilindro rotatório do potenciômetro que é fixado com uma pequena porca. O suporte é preso no quadro da bicicleta com um parafuso e uma porca, ficando paralelo ao garfo da bicicleta e bem próximo do mesmo, como pode ser visualizado na Figura 2.6. Para que houvesse aderência entre o garfo e o cilindro rotatório do potenciômetro foi utilizada uma pequena roda de borracha. Desta forma, quando o guidom da bicicleta é girado, o cilindro do potenciômetro também é girado, mas em sentido contrário.

Figura 2.5 - Potenciômetro acoplado ao seu suporte

 

Figura 2.6 - Potenciômetro acoplado ao quadro da bicicleta

A corrente elétrica enviada pelo potenciômetro para a Placa de Proteção varia de acordo com a corrente de entrada, mas a Placa de Conversão A/D só emite dados entre [0; 5]. Como o intervalo é fixo foi feita uma escala para transformar os valores de corrente elétrica para as possíveis direções. O valor 2.5 corresponde ao centro, ou seja, o guidom não está girado para nenhum lado. Da mesma forma, o valor 0 corresponde ao lado esquerdo e o valor 5 ao lado direito. As direções intermediárias são calculadas usando o mesmo raciocínio.

2.4 Dínamo para Leitura da Velocidade

Para possibilitar a leitura da velocidade da bicicleta, ou seja, a rapidez que o usuário está pedalando, foi utilizado um dínamo próprio para bicicletas, como é mostrado na Figura 2.7. Quando o dínamo é rotacionado ele gera eletricidade. A corrente elétrica gerada é do tipo alternada e diretamente proporcional à velocidade imposta ao dínamo.

 

Figura 2.7 - Dínamo

O dínamo é preso na parte traseira do quadro da bicicleta. A parte posterior do cilindro rotatório que sai do dínamo fica firmemente apoiada na roda da bicicleta, como é mostrado na Figura 2.8. Desta forma, quando a roda da bicicleta gira faz com que o cilindro do dínamo também gire. A corrente elétrica gerada por essa rotação é levada à Placa de Proteção por dois fios.

 

Figura 2.8 - Dínamo acoplado à bicicleta

Para a conversão de corrente elétrica alternada para contínua foi usado um retificador AC/DC do tipo RS405L. Os dois fios que saem do dínamo são ligados no retificador. Deste saem outros dois fios conduzindo a corrente elétrica contínua e que são conectados à Placa de Proteção, no pinos 8 e 11, como é mostrado na Figura 2.9

fig2_9.gif (3923 bytes)

Figura 2.9 - Esquema de conexão Dínamo / Retificador / Placa de Proteção

2.5 Rotinas para Leitura dos Dados

Para possibilitar a leitura dos dados emitidos pela Placa de Conversão A/D foi adquirido pela PUCRS um kit para programação da placa no ambiente Windows. Esse kit é composto por DLLs (Dinamic Link Library) e a documentação de suas funções. Toda a parte de programação direcionada para a Placa de Conversão A/D foi feita utilizando essa biblioteca.

As funções da biblioteca fazem uso de algumas estruturas que devem ser declaradas previamente. Como só foi utilizada a conversão analógica para digital, basta declarar a estrutura utilizada para este tipo de conversão, como mostra a Figura 2.10.

Type

AIPARM = Record  
    wChan: Integer         ' Canal AD utilizado
    wGainCode:  Integer    ' Código de ganho
    wExpChan: Integer      ' Canal da placa de expansão
    wExpGainCode: Integer  ' Código de ganho da placa de exp.
    fData: Single          ' Dado de saída
    hWnd: Long             ' Handle da janela
End

Figura 2.10 - Declaração do tipo AIPARM

A primeira coisa a ser feita para utilizar o kit é inicializar a biblioteca. Isso é feito basicamente pela chamada da função axInitialize que carrega alguns dados de configuração da Placa de Conversão A/D previamente gravados no arquivo INI passado por parâmetro para a função. Foi desenvolvida a função Inicializa_Placa_AD que além de inicializar a biblioteca, também inicializa as estruturas AIPARM utilizadas para capturar os valores enviados pela Placa A/D. Todo o código utilizado para inicialização da biblioteca é mostrado na Figura 2.11.

//Procedimento que Inicializa a Placa Analógica-Digital
procedure Inicializa;
begin
    lpstrIniFile := 'axdrv.ini';
    wErr := axInitialize(lpstrIniFile);
    if wErr > 0 then
    begin
        axGetErrMsg(wErr,lpstrDes);
        ShowMessage(lpstrDes);
    end;
end;

Figura 2.11 - Função para inicialização da biblioteca

Feita a inicialização da biblioteca com sucesso já é possível ler dados. A leitura é feita pela função axADInput. Foi desenvolvida a função Le_Entrada que recebe um parâmetro que define o número do Canal a ser lido. A Figura 2.12 mostra o código utilizado para capturar os dados advindos da Placa A/D.

//==== Função que lê os dados que estão sendo enviados para a placa ====//
   

function Le_Entrada(Channel : integer): Single;
Var

    Readlparm: AIPARM;
begin
    Readlparm.wChan := Channel;
    wErr := axADInput(wDev, Readlparm);
    if wErr > 0 then
    begin
        wErr := axGetErrMsg(wErr, lpstrDes);
        ShowMessage(lpstrDes);
        Le_Entrada := -200;
    end
    else
        Le_Entrada := Readlparm.fData;
end;

Figura 2.12 - Função para leitura de dados da Placa Conversora A/D

A função Le_Entrada é chamada por uma função controlada por um timer. Nesta rotina, primeiramente, é lido o canal 1 para obter a velocidade. Esta velocidade é então usada para atualizar a posição da bicicleta no mundo virtual. A seguir é lido o canal 2 para a atualização da direção de locomoção(figura 2.13).

// Faz a leitura dos dados da placa através de um timer
procedure TFormCidade.TimerPlacaTimer(Sender: TObject);
Var
    Guidao: Integer;
    NovaVelocidade:GLDouble;
begin
    Andou := false;
    // Controla a velocidade
    NovaVelocidade := Le_Entrada(1);
    AtualizaPosicao(NovaVelocidade); 
    // Controla a Guidão
    Guidao := Round(Le_Entrada(0)) ;
    AtualizaDirecao(Guidao);
end;

Figura 2.13 - Função para tratamento dos dados

3. Aquisição dos Dados do Óculos

Para capturar os dados do óculos ele deve ser conectado em uma das portas seriais (RS-232) livres existente no computador. Esta conexão é feita através de um cabo de interface DB-9 que através de um adaptador pode ser ligado a interfaces DB-25. Também ligados ao computador, estão um cabo de saída de vídeo e outro de entrada ao qual é ligado o monitor (VGA), com isso, é permitido a visualização do cenário tanto no óculos como no próprio monitor.

Além da utilização do óculos para a visualização, ele é utilizado no rastreamento dos movimentos do usuário através do tracker acoplado na parte traseira do óculos. Este tracker emite dados (em forma de pacotes) através da porta serial.

3.1 HMD I-Glasses! VPC da Virtual I/O

O I-glasses é um óculos que permite visualizar imagens 3D em videotapes, jogos eletrônicos, jogos de PC ou qualquer outra fonte de vídeo NTSC. Possui capacidade de rastreamento (head-tracker acoplado), compatibilidade com imagens advindas de vídeo e de computador e pode ser usado com óculos de grau. Também é possível, através de seus dois alto-falantes, emitir estímulos sonoros ao usuário.

Uma outra vantagem do I-glasses! é que ele permite visão estereoscópica. Ela a responsável pela sensação de imersão no ambiente. Esta sensação é obtida gerando-se uma imagem diferente para cada olho. Na verdade é uma única cena vista de perspectivas diferentes, e exibidas simultaneamente uma para cada olho, como demonstrado na Figura 3.1.

 

Figura 3.1 - Visão Estereoscópica

Para rastreamento dos movimentos da cabeça do usuário, o I-glasses! Possui três sensores, sendo um para cada eixo: X, Y e Z. Estes movimentos, demonstrados nos itens 1, 2 e 3 da Figura 3.2, são chamados de:

Pitch: é o movimento da cabeça para cima e para baixo, como quando você diz "sim". Eixo de rotação: X;
Roll: é o movimento lateral da cabeça para a direita e esquerda, como que tentando encostar a orelha no ombro. Eixo de Rotação: Z;
Yaw: é o movimento de rotação lateral da cabeça, como quando você diz "não". Eixo de rotação: Y.

Figura 3.2 - Movimentos Rastreados pelo I-Glasses!

Também é possível, através destes movimentos, fazer com que o óculos emule um mouse ou um joystick.

Somente é aceito como entrada pelo I-glasses! um sinal NTSC. Vídeo cassetes e videogames utilizam este sinal como saída, mas a maioria dos PC’s utilizam uma saída VGA. Para converter este sinal, usa-se o Virtual I-O PC Interface, mostrado na Figura 3.3. Este dispositivo possui uma entrada para VGA, uma saída para o head-tracker, um conector para áudio, uma saída para microfone, uma entrada para vídeo RCA e uma entrada de energia.

Figura 3.3 - Virtual I-O PC Interface

A leitura dos dados gerados pelo óculos é feita por algumas funções que são distribuídas junto com o mesmo. Estes dados são as variáveis P, R e Y (já descritas anteriormente) fornecidas pelo tracker.

Na Figura 3.4, mostramos exemplo de um programa em C que faz esta leitura dos dados gerados pelo óculos:

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include "vstrack1.h"

void main(int argc,char ** argv)

{
    TrackerData td;
    TrackerStatus ts;
    TrackerPort trackerPort;

    printf("Inicializando o tracker.\n");
    //Inicializa o tracker e determina em que porta serial     
    ts = initTrackerSearch(&td,&trackerPort,9600,timerSecs(2));
    printf("%s.\n",trackerInfo(ts));
    if (ts != TS_OK)
    {
        closeTracker(&td); //se der erro, fecha e sai do programa
        exit(0);
    }
    //envia parâmetros para iniciar leitura de dados
    ts = sendTrackerCMD(&td,"!M1,P,B\r",timerSecs(2));
    if (ts != TS_OK)
    {
        printf("%s.\n",trackerInfo(ts)); //se der erro mostra o status
        closeTracker(&td); //fecha        
        exit(0); //e sai do programa
    }
    requestTrackerData(&td); //requisita dados
    //inicia um loop eterno (até pressionar 'q') de leitura de dados
    while (1)
    {
        float y,p,p2,r;
        if (kbhit() && getch() == 'q') break;

        ts = readTracker(&td,timerSecs(1)); //checa tracker

        if (ts != TS_OK) //se houver erro
        {
            printf("\n%s.\n",trackerInfo(ts)); //mostra status e
            ts = resetTracker(&td,timerTSecs(1)); //reseta tracker
            if (ts != TS_OK)
            { //se novo erro
            printf("\n%s.\n",trackerInfo(ts)); //então mostra status
            }
        }
        requestTrackerData(&td); //requisita dados
        y = TOFLOAT(td.euler.y); //converte dados
        p = TOFLOAT(td.euler.x);
        r = TOFLOAT(td.euler.z);
        //imprime dados lidos no monitor
        printf("y %6.2f p %6.2f r %6.2f\n", y, p, r);
    }
    //encera leitura
    closeTracker(&td);
}

Figura 3.4 - Exemplo de Leitura do Tracker do I-Glasses!

 

3.2 Protocolo de Comunicação do Óculos

Para a leitura dos dados foi utilizado o componente MSComm32 ActiveX Control Versão 5.0 [MIC-97], desenvolvido pela Microsoft Corp., a qual disponibiliza este em todas as máquinas que possuem o sistema operacional Windows95 instalado. Este componente esta disponível no formato de OCX o que facilita na sua programação.

Para o funcionamento do óculos é necessário que a porta serial do computador esteja configurada conforme a tabela da figura 3.5.

Parâmetro

Valor

Bits per second

9600

Data bits

8

Parity

None

Stop Bits

1

Flow Control

Xon/Xoff

Figura 3.5 - Configurações da porta serial

No projeto foi utilizada a COMM=2 como porta de comunicação entre o computador e o tracker.

3.3 Inicialização

Para a inicialização da comunicação com o tracker do óculos, deve-se primeiro abrir a porta de comunicação. Na figura 3.6, a porta serial é configurada conforme os parâmetros citados anteirormente através da propriedade Settings, o número da porta COMM a ser utilizada é determinado pela propriedade CommPort, através da propriedade InputLen determina-se que o componente deve ler todo o buffer de entrada não importando o tamanho, e logo após, a propriedade PortOpen recebe True indicando que a porta deve ser aberta para dar início a comunicação.

MSComm1.Settings = "9600,N,8,1" ‘Configura porta serial
MSComm1.CommPort = 2 ‘Define COMM=2
MSComm1.InputLen = 0 ‘Configura para ler o buffer do tamanho que vier
MSComm1.PortOpen = True ‘Abre porta COMM

Figura 3.6 - Inicialização da porta serial

Após a abertura da porta serial deve ser enviado um comando de reset (inicialização) para o tracker através do envio da string "!R" seguida do símbolo de carriage-return (ASCII=13). Logo após a inicialização deve ser enviada uma string de configuração que o tracker utilizará como padrão para a comunicação. Na figura 3.7, mostra-se um trecho de código para efetuar a inicialização. Note que é utilizada a propriedade output tanto para o envio da string de inicialização, como para o envio da string de configuração ("!M2,P,A,7,7"), que define que o modo de leitura é "pulled" e ASCII, ou seja, os dados serão enviados somente quando solicitados e no formato ASCII. Observa-se que entre os dois envios a um loop onde o sistema permanece lendo a COMM até que seja recebido a string "0" identificando que a operação ocorreu com sucesso.

Aux = " " 'Inicializa variável auxiliar
Do
    MSComm1.output = "!R" +Chr$(13) 'Envia comando de reset ao traker
    Do
    Loop Until MSComm1.InBufferCount > 0 'Loop até que buffer com algo
    Aux = MSComm1.Input 'Armazena entrada na variável Aux
Loop Until Aux = "0" 'Permanece em loop até que receba "0" de OK

Aux = " "
Do
    MSComm1.output = "!M2,P,A,7,7" +Chr$(13) 'Envia string de configuração
    Do
    Loop Until MSComm1.InBufferCount > 0
    Aux = MSComm1.Input
Loop Until Aux = "0"

Figura 3.7 - Inicialização do óculos

3.4 Formato dos Pacotes de Dados

Os pacotes de dados são enviados a partir de cada pedido, este realizado através do envio do caracter "S". Conforme a string de configuração enviada anteriormente, os pacotes de dados são enviados em formato ASCII. Este pacote para que tenha validade (possa ser aceito pelo sistema) deve vir com a string "FF" como header. Caso esta exigência seja cumprida o resto do pacote pode ser tratado. Os dados de Yaw, Pitch e Row vêm agrupados separados por espaços em branco, sendo que o seus valores reais são enviados em formato Hexadecimal. Na Figura 3.8, é mostrado um exemplo de pacote de dados válidos que representa os seguintes valores: Yaw=15400, Pitch=230 e Row=1040.

"FF 3C28 00E6 0410"

Figura 3.8 - Exemplo de pacote de dados

3.5 Rotina de Leitura

A rotina de leitura consiste em:

Na figura 3.9 é mostrado um trecho que implementa a rotina de leitura. A função Trata_Dados é descrita na figura 3.10. Note que também foi incluído um trecho que serve para dar tempo para que o processador execute suas tarefas. Este trecho foi incluído para previnir, no caso de utilização em loops, que o sistema tranque por falha de processamento.

MSComm1.output = "S" ‘Solicita pacote de dados
Do
    Loop Until MSComm1.InBufferCount > 0 ‘Loop até que buffer receba algo
    s = MSComm1.Input ‘Variavel S recebe pacote de dados
    Duration = Timer + 0.1 ‘define um limite de espera de processamento
    Do
        DoEvents() ‘Ativa processador à executar todas tarefas pendentes
        Loop Until Timer > Duration ‘Loop até que processador exceda variável
        If ((Mid(s, 1, 3) = "FF ") AND (Len(s) >= 17)) Then ‘Se pacote válido
        Trata_Dados s,yaw,pitch,row ‘Chama função trata dados
End If

Figura 3.9 - Rotina de leitura dos dados

 

Private Sub Trata_Dados(linha, ByRef Y, ByRef p, ByRef r)
    Dim Cont, i As Integer 'Declaração de variáveis
    Dim valor_dec As Integer
    Dim tamanho As Integer
    Dim valor_hex As String

    tamanho = Len(linha) 'Armazena tamanho do pacote
    valor_hex = " " 'Variável para armazenamento de valores capturados
    Cont = 1 'Inicia contador de movimentos
    For i = 4 To tamanho 'Loop até o fim do pacote
        If (Mid(linha, i, 1) = " ") Then 'Caso separador de movimento
            valor_dec = Val("&H" + valor_hex) 'Converte valor Hexadecimal
            valor_hex = " " 'Zera string auxiliar
            If Cont = 1 Then 'Caso seja primeiro movimento
                Y = valor_dec 'Carrega valor de Yaw
            End If
            If Cont = 2 Then 'Caso seja segundo movimento
                p = valor_dec 'Carrega valor do Pitch
            End If
            If Cont = 3 Then 'Caso seja terceiro movimento
                r = valor_dec 'Carrega valor do Row
            End If
            Cont = Cont + 1 'Incrementa contador de movimento
        Else
            valor_hex=valor_hex+Mid(linha, i, 1) 'Captura valor hexadecimal
        End If
    Next I
End Sub

Figura 3.10 - Função para tratamento dos dados

 

4. Utilização dos dados do Óculos

Para a perfeita exibição das imagens no óculos é necessário atualizá-las a cada vez que o usuário mover a cabeça.

A partir dos pacotes de dados que são lidos da porta serial e da utilização da função Trata_Dados (convertendo os valores hexadecimal para decimal dos movimentos da cabeça) é identificada a rotação necessária para atualizar a imagem visualizada.

Esta rotação foi implementada com base na análise da diferença do valor capturado e o valor anterior. No trabalho foi implementado apenas o movimento de yaw, podendo posteirmente ser acrescentados os movimentos de pitch e row.

Para desenvolver uma fórmula que retornasse como resultado o ângulo em graus de rotação (este no eixo Y, representando o movimento de yaw) foi necessário identificar o intervalo de valores que o tracker retornava para este movimento. Com testes específicos foi verificado que eles variavam de [-16384;+16384], totalizando 32768 para um giro completo. Como este movimento suporta 360º graus de rotação chegamos a seguinte fórmula mostrada na Figura 4.1. Note que a fórmula representa exatamente a regra de três (também mostrada na Figura 4.1.) que demonstra a relação dos ângulos com os valores de yaw.

Ângulo –> valor de yaw (Ycapturado – Yanterior)
360º -> 32768
angulo = (360 * |Ycapturado – Yanterior|) / 32768

Figura 4.1 – Fórmula do ângulo de rotação

A função Rotaciona_Y mostrada na Figura 4.2 utiliza a fórmula descrita para atualizar o cenário conforme a variação de yaw. Note que é feita uma consistência para verificar se os valores variaram positivamente ou negativamente, e conforme o caso, rotaciona-se o observador (câmera) no sentido horário ou anti-horário.

Private Sub Rotaciona_Y(Y As Long)
    Dim angulo As Long

    Angulo = (360 * Abs(Y - Yaw)) \ 32768 ‘Obtem o ângulo
    If (Yaw < Y) Then
        View.RotateCamera angulo, 0, -1, 0 ‘Executa o giro da câmera
    Else
        View.RotateCamera angulo, 0, 1, 0
    End If
    Yaw = Y

End Sub

Figura 4.2– Função que rotaciona eixo Y

 

Na Figura 4.3. é mostrado o cenário do mundo virtual na sua posição inicial, e logo após na Figura 4.4 é demonstrado um pequeno giro da cabeça do usuário para a direita, correspondendo a uma rotação positiva no eixo Y.

Figura 4.3 – Posição inicial do usuário no mundo virtual

 

Figura 4.4 – Visão do usuário após movimento da cabeça

5. Utilização dos dados do Potenciômetro

Para definir a direção de deslocamento do usuário no mundo virtual é preciso ler a movimentação do guidom através do potenciômetro, e usar seus valores para rotacionar a câmera.

Com a utilização da função Le_Placa (parâmetro 0 para potenciômetro) descrita anteriormente, que retorna um valor de 0 a 180, correspondente ao ângulo de giro do guidom da bicicleta, consegue-se definir qual a direção na qual esta se movimenta.

Na figura 5.1. são mostrados os vetores principais de direção com os respectivos valores de intervalo retornados da função Le_Placa, comparados com os correspondentes valores de ângulo de rotação do guidom.

Para guardar a direção (atualizada) de movimentação do usuário foi criado o vetor direção, inicializado em (x=0,y=0 e z=1), indicando que até o usuário alterar a posição inicial do guidom (reto para a frente) a bicicleta andará ao longo do eixo Z do mundo virtual.

fig5_1.gif (2130 bytes)

Figura 5.1 – Relação dos valores do potenciômetro com os de rotação do guidom

Observando a figura 5.1, nota-se que a diferença entre os valores retornados do potenciômetro e os valores do ângulo de rotação do guidom é de exatamente 90 unidades. Com base nesta observação foi concluído que a partir dos valores coletados do potenciômetro deve-se diminuir de 90 unidades para ter o valores correspondentes do ângulo de rotação do guidom.

Para atualizarmos a direção de movimento do usuário (vetor de direção) foram utilizadas fórmulas de rotação em torno do eixo Y (figura 5.2.). Esta função recebe o ângulo de rotação do guidom e o emprega nas fórmulas para obter um novo vetor.

Private Sub Seta_VetDirecao(ByVal angulo As Integer)
   Dim pi, angx, angy, angz As Single

    pi=3.14159
    ang_rad=angulo*(pi/180) ‘Converte de graus para radianos
    angx=dirx*Cos(ang_rad)+dirz*Sin(ang_rad)
    angy=diry
    angz=-dirx*Sin(ang_rad)+dirz*Cos(ang_rad)
    dirx=angx ‘Aplica rotação
    diry=angy ‘em torno do
    dirz=angz ‘eixo Y
End Sub

Figura 5.2 – Função que atualiza vetor de direção

Para simular a correta realização de uma curva é preciso garantir que, na retomada da mesma (quando o guidom retorna à posição normal) o vetor direção não seja alterado. Ou seja, após uma conversão para a direita o vetor direção deve ficar em (1,0,0) mesmo após o retorno do guidom. Isto foi resolvido considerando para rotações, apenas o aumento (em valores absolutos) do ângulo do guidom.

6. Utilização dos dados do Dínamo

A utilização da função Le_Placa (parâmetro 1 para dínamo) descrita anteriormente, retorna um valor de 0 até o limite de velocidade conseguido pelo usuário. Através desta função consegue-se definir o valor que será multiplicado ao vetor de direção. Na figura 6.1 é mostrado um trecho de código que demonstra a atualização das coordenadas do observador (câmera) em relação ao mundo virtual. Note que quanto maior a velocidade, maior será o valor de atualização, com isso, o usuário têm a sensação de que está andando mais rapidamente.

View.cameraX = View.cameraX + (dirx * velocidade)
View.CameraY = View.CameraY + (diry * velocidade)
View.CameraZ = View.CameraZ + (dirz * velocidade)

Figura 6.1 – Trecho de código que atualiza observador no mundo virtual