Rede de sensores bastante complicada


9

Eu estava trabalhando em um projeto recentemente e foi o primeiro envolvido o suficiente para tornar a rede de sensores complicada. No final, acho que a comunicação foi o gargalo em termos de desempenho geral e estou me perguntando como as pessoas mais experientes teriam resolvido esse problema. Esta é uma leitura longa, mas acho que é bem interessante, por favor, continue com ela. O problema era projetar um dirigível autônomo capaz de navegar em uma pista de obstáculos e soltar bolas de pingue-pongue nos alvos das caixas marrons. Aqui vai:

Sensores

  • Módulo de câmera uCAM-TTL da 4D Systems - interface UART
  • Bússola digital HMC6352 - interface I2C
  • Maxbotix Sonar ez4 - interface analógica de 1 pin

Atuadores

  • 2 drivers de motor L293D (conectados a motores simples) - Estes eram usados ​​para acionar 6 motores bidirecionalmente. Eles exigiram entradas PWM para variar a velocidade. Agora, 3 de nossos motores estavam sempre fazendo a mesma coisa (os que controlavam o movimento para cima / para baixo), portanto, eles exigiam apenas 2 saídas PWM de nossos controladores para controlar todos os 3 motores. Os outros três motores que controlavam o movimento lateral precisavam de controle individual (para movimento omnidirecional), de modo que outras 6 saídas PWM eram necessárias em nossos controladores.
  • Servo motor - interface PWM

Controladores

Por razões que ficarão claras mais tarde, acabamos usando 2x ATmega328Ps. Usamos um Arduino Uno para programá-los (não tínhamos acesso a um provedor de serviços de Internet), mas fabricamos uma PCB personalizada, para que não precisássemos usar placas de arduino, pois isso acrescentaria peso desnecessário ao nosso dirigível. Quanto ao motivo pelo qual escolhemos o ATmega328P, eu estava muito familiarizado com o ambiente do arduino e acho que tornou o desenvolvimento do código muito mais rápido e fácil.

Comunicação e Processamento

  • 2x Xbee Basic
  • 2x ATmega328P
  • Computador de mesa executando C ++ com openCV

Como você pode ver no módulo da câmera, a maior parte do nosso projeto se baseou na visão computacional. Os dirigíveis podiam suportar tanto peso e não nos sentíamos confortáveis ​​implementando a visão computacional em um microcontrolador. Então, o que acabamos fazendo foi usar o XBee's para retransmitir os dados da imagem de volta para um computador desktop. Por isso, no lado do servidor, recebemos dados da imagem e usamos o openCV para processar a imagem e descobrir coisas a partir dela. Agora, o lado do servidor também precisava saber informações de altura (do sonar) e informações da bússola.

A primeira ruga foi que não conseguimos controlar a câmera por um microcontrolador por alguns motivos. O principal problema era que a memória interna no uP não suportava o armazenamento de um quadro inteiro. Pode ter havido maneiras de contornar isso através de uma codificação inteligente, mas para os fins desta pergunta, vamos fingir que era impossível. Portanto, para resolver esse problema, o servidor enviou comandos da câmera através do transceptor XBee e o receptor XBee (a bordo do dirigível) teve sua saída conectada à entrada da câmera.

A próxima ruga foi a de que não há PWMs suficientes em um único ATmega328P para controlar todos os motores PORQUE a interface I2C usa um dos pinos PWM (caramba ...). Por isso, decidimos usar o segundo. O código realmente se prestou perfeitamente ao processamento paralelo de qualquer maneira, porque o controle de altura era completamente independente do controle de movimento lateral (portanto, 2 micros eram provavelmente melhores do que um conectado a um controlador PWM). Portanto, o U1 foi responsável por 2 saídas PWM (para cima / para baixo) e pela leitura do Sonar. O U2 foi responsável pela leitura da bússola, pelo controle de 6 saídas PWM (os motores laterais) e também pela leitura do Sonar. O U2 também foi responsável por receber comandos do servidor através do XBee.

