ARDUINO Связь ардуино и питона. Проблема быстрой обработки данных.

ChKot

✩✩✩✩✩✩✩
28 Сен 2022
2
0
Есть очень быстрый процесс в процессе которого нужно считывать 1 показатель с аналогового пина. Этот показатель и время нужно сохранять.
Было 2 варианта, выплевывать в serial и чтобы потом питон обрабатывал данные. Либо сохранять в массивы и в конце процесса просто выплевывать массивы. Но массивы упираются в лимит памяти
Программа разбивает время (unsined long) и значение (short) на байты и записывает их в массив byte[6]
Каждый цикл происходит serial.write целого массива
Питон считывает serial и проводит обратную сборку
Примерно после 380 мс ардуино заполняет buff какими-то неподходящими значениями. Анализ значений не привел меня ни к чему
Возможно проблема в полном понимании битовых операций, но урок от гавера по ним я прочел

Код Arduino:
#define analogPin A0
#define RelPin 5

short analogValue;
byte buff[6];
unsigned long duration, startTime, Duration_AC, durationLimit, Delay;
String data_array[10];
String command;


void setup() {
  Serial.begin(1000000);

  pinMode(RelPin, OUTPUT);
  digitalWrite(RelPin, LOW);
}

void loop() {
  if (Serial.available() > 0) {
    command = Serial.readString();
    if (command == "s") {
      startTime = micros();
    } else {
      durationLimit = command.substring(command.indexOf("s") + 1).toInt();
      Duration_AC = durationLimit * 0.6;
      Delay = durationLimit * 0.1;
      startTime = micros();
    }
  }
 
  if (startTime != 0) {
    duration = micros() - startTime;
    if (duration < durationLimit) {
    
      if ( (duration >= Delay) && (duration <= (Delay + Duration_AC)) ) {
        digitalWrite(RelPin, HIGH);
      } else {
        digitalWrite(RelPin, LOW);
      }
      
      writeData();
    } else {
      digitalWrite(RelPin, LOW);
      startTime = 0;
    }
  }
}

void writeData() {
  analogValue = analogRead(analogPin);
  buff[0] = ((short)analogValue & 0xFF);
  buff[1] = ((short)analogValue >> 8) & 0xFF;
  buff[2] = ((unsigned long)duration & 0xFF);
  buff[3] = ((unsigned long)duration >> 8) & 0xFF;
  buff[4] = ((unsigned long)duration >> 16) & 0xFF;
  buff[5] = ((unsigned long)duration >> 24) & 0xFF;
  Serial.write(buff, sizeof(buff));
  memset(buff, 0, sizeof(buff));
}
Код для Python:
import matplotlib.pyplot as plt
import serial
import time
import numpy as np

arduino = serial.Serial('COM3', 1000000, timeout=1)
time.sleep(2)
arduino.write('s400000'.encode())  #после s устанавливается длительность процесса в мкс
time.sleep(2)
data = np.array([0, 0])
while True:
    try:
        buff = arduino.read(6)
        print(buff)
        t = buff[2] | (buff[3] << 8) | (buff[4] << 16) | (buff[5] << 24)
        value = buff[0] | (buff[1] << 8)
        print(f'{t} -> {value}')
        row = [int(t), int(value)]
        data = np.vstack([data, row])
    except IndexError:
        break

plt.plot(data[:, 0], data[:, 1], '.-')
plt.grid(linestyle='-', linewidth=1)
plt.show()
1664374915412.png
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
464
134
Вы используете высокие скорости обмена по Serial. Я бы сначала исключил влияние этого фактора уменьшив скорость раз в 10. Если ошибка не пропадет, то разбираться дальше. Если пропадет, разбираться, что нужно сделать чтобы увеличит скорость обмена.
 
  • Лойс +1
Реакции: ChKot

Геннадий П

★★★★★★✩
14 Апр 2021
1,966
632
44
1) Используйте стандартные скорости. 1000000 - нестандартная скорость для COM-порта.
2) Используйте маркеры начала и конца данных. Все что не входит в пакет - отбрасывайте. У вас получается начало 0xFF, а конец 0x00, но судя по последним пакетам вы их не используете, хотя данные судя по всему валидные.

Отметил разным цветом разные пакеты, начало ошибки у вас - это пустой пакет 0xFF 0x00.
1664388548113.png
 
  • Лойс +1
Реакции: ChKot

ChKot

✩✩✩✩✩✩✩
28 Сен 2022
2
0
Спасибо всем. Изменение скорости и вправду помогло. Даже на скорости 115200 данные нормально обрабатываются.

2) Используйте маркеры начала и конца данных. Все что не входит в пакет - отбрасывайте. У вас получается начало 0xFF, а конец 0x00, но судя по последним пакетам вы их не используете, хотя данные судя по всему валидные.
0хFF 0x00 не обязательно так. Из-за того, что дома сейчас проверял данные на анолог пине всегда 1023, в реальном эксперименте они не такие и в первом байте может быть и не 0xFF. А для того чтобы в конце был не 0x00 а хотябы 0x01 время эксперимента должно быть 33 554 432 мкс
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,966
632
44
@ChKot, Используйте маркеры, которых никогда не бывает в данных, это может быть не один байт, а два или больше, например "0xFF 0x00" - начало пакета, а "0x00 0xFF" - конец пакета. Без маркеров (хотя бы начала) у вас по какой либо ошибке могут потеряться некоторые данные и произойдет смещение данных, примерно как показал на картинке.
Если данные меняются плавно, то смотрите максимальную разницу с предыдущим результатом. Например температура не может резко меняться, и если сейчас она была 30 градусов, то в следующем пакете она не может быть нулевой или отрицательной.
Можно посылать CRC пакета, тогда можно точно определять валидность передаваемых данных.
 
  • Лойс +1
Реакции: poty

Bruzzer

★★★✩✩✩✩
23 Май 2020
464
134
Даже на скорости 115200 данные нормально обрабатываются.
Учтите, что если при скорости 1М оцифровка шла с максимальной частотой (при использовании AnalogRead) т.к. данные передавались быстрее, чем поступали новые. Теперь частота оцифровки ограничена скоростью передачи. Поэтому я и писал, что сначала убедиться, что проблема в скорости, а потом - если полученная частота оцифровки устраивает, то хорошо. Если не устраивает, пробовать поднимать скорость передачи.