Giảm độ trễ giữa arduino và bản phác thảo xử lý trên máy tính của tôi


13

Tôi hiện đang ở dự án # 14 của cuốn sách dự án Arduino.

Tôi đang cố gắng kiểm soát một bản phác thảo xử lý trên máy tính xách tay của mình bằng Arduino của tôi. Điều này được thực hiện bằng cách sử dụng một chiết áp để kiểm soát nền của hình ảnh.

Mã Arduino:

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

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

Chế biến:

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

Bây giờ, trong khi mã hoạt động và màu nền thay đổi khi tôi bật chiết áp, có một độ trễ lớn giữa việc xoay chiết áp và nhìn thấy màu thay đổi nền, và các giá trị từ Arduino / chiết áp thay đổi trên màn hình nối tiếp của quá trình xử lý.

Những gì tôi đã thử:

  • Thay đổi tốc độ giao tiếp nối tiếp

Tôi nhận thấy rằng khi tôi giảm tốc độ giao tiếp Nối tiếp, ví dụ khoảng 100, độ trễ giữa việc quay chiết áp và thấy nó thay đổi trên máy tính xách tay của tôi giảm xuống còn khoảng 1 giây. Tuy nhiên, khi tôi giảm tốc độ giao tiếp Nối tiếp hơn nữa, ví dụ giá trị 1, độ trễ sẽ tăng trở lại.

Mặt khác, ở tốc độ tiêu chuẩn 9600, độ trễ là rất lớn, khoảng 5sec ++ trước khi những thay đổi trong chiết áp xuất hiện trên máy tính xách tay / xử lý.

Tại sao việc giảm tốc độ liên lạc (lên đến một điểm nhất định) làm giảm độ trễ thời gian và tăng tốc độ trễ làm tăng độ trễ thời gian? Ngoài ra, có cách nào tôi có thể làm cho nó gần ngay lập tức?


3
Bạn đang xuất bản một lần đọc mỗi lần làm tròn Arduino loop(). Rất có thể chương trình Xử lý của bạn không chạy đủ nhanh để theo kịp chương trình. Hãy thử đặt một độ trễ vào loop()mã Arduino của bạn để làm chậm nó; ví dụ delay(50).
Peter Bloomfield

Xin chào peter, cảm ơn câu trả lời nhanh chóng, thêm một chút chậm trễ thực sự đã giải quyết vấn đề của tôi. Tuy nhiên, chỉ là một câu hỏi nhỏ khác, liệu có cách nào để tôi có thể xác định tốc độ của chương trình xử lý của mình trong tương lai để ngăn điều này xảy ra lần nữa hay không, việc máy tính xách tay / tốc độ xử lý tốt hơn có giải quyết được vấn đề không? ? Ngoài ra, tại sao bước vào một tốc độ truyền là 250 hoặc 300 mess lên đọc từ Arduino (các bài đọc tôi nhận được là xen kẽ giữa việc đọc và không Ví dụ 147,0,147,0)
Kenneth .J

Câu trả lời:


11

Bạn đang đọc kết quả mỗi lần đọc Arduino loop(), nên có vẻ như chương trình Xử lý của bạn không chạy đủ nhanh để theo kịp nó. Hãy thử đặt độ trễ vào loop()mã Arduino của bạn để làm chậm nó, ví dụ:

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

Theo như tôi biết, Xử lý nhằm mục đích chạy ở tốc độ khung hình nhất quán, mà bạn có thể sửa đổi bằng cách sử dụng frameRate()chức năng. Theo mặc định, đó là 60 khung hình mỗi giây, mặc dù nó có thể chạy chậm hơn trên các hệ thống cũ (hoặc nơi bạn đang chạy một chương trình chuyên sâu). Bạn có thể kiểm tra xem nó chạy nhanh như thế nào bằng cách đọc frameRatebiến.

Giới thiệu độ trễ 50 mili giây vào vòng lặp Arduino có nghĩa là nó sẽ được cập nhật một chút dưới 20 lần mỗi giây. Điều đó có nghĩa là nó phải đủ nhanh cho mục đích giao diện người dùng, nhưng cũng phải nằm trong khả năng của chương trình Xử lý của bạn.

Theo như tốc độ truyền (tốc độ truyền thông), việc điều chỉnh nó bằng số lượng tùy ý có thể sẽ có kết quả không thể đoán trước. Đó là bởi vì phần cứng sẽ chỉ hỗ trợ tốc độ cụ thể và cố gắng sử dụng bất cứ thứ gì khác có thể dẫn đến dữ liệu xuất hiện bị cắt xén ở đầu bên kia. Các Serial.begin()tài liệu có một số thông tin thêm về tốc độ baud được hỗ trợ.


