Сокращение отставания между 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
Вы выводите чтение каждый раз вокруг Arduino loop(). Вполне возможно, что ваша программа обработки не работает достаточно быстро, чтобы идти в ногу с ней. Попробуйте добавить задержку loop()в ваш код Arduino, чтобы замедлить его; например delay(50).
Питер Блумфилд,
Привет, Питер, спасибо за быстрый ответ, добавление небольшой задержки действительно решило мою проблему. Просто еще один маленький вопрос, могу ли я определить скорость моей программы обработки в будущем, чтобы предотвратить повторение этого, или решение проблемы может быть обеспечено лучшей скоростью работы ноутбука / ноутбука? Кроме того, почему ввод скорости связи 250 или 300 портит показания с Arduino? (Чтения, которые я получаю, чередуются между чтением и нулем, например 147,0,147,0)
Кеннет .J

Ответы:

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()замедлит это, но все еще продолжает кричать на Processing. В идеале вы хотите, чтобы Processing запрашивала значение, когда это удобно, а затем получила один ответ от вашего Arduino.

Введите SerialEvent().

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

И Обработка, и Arduino имеют серийный Ивент. Это serialEvent () на Arduino и serialEvent () в обработке. Используя serialEvent с обеих сторон, вот что произойдет:

  1. Обработка отправляет символ в последовательное соединение. Это может быть любой символ, но если мы предопределим его, мы можем отфильтровать любые нежелательные запросы, вызванные, например, шумовым сигналом. Для этого примера давайте отправим Vкаждый раз, когда мы хотим новое чтение вашего потметра. После того, как персонаж отправлен, мы продолжаем нашу работу как обычно. Не дожидаясь ответа здесь!

  2. На стороне Arduino ничего не происходит, пока он не получит данные в последовательный буфер. Он проверяет, является ли входящий символ V, и, к счастью для нас, это так. Arduino считывает значение расходомера один раз, выводит это значение в последовательный раз и возвращается к охлаждению, максимизируя расслабление. Подсказка: завершите значение символом ( *в нашем случае). Это поможет вам в следующем шаге.

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

  4. Вот и все. Обработка теперь знает новое значение датчика и продолжает то, что мы говорим, чтобы сделать. Тем временем ваш Arduino наслаждается погодой или обдумывает свое существование, пока не поступят последовательные данные.

Том
источник
1
И пока вы занимаетесь этим, поместите конденсатор параллельно с вашим потенциометром. Это сглаживает небольшие изменения на входе вашего ЦАП, возможно, предотвращая дрожание при обработке.
Том
Спасибо за этот хороший (и своего рода антропоморфный) ответ!
Zeta.Investigator
На самом деле, задавать вопросы через USB может быть идеей. Это связано с тем, что USB имеет гораздо большую задержку чем у последовательного порта, поэтому задание вопроса и ожидание ответа является более трудоемкой операцией, чем это было бы в противном случае, особенно по сравнению с тем, что можно сделать при высокой скорости передачи данных. Позволить Arduino работать немного быстрее, это нормально (хотя он не должен насыщать последовательную часть ссылки); подвох в том, что эскиз обработки должен истощать данные Arduino, когда они становятся доступными, и удерживать последнее полное значение, которое нужно использовать, когда оно необходимо.
Крис Страттон,
7

Ваш цикл опроса работает на полной скорости вашего процессора и записывает в последовательный порт в каждом раунде.

Таким образом, вы пишете намного чаще в последовательный порт, чем он может обработать.

Порт записывает данные так быстро, как вы его настроили, и буферизует данные, которые поступают из вашей программы слишком быстро , чтобы записать их как можно скорее. Если буфер заполнен, он просто сбрасывает новые данные.

Здесь важно то, что он будет сохранять порядок значений: это буфер FIFO , работающий в порядке «первый вошел / первый вышел».

Что происходит:
цикл заполняет буфер порта и сохраняет его на 100% заполненным.
Если вы включите потенциометр, измененное значение будет записано в конец буфера , порт будет работать как можно быстрее, чтобы выписать все элементы в буфере, которые все еще имеют старое значение.

И, наконец, значение, которое вас интересует. Самое актуальное значение, которое мы хотели увидеть сразу, было в конце FIFO, и первый вход / первый выход также означает последний вход / последний выход. Противоположность того, что мы хотим.

Максимальная частота, на которую имеет смысл читать ваши данные, - это частота, на которую вы можете записать их, поэтому вам следует использовать как минимум задержку, достаточную для записи байтов при текущей скорости порта.


В качестве другой независимой меры для предотвращения такого рода задержки в целом,
вы могли бы дополнительно установить буфер записи порта на минимум.

Это может привести к тому, что данные будут отбрасываться гораздо раньше, вместо того, чтобы сначала выполнять буферизацию.

Конечно, во многих приложениях это не то, что вам нужно; Если повезет, это может сработать в любом случае вначале и стать нестабильным в некоторых ситуациях, когда временные характеристики меняются в зависимости от таких факторов, как загрузка процессора, и только некоторые случайные выборки данных сбрасываются. Большой буфер обычно ведет себя гораздо более детерминированно, поэтому по умолчанию используйте большой буфер .

Volker Siegel
источник
Правильная идея, но не совсем правильная в утверждении «Если буфер заполнен, он просто сбрасывает новые данные». Как только буфер заполнен, данные не удаляются, а записывают блок, пока в исходящем буфере не будет места. Это означает, что входные и выходные данные скоро будут проходить с одинаковой средней скоростью, но между ними существует задержка в буфере.
Крис Страттон
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
Крис Страттон,