减少arduino和计算机上的处理草图之间的延迟


13

我目前在Arduino项目书的项目#14中。

我正在尝试使用Arduino控制笔记本电脑上的处理草图。这是通过使用电位器控制图像背景来实现的。

Arduino代码:

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

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

处理方式:

//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);
}

现在,虽然代码可以正常工作,并且在我打开电位器时背景颜色发生了变化,但是在转动电位器和看到背景更改颜色之间存在巨大的滞后,而Arduino /电位器中的值在处理的串行监视器上发生了变化。

我尝试过的

  • 更改串行通讯的速度

我注意到,当我降低串行通讯速度时,例如大约100,在打开电位计与在笔记本电脑上看到电位计变化之间的延迟减少到大约1秒。但是,当我进一步降低串行通信的速度(例如,值为1)时,延迟会再次增加。

另一方面,在9600的标准速度下,延迟是巨大的,大约5秒++,之后电位计的变化才会出现在笔记本电脑/处理器上。

为什么降低通讯速度(达到特定点)会减少时间延迟,而增加通讯时间却会增加时间延迟呢?此外,无论如何,我可以使它接近瞬时吗?


3
您每次在Arduino上输出一次读数loop()。您的Processing程序很有可能运行得不够快,无法跟上它的步伐。尝试loop()在您的Arduino代码中加入延迟以降低速度;例如delay(50)
彼得·布卢姆菲尔德

嗨,彼得,谢谢您的迅速回答,增加一点延迟确实可以解决我的问题。不过,这只是一个小问题,有什么办法可以确定将来我的处理程序的速度,以防止再次发生这种情况,或者获得更好的笔记本电脑/处理速度是否可以解决问题?另外,为什么输入通讯速度为250或300会弄乱arduino的读数?(我得到的读数在读数和零之间交替出现,例如147,0,147,0)
Kenneth .J

Answers:


11

您每次在Arduino周围都会输出一个读数loop(),因此您的Processing程序似乎运行得不够快,无法跟上它的步伐。尝试loop()在您的Arduino代码中加入延迟以使其变慢,例如:

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

据我所知,Processing旨在以一致的帧速率运行,您可以使用该frameRate()功能对其进行修改。默认情况下,它为每秒60帧,尽管在较旧的系统(或运行密集型程序的地方)上运行速度可能较慢。您可以通过读取frameRate变量来检查其运行速度。

在Arduino循环中引入50毫秒的延迟意味着它每秒将更新不到20次。这意味着对于用户界面而言,它应该足够快,但也应在处理程序的能力范围之内。

就波特率(通信速度)而言,随意调整它可能会产生不可预测的结果。这是因为硬件仅支持特定的速度,而尝试使用其他速度可能会导致另一端的数据出现乱码。该Serial.begin()文档提供了有关支持的波特率的更多信息。


14

如前所述,您的Arduino说得太快了。添加delay()会减慢它的速度,但仍会在处理中大吼大叫。理想情况下,您希望Processing在方便时询问该值,然后从Arduino收到一个答案。

输入SerialEvent()

loop()在Arduino和draw()Processing中不同,serialEvent()仅当串行缓冲区中有新内容时,内部所有内容才会执行。因此,与其进行尽可能快的提问,而不是让Arduino更快地大喊大叫,不如说他们可以进行愉快,礼貌的(异步)对话。

处理和Arduino都有一个serialEvent。这是Arduino上的serialEvent()这是Processing中的serialEvent()。双方使用serialEvent,将发生以下情况:

  1. 处理将字符发送到串行连接。这可以是任何字符,但是如果我们预先确定一个字符,则可以过滤掉例如由噪声信号引起的任何不需要的请求。对于此示例,让V我们在每次想要重新读取电位计时发送一次。发送角色后,我们将照常继续我们的业务。不要在这里等待答案!

  2. 在Arduino方面,什么也没有发生,直到它在串行缓冲区中接收到数据为止。它检查传入的字符是否为V,幸运的是,它是。Arduino一次读取电位计的值,一次将该值输出到串行,然后返回冷却,最大程度地放松所有冷却。提示:以字符终止值(*在我们的情况下)。这将对您有所帮助。

  3. 当突然在串行缓冲区中强制输入新数据时,处理过程将进行常规的界面像素处理。它切换到serialEvent(),并开始读取串行数据,直到*遇到终止情况。可以肯定的是,这是值得阅读的最后一个字符,我们现在可以将输入值存储在存储Arduino读数的变量中。

  4. 而已。现在,处理过程知道新的传感器值,并继续执行我们告诉它的所有操作。同时,您的Arduino正在享受天气或考虑天气的存在,直到收到串行数据为止。


