Зменшення відставання між ардуїно та ескізом обробки на моєму комп’ютері


13

Зараз я перебуваю на проекті № 14 книги «Ардуїно».

Я намагаюся керувати ескізом обробки на своєму ноутбуці за допомогою свого 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);
}

Тепер, коли код працює, а колір тла змінюється, коли я повертаю потенціометр, існує величезна відстань між поверненням потенціометра і побаченням зміни фону кольору, і значення ардуїно / потенціометра змінюються на послідовному моніторі обробки.

Що я спробував:

  • Зміна швидкості послідовного зв'язку

Я помітив, що коли я знижую швидкість послідовного зв’язку, наприклад, близько 100, затримка між повертанням потенціометра і переглядом його зміни на ноутбуці зменшується приблизно до 1 секунди. Однак, коли я ще більше знижую швидкість послідовного зв'язку, наприклад, значення 1, затримка знову збільшується.

З іншого боку, при стандартній швидкості 9600 затримка величезна, приблизно 5 сек ++, перш ніж зміни потенціометра з'являться на ноутбуці / обробці.

Чому зменшення швидкості зв'язку (до певного моменту) зменшує часовий затримку, а її збільшення збільшує часовий запіз? Крім того, чи все-таки я можу зробити це майже миттєвим?


3
Ви читаєте читання щоразу навколо Arduino loop(). Цілком можливо, що ваша програма обробки не працює досить швидко, щоб не відставати від неї. Спробуйте ввести затримку в loop()коді Arduino, щоб уповільнити його; напр delay(50).
Пітер Блумфілд

Привіт Пітер, дякую за швидку відповідь, додавши невелику затримку дійсно вирішив мою проблему. Ще одне невелике запитання, чи є якимось чином я можу визначити швидкість моєї програми обробки в майбутньому, щоб не допустити цього повторитись, чи покращення швидкості обробки ноутбука / швидкості вирішення проблеми? Крім того, чому введення швидкості зв'язку в 250 або 300 псує показання з ардуїно? (Показання, які я отримую, чергуються між читанням і нулем Напр. 147,0147,0)
Кеннет. JJ

Відповіді:


11

Ви виводите читання кожного разу навколо Arduino loop(), тому, здається, ймовірно, що ваша програма обробки не працює досить швидко, щоб не відставати від цього. Спробуйте ввести loop()код у своєму Arduino, щоб уповільнити його, наприклад:

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

Наскільки я знаю, Processing має на меті працювати в послідовному кадрі, який ви можете змінювати, використовуючи frameRate()функцію. За замовчуванням це 60 кадрів в секунду, хоча він може працювати повільніше в старих системах (або там, де ви запускаєте інтенсивну програму). Ви можете перевірити, наскільки швидко він працює, прочитавши frameRateзмінну.

Введення 50-мілісекундної затримки в цикл Arduino означає, що воно буде оновлюватися трохи менше 20 разів на секунду. Це означає, що він повинен бути досить швидким для цілей користувальницького інтерфейсу, але також повинен бути добре в межах можливостей вашої програми обробки.

Що стосується швидкості передачі даних (швидкості зв'язку), коригування її довільними сумами, ймовірно, матиме непередбачувані результати. Це тому, що апаратне забезпечення підтримуватиме лише певні швидкості, а спроба використовувати що-небудь інше може призвести до появи даних, зібраних на іншому кінці. У Serial.begin()документації є додаткова інформація про підтримувані швидкості передачі.


14

Як уже зазначалося, ваш Ардуїно занадто швидко говорить. Додавання delay()сповільнить це, але все одно продовжує кричати на Processing. В ідеалі ви хочете, щоб Processing запитував значення, коли це зручно, а потім отримував одну відповідь від свого Arduino.

Введіть SerialEvent().

На відміну loop()від вашого Arduino та draw()в Processing, все все serialEvent()виправдовується лише тоді, коли в послідовному буфері є щось нове. Тож замість обробки задавати питання як можна швидше, і ваш Ардуїно ще швидше кричить назад, вони можуть вести приємну, ввічливу (асинхронну) розмову.

І Processing, і Arduino мають серійний Event. Це serialEvent () на Arduino і це serialEvent () в Processing. Використовуючи serialEvent з обох сторін, ось що буде:

  1. Обробка надсилає персонаж до послідовного з'єднання. Це може бути будь-який символ, але якщо ми заздалегідь визначимо його, ми можемо відфільтрувати будь-які небажані запити, викликані, наприклад, шумовим сигналом. Для цього прикладу давайте Vщоразу, коли ми хочемо нове читання вашого гончаря. Після надсилання персонажа ми продовжуємо свою справу як завжди. Не чекаючи відповіді тут!

  2. З боку Arduino нічого не відбувається, поки він не отримає дані в послідовний буфер. Він перевіряє, чи є вхідний персонаж V, і нам пощастило. Ардуїно один раз зчитує значення потметра, виводить це значення на серійний раз і повертається до охолодження, максимум розслабляючи все круто. Підказка: припиніть значення символом ( *у нашому випадку). Це допоможе вам на наступному кроці.

  3. Обробка займається регулярним інтерфейсом піксельних операцій, коли раптом у послідовному буфері виникають порушення в роботі нових даних. Він перемикається на serialEvent()та починає читати послідовні дані, поки *не зустрінеться наше припинення . Знаючи напевно, що це останній символ, який варто прочитати, ми тепер можемо зберігати вхідне значення у змінній, яка зберігає читання Ардуїно.

  4. Це воно. Тепер обробка знає нове значення датчика і продовжує все, що ми скажемо. Тим часом ваш Ардуїно насолоджується погодою або роздумує про своє існування, поки не з’являться серійні дані.


1
І поки ви на це, поставте конденсатор паралельно вашому готчастому метру. Це згладжує невеликі зміни у вході ЦАП, можливо, запобігаючи тремтіння руху в обробці.
Том

Дякую за цю приємну (і своєрідну антропоморфну) відповідь!
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або постійної зміни високої роздільної здатності (що не стосується прикладу додатку тут)
Volker Siegel

0

Два простих рішення, які гарантовано працюють для тих, хто ще шукає: -

  1. Збільшити затримку до 50 до 100 мілісекунд.

  2. Додайте це після того , як Serial.begin(9600)в setup();

    Serial.setTimeout(50);

Крок другий - найважливіший. Це працювало для мене лише після того, як я додав вищевказаний код. Це не часто згадується на багатьох інших форумах, які я дивився, коли в мене була саме така проблема.


Це дещо помилково. Метод setTimeout () застосовується до вводу, а не до виводу - дивіться документацію на arduino.cc/en/Serial/SetTimeout
Chris Stratton
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.