ลดความล่าช้าระหว่าง arduino และร่างการประมวลผลบนคอมพิวเตอร์ของฉัน


13

ขณะนี้ฉันกำลังใช้โปรเจค # 14 ของหนังสือโครงการ Arduino

ฉันพยายามควบคุมร่างการประมวลผลบนแล็ปท็อปของฉันโดยใช้ 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
คุณกำลัง outputting อ่านทุกครั้งเดียวรอบ loop()Arduino เป็นไปได้ค่อนข้างที่โปรแกรมการประมวลผลของคุณจะไม่ทำงานเร็วพอที่จะทำตาม ลองทำการหน่วงเวลาloop()ในรหัส Arduino ของคุณเพื่อทำให้ช้าลง delay(50)เช่น
Peter Bloomfield

สวัสดีปีเตอร์ขอขอบคุณสำหรับคำตอบที่แจ้งการเพิ่มความล่าช้าเล็กน้อยแก้ปัญหาของฉันได้จริงๆ อีกคำถามเล็ก ๆ แต่มีวิธีที่ฉันสามารถกำหนดความเร็วของโปรแกรมประมวลผลของฉันในอนาคตเพื่อป้องกันไม่ให้เกิดขึ้นอีกครั้งหรือรับแล็ปท็อปที่ดีกว่า / ความเร็วในการประมวลผลแก้ไขปัญหาหรือไม่ นอกจากนี้ทำไมการป้อนความเร็วในการสื่อสารที่ 250 หรือ 300 ทำให้อ่านจาก arduino ยุ่งเหยิง (การอ่านที่ฉันได้รับเป็นทางเลือกระหว่างการอ่านและศูนย์เช่น 147,0,147,0)
Kenneth.

คำตอบ:


11

คุณกำลังแสดงผลการอ่านทุกครั้งรอบ Arduino loop()ดังนั้นดูเหมือนว่าโปรแกรมประมวลผลของคุณจะไม่ทำงานเร็วพอที่จะทำตาม ลองใส่ความล่าช้าloop()ในโค้ด Arduino ของคุณเพื่อทำให้ช้าลงเช่น:

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

เท่าที่ฉันรู้การประมวลผลมีวัตถุประสงค์เพื่อให้ทำงานในอัตราเฟรมสม่ำเสมอซึ่งคุณสามารถแก้ไขได้โดยใช้frameRate()ฟังก์ชัน ตามค่าเริ่มต้นคือ 60 เฟรมต่อวินาทีแม้ว่ามันอาจจะทำงานช้าลงในระบบเก่า (หรือที่คุณใช้งานโปรแกรมเข้มข้น) คุณสามารถตรวจสอบความเร็วในการทำงานโดยการอ่านframeRateตัวแปร

ขอแนะนำการหน่วงเวลา 50 มิลลิวินาทีในการวนลูป Arduino หมายความว่ามันจะอัปเดตน้อยกว่า 20 เท่าต่อวินาที ซึ่งหมายความว่าควรจะเร็วพอสำหรับจุดประสงค์ในการใช้งานอินเทอร์เฟซผู้ใช้ แต่ควรอยู่ในขีดความสามารถของโปรแกรมประมวลผลของคุณด้วย

เท่าที่อัตราการรับส่งข้อมูล (ความเร็วในการสื่อสาร) นั้นเกี่ยวข้องกับการปรับตามจำนวนที่กำหนดเองมีแนวโน้มที่จะได้ผลลัพธ์ที่คาดการณ์ไม่ได้ นั่นเป็นเพราะฮาร์ดแวร์จะรองรับเฉพาะความเร็วและการพยายามใช้สิ่งอื่นอาจส่งผลให้ข้อมูลปรากฏในส่วนอื่น ๆ Serial.begin()เอกสารมีข้อมูลบางอย่างเพิ่มเติมเกี่ยวกับอัตราการส่งข้อมูลได้รับการสนับสนุน


14

จากที่กล่าวมาแล้ว Arduino ของคุณกำลังพูดเร็วเกินไป การเพิ่มdelay()จะทำให้ช้าลง แต่ก็ยังคงทำให้เกิดการตะโกนในการประมวลผล เป็นการดีที่คุณต้องการการประมวลผลเพื่อขอค่าเมื่อสะดวกแล้วรับหนึ่งคำตอบจาก Arduino ของคุณ

SerialEvent()เข้าสู่

ตรงข้ามloop()กับ Arduino ของคุณและdraw()ในการประมวลผลทุกอย่างภายในserialEvent()เพียงข้อแก้ตัวเมื่อมีสิ่งใหม่ในบัฟเฟอร์อนุกรม ดังนั้นแทนที่จะประมวลผลถามคำถามให้เร็วที่สุดและ Arduino ของคุณตะโกนกลับมาเร็วขึ้นพวกเขาสามารถสนทนาได้ดีสุภาพ (แบบอะซิงโครนัส)