1
在使用时,将一个电容器与电位计并联。这样可以消除DAC输入的微小变化,从而可以防止处理中的抖动运动。
2014年

感谢您提供的这个不错的答案(有点拟人化)!
Zeta.Investigator

实际上,通过USB提出问题可以是一个主意。这是因为USB 比串行端口具有更多的延迟,因此,提出问题和等待响应比其他方式要耗费更多的时间,特别是与以高波特率进行的操作相比。让Arduino运行得更快一点是可以的(尽管它不应使链接的串行部分饱和)。不足之处是处理草图应在可用的Arduino数据耗尽时将其耗尽,并保留需要时使用的最后一个完整值。
克里斯·斯特拉顿

7

轮询循环以处理器的全速运行,并在每一轮中写入串行端口。

这样,您写到串行端口的方式比它处理的更多。

端口将以您配置的速度快地写出数据,并且将从程序中传入的数据缓存得太快以至于无法尽快写出。如果缓冲区已满,它将丢弃新数据。

这里重要的是,它将保持值的顺序:它是一个FIFO缓冲区,以先进先出顺序工作。

发生的情况是:
循环将填充端口缓冲区,并使其保持100%充满。
如果转动电位器,则将更改后的值get写入缓冲区的末尾,端口将以最快的速度写入缓冲区中所有仍具有旧值的元素。

最后是您感兴趣的值。我们想立即看到的最新值是在FIFO的末尾,先进先出也意味着后进/后出。与我们想要的相反。

读取数据有意义的最大频率是可以写入的频率,因此,您至少应使用足够长的延迟,以便以当前端口速度写入字节。


作为另一种通常
可以防止此类延迟的独立措施,您可以另外将端口的写缓冲区设置为最小。

这将导致数据删除的时间更早,而不是先进行大量缓冲。

当然,在许多应用程序中,这并不是您所需要的。运气不好的话,它一开始可能仍然可以工作,并且在某些情况下,由于处理器负载等因素导致时序变化时,它会变得不稳定,并且只会丢弃一些随机数据样本。通常,大缓冲区的行为更具确定性,因此默认情况下使用大缓冲区


正确的主意,但声明“缓冲区已满,只会丢弃新数据”,这不太正确。一旦缓冲区被填满,数据就不会被丢弃,而是写入块,直到传出缓冲区中有空间为止。这意味着输入和输出很快就会以相同的平均速率流过,但是在它们之间有一个缓冲区值得等待。
克里斯·斯特拉顿

6

而不是不断发送串行数据,仅当电位计的值变化超过某个阈值时才发送数据。

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
loop()不是用相等的样本填充输出缓冲区,这很好。但是它仍然以处理器的全速运行,这可能是所需速度的100倍。这意味着,如果输入频繁变化(例如,来自上方的噪声threshold或高分辨率的连续变化),它仍然可以迅速将缓冲区填充到极限,这在此处示例应用程序中不是这种情况
Volker Siegel,2014年

0

有两种简单的解决方案可保证仍在为任何仍在寻找的人工作:-

  1. 将延迟增加到50到100毫秒。

  2. Serial.begin(9600)in 之后添加此内容setup()

    Serial.setTimeout(50);

第二步是最重要的。只有添加以上代码后,它才对我有用。当我遇到完全相同的问题时,我在许多其他论坛中都很少提及这一点。


这有些误解。setTimeout()方法适用于输入,而不适用于输出-请参阅arduino.cc/en/Serial/SetTimeout中
克里斯·斯特拉顿
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.