14

Như đã chỉ ra, Arduino của bạn đang nói quá nhiều quá nhanh. Thêm delay()sẽ làm chậm nó, nhưng nó vẫn tiếp tục la hét khi xử lý. Lý tưởng nhất là bạn muốn Xử lý yêu cầu giá trị khi thuận tiện và sau đó nhận một câu trả lời từ Arduino của bạn.

Nhập SerialEvent().

Trái ngược với loop()trên Arduino của bạn và draw()trong Chế biến, mọi thứ bên trong serialEvent()chỉ hoạt động khi có một cái gì đó mới trong bộ đệm nối tiếp. Vì vậy, thay vì Xử lý đặt câu hỏi nhanh nhất có thể và Arduino của bạn hét lại nhanh hơn, họ có thể có một cuộc trò chuyện tốt đẹp, lịch sự (không đồng bộ).

Cả Chế biến và Arduino đều có serialEvent. Đây là serialEvent () trên Arduinođây là serialEvent () trong Chế biến. Sử dụng serialEvent trên cả hai mặt, đây là điều sẽ xảy ra:

  1. Xử lý gửi một ký tự đến kết nối nối tiếp. Đây có thể là bất kỳ ký tự nào, nhưng nếu chúng ta xác định trước, chúng ta có thể lọc bất kỳ yêu cầu không mong muốn nào gây ra bởi ví dụ như tín hiệu nhiễu. Trong ví dụ này, hãy gửi Vmỗi lần chúng tôi muốn đọc số mới của bạn. Sau khi nhân vật được gửi, chúng tôi tiếp tục kinh doanh như bình thường. Không chờ đợi một câu trả lời ở đây!

  2. Về phía Arduino, không có gì xảy ra, cho đến khi nó nhận được dữ liệu trong bộ đệm nối tiếp. Nó kiểm tra xem nhân vật đến có phải là một V, và may mắn cho chúng ta, đó là. Arduino đọc giá trị của potmeter một lần, đưa giá trị đó thành nối tiếp một lần và quay trở lại để thư giãn, thư giãn tối đa. Protip: chấm dứt giá trị bằng một ký tự ( *trong trường hợp của chúng tôi). Điều này sẽ giúp bạn trong bước tiếp theo.

  3. Quá trình xử lý đang thực hiện công việc pixel giao diện thường xuyên của nó khi đột nhiên có sự xáo trộn trong việc buộc dữ liệu mới trong bộ đệm nối tiếp. Nó chuyển sang serialEvent()và bắt đầu đọc dữ liệu nối tiếp cho đến khi *gặp phải sự chấm dứt của chúng tôi . Biết chắc chắn đây là ký tự cuối cùng đáng đọc, giờ đây chúng ta có thể lưu trữ giá trị đến trong một biến lưu trữ cách đọc của Arduino.

  4. Đó là nó. Bây giờ xử lý biết giá trị cảm biến mới và tiếp tục với bất cứ điều gì chúng ta bảo nó làm. Trong khi đó, Arduino của bạn đang tận hưởng thời tiết hoặc suy ngẫm về sự tồn tại của nó cho đến khi có dữ liệu nối tiếp đến.


1
Và trong khi bạn đang ở đó, đặt một tụ điện song song với đồng hồ đo của bạn. Điều này giúp loại bỏ những thay đổi nhỏ trong đầu vào của bộ DAC của bạn, có thể ngăn chặn chuyển động lộn xộn trong Xử lý.
Tom

Cảm ơn bạn cho câu trả lời tốt đẹp (và kinda anthropomorphic) này!
Zeta.Điều tra viên

Trên thực tế, đặt câu hỏi qua USB có thể là một ý tưởng. Điều này là do USB có độ trễ cao hơn nhiều so với cổng nối tiếp, do đó, đặt câu hỏi và chờ phản hồi là một hoạt động tốn thời gian hơn so với trước đây, đặc biệt là so với những gì có thể được thực hiện ở tốc độ truyền cao. Để Arduino chạy nhanh hơn một chút là tốt (mặc dù nó không nên bão hòa phần nối tiếp của liên kết); Điều đáng chú ý là bản phác thảo xử lý sẽ rút hết dữ liệu Arduino khi nó có sẵn và giữ giá trị hoàn chỉnh cuối cùng để sử dụng khi cần.
Chris Stratton

7

Vòng bỏ phiếu của bạn chạy ở tốc độ tối đa của bộ xử lý của bạn và ghi vào cổng nối tiếp trong mỗi vòng.

