Reduzindo o atraso entre o arduino e um esboço de processamento no meu computador


13

Atualmente, estou no projeto nº 14 do livro do projeto Arduino.

Estou tentando controlar um esboço de processamento no meu laptop usando meu Arduino. Isso é feito usando um potenciômetro para controlar o fundo de uma imagem.

Código Arduino:

void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.write(analogRead(A0)/4);
}

Em processamento:

//imports serial library
import processing.serial.*;
//setups the serial object
Serial myPort;
//creates an object for the image
PImage logo;
//variable to store background color
int bgcolor = 0;

void setup(){
  colorMode(HSB,255);
  logo = loadImage("http://arduino.cc/logo.png");
  size(logo.width,logo.height);
  println("Available serial ports");
  println(Serial.list());
  myPort = new Serial(this,Serial.list()[0],9600);
}
//equivalent of arduino's loop function
void draw(){
  if(myPort.available() > 0)
  {
    bgcolor = myPort.read();
    println(bgcolor);
  }

  background(bgcolor,255,255);
  image(logo,0,0);
}

Agora, enquanto o código funciona e a cor de fundo muda à medida que eu ligo o potenciômetro, há um grande atraso entre girar o potenciômetro e ver o fundo mudar de cor, e os valores do Arduino / potenciômetro mudam no monitor serial do processamento.

O que eu tentei:

  • Alterando a velocidade da comunicação serial

Percebi que quando diminuo a velocidade da comunicação serial, por exemplo, cerca de 100, o atraso entre girar o potenciômetro e vê-lo mudar no meu laptop diminui para cerca de 1 segundo. No entanto, quando diminuo ainda mais a velocidade da comunicação serial, por exemplo, um valor 1, o atraso aumenta novamente.

Por outro lado, na velocidade padrão de 9600, o atraso é enorme, aproximadamente 5s ++, antes que as alterações no potenciômetro apareçam no laptop / processamento.

Por que diminuir a velocidade da comunicação (até um certo ponto) diminui o intervalo de tempo e aumenta-o? Além disso, existe alguma maneira que eu possa torná-lo quase instantâneo?


3
Você está produzindo uma leitura todas as vezes em torno do Arduino loop(). É bem possível que seu programa de processamento não esteja funcionando rápido o suficiente para acompanhá-lo. Tente colocar um atraso no loop()código do Arduino para reduzi-lo; por exemplo delay(50).
Peter Bloomfield

Oi Pedro, obrigado pela resposta rápida, a adição de um pequeno atraso realmente resolveu o meu problema. Apenas mais uma pequena pergunta: existe alguma maneira de determinar a velocidade do meu programa de processamento no futuro para evitar que isso aconteça novamente ou obter uma melhor velocidade de laptop / processamento resolva o problema? Além disso, por que inserir uma velocidade de comunicação de 250 ou 300 atrapalha as leituras do arduino? (As leituras que recebo são alternadas entre a leitura e o zero, por exemplo, 147,0,147,0)
Kenneth .J

Respostas:


11

Você está produzindo uma leitura sempre do Arduino loop(), portanto, parece provável que o seu programa de processamento não esteja sendo executado rápido o suficiente para acompanhá-lo. Tente colocar um atraso no loop()código do Arduino para reduzi-lo, por exemplo:

void loop(){
    Serial.write(analogRead(A0)/4);
    delay(50);
}

Tanto quanto eu sei, o processamento visa executar em uma taxa de quadros consistente, que você pode modificar usando a frameRate()função Por padrão, são 60 quadros por segundo, embora possa ser mais lento em sistemas mais antigos (ou onde você está executando um programa intensivo). Você pode verificar a velocidade da execução lendo a frameRatevariável

Introduzir um atraso de 50 milissegundos no loop do Arduino significa que ele será atualizado um pouco menos de 20 vezes por segundo. Isso significa que deve ser rápido o suficiente para fins de interface com o usuário, mas também deve estar dentro dos recursos do seu programa de processamento.

No que diz respeito à taxa de transmissão (velocidade de comunicação), é provável que o ajuste por valores arbitrários tenha resultados imprevisíveis. Isso ocorre porque o hardware suporta apenas velocidades específicas, e tentar usar qualquer outra coisa pode resultar na exibição de dados ilegíveis na outra extremidade. A Serial.begin()documentação possui mais informações sobre taxas de transmissão suportadas.


14

Como já apontado, seu Arduino está dizendo muito rápido demais. A adição delay()diminuirá a velocidade, mas continuará gritando com o processamento. Idealmente, você deseja que o Processing solicite o valor quando for conveniente e receba uma resposta do seu Arduino.

Enter SerialEvent().

Ao contrário do loop()Arduino e draw()do Processing, tudo o serialEvent()que existe no interior só funciona quando há algo novo no buffer serial. Portanto, em vez de Processar, fazer perguntas o mais rápido possível e seu Arduino gritando ainda mais rápido, eles podem ter uma conversa agradável e educada (assíncrona).