ทั้งการประมวลผลและ Arduino มี serialEvent นี่คือ serialEvent () บน Arduinoและนี่คือ serialEvent () ในการประมวลผล การใช้ serialEvent ทั้งสองด้านนี่คือสิ่งที่จะเกิดขึ้น:

  1. การประมวลผลส่งอักขระไปยังการเชื่อมต่อแบบอนุกรม นี่อาจเป็นตัวละครใดก็ได้ แต่หากเรากำหนดไว้ล่วงหน้าเราสามารถกรองคำขอที่ไม่ต้องการใด ๆ ที่เกิดจากสัญญาณที่มีเสียงดัง สำหรับตัวอย่างนี้เราจะส่งVทุกครั้งที่เราต้องการอ่าน potmeter ของคุณใหม่ หลังจากส่งตัวละครแล้วเราจะดำเนินธุรกิจตามปกติ ไม่ต้องรอคำตอบที่นี่!

  2. ที่ Arduino ไม่มีอะไรเกิดขึ้นจนกระทั่งได้รับข้อมูลในบัฟเฟอร์แบบอนุกรม มันตรวจสอบว่าตัวละครที่เข้ามาเป็นVและโชคดีที่เราเป็น Arduino อ่านค่าของโพเทนมิเตอร์หนึ่งครั้งส่งออกค่านั้นเป็นอนุกรมหนึ่งครั้งและกลับไปที่ความหนาวเหน็บ Protip: ยุติค่าด้วยอักขระ ( *ในกรณีของเรา) สิ่งนี้จะช่วยคุณในขั้นตอนต่อไป

  3. การประมวลผลกำลังทำธุรกิจพิกเซลอินเทอร์เฟซปกติเป็นประจำเมื่อจู่ ๆ ก็มีสิ่งรบกวนในการบังคับใช้ข้อมูลใหม่ในบัฟเฟอร์อนุกรม มันสลับไปที่serialEvent()และเริ่มอ่านข้อมูลอนุกรมจนกว่า*จะพบการยุติของเรา รู้ว่านี่เป็นตัวอักษรตัวสุดท้ายที่เราควรอ่านตอนนี้เราสามารถเก็บค่าขาเข้าในตัวแปรที่เก็บการอ่านของ Arduino

  4. แค่นั้นแหละ. ตอนนี้การประมวลผลจะรู้ค่าเซ็นเซอร์ใหม่และดำเนินการกับสิ่งที่เราบอกให้ทำ ในขณะเดียวกัน Arduino ของคุณก็สามารถเพลิดเพลินไปกับสภาพอากาศหรือพิจารณาการมีอยู่ของมันจนกว่าจะมีข้อมูลอนุกรมขาเข้า


1
และในขณะที่คุณกำลังอยู่ให้ใส่ตัวเก็บประจุขนานกับโพเทนมิเตอร์ของคุณ วิธีนี้จะทำให้การเปลี่ยนแปลงเล็กน้อยในอินพุตของ DAC ของคุณราบรื่นขึ้น
Tom

ขอบคุณสำหรับคำตอบที่ดี (และมนุษยชาติ)
Zeta.Investigator

ที่จริงแล้วการถามคำถามผ่าน USB อาจเป็นแนวคิด เนื่องจาก USB มีความหน่วงแฝงมากกว่าพอร์ตอนุกรมดังนั้นการถามคำถามและการรอการตอบกลับเป็นการดำเนินการที่ใช้เวลานานกว่าที่ควรจะเป็นโดยเฉพาะเมื่อเปรียบเทียบกับสิ่งที่สามารถทำได้ในอัตราบอดสูง การปล่อยให้ Arduino ทำงานได้เร็วขึ้นเล็กน้อยนั้นเป็นเรื่องปกติ (แม้ว่ามันจะไม่ควรทำให้อิ่มตัวในส่วนการเชื่อมต่อ); สิ่งที่จับได้คือร่างการประมวลผลควรระบายข้อมูล Arduino เมื่อมันพร้อมใช้งานและเก็บค่าที่สมบูรณ์ล่าสุดที่จะใช้เมื่อต้องการ
Chris Stratton

7

หน่วยประมวลผลวนซ้ำของคุณทำงานที่ความเร็วสูงสุดของโปรเซสเซอร์และเขียนไปยังพอร์ตอนุกรมในแต่ละรอบ

ด้วยวิธีนี้คุณกำลังเขียนวิธีไปยังพอร์ตอนุกรมบ่อยกว่าที่สามารถจัดการได้