Bằng cách này, bạn đang viết cách thường xuyên hơn tới cổng nối tiếp hơn mức có thể xử lý.

Cổng ghi dữ liệu nhanh như bạn đã cấu hình nó và dữ liệu đệm được chuyển đến từ chương trình của bạn quá nhanh , để ghi nó ra càng sớm càng tốt. Nó là bộ đệm đầy, nó chỉ bỏ dữ liệu mới.

Điều quan trọng ở đây là nó sẽ giữ thứ tự của các giá trị: Đó là bộ đệm FIFO , hoạt động theo thứ tự vào / ra trước.

Điều gì xảy ra là:
Vòng lặp lấp đầy bộ đệm cổng và giữ cho nó đầy đủ 100%.
Nếu bạn xoay chiết áp, giá trị thay đổi sẽ được ghi vào cuối bộ đệm , cổng hoạt động nhanh nhất có thể để ghi ra tất cả các phần tử trong bộ đệm, vẫn còn giá trị cũ.

Và cuối cùng là giá trị mà bạn quan tâm. Giá trị hiện tại nhất mà chúng tôi muốn thấy ngay lập tức là vào cuối của FIFO, và đầu tiên vào / ra trước cũng có nghĩa là vào / ra cuối cùng. Trái ngược với những gì chúng ta muốn.

Tần số tối đa có ý nghĩa để đọc dữ liệu của bạn, là tần số bạn có thể ghi ra, vì vậy bạn nên sử dụng ít nhất một độ trễ đủ dài để ghi ra các byte ở tốc độ cổng hiện tại.


Như một biện pháp độc lập khác để ngăn chặn loại trì hoãn này nói chung,
bạn cũng có thể đặt bộ đệm ghi của cổng ở mức tối thiểu.

Điều đó sẽ khiến dữ liệu bị loại bỏ sớm hơn nhiều, thay vì đệm rất nhiều trước tiên.

Tất nhiên, trong nhiều ứng dụng không phải là thứ bạn cần; Nếu không may mắn, nó có thể hoạt động ngay từ đầu và không ổn định trong một số trường hợp khi thời gian thay đổi dựa trên những thứ như tải bộ xử lý và chỉ có một số mẫu dữ liệu ngẫu nhiên bị loại bỏ. Một bộ đệm lớn thường hoạt động mang tính quyết định hơn nhiều, do đó, mặc định sử dụng một bộ đệm lớn .


Đúng ý tưởng, nhưng không hoàn toàn đúng với tuyên bố "Nó là bộ đệm đầy, nó chỉ làm rơi dữ liệu mới." Khi bộ đệm được lấp đầy, dữ liệu sẽ không bị hủy, thay vào đó là khối ghi cho đến khi có khoảng trống trong bộ đệm gửi đi. Điều này có nghĩa là đầu vào và đầu ra cuối cùng sẽ chảy với tốc độ trung bình như nhau, nhưng có độ trễ giữa các bộ đệm.
Chris Stratton

6

Thay vì liên tục gửi dữ liệu nối tiếp, chỉ gửi dữ liệu khi giá trị của chiết áp đã thay đổi theo một ngưỡng nhất định.

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()là không lấp đầy bộ đệm đầu ra với các mẫu bằng nhau, điều đó tốt. Nhưng nó vẫn chạy ở tốc độ tối đa của bộ xử lý, có thể nhanh gấp 100 lần so với mức cần thiết. Điều đó có nghĩa là nó vẫn có thể nhanh chóng lấp đầy bộ đệm đến giới hạn nếu đầu vào thay đổi thường xuyên, ví dụ như nhiễu ở trên thresholdhoặc thay đổi liên tục ở độ phân giải cao (không phải là trường hợp trong ứng dụng ví dụ ở đây)
Volker Siegel

0

Hai giải pháp đơn giản được đảm bảo hoạt động cho bất cứ ai vẫn đang tìm kiếm: -

  1. Tăng độ trễ lên 50 đến 100 mili giây.

  2. Thêm điều này sau khi Serial.begin(9600)vào setup();

    Serial.setTimeout(50);

Bước hai là quan trọng nhất. Nó chỉ làm việc cho tôi sau khi tôi thêm đoạn mã trên. Điều này không được đề cập rất thường xuyên trong nhiều diễn đàn khác mà tôi đã xem khi tôi gặp vấn đề tương tự.


Điều này có phần sai lầm. Phương thức setTimeout () áp dụng cho đầu vào, không phải đầu ra - xem tài liệu tại arduino.cc/en/Serial/setTimeout
Chris Stratton
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.