Isso levou ao nosso primeiro problema de comunicação. A linha XBee DOUT foi conectada ao microcontrolador e à câmera. Agora, é claro, nós projetamos um protocolo para que nossos micro comandos ignorassem os comandos da câmera e os comandos da câmera ignorassem os micro comandos, o que era bom. No entanto, a câmera, ao ignorar nossos micro comandos, enviava dados de NAK em sua linha de saída. Como o comando foi feito para o micro, precisamos de alguma maneira desligar a saída da câmera para o XBee. Para resolver isso, fizemos os FETs de microcontrole 2 que estavam entre a câmera e o XBee (esse é o primeiro FET) e também entre o U2 e o XBee (esse é o segundo FET). Portanto, quando a câmera estava tentando enviar informações de volta ao servidor, o primeiro FET estava 'ligado' e o segundo FET estava 'desligado'.

Então, para lhe dar uma idéia de como isso funcionou, aqui estão alguns exemplos:

  1. O servidor solicita uma imagem - PIC_REQUEST passa pelo XBee e chega ao U2 e à câmera. O U2 o ignora e a câmera envia os dados da imagem.
  2. O servidor acabou de processar uma imagem e está enviando dados do motor para dizer ao dirigível que vire à direita - MOTOR_ANGLE (70) passa pelo XBee e chega ao U2 e à câmera. O U2 reconhece como um micro comando e, portanto, desliga o FET da câmera (mas talvez a câmera já tenha respondido com um NAK ?? quem sabe ...). O U2 responde ao comando alterando as saídas PWM do motor. Em seguida, reativa o FET da câmera (essa era a configuração padrão, pois os dados da imagem eram mais importantes).
  3. O servidor percebe que chegamos a um ponto na pista de obstáculos em que nossa altura de foco padrão agora precisa ser de 90 polegadas em vez de 50 polegadas. SET_HEIGHT passa pelo XBee e o mesmo acontece no exemplo 2. U2 reconhece o comando SET_HEIGHT e aciona uma interrupção no U1. O U1 agora sai do loop de controle de altura e aguarda para receber dados seriais do U2. É isso mesmo, mais dados seriais. Nesse ponto, o FET do U2 está ativado (e o FET da câmera está desativado), de modo que o servidor recebe a altura que o U2 também está enviando para o U1. Isso foi para fins de verificação. Agora o U1 redefine sua variável interna para height2HoverAt. O U2 agora desliga o FET e liga novamente o FET da câmera.

Eu definitivamente deixei de fora uma boa quantidade de informações, mas acho que isso é suficiente para entender algumas das complicações. No final, nossos problemas estavam apenas sincronizando tudo. Às vezes, restavam dados nos buffers, mas apenas 3 bytes (todos os nossos comandos eram sequências de 6 bytes). Às vezes, perdíamos a conexão com nossa câmera e precisávamos ressincronizá-la.

Então, minha pergunta é: que técnicas vocês sugerem para tornar a comunicação entre todos esses componentes mais confiável / robusta / mais simples / melhor?

Por exemplo, eu sei que alguém teria sido adicionar um circuito de atraso entre a saída do XBee a bordo e a câmera, para que o micro tivesse a chance de desligar a linha de conversa da câmera antes de responder aos micro comandos com NAKs. Alguma outra idéia assim?

Obrigado e tenho certeza de que isso exigirá muitas edições, portanto, fique atento.


Edit1:A emenda dos dados UART da câmera através de um dos micros não nos parecia possível. Havia duas opções para dados da câmera, mapa de bits bruto ou JPEG. Para um bitmap bruto, a câmera envia os dados para você o mais rápido possível. O ATmega328P possui apenas 128 bytes para um buffer serial (tecnicamente isso é configurável, mas não sei como) e não achamos que conseguiríamos tirá-lo do buffer e passar para o XBee com rapidez suficiente. Isso deixou o método JPEG, onde ele envia cada pacote e aguarda que o controlador o aceite (pequeno protocolo de handshaking). O mais rápido possível foi 115200 baud. Agora, por alguma razão, o mais rápido que pudemos transmitir grandes quantidades de dados de maneira confiável pelo XBee foi de 57600 baud (isso ocorreu mesmo após o emparelhamento nó / rede para permitir o recurso de reenvio automático). A adição de uma parada extra em nossa rede (câmera para micro para XBee em vez de apenas câmera para XBee) para o micro simplesmente diminuiu o tempo necessário para transferir muito uma imagem. Precisávamos de uma certa taxa de atualização nas imagens para que nosso algoritmo de controle motor funcionasse.


