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()。処理プログラムがそれに追いつくのに十分な速度で実行されていない可能性があります。loop()Arduinoコードに遅延をかけて、速度を落としてみてください。例えばdelay(50)
ピーターブルームフィールド14年

こんにちはピーター、迅速な回答のおかげで、小さな遅延を追加すると本当に問題が解決しました。ただし、もう1つ小さな質問があります。これが再び発生するのを防ぐために、将来の処理プログラムの速度を決定する方法はありますか、またはより良いラップトップ/処理速度を取得して問題を解決しますか?また、なぜアルドゥイーノから読み取り最大250のまたは300混乱の通信速度を入力しない(iを得る測定値が読み出し及びゼロ例:147,0,147,0間で交互である)?
ケネス.J

回答:


11

Arduinoの周りで毎回読み取り値を出力しているloop()ので、Processingプログラムがそれに追いつくのに十分な速度で実行されていない可能性があります。loop()Arduinoコードに遅延を入れて速度を落としてみてください。例:

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

私の知る限り、Processingは一貫したフレームレートで実行することを目指しており、frameRate()関数を使用して変更できます。デフォルトでは、1秒あたり60フレームですが、古いシステム(または集中的なプログラムを実行している場所)では実行速度が遅くなる場合があります。frameRate変数を読み取ることで、実行速度を確認できます。

Arduinoループに50ミリ秒の遅延を導入すると、毎秒20回弱の更新が行われます。つまり、ユーザーインターフェイスの目的には十分な速度である必要がありますが、処理プログラムの機能の範囲内であることも必要です。

ボーレート(通信速度)に関する限り、任意の量で調整すると、予測できない結果になる可能性があります。これは、ハードウェアが特定の速度のみをサポートし、他の何かを使用しようとすると、反対側でデータが文字化けする可能性があるためです。Serial.begin()ドキュメントは、サポートされているボーレートに関するいくつかのより多くの情報を持っています。


14

既に指摘したように、Arduinoの発言が速すぎます。追加するdelay()と速度が低下しますが、それでも処理を叫び続けます。理想的には、Processingが都合の良いときに値を要求し、Arduinoから1つの答えを受け取るようにします。

を入力しSerialEvent()ます。

loop()Arduinoやdraw()ProcessingではserialEvent()なく、内部のすべては、シリアルバッファに何か新しいものがある場合にのみ実行されます。したがって、できるだけ早く質問を処理してArduinoがさらに速く叫ぶ代わりに、彼らは素敵で丁寧な(非同期の)会話をすることができます。

ProcessingとArduinoの両方にserialEventがあります。これは、Arduinoの上serialEvent()である、これは処理中serialEvent()です。両側でserialEventを使用すると、次のようになります。

  1. 処理は、文字をシリアル接続に送信します。これはどんな文字でも構いませんが、事前に決定しておけば、ノイズの多い信号などに起因する不要な要求を除外できます。この例でVは、ポットメーターの新しい読み取り値が必要になるたびにを送信しましょう。キャラクターが送られた後、私たちはいつものようにビジネスを続けます。ここで答えを待っていません!

  2. Arduino側では、シリアルバッファーでデータを受信するまで何も起こりません。入ってくるキャラクターがであるかどうかをチェックしV、幸運なことです。Arduinoはポットメーターの値を1回読み取り、その値をシリアルに1回出力し、冷却に戻り、すべてのクールを最大限にリラックスさせます。ヒント:値を文字で終了します(*この場合)。これは、次のステップで役立ちます。

  3. シリアルバッファー内の新しいデータの強制に突然障害が発生した場合、処理は通常のインターフェイスピクセルビジネスを実行しています。serialEvent()終端に到達するまで、に切り替わり、シリアルデータの読み取りを開始*します。これが読む価値のある最後の文字であることが確実にわかったので、Arduinoの読み取り値を格納する変数に着信値を格納できるようになりました。

  4. それでおしまい。これで処理は新しいセンサー値を認識し、指示どおりに処理を続行します。その間、Arduinoは天気を楽しんでいるか、シリアルデータが着信するまでその存在を熟考しています。


1
そして、あなたがそこにいる間に、あなたのポットメーターと並列にコンデンサを入れてください。これにより、DACの入力のわずかな変化が滑らかになり、Processingでのジッター動作が防止される可能性があります。
トム14年

この素敵な(そしてちょっと擬人化された)答えをありがとう!
-Zeta.Investigator

実際、USB経由で質問することはアイデアです。これは、USBの方がシリアルポートよりもレイテンシが大きいため、質問をして応答を待つことは、特に高いボーレートで実行できることと比較して、そうでない場合よりも時間がかかる操作であるためです。Arduinoを少し速く実行することは問題ありません(ただし、リンクのシリアル部分を飽和させるべきではありません)。キャッチは、処理スケッチがArduinoデータが利用可能になるとそれを排出し、必要なときに使用する最後の完全な値を保持することです。
クリスストラットン

7

ポーリングループはプロセッサの最高速度で実行され、各ラウンドでシリアルポートに書き込みます。

この方法では、シリアルポートに処理できるよりも頻繁に書き込みます。

ポートは、設定した速度でデータを書き出し、プログラムから入ってくるデータバッファリングして、できるだけ早く書き出すことができません。バッファがいっぱいになり、新しいデータがドロップされるだけです。

ここで重要なのは、値の順序を維持することです。FIFOバッファーで、先入れ先出しの順序で動作します。

起こることは次のとおりです
。ループはポートバッファをいっぱいにし、100%いっぱいに保ちます。
ポテンショメータを回すと、変更された値がバッファの最後に書き込まれ、ポートは可能な限り高速で動作し、バッファ内の古い値を保持しているすべての要素を書き出します。

最後に、興味のある値です。すぐに見たい最新の値は、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、または(ここでの例のアプリケーションでは当てはまらない)、高解像度の連続的な変化
フォルカーシーゲル

0

まだ探している人のために働くことが保証されている2つのシンプルなソリューション:-

  1. 遅延を50〜100ミリ秒に増やします。

  2. これをSerial.begin(9600)inの後に追加しsetup()ます。

    Serial.setTimeout(50);

ステップ2が最も重要です。上記のコードを追加して初めて機能しました。これは、まったく同じ問題が発生したときに調べた他の多くのフォーラムではあまり言及されていません。


これは少し間違っています。setTimeout()メソッドは、入力、出力しないに適用される-のドキュメントを参照arduino.cc/en/Serial/SetTimeout
クリス・ストラットン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.