พอร์ตจะเขียนข้อมูลให้เร็วที่สุดเท่าที่คุณกำหนดค่าและบัฟเฟอร์ข้อมูลที่มาจากโปรแกรมของคุณเร็วเกินไปเพื่อเขียนออกมาโดยเร็วที่สุด บัฟเฟอร์เต็มแล้วเพียงแค่หยดข้อมูลใหม่

สิ่งสำคัญที่นี่คือมันจะเก็บลำดับของค่า: มันเป็นบัฟเฟอร์ FIFOซึ่งทำงานในคำสั่ง First In / First Out

เกิดอะไรขึ้นคือ:
ลูปเติมบัฟเฟอร์พอร์ตและทำให้เต็ม 100%
หากคุณหมุนโพเทนชิออมิเตอร์การเปลี่ยนแปลงจะถูกเขียนไปที่ท้ายบัฟเฟอร์พอร์ตจะทำงานเร็วที่สุดเท่าที่จะทำได้เพื่อเขียนองค์ประกอบทั้งหมดในบัฟเฟอร์ที่ยังคงมีค่าเดิมอยู่

และในที่สุดคุณค่าที่คุณสนใจมูลค่าปัจจุบันที่สุดที่เราต้องการเห็นทันทีคือในตอนท้ายของ FIFO และการเข้า / ออกครั้งแรกก็หมายถึงการเข้า / ออกครั้งสุดท้าย ตรงข้ามกับสิ่งที่เราต้องการ

ความถี่สูงสุดที่เหมาะสมในการอ่านข้อมูลของคุณคือความถี่ที่คุณสามารถเขียนออกมาได้ดังนั้นคุณควรใช้ความล่าช้าอย่างน้อยนานพอที่จะเขียนไบต์ที่ความเร็วพอร์ตปัจจุบัน


การวัดอิสระเพื่อป้องกันความล่าช้าชนิดนี้โดยทั่วไป
คุณสามารถตั้งค่าบัฟเฟอร์การเขียนของพอร์ตให้น้อยที่สุดได้

นั่นจะทำให้ข้อมูลลดลงเร็วกว่าเดิมมากแทนที่จะเป็นบัฟเฟอร์มากก่อน

แน่นอนว่าในหลาย ๆ แอปพลิเคชั่นนั้นไม่ใช่สิ่งที่คุณต้องการ ด้วยความโชคร้ายมันสามารถทำงานได้ในตอนเริ่มต้นและไม่เสถียรในบางสถานการณ์เมื่อเวลาเปลี่ยนไปตามสิ่งต่าง ๆ เช่นตัวประมวลผลโหลดและมีเพียงบางตัวอย่างข้อมูลแบบสุ่มที่ลดลง บัฟเฟอร์ขนาดใหญ่โดยทั่วไปมีลักษณะการทำงานอื่น ๆ อีกมากมายที่กำหนดจึงใช้บัฟเฟอร์ขนาดใหญ่โดยค่าเริ่มต้น


ความคิดที่ถูกต้อง แต่ไม่ถูกต้องนักสำหรับคำสั่ง "บัฟเฟอร์เต็มแล้วเพียงแค่หยดข้อมูลใหม่" เมื่อบัฟเฟอร์เต็มแล้วข้อมูลจะไม่ลดลง แต่จะเป็นบล็อกการเขียนจนกว่าจะมีพื้นที่ในบัฟเฟอร์ขาออก ซึ่งหมายความว่าอินพุตและเอาต์พุตในไม่ช้าจะจบลงด้วยอัตราเฉลี่ยเดียวกัน แต่มีความล่าช้าของบัฟเฟอร์ระหว่างค่าเหล่านั้น
Chris Stratton

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

0

สองวิธีง่ายๆที่รับประกันว่าจะทำงานสำหรับทุกคนที่ยังคงมองหา: -

  1. เพิ่มความล่าช้าเป็น 50 ถึง 100 มิลลิวินาที

  2. เพิ่มสิ่งนี้หลังจากSerial.begin(9600)ในsetup();

    Serial.setTimeout(50);

ขั้นตอนที่สองเป็นสิ่งสำคัญที่สุด มันใช้งานได้สำหรับฉันหลังจากที่ฉันเพิ่มรหัสข้างต้นแล้วเท่านั้น นี่ไม่ได้กล่าวถึงบ่อยนักในฟอรัมอื่น ๆ ที่ฉันดูเมื่อฉันมีปัญหาเดียวกัน


ข้อผิดพลาดนี้ค่อนข้าง เมธอด setTimeout () ใช้กับอินพุตไม่ใช่เอาต์พุต - ดูเอกสารประกอบที่arduino.cc/en/Serial/SetTimeout
Chris Stratton
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.