3
Você está se esforçando muito para não expandir suas habilidades no microcontrolador. Não tenho nada contra o Arduino, mas simplesmente não é muito apropriado para isso. Você pode finalmente fazê-lo funcionar? Provavelmente. No entanto, seria muito mais útil aprender uma plataforma mais capaz. Se você está perguntando como pessoas mais experientes fariam isso, eu diria algo como um SBC ARM para openCV e controle e um FPGA que serve como ponte para todas as interfaces. No entanto, isso seria um salto, então eu sugiro apenas tentar uma coisa importante ... talvez um micro de 32 bits com periféricos suficientes para fazer interface com tudo?
darron

Haha sim, eu estava indo muito longe. Ver que este projeto era uma tarefa da escola e queríamos focar em fazê-lo funcionar, não fazendo com que um dos dois EEs da equipe aprendesse uma nova plataforma de microcontrolador. Estou pensando em entrar no ARM e em micros mais avançados neste verão, na verdade. O outro EE em nossa equipe tinha tomado uma classe em FPGAs na verdade ... Eu vou gritar com ele por não sugerindo que = P
NickHalden

Respostas:


4

Entendo que você desejava escolher um ambiente de desenvolvimento com o qual se familiarizasse, de modo a poder começar a trabalhar, mas acho que a troca de hardware / software pode ter ajudado você a aderir ao Arduino e a não escolher uma parte que tivesse tudo os periféricos de hardware necessários e escrevendo tudo em C orientado a interrupções.

Eu concordo com a sugestão de Matt Jenkins e gostaria de expandi-la.

Eu teria escolhido um uC com 2 UARTs. Um conectado ao Xbee e outro conectado à câmera. O uC aceita um comando do servidor para iniciar uma leitura da câmera e uma rotina pode ser gravada para transferir dados do canal UART da câmera para o canal UART XBee em um byte por byte - portanto, não há buffer (ou, no máximo, apenas um tamanho muito pequeno). um) necessário. Eu tentaria eliminar todos os outros UCs ​​escolhendo uma peça que também acomodasse todas as suas necessidades de PWM (8 canais PWM?) E se você quisesse ficar com 2 UCs diferentes, cuidando de seus respectivos eixos, talvez um uma interface de comunicação diferente teria sido melhor, pois todos os seus outros UARTs seriam utilizados.

Outra pessoa também sugeriu a mudança para uma plataforma Linux embutida para executar tudo (incluindo o openCV) e acho que isso teria sido algo a ser explorado também. Eu já estive lá antes, porém, um projeto escolar de 4 meses e você só precisa concluí-lo o mais rápido possível, não pode ser paralisado pela análise - espero que tenha sido bom para você!


EDIT # 1 Em resposta aos comentários @JGord:

Eu fiz um projeto que implementou o encaminhamento de UART com um ATmega164p. Possui 2 UARTs. Aqui está uma imagem de uma captura do analisador lógico (analisador lógico Saleae USB) desse projeto mostrando o encaminhamento do UART: captura do analisador

A linha superior são os dados de origem (nesse caso, seria sua câmera) e a linha inferior é o canal UART que está sendo encaminhado para (XBee no seu caso). A rotina escrita para fazer isso manipulou a interrupção de recebimento do UART. Agora, você acreditaria que, enquanto esse encaminhamento do UART estiver em andamento, você poderá configurar com facilidade seus canais PWM e também lidar com suas rotinas I2C? Deixe-me explicar como.