O Processing e o Arduino têm um serialEvent. Este é serialEvent () no Arduino e é serialEvent () no Processing. Usando serialEvent nos dois lados, é isso que aconteceria:

  1. O processamento envia um caractere para a conexão serial. Pode ser qualquer caractere, mas se predeterminarmos um, podemos filtrar quaisquer solicitações indesejadas causadas por, por exemplo, um sinal barulhento. Neste exemplo, vamos enviar Vsempre que desejamos uma nova leitura do seu medidor de potenciômetro. Depois que o personagem é enviado, continuamos nossos negócios como de costume. Não estou esperando por uma resposta aqui!

  2. No lado do Arduino, nada está acontecendo, até receber dados no buffer serial. Ele verifica se o personagem recebido é um Ve, para nossa sorte, é. O Arduino lê o valor do medidor de potenciômetro uma vez, gera esse valor para serial uma vez e volta a relaxar, maximizando o descontração. Protip: finalize o valor com um caractere ( *no nosso caso). Isso o ajudará na próxima etapa.

  3. O processamento está realizando seus negócios regulares de pixel de interface quando, de repente, ocorre uma perturbação na força de novos dados no buffer serial. Ele muda para serialEvent()e começa a ler os dados seriais, até que nosso término *seja encontrado. Sabendo com certeza que esse foi o último caractere que vale a pena ler, agora podemos armazenar o valor recebido em uma variável que armazena a leitura do Arduino.

  4. É isso aí. O processamento agora conhece o novo valor do sensor e continua com o que pedimos. Enquanto isso, seu Arduino está aproveitando o tempo ou contemplando sua existência até que haja dados seriais recebidos.


1
E enquanto você está nisso, coloque um capacitor em paralelo com o seu medidor de potenciômetro. Isso suaviza pequenas alterações na entrada do seu DAC, possivelmente impedindo movimentos instáveis ​​no processamento.
Tom

Obrigado por esta resposta agradável (e meio antropomórfica)!
Zeta.Investigator

Na verdade, fazer perguntas por USB pode ser uma ideia. Isso ocorre porque o USB possui muito mais latência do que uma porta serial, portanto, fazer uma pergunta e aguardar uma resposta é uma operação que consome mais tempo do que seria, especialmente se comparado ao que poderia ser feito com altas taxas de transmissão. Deixar o Arduino rodar um pouco mais rápido é bom (embora não deva saturar a parte serial do link); O problema é que o esboço do processamento deve drenar os dados do Arduino à medida que se tornam disponíveis e manter o último valor completo a ser usado quando necessário.
Chris Stratton

7

Seu loop de pesquisa é executado na velocidade máxima do seu processador e grava na porta serial em cada rodada.

Dessa forma, você está gravando com mais frequência na porta serial do que ela pode suportar.

A porta grava os dados o mais rápido que você o configurou e armazena em buffer os dados que chegam do seu programa com muita rapidez , para gravá-los o mais rápido possível. O buffer está cheio, apenas lança novos dados.

O importante aqui é que ele manterá a ordem dos valores: é um buffer FIFO , trabalhando na ordem de entrada / saída.

O que acontece é:
O loop preenche o buffer da porta e o mantém 100% cheio.
Se você girar o potenciômetro, o valor alterado será gravado no final do buffer , a porta funcionará o mais rápido possível para gravar todos os elementos no buffer, que ainda possuem o valor antigo.

E, finalmente, o valor em que você está interessado. O valor mais atual que queríamos ver imediatamente era no final do FIFO, e primeiro a entrar / primeiro a sair também significa último a entrar / sair a última. O oposto do que queremos.

A frequência máxima em que faz sentido ler seus dados é a frequência em que você pode gravá-los; portanto, você deve usar pelo menos um atraso que seja longo o suficiente para gravar os bytes na velocidade atual da porta.


Como outra medida independente para evitar esse tipo de atraso em geral,
você pode definir adicionalmente o buffer de gravação da porta para um mínimo.

Isso faria com que os dados fossem descartados muito antes, em vez de armazenar muito em buffer primeiro.

Obviamente, em muitos aplicativos, não é disso que você precisa; Com má sorte, ele poderia funcionar de qualquer maneira no começo e ficar instável em algumas situações quando o tempo muda com base em coisas como carga do processador e existem apenas algumas amostras aleatórias de dados que são descartadas. Um buffer grande geralmente se comporta muito mais determinístico, portanto, use um buffer grande por padrão .


Idéia certa, mas não muito certa na afirmação "O buffer está cheio, apenas lança novos dados". Depois que o buffer é preenchido, os dados não são descartados, mas o bloco de gravações até que haja espaço no buffer de saída. Isso significa que a entrada e a saída logo acabam fluindo na mesma taxa média, mas que existe uma latência de buffer entre elas.
Chris Stratton

6

Em vez de enviar constantemente dados seriais, envie dados apenas quando o valor do potenciômetro tiver mudado acima de um determinado limite.

int oldValue = 0;
const int threshold = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(A0, INPUT)
}

void loop()
{
  if(oldValue >= analogRead(A0)+threshold || oldValue <= analogRead(A0)-threshold)
  {
    Serial.println(analogRead(A0));
    oldValue = analogRead(A0);
  }
}

1
Isso loop()não é preencher o buffer de saída com amostras iguais, isso é bom. Mas ele ainda roda na velocidade máxima do processador, o que pode ser 100 vezes mais rápido que o necessário. Isso significa que pode ainda preencher o tampão para o limite rapidamente se a entrada muda frequentemente, por exemplo, a partir de ruído acima threshold, ou uma mudança contínua de alta resolução (o que não é o caso no exemplo de aplicação aqui)
Volker Siegel

0

Duas soluções simples que garantem o funcionamento de quem ainda procura: -

  1. Aumente o atraso para 50 a 100 milissegundos.

  2. Adicione isso após o Serial.begin(9600)in setup();

    Serial.setTimeout(50);

O segundo passo é o mais importante. Funcionou para mim somente depois que adicionei o código acima. Isso não é mencionado com muita frequência em muitos outros fóruns que procurei quando tive exatamente o mesmo problema.


Isso está um pouco errado. O método setTimeout () aplica-se a entrada, não de saída - veja a documentação no arduino.cc/en/Serial/SetTimeout
Chris Stratton
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.