Cada periférico UART (para o meu AVR de qualquer maneira) é composto de dois registros de turno, um registro de dados e um registro de controle / status. Esse hardware fará as coisas por conta própria (supondo que você já tenha inicializado a taxa de transmissão e outras) sem nenhuma intervenção, se:

  1. Um byte entra ou
  2. Um byte é colocado em seu registro de dados e sinalizado para saída

De importância aqui é o registro de turno e o registro de dados. Vamos supor que um byte esteja chegando no UART0 e queremos encaminhar esse tráfego para a saída do UART1. Quando um novo byte é deslocado para o registro de deslocamento de entrada do UART0, ele é transferido para o registro de dados do UART0 e uma interrupção de recebimento do UART0 é acionada. Se você escreveu um ISR para ele, pode pegar o byte no registro de dados UART0 e movê-lo para o registro de dados UART1 e, em seguida, definir o registro de controle para o UART1 para iniciar a transferência. O que isso faz é dizer ao periférico UART1 para pegar o que você acabou de colocar no registro de dados, colocar no registro de deslocamento de saída e começar a retirá-lo. A partir daqui, você pode retornar do seu ISR e voltar para qualquer tarefa que o seu uC estivesse fazendo antes de ser interrompido. Agora UART0, depois de ter seu registro de turnos limpo e o registro de dados limpo, você pode começar a trocar novos dados, se ainda não o fez durante o ISR, e o UART1 está transferindo o byte que você acabou de colocar - tudo isso acontece no sem a sua intervenção enquanto o seu uC estiver fora, realizando alguma outra tarefa. Todo o ISR leva microssegundos para ser executado, pois estamos movendo apenas 1 byte em torno de alguma memória, e isso deixa muito tempo para sair e fazer outras coisas até que o próximo byte no UART0 chegue (o que leva centenas de microssegundos). e o UART1 está mudando o byte que você acabou de inserir - tudo isso acontece por si só, sem a sua intervenção enquanto o seu UC está desligado, realizando alguma outra tarefa. Todo o ISR leva microssegundos para ser executado, pois estamos movendo apenas 1 byte em torno de alguma memória, e isso deixa muito tempo para sair e fazer outras coisas até que o próximo byte no UART0 chegue (o que leva centenas de microssegundos). e o UART1 está mudando o byte que você inseriu nele - tudo isso acontece por si só, sem a sua intervenção enquanto o seu UC está desligado, realizando alguma outra tarefa. Todo o ISR leva microssegundos para ser executado, pois estamos movendo apenas 1 byte em torno de alguma memória, e isso deixa muito tempo para sair e fazer outras coisas até que o próximo byte no UART0 chegue (o que leva centenas de microssegundos).

Essa é a beleza de ter periféricos de hardware - você apenas escreve em alguns registros de memória mapeados e ele cuida do resto a partir daí e sinaliza sua atenção através de interrupções como a que acabei de explicar acima. Esse processo acontece toda vez que um novo byte entra no UART0.

Observe como existe apenas um atraso de 1 byte na captura lógica, pois estamos sempre "armazenando em buffer" 1 byte se você quiser pensar dessa maneira. Não tenho certeza de como você fez sua O(2N)estimativa - vou assumir que você alojou as funções da biblioteca serial do Arduino em um loop de bloqueio aguardando dados. Se considerarmos a sobrecarga de ter que processar um comando "ler câmera" no uC, o método acionado por interrupção é mais parecido com O(N+c)onde cabrange o atraso de um byte e a instrução "ler câmera". Isso seria extremamente pequeno, pois você está enviando uma grande quantidade de dados (dados de imagem, certo?).

Todo esse detalhe sobre o periférico UART (e todos os periféricos do uC) é explicado detalhadamente na ficha técnica e é acessível em C. Não sei se o ambiente do Arduino oferece pouco acesso para que você possa começar a acessar. registra - e é isso - se não o fizer, você será limitado pela implementação deles. Você está no controle de tudo se tiver escrito em C (ainda mais se for feito em montagem) e poderá realmente empurrar o microcontrolador para o seu potencial real.


Acho que não expliquei isso muito bem. O problema de colocar o micro entre a câmera e o XBee é que o micro não pode fazer mais nada enquanto está obtendo os dados da imagem, a menos que tudo o mais esteja ligado. Além disso, se você presumir que tirar uma foto com N pixels levou 5 segundos, quando você coloca o micro, agora leva cerca de 10 segundos. Certamente ainda é O (N), mas é realmente um tempo de execução de 2N, que neste caso foi suficiente para arruinar nossa meta de taxa de atualização. Em última análise, o espaço da memória não era realmente o fator limitante, era principalmente a velocidade. Sigh, parece que a resposta só real ...
NickHalden

era usar hardware mais avançado. Eu esperava que alguém sugerisse algo realmente inteligente que servisse como um truque útil para todos os meus anos como EE. Oh, bem, acho que pelo menos significa que não fui burra demais para pensar no truque = P Ah, e funcionou muito bem.
NickHalden

@JGord, por coincidência, eu fiz um projeto usando o encaminhamento UART da maneira que estou descrevendo entre um cartão com chip e uma máquina de lavar usando um ATmega164 jonathan-lee.ca/DC18-Smart-Card.html de que estou falando usando ISRs do UART para mover um único byte de um registro de dados UART para o outro registro de dados do UART e retornando. O hardware UART age de forma independente e retira os dados de lá. O ISR leva microssegundos e, uma vez retornado, o uC fica livre para fazer o que quiser até que o próximo byte seja alterado. Melhor explicação em uma edição, sempre que eu chego em casa.
Jon L

Interessante. Então, sua sugestão seria que o servidor falasse apenas com o micro? Então esse micro retransmite comandos de servidor para câmera e respostas de câmera para servidor? Assim, no UART0 ISR (finalizando a conversa com o servidor), pude verificar se não é um comando de câmera. Se sim, espelhe o UART1 na câmera. Caso contrário, não espelhe e, em vez disso, altere algum valor (ângulo lateral, altura etc. alguma variável que meu loop de controle verifique). O ISR do UART1 seria simplesmente espelhar os dados da imagem no UART0 sempre. Sim, acho que isso funcionaria. Então, realmente apenas começando um micro com 2 UARTs ...
NickHalden

e canais PWM suficientes teriam sido o caminho a percorrer. Então agora vamos dizer que o servidor enviou uma solicitação get_data que o micro deve responder com uma sequência de 6 bytes na qual envia uma altura, direção da bússola e alguns ECCs. Por alguma razão, o servidor lê 6 bytes, mas não possui o protocolo correto, ou seja, os bytes da mensagem inicial e final não se alinham. Quem sabe porque? Você jogaria fora a mensagem e solicitaria novamente ou o quê? Porque se houvesse algum byte fictício no buffer, a mensagem de 6 bytes nunca se alinharia novamente? Você recomendaria a descarga após uma falha na mensagem?
NickHalden

1

Por que você não conseguiu canalizar os dados da câmera através do µC? Não me refiro ao buffer das imagens, mas à retransmissão dos dados do UART através do µC para que ele possa decidir o que deve ser enviado de volta e o que não deve.

Mais fácil se você possui um µC com dois UARTs, mas pode ser emulado no software.


Hah, sim, essa foi uma das coisas que deixei de fora ... pergunta de edição agora. Boa ideia, porém, ficamos muito animados quando pensamos nisso também.
NickHalden

1

Outra opção me ocorreu, mas isso pode ser um pouco volumoso e muito pesado para o seu projeto: -

  • Por que não usar um servidor USB sem fio, vinculado a um pequeno hub USB, com adaptadores USB-> RS232 para fornecer vários canais de controle para as diferentes partes do sistema?

Sim, volumoso, mas se você reduzi-los, e talvez use USB em vez de RS232 sempre que possível, você pode se safar ...

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.